activemerchant 1.125.0 → 1.126.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.
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?