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
@@ -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)