activemerchant 1.125.0 → 1.126.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +75 -0
  3. data/lib/active_merchant/billing/credit_card_methods.rb +12 -0
  4. data/lib/active_merchant/billing/gateway.rb +2 -1
  5. data/lib/active_merchant/billing/gateways/adyen.rb +7 -4
  6. data/lib/active_merchant/billing/gateways/airwallex.rb +341 -0
  7. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
  8. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  9. data/lib/active_merchant/billing/gateways/blue_snap.rb +31 -21
  10. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
  11. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
  12. data/lib/active_merchant/billing/gateways/braintree_blue.rb +87 -15
  13. data/lib/active_merchant/billing/gateways/card_connect.rb +1 -1
  14. data/lib/active_merchant/billing/gateways/checkout_v2.rb +1 -1
  15. data/lib/active_merchant/billing/gateways/credorax.rb +10 -0
  16. data/lib/active_merchant/billing/gateways/cyber_source.rb +13 -33
  17. data/lib/active_merchant/billing/gateways/d_local.rb +49 -0
  18. data/lib/active_merchant/billing/gateways/decidir.rb +17 -1
  19. data/lib/active_merchant/billing/gateways/decidir_plus.rb +185 -14
  20. data/lib/active_merchant/billing/gateways/ebanx.rb +3 -2
  21. data/lib/active_merchant/billing/gateways/global_collect.rb +26 -16
  22. data/lib/active_merchant/billing/gateways/ipg.rb +1 -2
  23. data/lib/active_merchant/billing/gateways/litle.rb +93 -1
  24. data/lib/active_merchant/billing/gateways/moneris.rb +35 -8
  25. data/lib/active_merchant/billing/gateways/nmi.rb +12 -7
  26. data/lib/active_merchant/billing/gateways/orbital.rb +349 -327
  27. data/lib/active_merchant/billing/gateways/payflow.rb +62 -0
  28. data/lib/active_merchant/billing/gateways/paymentez.rb +26 -7
  29. data/lib/active_merchant/billing/gateways/paysafe.rb +15 -15
  30. data/lib/active_merchant/billing/gateways/payu_latam.rb +25 -15
  31. data/lib/active_merchant/billing/gateways/priority.rb +158 -136
  32. data/lib/active_merchant/billing/gateways/rapyd.rb +258 -0
  33. data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -4
  34. data/lib/active_merchant/billing/gateways/simetrik.rb +362 -0
  35. data/lib/active_merchant/billing/gateways/stripe.rb +4 -2
  36. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +93 -48
  37. data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
  38. data/lib/active_merchant/version.rb +1 -1
  39. metadata +6 -2
@@ -50,69 +50,75 @@ module ActiveMerchant #:nodoc:
50
50
 
51
51
  def purchase(amount, credit_card, options = {})
52
52
  params = {}
53
- params['amount'] = localized_amount(amount.to_f, options[:currency])
54
53
  params['authOnly'] = false
54
+ params['isSettleFunds'] = true
55
55
 
56
- add_credit_card(params, credit_card, 'purchase', options)
57
- add_type_merchant_purchase(params, @options[:merchant_id], true, options)
58
- commit('purchase', params: params, jwt: options)
56
+ add_merchant_id(params)
57
+ add_amount(params, amount, options)
58
+ add_auth_purchase_params(params, credit_card, options)
59
+
60
+ commit('purchase', params: params)
59
61
  end
60
62
 
61
63
  def authorize(amount, credit_card, options = {})
62
64
  params = {}
63
- params['amount'] = localized_amount(amount.to_f, options[:currency])
64
65
  params['authOnly'] = true
66
+ params['isSettleFunds'] = false
65
67
 
66
- add_credit_card(params, credit_card, 'purchase', options)
67
- add_type_merchant_purchase(params, @options[:merchant_id], false, options)
68
- commit('purchase', params: params, jwt: options)
68
+ add_merchant_id(params)
69
+ add_amount(params, amount, options)
70
+ add_auth_purchase_params(params, credit_card, options)
71
+
72
+ commit('purchase', params: params)
69
73
  end
70
74
 
71
75
  def refund(amount, authorization, options = {})
72
76
  params = {}
73
- params['merchantId'] = @options[:merchant_id]
74
- params['paymentToken'] = get_hash(authorization)['payment_token'] || options[:payment_token]
77
+ add_merchant_id(params)
78
+ params['paymentToken'] = payment_token(authorization) || options[:payment_token]
79
+
75
80
  # refund amounts must be negative
76
81
  params['amount'] = ('-' + localized_amount(amount.to_f, options[:currency])).to_f
77
- commit('refund', params: params, jwt: options)
82
+
83
+ commit('refund', params: params)
78
84
  end
79
85
 
80
86
  def capture(amount, authorization, options = {})
81
87
  params = {}
82
- params['amount'] = localized_amount(amount.to_f, options[:currency])
83
- params['authCode'] = options[:authCode]
84
- params['merchantId'] = @options[:merchant_id]
85
- params['paymentToken'] = get_hash(authorization)['payment_token']
86
- params['shouldGetCreditCardLevel'] = true
87
- params['source'] = options[:source]
88
- params['tenderType'] = 'Card'
88
+ add_merchant_id(params)
89
+ add_amount(params, amount, options)
90
+ params['paymentToken'] = payment_token(authorization) || options[:payment_token]
91
+ params['tenderType'] = options[:tender_type].present? ? options[:tender_type] : 'Card'
89
92
 
90
- commit('capture', params: params, jwt: options)
93
+ commit('capture', params: params)
91
94
  end
92
95
 
93
96
  def void(authorization, options = {})
94
- commit('void', iid: get_hash(authorization)['id'], jwt: options)
97
+ params = {}
98
+
99
+ commit('void', params: params, iid: payment_id(authorization))
95
100
  end
96
101
 
97
- def verify(credit_card, options)
98
- jwt = options[:jwt_token]
102
+ def verify(credit_card, _options = {})
103
+ jwt = create_jwt.params['jwtToken']
104
+
99
105
  commit('verify', card_number: credit_card.number, jwt: jwt)
100
106
  end
101
107
 
102
- def supports_scrubbing?
103
- true
108
+ def get_payment_status(batch_id)
109
+ commit('get_payment_status', params: batch_id)
104
110
  end
105
111
 
106
- def get_payment_status(batch_id, options)
107
- commit('get_payment_status', params: batch_id, jwt: options)
112
+ def close_batch(batch_id)
113
+ commit('close_batch', params: batch_id)
108
114
  end
109
115
 
110
- def close_batch(batch_id, options)
111
- commit('close_batch', params: batch_id, jwt: options)
116
+ def create_jwt
117
+ commit('create_jwt', params: @options[:merchant_id])
112
118
  end
113
119
 
114
- def create_jwt(options)
115
- commit('create_jwt', params: @options[:merchant_id], jwt: options)
120
+ def supports_scrubbing?
121
+ true
116
122
  end
117
123
 
118
124
  def scrub(transcript)
@@ -122,11 +128,33 @@ module ActiveMerchant #:nodoc:
122
128
  gsub(%r((cvv\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
123
129
  end
124
130
 
131
+ private
132
+
133
+ def add_amount(params, amount, options)
134
+ params['amount'] = localized_amount(amount.to_f, options[:currency])
135
+ end
136
+
137
+ def add_merchant_id(params)
138
+ params['merchantId'] = @options[:merchant_id]
139
+ end
140
+
141
+ def add_auth_purchase_params(params, credit_card, options)
142
+ add_replay_id(params, options)
143
+ add_credit_card(params, credit_card, 'purchase', options)
144
+ add_purchases_data(params, options)
145
+ add_shipping_data(params, options)
146
+ add_pos_data(params, options)
147
+ add_additional_data(params, options)
148
+ end
149
+
150
+ def add_replay_id(params, options)
151
+ params['replayId'] = options[:replay_id] if options[:replay_id]
152
+ end
153
+
125
154
  def add_credit_card(params, credit_card, action, options)
126
155
  return unless credit_card&.is_a?(CreditCard)
127
156
 
128
157
  card_details = {}
129
-
130
158
  card_details['expiryMonth'] = format(credit_card.month, :two_digits).to_s
131
159
  card_details['expiryYear'] = format(credit_card.year, :two_digits).to_s
132
160
  card_details['expiryDate'] = exp_date(credit_card)
@@ -134,23 +162,8 @@ module ActiveMerchant #:nodoc:
134
162
  card_details['last4'] = credit_card.last_digits
135
163
  card_details['cvv'] = credit_card.verification_value
136
164
  card_details['number'] = credit_card.number
137
-
138
- card_details['entryMode'] = options['entryMode'].blank? ? 'Keyed' : options['entryMode']
139
-
140
- case action
141
- when 'purchase'
142
- card_details['avsStreet'] = options[:billing_address][:address1] if options[:billing_address]
143
- card_details['avsZip'] = options[:billing_address][:zip] if options[:billing_address]
144
- when 'refund'
145
- card_details['cardId'] = options[:card_id]
146
- card_details['cardPresent'] = options[:card_present]
147
- card_details['hasContract'] = options[:has_contract]
148
- card_details['isCorp'] = options[:is_corp]
149
- card_details['isDebit'] = options[:is_debit]
150
- card_details['token'] = options[:token]
151
- else
152
- card_details
153
- end
165
+ card_details['avsStreet'] = options[:billing_address][:address1] if options[:billing_address]
166
+ card_details['avsZip'] = options[:billing_address][:zip] if options[:billing_address]
154
167
 
155
168
  params['cardAccount'] = card_details
156
169
  end
@@ -159,29 +172,86 @@ module ActiveMerchant #:nodoc:
159
172
  "#{format(credit_card.month, :two_digits)}/#{format(credit_card.year, :two_digits)}"
160
173
  end
161
174
 
162
- def purchases
163
- [{ taxRate: '0.0000', additionalTaxRate: nil, discountRate: nil }]
175
+ def add_additional_data(params, options)
176
+ params['isAuth'] = options[:is_auth].present? ? options[:is_auth] : 'true'
177
+ params['paymentType'] = options[:payment_type].present? ? options[:payment_type] : 'Sale'
178
+ params['tenderType'] = options[:tender_type].present? ? options[:tender_type] : 'Card'
179
+ params['taxExempt'] = options[:tax_exempt].present? ? options[:tax_exempt] : 'false'
180
+ params['taxAmount'] = options[:tax_amount] if options[:tax_amount]
181
+ params['shouldGetCreditCardLevel'] = options[:should_get_credit_card_level] if options[:should_get_credit_card_level]
182
+ params['source'] = options[:source] if options[:source]
183
+ params['invoice'] = options[:invoice] if options[:invoice]
184
+ end
185
+
186
+ def add_pos_data(params, options)
187
+ pos_data = {}
188
+
189
+ pos_data['cardholderPresence'] = options.dig(:pos_data, :cardholder_presence) || 'Ecom'
190
+ pos_data['deviceAttendance'] = options.dig(:pos_data, :device_attendance) || 'HomePc'
191
+ pos_data['deviceInputCapability'] = options.dig(:pos_data, :device_input_capability) || 'Unknown'
192
+ pos_data['deviceLocation'] = options.dig(:pos_data, :device_location) || 'HomePc'
193
+ pos_data['panCaptureMethod'] = options.dig(:pos_data, :pan_capture_method) || 'Manual'
194
+ pos_data['partialApprovalSupport'] = options.dig(:pos_data, :partial_approval_support) || 'NotSupported'
195
+ pos_data['pinCaptureCapability'] = options.dig(:pos_data, :pin_capture_capability) || 'Incapable'
196
+
197
+ params['posData'] = pos_data
198
+ end
199
+
200
+ def add_purchases_data(params, options)
201
+ return unless options[:purchases]
202
+
203
+ params['purchases'] = []
204
+
205
+ options[:purchases].each do |purchase|
206
+ purchase_object = {}
207
+
208
+ purchase_object['name'] = purchase[:name] if purchase[:name]
209
+ purchase_object['description'] = purchase[:description] if purchase[:description]
210
+ purchase_object['code'] = purchase[:code] if purchase[:code]
211
+ purchase_object['unitOfMeasure'] = purchase[:unit_of_measure] if purchase[:unit_of_measure]
212
+ purchase_object['unitPrice'] = purchase[:unit_price] if purchase[:unit_price]
213
+ purchase_object['quantity'] = purchase[:quantity] if purchase[:quantity]
214
+ purchase_object['taxRate'] = purchase[:tax_rate] if purchase[:tax_rate]
215
+ purchase_object['taxAmount'] = purchase[:tax_amount] if purchase[:tax_amount]
216
+ purchase_object['discountRate'] = purchase[:discount_rate] if purchase[:discount_rate]
217
+ purchase_object['discountAmount'] = purchase[:discount_amount] if purchase[:discount_amount]
218
+ purchase_object['extendedAmount'] = purchase[:extended_amount] if purchase[:extended_amount]
219
+ purchase_object['lineItemId'] = purchase[:line_item_id] if purchase[:line_item_id]
220
+
221
+ params['purchases'].append(purchase_object)
222
+ end
223
+ end
224
+
225
+ def add_shipping_data(params, options)
226
+ params['shipAmount'] = options[:ship_amount] if options[:ship_amount]
227
+
228
+ shipping_country = shipping_country_from(options)
229
+ params['shipToCountry'] = shipping_country if shipping_country
230
+
231
+ shipping_zip = shipping_zip_from(options)
232
+ params['shipToZip'] = shipping_zip if shipping_zip
233
+ end
234
+
235
+ def shipping_country_from(options)
236
+ options[:ship_to_country] || options.dig(:shipping_address, :country) || options.dig(:billing_address, :country)
237
+ end
238
+
239
+ def shipping_zip_from(options)
240
+ options[:ship_to_zip] || options.dig(:shipping_addres, :zip) || options.dig(:billing_address, :zip)
164
241
  end
165
242
 
166
- def add_type_merchant_purchase(params, merchant, is_settle_funds, options)
167
- params['cardPresent'] = false
168
- params['cardPresentType'] = 'CardNotPresent'
169
- params['isAuth'] = true
170
- params['isSettleFunds'] = is_settle_funds
171
- params['isTicket'] = false
243
+ def payment_token(authorization)
244
+ return unless authorization
245
+ return authorization unless authorization.include?('|')
172
246
 
173
- params['merchantId'] = merchant
174
- params['mxAdvantageEnabled'] = false
175
- params['paymentType'] = 'Sale'
247
+ authorization.split('|').last
248
+ end
176
249
 
177
- params['purchases'] = purchases
250
+ def payment_id(authorization)
251
+ return unless authorization
252
+ return authorization unless authorization.include?('|')
178
253
 
179
- params['shouldGetCreditCardLevel'] = true
180
- params['shouldVaultCard'] = true
181
- params['source'] = options[:source]
182
- params['sourceZip'] = options[:billing_address][:zip] if options[:billing_address]
183
- params['taxExempt'] = false
184
- params['tenderType'] = 'Card'
254
+ authorization.split('|').first
185
255
  end
186
256
 
187
257
  def commit(action, params: '', iid: '', card_number: nil, jwt: '')
@@ -200,10 +270,12 @@ module ActiveMerchant #:nodoc:
200
270
  parse(ssl_post(url(action, params), post_data(params), request_headers))
201
271
  end
202
272
  rescue ResponseError => e
203
- parse(e.response.body)
273
+ # currently Priority returns a 404 with no body on certain calls. In those cases we will substitute the response status from response.message
274
+ gateway_response = e.response.body.presence || e.response.message
275
+ parse(gateway_response)
204
276
  end
277
+
205
278
  success = success_from(response, action)
206
- response = { 'code' => '204' } if response == ''
207
279
  Response.new(
208
280
  success,
209
281
  message_from(response),
@@ -214,16 +286,6 @@ module ActiveMerchant #:nodoc:
214
286
  )
215
287
  end
216
288
 
217
- def handle_response(response)
218
- if response.code != '204' && (200...300).cover?(response.code.to_i)
219
- response.body
220
- elsif response.code == '204' || response == ''
221
- response.body = { 'code' => '204' }
222
- else
223
- raise ResponseError.new(response)
224
- end
225
- end
226
-
227
289
  def url(action, params, ref_number: '', credit_card_number: nil)
228
290
  case action
229
291
  when 'void'
@@ -255,10 +317,22 @@ module ActiveMerchant #:nodoc:
255
317
  test? ? self.test_url_batch : self.live_url_batch
256
318
  end
257
319
 
320
+ def handle_response(response)
321
+ case response.code.to_i
322
+ when 204
323
+ { status: 'Success' }.to_json
324
+ when 200...300
325
+ response.body
326
+ else
327
+ raise ResponseError.new(response)
328
+ end
329
+ end
330
+
258
331
  def parse(body)
259
- return body if body['code'] == '204'
332
+ return {} if body.blank?
260
333
 
261
- JSON.parse(body)
334
+ parsed_response = JSON.parse(body)
335
+ parsed_response.is_a?(String) ? { 'message' => parsed_response } : parsed_response
262
336
  rescue JSON::ParserError
263
337
  message = 'Invalid JSON response received from Priority Gateway. Please contact Priority Gateway if you continue to receive this message.'
264
338
  message += " (The raw response returned by the API was #{body.inspect})"
@@ -268,10 +342,9 @@ module ActiveMerchant #:nodoc:
268
342
  end
269
343
 
270
344
  def success_from(response, action)
271
- success = response['status'] == 'Approved' || response['status'] == 'Open' if response['status']
272
- success = response['code'] == '204' if action == 'void'
273
- success = !response['bank'].empty? if action == 'verify' && response['bank']
274
- success
345
+ return !response['bank'].empty? if action == 'verify' && response['bank']
346
+
347
+ %w[Approved Open Success Settled Voided].include?(response['status'])
275
348
  end
276
349
 
277
350
  def message_from(response)
@@ -281,10 +354,7 @@ module ActiveMerchant #:nodoc:
281
354
  end
282
355
 
283
356
  def authorization_from(response)
284
- {
285
- 'payment_token' => response['paymentToken'],
286
- 'id' => response['id']
287
- }
357
+ [response['id'], response['paymentToken']].join('|')
288
358
  end
289
359
 
290
360
  def error_from(response)
@@ -294,54 +364,6 @@ module ActiveMerchant #:nodoc:
294
364
  def post_data(params)
295
365
  params.to_json
296
366
  end
297
-
298
- def add_pos_data(options)
299
- pos_options = {}
300
- pos_options['panCaptureMethod'] = options[:pan_capture_method]
301
-
302
- pos_options
303
- end
304
-
305
- def add_purchases_data(options)
306
- purchases = {}
307
-
308
- purchases['dateCreated'] = options[:date_created]
309
- purchases['iId'] = options[:i_id]
310
- purchases['transactionIId'] = options[:transaction_i_id]
311
- purchases['transactionId'] = options[:transaction_id]
312
- purchases['name'] = options[:name]
313
- purchases['description'] = options[:description]
314
- purchases['code'] = options[:code]
315
- purchases['unitOfMeasure'] = options[:unit_of_measure]
316
- purchases['unitPrice'] = options[:unit_price]
317
- purchases['quantity'] = options[:quantity]
318
- purchases['taxRate'] = options[:tax_rate]
319
- purchases['taxAmount'] = options[:tax_amount]
320
- purchases['discountRate'] = options[:discount_rate]
321
- purchases['discountAmount'] = options[:discount_amt]
322
- purchases['extendedAmount'] = options[:extended_amt]
323
- purchases['lineItemId'] = options[:line_item_id]
324
-
325
- purchase_arr = []
326
- purchase_arr[0] = purchases
327
- purchase_arr
328
- end
329
-
330
- def add_risk_data(options)
331
- risk = {}
332
- risk['cvvResponseCode'] = options[:cvv_response_code]
333
- risk['cvvResponse'] = options[:cvv_response]
334
- risk['cvvMatch'] = options[:cvv_match]
335
- risk['avsResponse'] = options[:avs_response]
336
- risk['avsAddressMatch'] = options[:avs_address_match]
337
- risk['avsZipMatch'] = options[:avs_zip_match]
338
-
339
- risk
340
- end
341
-
342
- def get_hash(string)
343
- JSON.parse(string.gsub('=>', ':'))
344
- end
345
367
  end
346
368
  end
347
369
  end
@@ -0,0 +1,258 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class RapydGateway < Gateway
4
+ self.test_url = 'https://sandboxapi.rapyd.net/v1/'
5
+ self.live_url = 'https://api.rapyd.net/v1/'
6
+
7
+ self.supported_countries = %w(US BR CA CL CO DO SV MX PE PT VI AU HK IN ID JP MY NZ PH SG KR TW TH VN AD AT BE BA BG HR CY CZ DK EE FI FR GE DE GI GR GL HU IS IE IL IT LV LI LT LU MK MT MD MC ME NL GB NO PL RO RU SM SK SI ZA ES SE CH TR VA)
8
+ self.default_currency = 'USD'
9
+ self.supported_cardtypes = %i[visa master american_express discover]
10
+
11
+ self.homepage_url = 'https://www.rapyd.net/'
12
+ self.display_name = 'Rapyd Gateway'
13
+
14
+ STANDARD_ERROR_CODE_MAPPING = {}
15
+
16
+ def initialize(options = {})
17
+ requires!(options, :secret_key, :access_key)
18
+ super
19
+ end
20
+
21
+ def purchase(money, payment, options = {})
22
+ post = {}
23
+ add_invoice(post, money, options)
24
+ add_payment(post, payment, options)
25
+ add_address(post, payment, options)
26
+ add_metadata(post, options)
27
+ add_ewallet(post, options)
28
+ post[:capture] = true if payment_is_card?(options)
29
+
30
+ if payment_is_ach?(options)
31
+ MultiResponse.run do |r|
32
+ r.process { commit(:post, 'payments', post) }
33
+ post = {}
34
+ post[:token] = r.authorization
35
+ post[:param2] = r.params.dig('data', 'original_amount').to_s
36
+ r.process { commit(:post, 'payments/completePayment', post) }
37
+ end
38
+ else
39
+ commit(:post, 'payments', post)
40
+ end
41
+ end
42
+
43
+ def authorize(money, payment, options = {})
44
+ post = {}
45
+ add_invoice(post, money, options)
46
+ add_payment(post, payment, options)
47
+ add_address(post, payment, options)
48
+ add_metadata(post, options)
49
+ add_ewallet(post, options)
50
+ post[:capture] = false
51
+ commit(:post, 'payments', post)
52
+ end
53
+
54
+ def capture(money, authorization, options = {})
55
+ post = {}
56
+ commit(:post, "payments/#{authorization}/capture", post)
57
+ end
58
+
59
+ def refund(money, authorization, options = {})
60
+ post = {}
61
+ post[:payment] = authorization
62
+ add_invoice(post, money, options)
63
+ add_metadata(post, options)
64
+ commit(:post, 'refunds', post)
65
+ end
66
+
67
+ def void(authorization, options = {})
68
+ post = {}
69
+ commit(:delete, "payments/#{authorization}", post)
70
+ end
71
+
72
+ # Gateway returns an error if trying to run a $0 auth as invalid payment amount
73
+ # Gateway does not support void on a card transaction and refunds can only be done on completed transactions
74
+ # (such as a purchase). Authorize transactions are considered 'active' and not 'complete' until they are captured.
75
+ def verify(credit_card, options = {})
76
+ MultiResponse.run do |r|
77
+ r.process { purchase(100, credit_card, options) }
78
+ r.process { refund(100, r.authorization, options) }
79
+ end
80
+ end
81
+
82
+ def supports_scrubbing?
83
+ true
84
+ end
85
+
86
+ def scrub(transcript)
87
+ transcript.
88
+ gsub(%r((Access_key: )\w+), '\1[FILTERED]').
89
+ gsub(%r(("number\\?":\\?")\d+), '\1[FILTERED]').
90
+ gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]')
91
+ end
92
+
93
+ private
94
+
95
+ def payment_is_ach?(options)
96
+ return unless options[:pm_type]
97
+
98
+ return true if options[:pm_type].include?('_bank')
99
+ end
100
+
101
+ def payment_is_card?(options)
102
+ return unless options[:pm_type]
103
+
104
+ return true if options[:pm_type].include?('_card')
105
+ end
106
+
107
+ def add_address(post, creditcard, options)
108
+ return unless address = options[:address]
109
+
110
+ post[:address] = {}
111
+ # name and line_1 are required at the gateway
112
+ post[:address][:name] = address[:name] if address[:name]
113
+ post[:address][:line_1] = address[:address1] if address[:address1]
114
+ post[:address][:line_2] = address[:address2] if address[:address2]
115
+ post[:address][:city] = address[:city] if address[:city]
116
+ post[:address][:state] = address[:state] if address[:state]
117
+ post[:address][:country] = address[:country] if address[:country]
118
+ post[:address][:zip] = address[:zip] if address[:zip]
119
+ post[:address][:phone_number] = address[:phone] if address[:phone]
120
+ end
121
+
122
+ def add_invoice(post, money, options)
123
+ post[:amount] = amount(money).to_f.to_s
124
+ post[:currency] = (options[:currency] || currency(money))
125
+ end
126
+
127
+ def add_payment(post, payment, options)
128
+ if payment_is_card?(options)
129
+ add_creditcard(post, payment, options)
130
+ elsif payment_is_ach?(options)
131
+ add_ach(post, payment, options)
132
+ end
133
+ end
134
+
135
+ def add_creditcard(post, payment, options)
136
+ post[:payment_method] = {}
137
+ post[:payment_method][:fields] = {}
138
+ pm_fields = post[:payment_method][:fields]
139
+
140
+ post[:payment_method][:type] = options[:pm_type]
141
+ pm_fields[:number] = payment.number
142
+ pm_fields[:expiration_month] = payment.month.to_s
143
+ pm_fields[:expiration_year] = payment.year.to_s
144
+ pm_fields[:cvv] = payment.verification_value.to_s
145
+ pm_fields[:name] = "#{payment.first_name} #{payment.last_name}"
146
+ end
147
+
148
+ def add_ach(post, payment, options)
149
+ post[:payment_method] = {}
150
+ post[:payment_method][:fields] = {}
151
+
152
+ post[:payment_method][:type] = options[:pm_type]
153
+ post[:payment_method][:fields][:proof_of_authorization] = options[:proof_of_authorization]
154
+ post[:payment_method][:fields][:first_name] = payment.first_name if payment.first_name
155
+ post[:payment_method][:fields][:last_name] = payment.last_name if payment.last_name
156
+ post[:payment_method][:fields][:routing_number] = payment.routing_number
157
+ post[:payment_method][:fields][:account_number] = payment.account_number
158
+ post[:payment_method][:fields][:payment_purpose] = options[:payment_purpose] if options[:payment_purpose]
159
+ end
160
+
161
+ def add_metadata(post, options)
162
+ post[:metadata] = options[:metadata] if options[:metadata]
163
+ end
164
+
165
+ def add_ewallet(post, options)
166
+ post[:ewallet_id] = options[:ewallet_id] if options[:ewallet_id]
167
+ end
168
+
169
+ def parse(body)
170
+ return {} if body.empty? || body.nil?
171
+
172
+ JSON.parse(body)
173
+ end
174
+
175
+ def commit(method, action, parameters)
176
+ url = (test? ? test_url : live_url) + action.to_s
177
+ rel_path = "#{method}/v1/#{action}"
178
+ response = api_request(method, url, rel_path, parameters)
179
+
180
+ Response.new(
181
+ success_from(response),
182
+ message_from(response),
183
+ response,
184
+ authorization: authorization_from(response),
185
+ avs_result: avs_result(response),
186
+ cvv_result: cvv_result(response),
187
+ test: test?,
188
+ error_code: error_code_from(response)
189
+ )
190
+ end
191
+
192
+ def api_request(method, url, rel_path, params)
193
+ params == {} ? body = '' : body = params.to_json
194
+ parse(ssl_request(method, url, body, headers(rel_path, body)))
195
+ end
196
+
197
+ def headers(rel_path, payload)
198
+ salt = SecureRandom.base64(12)
199
+ timestamp = Time.new.to_i.to_s
200
+ {
201
+ 'Content-Type' => 'application/json',
202
+ 'access_key' => @options[:access_key],
203
+ 'salt' => salt,
204
+ 'timestamp' => timestamp,
205
+ 'signature' => generate_hmac(rel_path, salt, timestamp, payload)
206
+ }
207
+ end
208
+
209
+ def generate_hmac(rel_path, salt, timestamp, payload)
210
+ signature = "#{rel_path}#{salt}#{timestamp}#{@options[:access_key]}#{@options[:secret_key]}#{payload}"
211
+ hash = Base64.urlsafe_encode64(OpenSSL::HMAC.hexdigest('sha256', @options[:secret_key], signature))
212
+ hash
213
+ end
214
+
215
+ def avs_result(response)
216
+ return nil unless (code = response.dig('data', 'payment_method_data', 'acs_check'))
217
+
218
+ AVSResult.new(code: code)
219
+ end
220
+
221
+ def cvv_result(response)
222
+ return nil unless (code = response.dig('data', 'payment_method_data', 'cvv_check'))
223
+
224
+ CVVResult.new(code)
225
+ end
226
+
227
+ def success_from(response)
228
+ response.dig('status', 'status') == 'SUCCESS' && response.dig('status', 'error') != 'ERR'
229
+ end
230
+
231
+ def message_from(response)
232
+ case response.dig('status', 'status')
233
+ when 'ERROR'
234
+ response.dig('status', 'message') == '' ? response.dig('status', 'error_code') : response.dig('status', 'message')
235
+ else
236
+ response.dig('status', 'status')
237
+ end
238
+ end
239
+
240
+ def authorization_from(response)
241
+ response.dig('data') ? response.dig('data', 'id') : response.dig('status', 'operation_id')
242
+ end
243
+
244
+ def error_code_from(response)
245
+ response.dig('status', 'error_code') unless success_from(response)
246
+ end
247
+
248
+ def handle_response(response)
249
+ case response.code.to_i
250
+ when 200...300, 400, 401, 404
251
+ response.body
252
+ else
253
+ raise ResponseError.new(response)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -107,10 +107,7 @@ module ActiveMerchant #:nodoc:
107
107
  end
108
108
 
109
109
  def verify(credit_card, options = {})
110
- MultiResponse.run(:use_first_response) do |r|
111
- r.process { authorize(100, credit_card, options) }
112
- r.process(:ignore_result) { void(r.authorization, options) }
113
- end
110
+ authorize(0, credit_card, options)
114
111
  end
115
112
 
116
113
  def supports_scrubbing?