activemerchant 1.55.0 → 1.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e8d9130eaadeedc543af0a5b2cf5b060a2f1399
4
- data.tar.gz: 868d04daf3f0dbd97f7f87adb0d96add9e868a57
3
+ metadata.gz: 8a7fa5a42dbb05e1c335a93e8cefc85781e7cbae
4
+ data.tar.gz: 928d0561eab8bc6a0fcac4245ccc2342d4e8fa6a
5
5
  SHA512:
6
- metadata.gz: c079b666da79f7f8e77f47ca08003f2712a5daefb801b927454234c9c25f7b1b7d311e63b9432f758dcbb1594d111a323a8606e8636d50cd45da33a87cc35434
7
- data.tar.gz: 0c77b4dbbf7f9f8b3bcbb5e1ae1bbc9af213cab749a0690b1337d46772813043dc65aefb2cc816b86c26dd8fe42cf53b0ce11cb93de53587bc2af3b55b6ecd49
6
+ metadata.gz: bf1511639e9a72d13e6be69a301e0f0553cd4b52e65205a0e9f777c951e355124c16440182716a622beb5cd56037d98aea1659e1bdf49541b20d0438d2bf3802
7
+ data.tar.gz: 83dd4208bb147e5dce68f4751ff27e7298f03fffa0ef8c32f0f335fbf8fa79b1c4177d4839b236510fc7d9f66bedf98a4237085fbb2dcc94a9509d57f3004feb
data/CHANGELOG CHANGED
@@ -1,5 +1,26 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.56.0 (December 1, 2015)
4
+ * Add Cardknox gateway [dlehren]
5
+ * Mercury: Add support for card present track 2 [ryanbalsdon]
6
+ * Cardstream: Improve default currency handling [duff]
7
+ * Mercury: Strip start and end sentinels on track 2 [ryanbalsdon]
8
+ * Redsys: Support new SHA256 authentication method [davidsantoso]
9
+ * Cashnet: Allow custcode override [duff]
10
+ * Add Rails 5 support [rafaelfranca]
11
+ * Set required Ruby version for install to 2 or greater [rafaelfranca]
12
+ * JetPay: Pass ud_fields in capture too [duff]
13
+ * Stripe: Correctly detect test mode refunds [aprofeit]
14
+ * Fix variables in remote gateways test template [sdball]
15
+ * Micropayment: Update fieldnames for new API [duff]
16
+ * Fix CreditCard#valid_number? erroring on non-digit characters [PatrickTulskie]
17
+ * Stripe: Correctly detect test mode voids [methodmissing]
18
+ * Garanti: Add test mode URL and update remote test credentials [cbilgili]
19
+ * Cashnet: Allow custcode override on refund [duff]
20
+ * Omise: Add a new optional api_version config [zdk]
21
+ * Elavon: Include IP address in purchase and authorize requests [aprofeit]
22
+ * TransFirst: Add support for ACH and more operations [davidsantoso]
23
+ * FirstData_e4: Fix void for even dollar transactions [duff]
3
24
 
4
25
  == Version 1.55.0 (November 9, 2015)
5
26
  * CyberSource: send customer IP address when provided [fastjames]
@@ -51,6 +72,7 @@
51
72
  * PayBox Direct: Refunds and working test credentials [ivanfer]
52
73
  * Vanco: Handle case of no billing_address [duff]
53
74
  * BluePay: Add support for CUSTOM_ID2 field [ajporterfield]
75
+ * Creditcall: Handle no verification_value [duff]
54
76
 
55
77
  == Version 1.54.0 (October 2, 2015)
56
78
  * Beanstream: Add Network Tokenization support [girasquid]
data/CONTRIBUTORS CHANGED
@@ -562,3 +562,7 @@ Payeezy (October 2015)
562
562
  Clearhaus (October 2015)
563
563
 
564
564
  * Dinesh Yadav (dinesh)
565
+
566
+ Cardknox (November 2015)
567
+
568
+ * (dlehren)
data/README.md CHANGED
@@ -97,6 +97,7 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
97
97
  * [Borgun](https://www.borgun.is/) - IS
98
98
  * [Braintree](http://www.braintreepaymentsolutions.com) - US, CA, AU, AD, AT, BE, BG, CY, CZ, DK, EE, FI, FR, GI, DE, GR, HU, IS, IM, IE, IT, LV, LI, LT, LU, MT, MC, NL, NO, PL, PT, RO, SM, SK, SI, ES, SE, CH, TR, GB
99
99
  * [BridgePay](http://www.bridgepaynetwork.com/) - CA, US
100
+ * [Cardknox](https://www.cardknox.com/) - US, CA, GB
100
101
  * [CardSave](http://www.cardsave.net/) - GB
101
102
  * [CardStream](http://www.cardstream.com/) - GB
102
103
  * [Cashnet](http://www.higherone.com/) - US
@@ -33,7 +33,6 @@ if(!defined?(ActiveSupport::VERSION) || (ActiveSupport::VERSION::STRING < "4.1")
33
33
  require 'active_support/core_ext/class/attribute_accessors'
34
34
  end
35
35
 
36
- require 'active_support/core_ext/class/delegating_attributes'
37
36
  require 'active_support/core_ext/module/attribute_accessors'
38
37
 
39
38
  require 'base64'
@@ -68,6 +68,7 @@ module ActiveMerchant #:nodoc:
68
68
  def valid_number?(number)
69
69
  valid_test_mode_card_number?(number) ||
70
70
  valid_card_number_length?(number) &&
71
+ valid_card_number_characters?(number) &&
71
72
  valid_checksum?(number)
72
73
  end
73
74
 
@@ -138,6 +139,10 @@ module ActiveMerchant #:nodoc:
138
139
  number.to_s.length >= 12
139
140
  end
140
141
 
142
+ def valid_card_number_characters?(number) #:nodoc:
143
+ !number.to_s.match(/\D/)
144
+ end
145
+
141
146
  def valid_test_mode_card_number?(number) #:nodoc:
142
147
  ActiveMerchant::Billing::Base.test? &&
143
148
  %w[1 2 3 success failure error].include?(number.to_s)
@@ -6,8 +6,8 @@ module ActiveMerchant #:nodoc:
6
6
  self.display_name = "BridgePay"
7
7
  self.homepage_url = "http://www.bridgepaynetwork.com/"
8
8
 
9
- self.test_url = "https://gatewaystage.itstgate.com/SmartPayments/transact.asmx/ProcessCreditCard"
10
- self.live_url = "https://gateway.itstgate.com/SmartPayments/transact.asmx/ProcessCreditCard"
9
+ self.test_url = "https://gatewaystage.itstgate.com/SmartPayments/transact.asmx"
10
+ self.live_url = "https://gateway.itstgate.com/SmartPayments/transact.asmx"
11
11
 
12
12
  self.supported_countries = ["CA", "US"]
13
13
  self.default_currency = "USD"
@@ -19,30 +19,30 @@ module ActiveMerchant #:nodoc:
19
19
  super
20
20
  end
21
21
 
22
- def purchase(amount, creditcard, options={})
23
- post = post_required_fields("Sale")
22
+ def purchase(amount, payment_method, options={})
23
+ post = initialize_required_fields("Sale")
24
24
 
25
25
  # Allow the same amount in multiple transactions.
26
26
  post[:ExtData] = "<Force>T</Force>"
27
27
  add_invoice(post, amount, options)
28
- add_creditcard(post, creditcard)
28
+ add_payment_method(post, payment_method)
29
29
  add_customer_data(post, options)
30
30
 
31
31
  commit(post)
32
32
  end
33
33
 
34
- def authorize(amount, creditcard, options={})
35
- post = post_required_fields("Auth")
34
+ def authorize(amount, payment_method, options={})
35
+ post = initialize_required_fields("Auth")
36
36
 
37
37
  add_invoice(post, amount, options)
38
- add_creditcard(post, creditcard)
38
+ add_payment_method(post, payment_method)
39
39
  add_customer_data(post, options)
40
40
 
41
41
  commit(post)
42
42
  end
43
43
 
44
44
  def capture(amount, authorization, options={})
45
- post = post_required_fields("Force")
45
+ post = initialize_required_fields("Force")
46
46
 
47
47
  add_invoice(post, amount, options)
48
48
  add_reference(post, authorization)
@@ -52,7 +52,7 @@ module ActiveMerchant #:nodoc:
52
52
  end
53
53
 
54
54
  def refund(amount, authorization, options={})
55
- post = post_required_fields("Return")
55
+ post = initialize_required_fields("Return")
56
56
 
57
57
  add_invoice(post, amount, options)
58
58
  add_reference(post, authorization)
@@ -61,7 +61,7 @@ module ActiveMerchant #:nodoc:
61
61
  end
62
62
 
63
63
  def void(authorization, options={})
64
- post = post_required_fields("Void")
64
+ post = initialize_required_fields("Void")
65
65
 
66
66
  add_reference(post, authorization)
67
67
 
@@ -75,9 +75,37 @@ module ActiveMerchant #:nodoc:
75
75
  end
76
76
  end
77
77
 
78
+ def supports_scrubbing?
79
+ true
80
+ end
81
+
82
+ def scrub(transcript)
83
+ transcript.
84
+ gsub(%r((&?CardNum=)[^&]*)i, '\1[FILTERED]').
85
+ gsub(%r((&?CVNum=)[^&]*)i, '\1[FILTERED]').
86
+ gsub(%r((&?Password=)[^&]*)i, '\1[FILTERED]').
87
+ gsub(%r((&?TransitNum=)[^&]*)i, '\1[FILTERED]').
88
+ gsub(%r((&?AccountNum=)[^&]*)i, '\1[FILTERED]')
89
+ end
90
+
78
91
  private
79
92
 
80
- def post_required_fields(transaction_type)
93
+ def add_payment_method(post, payment_method)
94
+ if payment_method.respond_to? :brand
95
+ post[:NameOnCard] = payment_method.name if payment_method.name
96
+ post[:ExpDate] = expdate(payment_method)
97
+ post[:CardNum] = payment_method.number
98
+ post[:CVNum] = payment_method.verification_value
99
+ else
100
+ post[:CheckNum] = payment_method.number
101
+ post[:TransitNum] = payment_method.routing_number
102
+ post[:AccountNum] = payment_method.account_number
103
+ post[:NameOnCheck] = payment_method.name
104
+ post[:ExtData] = "<AccountType>#{payment_method.account_type.capitalize}</AccountType>"
105
+ end
106
+ end
107
+
108
+ def initialize_required_fields(transaction_type)
81
109
  post = {}
82
110
  post[:TransType] = transaction_type
83
111
  post[:Amount] = ""
@@ -92,6 +120,12 @@ module ActiveMerchant #:nodoc:
92
120
  post[:CVNum] = ""
93
121
  post[:MagData] = ""
94
122
  post[:ExtData] = ""
123
+ post[:MICR] = ""
124
+ post[:DL] = ""
125
+ post[:SS] = ""
126
+ post[:DOB] = ""
127
+ post[:StateCode] = ""
128
+ post[:CheckType] = ""
95
129
  post
96
130
  end
97
131
 
@@ -107,13 +141,6 @@ module ActiveMerchant #:nodoc:
107
141
  post[:InvNum] = options[:order_id]
108
142
  end
109
143
 
110
- def add_creditcard(post, creditcard)
111
- post[:NameOnCard] = creditcard.name if creditcard.name
112
- post[:ExpDate] = expdate(creditcard)
113
- post[:CardNum] = creditcard.number
114
- post[:CVNum] = creditcard.verification_value
115
- end
116
-
117
144
  def expdate(creditcard)
118
145
  "#{format(creditcard.month, :two_digits)}#{format(creditcard.year, :two_digits)}"
119
146
  end
@@ -137,12 +164,12 @@ module ActiveMerchant #:nodoc:
137
164
  end
138
165
 
139
166
  def commit(parameters)
140
- url = (test? ? test_url : live_url)
167
+ url = url(parameters[:TransitNum] ? 'ProcessCheck' : 'ProcessCreditCard')
141
168
  data = post_data(parameters)
142
169
  raw = parse(ssl_post(url, data))
143
170
 
144
171
  Response.new(
145
- success_from(raw[:respmsg]),
172
+ success_from(raw),
146
173
  message_from(raw),
147
174
  raw,
148
175
  authorization: authorization_from(raw),
@@ -150,17 +177,17 @@ module ActiveMerchant #:nodoc:
150
177
  )
151
178
  end
152
179
 
153
- def success_from(result)
154
- case result
155
- when "Approved"
156
- true
157
- else
158
- false
159
- end
180
+ def url(action)
181
+ base = test? ? test_url : live_url
182
+ "#{base}/#{action}"
183
+ end
184
+
185
+ def success_from(response)
186
+ response[:result] == "0"
160
187
  end
161
188
 
162
189
  def message_from(response)
163
- response[:respmsg]
190
+ response[:respmsg] || response[:message]
164
191
  end
165
192
 
166
193
  def authorization_from(response)
@@ -14,6 +14,7 @@ module ActiveMerchant #:nodoc:
14
14
 
15
15
  CURRENCY_CODES = {
16
16
  "AUD" => '036',
17
+ "BRL" => '986',
17
18
  "CAD" => '124',
18
19
  "CZK" => '203',
19
20
  "DKK" => '208',
@@ -132,7 +133,7 @@ module ActiveMerchant #:nodoc:
132
133
 
133
134
  def add_amount(post, money, options)
134
135
  add_pair(post, :amount, amount(money), :required => true)
135
- add_pair(post, :currencyCode, currency_code(options[:currency] || currency(money)) || currency_code(self.default_currency))
136
+ add_pair(post, :currencyCode, currency_code(options[:currency] || currency(money)))
136
137
  end
137
138
 
138
139
  def add_customer_data(post, options)
@@ -0,0 +1,328 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class CardknoxGateway < Gateway
4
+ self.live_url = 'https://x1.cardknox.com/gateway'
5
+
6
+ self.supported_countries = ['US','CA','GB']
7
+ self.default_currency = 'USD'
8
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
9
+
10
+ self.homepage_url = 'https://www.cardknox.com/'
11
+ self.display_name = 'Cardknox'
12
+
13
+ COMMANDS = {
14
+ credit_card: {
15
+ purchase: 'cc:sale',
16
+ authorization: 'cc:authonly',
17
+ capture: 'cc:capture',
18
+ refund: 'cc:refund',
19
+ void: 'cc:void',
20
+ save: 'cc:save'
21
+ },
22
+ check: {
23
+ purchase: 'check:sale',
24
+ refund: 'check:refund',
25
+ void: 'check:void',
26
+ save: 'check:save'
27
+ }
28
+ }
29
+
30
+ def initialize(options={})
31
+ requires!(options, :api_key)
32
+ super
33
+ end
34
+
35
+ # There are three sources for doing a purchase transation:
36
+ # - credit card
37
+ # - check
38
+ # - cardknox token, which is returned in the the authorization string "ref_num;token;command"
39
+
40
+ def purchase(amount, source, options={})
41
+ post = {}
42
+ add_amount(post, amount, options)
43
+ add_invoice(post, options)
44
+ add_source(post, source)
45
+ add_address(post, source, options)
46
+ add_customer_data(post, options)
47
+ add_custom_fields(post, options)
48
+ commit(:purchase, source_type(source), post)
49
+ end
50
+
51
+ def authorize(amount, source, options={})
52
+ post = {}
53
+ add_amount(post, amount)
54
+ add_invoice(post, options)
55
+ add_source(post, source)
56
+ add_address(post, source, options)
57
+ add_customer_data(post, options)
58
+ add_custom_fields(post, options)
59
+ commit(:authorization, source_type(source), post)
60
+ end
61
+
62
+ def capture(amount, authorization, options = {})
63
+ post = {}
64
+ add_reference(post, authorization)
65
+ add_amount(post, amount)
66
+ commit(:capture, source_type(authorization), post)
67
+ end
68
+
69
+ def refund(amount, authorization, options={})
70
+ post = {}
71
+ add_reference(post, authorization)
72
+ add_amount(post, amount)
73
+ commit(:refund, source_type(authorization), post)
74
+ end
75
+
76
+ def void(authorization, options = {})
77
+ post = {}
78
+ add_reference(post, authorization)
79
+ commit(:void, source_type(authorization), post)
80
+ end
81
+
82
+ def verify(credit_card, options={})
83
+ MultiResponse.run(:use_first_response) do |r|
84
+ r.process { authorize(100, credit_card, options) }
85
+ r.process(:ignore_result) { void(r.authorization, options) }
86
+ end
87
+ end
88
+
89
+ def store(source, options = {})
90
+ post = {}
91
+ add_source(post, source)
92
+ add_address(post, source, options)
93
+ add_invoice(post, options)
94
+ add_customer_data(post, options)
95
+ add_custom_fields(post, options)
96
+ commit(:save, source_type(source), post)
97
+ end
98
+
99
+ def supports_scrubbing?
100
+ true
101
+ end
102
+
103
+ def scrub(transcript)
104
+ transcript.
105
+ gsub(%r((xCardNum=)\d+), '\1[FILTERED]').
106
+ gsub(%r((xCVV=)\d+), '\1[FILTERED]').
107
+ gsub(%r((xAccount=)\d+), '\1[FILTERED]').
108
+ gsub(%r((xRouting=)\d+), '\1[FILTERED]').
109
+ gsub(%r((xKey=)\w+), '\1[FILTERED]')
110
+ end
111
+
112
+ private
113
+
114
+ def split_authorization(authorization)
115
+ authorization.split(";")
116
+ end
117
+
118
+ def add_reference(post, reference)
119
+ reference, _, _ = split_authorization(reference)
120
+ post[:Refnum] = reference
121
+ end
122
+
123
+ def source_type(source)
124
+ if source.respond_to?(:brand)
125
+ :credit_card
126
+ elsif source.respond_to?(:routing_number)
127
+ :check
128
+ elsif source.kind_of?(String)
129
+ source_type_from(source)
130
+ else
131
+ raise ArgumentError, "Unknown source #{source.inspect}"
132
+ end
133
+ end
134
+
135
+ def source_type_from(authorization)
136
+ _, _, source_type = split_authorization(authorization)
137
+ (source_type || "credit_card").to_sym
138
+ end
139
+
140
+ def add_source(post, source)
141
+ if source.respond_to?(:brand)
142
+ add_credit_card(post, source)
143
+ elsif source.respond_to?(:routing_number)
144
+ add_check(post, source)
145
+ elsif source.kind_of?(String)
146
+ add_cardknox_token(post, source)
147
+ else
148
+ raise ArgumentError, "Invalid payment source #{source.inspect}"
149
+ end
150
+ end
151
+
152
+ # Subtotal + Tax + Tip = Amount.
153
+
154
+ def add_amount(post, money, options = {})
155
+ post[:Tip] = amount(options[:tip])
156
+ post[:Amount] = amount(money)
157
+ end
158
+
159
+ def expdate(credit_card)
160
+ year = format(credit_card.year, :two_digits)
161
+ month = format(credit_card.month, :two_digits)
162
+ "#{month}#{year}"
163
+ end
164
+
165
+ def add_customer_data(post, options)
166
+ address = options[:billing_address] || {}
167
+ post[:Street] = address[:address1]
168
+ post[:Zip] = address[:zip]
169
+ post[:PONum] = options[:po_number]
170
+ post[:Fax] = options[:fax]
171
+ post[:Email] = options[:email]
172
+ post[:IP] = options[:ip]
173
+ end
174
+
175
+ def add_address(post, source, options)
176
+ add_address_for_type(:billing, post, source, options[:billing_address]) if options[:billing_address]
177
+ add_address_for_type(:shipping, post, source, options[:shipping_address]) if options[:shipping_address]
178
+ end
179
+
180
+ def add_address_for_type(type, post, source, address)
181
+ prefix = address_key_prefix(type)
182
+ if source.respond_to?(:first_name)
183
+ post[address_key(prefix, 'FirstName')] = source.first_name
184
+ post[address_key(prefix, 'LastName')] = source.last_name
185
+ else
186
+ post[address_key(prefix, 'FirstName')] = address[:first_name]
187
+ post[address_key(prefix, 'LastName')] = address[:last_name]
188
+ end
189
+ post[address_key(prefix, 'MiddleName')] = address[:middle_name]
190
+
191
+ post[address_key(prefix, 'Company')] = address[:company]
192
+ post[address_key(prefix, 'Street')] = address[:address1]
193
+ post[address_key(prefix, 'Street2')] = address[:address2]
194
+ post[address_key(prefix, 'City')] = address[:city]
195
+ post[address_key(prefix, 'State')] = address[:state]
196
+ post[address_key(prefix, 'Zip')] = address[:zip]
197
+ post[address_key(prefix, 'Country')] = address[:country]
198
+ post[address_key(prefix, 'Phone')] = address[:phone]
199
+ post[address_key(prefix, 'Mobile')] = address[:mobile]
200
+ end
201
+
202
+ def address_key_prefix(type)
203
+ case type
204
+ when :shipping then 'Ship'
205
+ when :billing then 'Bill'
206
+ else
207
+ raise ArgumentError, "Unknown address key prefix: #{type}"
208
+ end
209
+ end
210
+
211
+ def address_key(prefix, key)
212
+ "#{prefix}#{key}".to_sym
213
+ end
214
+
215
+ def add_invoice(post, options)
216
+ post[:Invoice] = options[:invoice]
217
+ post[:OrderID] = options[:order_id]
218
+ post[:Comments] = options[:comments]
219
+ post[:Description] = options[:description]
220
+ post[:Tax] = amount(options[:tax])
221
+ end
222
+
223
+ def add_custom_fields(post, options)
224
+ options.keys.grep(/^custom(?:[01]\d|20)$/) do |key|
225
+ post[key.to_s.capitalize] = options[key]
226
+ end
227
+ end
228
+
229
+ def add_credit_card(post, credit_card)
230
+ if credit_card.track_data.present?
231
+ post[:Magstripe] = credit_card.track_data
232
+ post[:Cardpresent] = true
233
+ else
234
+ post[:CardNum] = credit_card.number
235
+ post[:CVV] = credit_card.verification_value
236
+ post[:Exp] = expdate(credit_card)
237
+ post[:Name] = credit_card.name
238
+ post[:CardPresent] = true if credit_card.manual_entry
239
+ end
240
+ end
241
+
242
+ def add_check(post, check)
243
+ post[:Routing] = check.routing_number
244
+ post[:Account] = check.account_number
245
+ post[:Name] = check.name
246
+ post[:CheckNum] = check.number
247
+ end
248
+
249
+ def add_cardknox_token(post, authorization)
250
+ _, token, _ = split_authorization(authorization)
251
+
252
+ post[:Token] = token
253
+ end
254
+
255
+ def parse(body)
256
+ fields = {}
257
+ for line in body.split('&')
258
+ key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
259
+ fields[key] = CGI.unescape(value.to_s)
260
+ end
261
+
262
+ {
263
+ result: fields['xResult'],
264
+ status: fields['xStatus'],
265
+ error: fields['xError'],
266
+ auth_code: fields['xAuthCode'],
267
+ ref_num: fields['xRefNum'],
268
+ current_ref_num: fields['xRefNumCurrent'],
269
+ token: fields['xToken'],
270
+ batch: fields['xBatch'],
271
+ avs_result: fields['xAvsResult'],
272
+ avs_result_code: fields['xAvsResultCode'],
273
+ cvv_result: fields['xCvvResult'],
274
+ cvv_result_code: fields['xCvvResultCode'],
275
+ remaining_balance: fields['xRemainingBalance'],
276
+ amount: fields['xAuthAmount'],
277
+ masked_card_num: fields['xMaskedCardNumber'],
278
+ masked_account_number: fields['MaskedAccountNumber']
279
+ }.delete_if{|k, v| v.nil?}
280
+ end
281
+
282
+
283
+ def commit(action, source_type, parameters)
284
+ response = parse(ssl_post(live_url, post_data(COMMANDS[source_type][action], parameters)))
285
+
286
+ Response.new(
287
+ (response[:status] == 'Approved'),
288
+ message_from(response),
289
+ response,
290
+ authorization: authorization_from(response, source_type),
291
+ avs_result: { code: response[:avs_result_code] },
292
+ cvv_result: response[:cvv_result_code]
293
+ )
294
+ end
295
+
296
+ def message_from(response)
297
+ if response[:status] == "Approved"
298
+ "Success"
299
+ elsif response[:error].blank?
300
+ "Unspecified error"
301
+ else
302
+ response[:error]
303
+ end
304
+ end
305
+
306
+ def authorization_from(response, source_type)
307
+ "#{response[:ref_num]};#{response[:token]};#{source_type}"
308
+ end
309
+
310
+ def post_data(command, parameters = {})
311
+ initial_parameters = {
312
+ Key: @options[:api_key],
313
+ Version: "4.5.4",
314
+ SoftwareName: 'Active Merchant',
315
+ SoftwareVersion: "#{ActiveMerchant::VERSION}",
316
+ Command: command,
317
+ }
318
+
319
+ seed = SecureRandom.hex(32).upcase
320
+ hash = Digest::SHA1.hexdigest("#{initial_parameters[:command]}:#{@options[:pin]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
321
+ initial_parameters[:Hash] = "s/#{seed}/#{hash}/n" unless @options[:pin].blank?
322
+ parameters = initial_parameters.merge(parameters)
323
+
324
+ parameters.reject{|k, v| v.blank?}.collect{ |key, value| "x#{key}=#{CGI.escape(value.to_s)}" }.join("&")
325
+ end
326
+ end
327
+ end
328
+ end