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
@@ -83,6 +83,7 @@ module ActiveMerchant #:nodoc:
83
83
  options[:name] = credit_card.name if options[:name].blank? && credit_card
84
84
  request = build_recurring_request(options[:profile_id] ? :modify : :add, money, options) do |xml|
85
85
  add_credit_card(xml, credit_card, options) if credit_card
86
+ add_stored_credential(xml, options[:stored_credential])
86
87
  end
87
88
  commit(request, options.merge(request_type: :recurring))
88
89
  end
@@ -158,6 +159,7 @@ module ActiveMerchant #:nodoc:
158
159
  xml.tag! 'ExtData', 'Name' => 'ORIGID', 'Value' => reference
159
160
  end
160
161
  end
162
+ add_stored_credential(xml, options[:stored_credential])
161
163
  end
162
164
  xml.tag! 'ExtData', 'Name' => 'BUTTONSOURCE', 'Value' => application_id unless application_id.blank?
163
165
  end
@@ -190,15 +192,43 @@ module ActiveMerchant #:nodoc:
190
192
  xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
191
193
  end
192
194
 
195
+ if %i(authorization purchase).include? action
196
+ add_mpi_3ds(xml, options[:three_d_secure]) if options[:three_d_secure]
197
+ end
198
+
193
199
  xml.tag! 'Tender' do
194
200
  add_credit_card(xml, credit_card, options)
195
201
  end
202
+ add_stored_credential(xml, options[:stored_credential])
196
203
  end
197
204
  xml.tag! 'ExtData', 'Name' => 'BUTTONSOURCE', 'Value' => application_id unless application_id.blank?
198
205
  end
199
206
  add_level_two_three_fields(xml.target!, options)
200
207
  end
201
208
 
209
+ def add_mpi_3ds(xml, three_d_secure_options)
210
+ # structure as per https://developer.paypal.com/api/nvp-soap/payflow/3d-secure-mpi/
211
+ authentication_id = three_d_secure_options[:authentication_id]
212
+ authentication_status = three_d_secure_options[:authentication_response_status]
213
+
214
+ eci = three_d_secure_options[:eci]
215
+ cavv = three_d_secure_options[:cavv]
216
+ xid = three_d_secure_options[:xid]
217
+ version = three_d_secure_options[:version]
218
+
219
+ # 3DS2 only
220
+ ds_transaction_id = three_d_secure_options[:ds_transaction_id] if version_2_or_newer?(three_d_secure_options)
221
+
222
+ xml.tag!('ExtData', 'Name' => 'AUTHENTICATION_ID', 'Value' => authentication_id) unless authentication_id.blank?
223
+ xml.tag!('ExtData', 'Name' => 'AUTHENTICATION_STATUS', 'Value' => authentication_status) unless authentication_status.blank?
224
+
225
+ xml.tag!('ExtData', 'Name' => 'CAVV', 'Value' => cavv) unless cavv.blank?
226
+ xml.tag!('ExtData', 'Name' => 'ECI', 'Value' => eci) unless eci.blank?
227
+ xml.tag!('ExtData', 'Name' => 'XID', 'Value' => xid) unless xid.blank?
228
+ xml.tag!('ExtData', 'Name' => 'THREEDSVERSION', 'Value' => version) unless version.blank?
229
+ xml.tag!('ExtData', 'Name' => 'DSTRANSACTIONID', 'Value' => ds_transaction_id) unless ds_transaction_id.blank?
230
+ end
231
+
202
232
  def add_level_two_three_fields(xml_string, options)
203
233
  if options[:level_two_fields] || options[:level_three_fields]
204
234
  xml_doc = Nokogiri::XML.parse(xml_string)
@@ -258,6 +288,7 @@ module ActiveMerchant #:nodoc:
258
288
  xml.tag! 'ABA', check.routing_number
259
289
  end
260
290
  end
291
+ add_stored_credential(xml, options[:stored_credential])
261
292
  end
262
293
  xml.tag! 'ExtData', 'Name' => 'BUTTONSOURCE', 'Value' => application_id unless application_id.blank?
263
294
  end
@@ -278,6 +309,37 @@ module ActiveMerchant #:nodoc:
278
309
  end
279
310
  end
280
311
 
312
+ def add_stored_credential(xml, stored_credential)
313
+ return unless stored_credential
314
+
315
+ xml.tag! 'CardOnFile', add_card_on_file_type(stored_credential)
316
+ xml.tag! 'TxnId', stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
317
+ end
318
+
319
+ def card_on_file_initiator(initator)
320
+ case initator
321
+ when 'merchant'
322
+ 'MIT'
323
+ when 'cardholder'
324
+ 'CIT'
325
+ end
326
+ end
327
+
328
+ def card_on_file_reason(stored_credential)
329
+ return 'I' if stored_credential[:initial_transaction] && stored_credential[:reason_type] == 'unscheduled'
330
+
331
+ case stored_credential[:reason_type]
332
+ when 'recurring', 'installment'
333
+ 'R'
334
+ when 'unscheduled'
335
+ 'U'
336
+ end
337
+ end
338
+
339
+ def add_card_on_file_type(stored_credential)
340
+ card_on_file_initiator(stored_credential[:initiator]).to_s + card_on_file_reason(stored_credential).to_s
341
+ end
342
+
281
343
  def add_three_d_secure(options, xml)
282
344
  if options[:three_d_secure]
283
345
  three_d_secure = options[:three_d_secure]
@@ -34,6 +34,8 @@ module ActiveMerchant #:nodoc:
34
34
  28 => :card_declined
35
35
  }.freeze
36
36
 
37
+ SUCCESS_STATUS = ['success', 'pending', 1, 0]
38
+
37
39
  CARD_MAPPING = {
38
40
  'visa' => 'vi',
39
41
  'master' => 'mc',
@@ -67,6 +69,8 @@ module ActiveMerchant #:nodoc:
67
69
  end
68
70
 
69
71
  def authorize(money, payment, options = {})
72
+ return purchase(money, payment, options) if options[:otp_flow]
73
+
70
74
  post = {}
71
75
 
72
76
  add_invoice(post, money, options)
@@ -77,13 +81,21 @@ module ActiveMerchant #:nodoc:
77
81
  commit_transaction('authorize', post)
78
82
  end
79
83
 
80
- def capture(money, authorization, _options = {})
84
+ def capture(money, authorization, options = {})
81
85
  post = {
82
86
  transaction: { id: authorization }
83
87
  }
84
- post[:order] = { amount: amount(money).to_f } if money
88
+ verify_flow = options[:type] && options[:value]
89
+
90
+ if verify_flow
91
+ add_customer_data(post, options)
92
+ add_verify_value(post, options)
93
+ elsif money
94
+ post[:order] = { amount: amount(money).to_f }
95
+ end
85
96
 
86
- commit_transaction('capture', post)
97
+ action = verify_flow ? 'verify' : 'capture'
98
+ commit_transaction(action, post)
87
99
  end
88
100
 
89
101
  def refund(money, authorization, options = {})
@@ -139,10 +151,10 @@ module ActiveMerchant #:nodoc:
139
151
  private
140
152
 
141
153
  def add_customer_data(post, options)
142
- requires!(options, :user_id, :email)
154
+ requires!(options, :user_id)
143
155
  post[:user] ||= {}
144
156
  post[:user][:id] = options[:user_id]
145
- post[:user][:email] = options[:email]
157
+ post[:user][:email] = options[:email] if options[:email]
146
158
  post[:user][:ip_address] = options[:ip] if options[:ip]
147
159
  post[:user][:fiscal_number] = options[:fiscal_number] if options[:fiscal_number]
148
160
  if phone = options[:phone] || options.dig(:billing_address, :phone)
@@ -179,6 +191,11 @@ module ActiveMerchant #:nodoc:
179
191
  end
180
192
  end
181
193
 
194
+ def add_verify_value(post, options)
195
+ post[:type] = options[:type] if options[:type]
196
+ post[:value] = options[:value] if options[:value]
197
+ end
198
+
182
199
  def add_extra_params(post, options)
183
200
  extra_params = {}
184
201
  extra_params.merge!(options[:extra_params]) if options[:extra_params]
@@ -262,7 +279,9 @@ module ActiveMerchant #:nodoc:
262
279
  end
263
280
 
264
281
  def success_from(response)
265
- !response.include?('error') && (response['status'] || response['transaction']['status']) == 'success'
282
+ return false if response.include?('error')
283
+
284
+ SUCCESS_STATUS.include?(response['status'] || response['transaction']['status'])
266
285
  end
267
286
 
268
287
  def card_success_from(response)
@@ -278,7 +297,7 @@ module ActiveMerchant #:nodoc:
278
297
  if !success_from(response) && response['error']
279
298
  response['error'] && response['error']['type']
280
299
  else
281
- response['transaction'] && response['transaction']['message']
300
+ (response['transaction'] && response['transaction']['message']) || (response['message'])
282
301
  end
283
302
  end
284
303
 
@@ -95,7 +95,7 @@ module ActiveMerchant #:nodoc:
95
95
  end
96
96
 
97
97
  def unstore(pm_profile_id)
98
- commit_for_unstore(:delete, "profiles/#{pm_profile_id}", nil, nil)
98
+ commit(:delete, "profiles/#{get_id_from_store_auth(pm_profile_id)}", nil, nil)
99
99
  end
100
100
 
101
101
  def supports_scrubbing?
@@ -179,7 +179,7 @@ module ActiveMerchant #:nodoc:
179
179
  def add_payment(post, payment)
180
180
  if payment.is_a?(String)
181
181
  post[:card] = {}
182
- post[:card][:paymentToken] = payment
182
+ post[:card][:paymentToken] = get_pm_from_store_auth(payment)
183
183
  else
184
184
  post[:card] = { cardExpiry: {} }
185
185
  post[:card][:cardNum] = payment.number
@@ -299,7 +299,7 @@ module ActiveMerchant #:nodoc:
299
299
  end
300
300
 
301
301
  case options[:stored_credential][:reason_type]
302
- when 'recurring' || 'installment'
302
+ when 'recurring', 'installment'
303
303
  post[:storedCredential][:type] = 'RECURRING'
304
304
  when 'unscheduled'
305
305
  if options[:stored_credential][:initiator] == 'merchant'
@@ -321,6 +321,8 @@ module ActiveMerchant #:nodoc:
321
321
  end
322
322
 
323
323
  def parse(body)
324
+ return {} if body.empty?
325
+
324
326
  JSON.parse(body)
325
327
  end
326
328
 
@@ -342,17 +344,6 @@ module ActiveMerchant #:nodoc:
342
344
  )
343
345
  end
344
346
 
345
- def commit_for_unstore(method, action, parameters, options)
346
- url = url(action)
347
- response = raw_ssl_request(method, url, post_data(parameters, options), headers)
348
- success = true if response.code == '200'
349
-
350
- Response.new(
351
- success,
352
- message: response.message
353
- )
354
- end
355
-
356
347
  def headers
357
348
  {
358
349
  'Content-Type' => 'application/json',
@@ -384,12 +375,21 @@ module ActiveMerchant #:nodoc:
384
375
 
385
376
  def authorization_from(action, response)
386
377
  if action == 'profiles'
387
- response['cards'].first['paymentToken']
378
+ pm = response['cards'].first['paymentToken']
379
+ "#{pm}|#{response['id']}"
388
380
  else
389
381
  response['id']
390
382
  end
391
383
  end
392
384
 
385
+ def get_pm_from_store_auth(authorization)
386
+ authorization.split('|')[0]
387
+ end
388
+
389
+ def get_id_from_store_auth(authorization)
390
+ authorization.split('|')[1]
391
+ end
392
+
393
393
  def post_data(parameters = {}, options = {})
394
394
  return unless parameters.present?
395
395
 
@@ -392,26 +392,36 @@ module ActiveMerchant #:nodoc:
392
392
  def message_from(action, success, response)
393
393
  case action
394
394
  when 'store'
395
- return response['code'] if success
396
-
397
- error_description = response['creditCardToken']['errorDescription'] if response['creditCardToken']
398
- response['error'] || error_description || 'FAILED'
395
+ message_from_store(success, response)
399
396
  when 'verify_credentials'
400
- return 'VERIFIED' if success
401
-
402
- 'FAILED'
397
+ message_from_verify_credentials(success)
403
398
  else
404
- if response['transactionResponse']
405
- response_message = response['transactionResponse']['responseMessage']
399
+ message_from_transaction_response(success, response)
400
+ end
401
+ end
402
+
403
+ def message_from_store(success, response)
404
+ return response['code'] if success
406
405
 
407
- response_code = response['transactionResponse']['responseCode'] || response['transactionResponse']['pendingReason']
406
+ error_description = response['creditCardToken']['errorDescription'] if response['creditCardToken']
407
+ response['error'] || error_description || 'FAILED'
408
+ end
408
409
 
409
- response_message = response_code + ' | ' + response['transactionResponse']['paymentNetworkResponseErrorMessage'] unless response['transactionResponse']['paymentNetworkResponseErrorMessage'].nil?
410
- end
411
- return response_code if success
410
+ def message_from_verify_credentials(success)
411
+ return 'VERIFIED' if success
412
412
 
413
- response_message || response['error'] || response_code || 'FAILED'
414
- end
413
+ 'FAILED'
414
+ end
415
+
416
+ def message_from_transaction_response(success, response)
417
+ response_code = response.dig('transactionResponse', 'responseCode') || response.dig('transactionResponse', 'pendingReason')
418
+ return response_code if success
419
+ return response_code + ' | ' + response.dig('transactionResponse', 'paymentNetworkResponseErrorMessage') if response.dig('transactionResponse', 'paymentNetworkResponseErrorMessage')
420
+ return response.dig('transactionResponse', 'responseMessage') if response.dig('transactionResponse', 'responseMessage')
421
+ return response['error'] if response['error']
422
+ return response_code if response_code
423
+
424
+ 'FAILED'
415
425
  end
416
426
 
417
427
  def authorization_from(action, response)