activemerchant 1.131.0 → 1.137.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +262 -0
  3. data/lib/active_merchant/billing/check.rb +2 -2
  4. data/lib/active_merchant/billing/compatibility.rb +4 -4
  5. data/lib/active_merchant/billing/credit_card.rb +13 -8
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +4 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +64 -7
  8. data/lib/active_merchant/billing/gateway.rb +9 -0
  9. data/lib/active_merchant/billing/gateways/adyen.rb +240 -41
  10. data/lib/active_merchant/billing/gateways/airwallex.rb +26 -12
  11. data/lib/active_merchant/billing/gateways/alelo.rb +23 -5
  12. data/lib/active_merchant/billing/gateways/authorize_net.rb +43 -35
  13. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +10 -6
  14. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +1 -3
  15. data/lib/active_merchant/billing/gateways/axcessms.rb +6 -2
  16. data/lib/active_merchant/billing/gateways/banwire.rb +4 -2
  17. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +7 -3
  18. data/lib/active_merchant/billing/gateways/blue_pay.rb +13 -5
  19. data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -5
  20. data/lib/active_merchant/billing/gateways/borgun.rb +6 -4
  21. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +65 -20
  22. data/lib/active_merchant/billing/gateways/braintree_blue.rb +226 -73
  23. data/lib/active_merchant/billing/gateways/braintree_orange.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/card_connect.rb +5 -2
  25. data/lib/active_merchant/billing/gateways/card_stream.rb +4 -6
  26. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -1
  27. data/lib/active_merchant/billing/gateways/cecabank/cecabank_common.rb +36 -0
  28. data/lib/active_merchant/billing/gateways/cecabank/cecabank_json.rb +316 -0
  29. data/lib/active_merchant/billing/gateways/cecabank/cecabank_xml.rb +220 -0
  30. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -240
  31. data/lib/active_merchant/billing/gateways/checkout_v2.rb +242 -41
  32. data/lib/active_merchant/billing/gateways/commerce_hub.rb +69 -8
  33. data/lib/active_merchant/billing/gateways/credorax.rb +3 -5
  34. data/lib/active_merchant/billing/gateways/cyber_source.rb +192 -41
  35. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +102 -58
  36. data/lib/active_merchant/billing/gateways/d_local.rb +26 -15
  37. data/lib/active_merchant/billing/gateways/data_cash.rb +21 -17
  38. data/lib/active_merchant/billing/gateways/datatrans.rb +279 -0
  39. data/lib/active_merchant/billing/gateways/decidir.rb +53 -18
  40. data/lib/active_merchant/billing/gateways/decidir_plus.rb +4 -1
  41. data/lib/active_merchant/billing/gateways/deepstack.rb +382 -0
  42. data/lib/active_merchant/billing/gateways/ebanx.rb +40 -36
  43. data/lib/active_merchant/billing/gateways/efsnet.rb +6 -2
  44. data/lib/active_merchant/billing/gateways/elavon.rb +99 -33
  45. data/lib/active_merchant/billing/gateways/element.rb +36 -7
  46. data/lib/active_merchant/billing/gateways/epay.rb +6 -2
  47. data/lib/active_merchant/billing/gateways/evo_ca.rb +6 -2
  48. data/lib/active_merchant/billing/gateways/eway.rb +4 -2
  49. data/lib/active_merchant/billing/gateways/eway_managed.rb +6 -2
  50. data/lib/active_merchant/billing/gateways/exact.rb +6 -2
  51. data/lib/active_merchant/billing/gateways/fat_zebra.rb +31 -3
  52. data/lib/active_merchant/billing/gateways/federated_canada.rb +6 -2
  53. data/lib/active_merchant/billing/gateways/first_pay/first_pay_common.rb +15 -0
  54. data/lib/active_merchant/billing/gateways/first_pay/first_pay_json.rb +190 -0
  55. data/lib/active_merchant/billing/gateways/first_pay/first_pay_xml.rb +183 -0
  56. data/lib/active_merchant/billing/gateways/first_pay.rb +6 -172
  57. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -2
  58. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +7 -3
  59. data/lib/active_merchant/billing/gateways/flex_charge.rb +347 -0
  60. data/lib/active_merchant/billing/gateways/garanti.rb +4 -2
  61. data/lib/active_merchant/billing/gateways/global_collect.rb +45 -37
  62. data/lib/active_merchant/billing/gateways/hi_pay.rb +286 -0
  63. data/lib/active_merchant/billing/gateways/hps.rb +1 -1
  64. data/lib/active_merchant/billing/gateways/iats_payments.rb +7 -2
  65. data/lib/active_merchant/billing/gateways/inspire.rb +6 -4
  66. data/lib/active_merchant/billing/gateways/instapay.rb +7 -4
  67. data/lib/active_merchant/billing/gateways/ipg.rb +10 -6
  68. data/lib/active_merchant/billing/gateways/iridium.rb +15 -5
  69. data/lib/active_merchant/billing/gateways/itransact.rb +6 -2
  70. data/lib/active_merchant/billing/gateways/iveri.rb +3 -3
  71. data/lib/active_merchant/billing/gateways/ixopay.rb +2 -2
  72. data/lib/active_merchant/billing/gateways/jetpay.rb +4 -2
  73. data/lib/active_merchant/billing/gateways/jetpay_v2.rb +4 -2
  74. data/lib/active_merchant/billing/gateways/kushki.rb +73 -13
  75. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -2
  76. data/lib/active_merchant/billing/gateways/litle.rb +33 -50
  77. data/lib/active_merchant/billing/gateways/mastercard.rb +4 -4
  78. data/lib/active_merchant/billing/gateways/maxipago.rb +2 -2
  79. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +8 -5
  80. data/lib/active_merchant/billing/gateways/merchant_ware.rb +11 -4
  81. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +11 -4
  82. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +19 -3
  83. data/lib/active_merchant/billing/gateways/mercury.rb +6 -2
  84. data/lib/active_merchant/billing/gateways/metrics_global.rb +8 -6
  85. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +1 -0
  86. data/lib/active_merchant/billing/gateways/migs.rb +6 -2
  87. data/lib/active_merchant/billing/gateways/mit.rb +25 -20
  88. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +18 -10
  89. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  90. data/lib/active_merchant/billing/gateways/moneris.rb +9 -3
  91. data/lib/active_merchant/billing/gateways/money_movers.rb +6 -2
  92. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -4
  93. data/lib/active_merchant/billing/gateways/net_registry.rb +6 -2
  94. data/lib/active_merchant/billing/gateways/netbanx.rb +1 -3
  95. data/lib/active_merchant/billing/gateways/netbilling.rb +6 -2
  96. data/lib/active_merchant/billing/gateways/network_merchants.rb +6 -2
  97. data/lib/active_merchant/billing/gateways/nmi.rb +23 -6
  98. data/lib/active_merchant/billing/gateways/ogone.rb +6 -2
  99. data/lib/active_merchant/billing/gateways/openpay.rb +4 -2
  100. data/lib/active_merchant/billing/gateways/opp.rb +1 -2
  101. data/lib/active_merchant/billing/gateways/optimal_payment.rb +6 -2
  102. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +1 -3
  103. data/lib/active_merchant/billing/gateways/orbital.rb +83 -24
  104. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +7 -4
  105. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +6 -2
  106. data/lib/active_merchant/billing/gateways/pay_hub.rb +4 -2
  107. data/lib/active_merchant/billing/gateways/pay_junction.rb +6 -2
  108. data/lib/active_merchant/billing/gateways/pay_secure.rb +6 -2
  109. data/lib/active_merchant/billing/gateways/pay_trace.rb +31 -18
  110. data/lib/active_merchant/billing/gateways/payeezy.rb +19 -8
  111. data/lib/active_merchant/billing/gateways/payex.rb +4 -2
  112. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  113. data/lib/active_merchant/billing/gateways/payflow.rb +1 -3
  114. data/lib/active_merchant/billing/gateways/payment_express.rb +8 -4
  115. data/lib/active_merchant/billing/gateways/paymentez.rb +23 -11
  116. data/lib/active_merchant/billing/gateways/paysafe.rb +12 -11
  117. data/lib/active_merchant/billing/gateways/payscout.rb +7 -4
  118. data/lib/active_merchant/billing/gateways/paystation.rb +7 -3
  119. data/lib/active_merchant/billing/gateways/payway.rb +6 -2
  120. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  121. data/lib/active_merchant/billing/gateways/pin.rb +22 -4
  122. data/lib/active_merchant/billing/gateways/plexo.rb +49 -10
  123. data/lib/active_merchant/billing/gateways/plugnpay.rb +6 -2
  124. data/lib/active_merchant/billing/gateways/priority.rb +6 -5
  125. data/lib/active_merchant/billing/gateways/psigate.rb +6 -2
  126. data/lib/active_merchant/billing/gateways/psl_card.rb +6 -2
  127. data/lib/active_merchant/billing/gateways/qbms.rb +6 -2
  128. data/lib/active_merchant/billing/gateways/quantum.rb +6 -2
  129. data/lib/active_merchant/billing/gateways/quickbooks.rb +6 -5
  130. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +7 -4
  131. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +6 -2
  132. data/lib/active_merchant/billing/gateways/rapyd.rb +148 -46
  133. data/lib/active_merchant/billing/gateways/reach.rb +11 -4
  134. data/lib/active_merchant/billing/gateways/redsys.rb +2 -10
  135. data/lib/active_merchant/billing/gateways/redsys_rest.rb +507 -0
  136. data/lib/active_merchant/billing/gateways/s5.rb +3 -3
  137. data/lib/active_merchant/billing/gateways/safe_charge.rb +38 -17
  138. data/lib/active_merchant/billing/gateways/sage.rb +12 -4
  139. data/lib/active_merchant/billing/gateways/sage_pay.rb +79 -5
  140. data/lib/active_merchant/billing/gateways/sallie_mae.rb +6 -2
  141. data/lib/active_merchant/billing/gateways/secure_net.rb +6 -2
  142. data/lib/active_merchant/billing/gateways/secure_pay.rb +8 -6
  143. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -4
  144. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +6 -2
  145. data/lib/active_merchant/billing/gateways/securion_pay.rb +24 -10
  146. data/lib/active_merchant/billing/gateways/shift4.rb +17 -20
  147. data/lib/active_merchant/billing/gateways/shift4_v2.rb +117 -0
  148. data/lib/active_merchant/billing/gateways/simetrik.rb +17 -11
  149. data/lib/active_merchant/billing/gateways/skip_jack.rb +6 -2
  150. data/lib/active_merchant/billing/gateways/smart_ps.rb +7 -4
  151. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +4 -2
  152. data/lib/active_merchant/billing/gateways/spreedly_core.rb +2 -4
  153. data/lib/active_merchant/billing/gateways/stripe.rb +63 -19
  154. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +280 -88
  155. data/lib/active_merchant/billing/gateways/sum_up.rb +223 -0
  156. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +4 -2
  157. data/lib/active_merchant/billing/gateways/telr.rb +3 -4
  158. data/lib/active_merchant/billing/gateways/trans_first.rb +1 -2
  159. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +8 -16
  160. data/lib/active_merchant/billing/gateways/transact_pro.rb +1 -1
  161. data/lib/active_merchant/billing/gateways/trust_commerce.rb +6 -2
  162. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +9 -8
  163. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +6 -2
  164. data/lib/active_merchant/billing/gateways/vanco.rb +2 -4
  165. data/lib/active_merchant/billing/gateways/vantiv_express.rb +587 -0
  166. data/lib/active_merchant/billing/gateways/verifi.rb +6 -2
  167. data/lib/active_merchant/billing/gateways/viaklix.rb +6 -2
  168. data/lib/active_merchant/billing/gateways/visanet_peru.rb +2 -2
  169. data/lib/active_merchant/billing/gateways/vpos.rb +4 -4
  170. data/lib/active_merchant/billing/gateways/wirecard.rb +7 -3
  171. data/lib/active_merchant/billing/gateways/wompi.rb +5 -0
  172. data/lib/active_merchant/billing/gateways/worldpay.rb +141 -93
  173. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +13 -10
  174. data/lib/active_merchant/billing/gateways/xpay.rb +242 -0
  175. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  176. data/lib/active_merchant/billing/response.rb +2 -2
  177. data/lib/active_merchant/connection.rb +3 -17
  178. data/lib/active_merchant/country.rb +1 -0
  179. data/lib/active_merchant/errors.rb +10 -0
  180. data/lib/active_merchant/version.rb +1 -1
  181. data/lib/support/gateway_support.rb +2 -2
  182. data/lib/support/ssl_verify.rb +4 -4
  183. data/lib/support/ssl_version.rb +6 -6
  184. metadata +30 -9
@@ -68,6 +68,9 @@ module ActiveMerchant #:nodoc:
68
68
  add_application_info(post, options)
69
69
  add_level_2_data(post, options)
70
70
  add_level_3_data(post, options)
71
+ add_data_airline(post, options)
72
+ add_data_lodging(post, options)
73
+ add_metadata(post, options)
71
74
  commit('authorise', post, options)
72
75
  end
73
76
 
@@ -91,12 +94,28 @@ module ActiveMerchant #:nodoc:
91
94
  end
92
95
 
93
96
  def credit(money, payment, options = {})
94
- action = 'refundWithData'
97
+ action = options[:payout] ? 'payout' : 'refundWithData'
95
98
  post = init_post(options)
96
99
  add_invoice(post, money, options)
97
100
  add_payment(post, payment, options, action)
98
101
  add_shopper_reference(post, options)
99
102
  add_network_transaction_reference(post, options)
103
+
104
+ if action == 'payout'
105
+ add_shopper_interaction(post, payment, options)
106
+ add_fraud_offset(post, options)
107
+ add_fund_source(post, options)
108
+ add_recurring_contract(post, options)
109
+ add_shopper_data(post, payment, options)
110
+
111
+ if (address = options[:billing_address] || options[:address]) && address[:country]
112
+ add_billing_address(post, options, address)
113
+ end
114
+
115
+ post[:dateOfBirth] = options[:date_of_birth] if options[:date_of_birth]
116
+ post[:nationality] = options[:nationality] if options[:nationality]
117
+ end
118
+
100
119
  commit(action, post, options)
101
120
  end
102
121
 
@@ -228,12 +247,30 @@ module ActiveMerchant #:nodoc:
228
247
 
229
248
  def add_extra_data(post, payment, options)
230
249
  post[:telephoneNumber] = (options[:billing_address][:phone_number] if options.dig(:billing_address, :phone_number)) || (options[:billing_address][:phone] if options.dig(:billing_address, :phone)) || ''
231
- post[:fraudOffset] = options[:fraud_offset] if options[:fraud_offset]
232
250
  post[:selectedBrand] = options[:selected_brand] if options[:selected_brand]
233
251
  post[:selectedBrand] ||= NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
234
252
  post[:deliveryDate] = options[:delivery_date] if options[:delivery_date]
235
253
  post[:merchantOrderReference] = options[:merchant_order_reference] if options[:merchant_order_reference]
236
254
  post[:captureDelayHours] = options[:capture_delay_hours] if options[:capture_delay_hours]
255
+ post[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
256
+ post[:shopperIP] = options[:shopper_ip] || options[:ip] if options[:shopper_ip] || options[:ip]
257
+ post[:shopperStatement] = options[:shopper_statement] if options[:shopper_statement]
258
+ post[:store] = options[:store] if options[:store]
259
+ post[:mcc] = options[:mcc] if options[:mcc]
260
+
261
+ add_shopper_data(post, payment, options)
262
+ add_additional_data(post, payment, options)
263
+ add_risk_data(post, options)
264
+ add_shopper_reference(post, options)
265
+ add_merchant_data(post, options)
266
+ add_fraud_offset(post, options)
267
+ end
268
+
269
+ def add_fraud_offset(post, options)
270
+ post[:fraudOffset] = options[:fraud_offset] if options[:fraud_offset]
271
+ end
272
+
273
+ def add_additional_data(post, payment, options)
237
274
  post[:additionalData] ||= {}
238
275
  post[:additionalData][:overwriteBrand] = normalize(options[:overwrite_brand]) if options[:overwrite_brand]
239
276
  post[:additionalData][:customRoutingFlag] = options[:custom_routing_flag] if options[:custom_routing_flag]
@@ -242,11 +279,7 @@ module ActiveMerchant #:nodoc:
242
279
  post[:additionalData][:adjustAuthorisationData] = options[:adjust_authorisation_data] if options[:adjust_authorisation_data]
243
280
  post[:additionalData][:industryUsage] = options[:industry_usage] if options[:industry_usage]
244
281
  post[:additionalData][:RequestedTestAcquirerResponseCode] = options[:requested_test_acquirer_response_code] if options[:requested_test_acquirer_response_code] && test?
245
- post[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
246
- add_shopper_data(post, options)
247
- add_risk_data(post, options)
248
- add_shopper_reference(post, options)
249
- add_merchant_data(post, options)
282
+ post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement]
250
283
  end
251
284
 
252
285
  def extract_and_transform(mapper, from)
@@ -291,13 +324,82 @@ module ActiveMerchant #:nodoc:
291
324
  post[:additionalData].compact!
292
325
  end
293
326
 
294
- def add_shopper_data(post, options)
295
- post[:shopperEmail] = options[:email] if options[:email]
296
- post[:shopperEmail] = options[:shopper_email] if options[:shopper_email]
297
- post[:shopperIP] = options[:ip] if options[:ip]
298
- post[:shopperIP] = options[:shopper_ip] if options[:shopper_ip]
299
- post[:shopperStatement] = options[:shopper_statement] if options[:shopper_statement]
300
- post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement]
327
+ def add_data_airline(post, options)
328
+ return unless options[:additional_data_airline]
329
+
330
+ mapper = %w[
331
+ agency_invoice_number
332
+ agency_plan_name
333
+ airline_code
334
+ airline_designator_code
335
+ boarding_fee
336
+ computerized_reservation_system
337
+ customer_reference_number
338
+ document_type
339
+ flight_date
340
+ ticket_issue_address
341
+ ticket_number
342
+ travel_agency_code
343
+ travel_agency_name
344
+ passenger_name
345
+ ].each_with_object({}) { |value, hash| hash["airline.#{value}"] = value }
346
+
347
+ post[:additionalData].merge!(extract_and_transform(mapper, options[:additional_data_airline]))
348
+
349
+ if options[:additional_data_airline][:leg].present?
350
+ leg_data = %w[
351
+ carrier_code
352
+ class_of_travel
353
+ date_of_travel
354
+ depart_airport
355
+ depart_tax
356
+ destination_code
357
+ fare_base_code
358
+ flight_number
359
+ stop_over_code
360
+ ].each_with_object({}) { |value, hash| hash["airline.leg.#{value}"] = value }
361
+
362
+ post[:additionalData].merge!(extract_and_transform(leg_data, options[:additional_data_airline][:leg]))
363
+ end
364
+
365
+ if options[:additional_data_airline][:passenger].present?
366
+ passenger_data = %w[
367
+ date_of_birth
368
+ first_name
369
+ last_name
370
+ telephone_number
371
+ traveller_type
372
+ ].each_with_object({}) { |value, hash| hash["airline.passenger.#{value}"] = value }
373
+
374
+ post[:additionalData].merge!(extract_and_transform(passenger_data, options[:additional_data_airline][:passenger]))
375
+ end
376
+ post[:additionalData].compact!
377
+ end
378
+
379
+ def add_data_lodging(post, options)
380
+ return unless options[:additional_data_lodging]
381
+
382
+ mapper = {
383
+ 'lodging.checkInDate': 'check_in_date',
384
+ 'lodging.checkOutDate': 'check_out_date',
385
+ 'lodging.customerServiceTollFreeNumber': 'customer_service_toll_free_number',
386
+ 'lodging.fireSafetyActIndicator': 'fire_safety_act_indicator',
387
+ 'lodging.folioCashAdvances': 'folio_cash_advances',
388
+ 'lodging.folioNumber': 'folio_number',
389
+ 'lodging.foodBeverageCharges': 'food_beverage_charges',
390
+ 'lodging.noShowIndicator': 'no_show_indicator',
391
+ 'lodging.prepaidExpenses': 'prepaid_expenses',
392
+ 'lodging.propertyPhoneNumber': 'property_phone_number',
393
+ 'lodging.room1.numberOfNights': 'number_of_nights',
394
+ 'lodging.room1.rate': 'rate',
395
+ 'lodging.totalRoomTax': 'total_room_tax',
396
+ 'lodging.totalTax': 'totalTax',
397
+ 'travelEntertainmentAuthData.duration': 'duration',
398
+ 'travelEntertainmentAuthData.market': 'market'
399
+ }
400
+
401
+ post[:additionalData].merge!(extract_and_transform(mapper, options[:additional_data_lodging]))
402
+ post[:additionalData].compact!
301
403
  end
302
404
 
303
405
  def add_shopper_statement(post, options)
@@ -323,7 +425,7 @@ module ActiveMerchant #:nodoc:
323
425
 
324
426
  def add_risk_data(post, options)
325
427
  if (risk_data = options[:risk_data])
326
- risk_data = Hash[risk_data.map { |k, v| ["riskdata.#{k}", v] }]
428
+ risk_data = risk_data.map { |k, v| ["riskdata.#{k}", v] }.to_h
327
429
  post[:additionalData].merge!(risk_data)
328
430
  end
329
431
  end
@@ -393,30 +495,39 @@ module ActiveMerchant #:nodoc:
393
495
  def add_address(post, options)
394
496
  if address = options[:shipping_address]
395
497
  post[:deliveryAddress] = {}
396
- post[:deliveryAddress][:street] = address[:address1] || 'NA'
397
- post[:deliveryAddress][:houseNumberOrName] = address[:address2] || 'NA'
498
+ post[:deliveryAddress][:street] = options[:address_override] == true ? address[:address2] : address[:address1] || 'NA'
499
+ post[:deliveryAddress][:houseNumberOrName] = options[:address_override] == true ? address[:address1] : address[:address2] || 'NA'
398
500
  post[:deliveryAddress][:postalCode] = address[:zip] if address[:zip]
399
501
  post[:deliveryAddress][:city] = address[:city] || 'NA'
400
502
  post[:deliveryAddress][:stateOrProvince] = get_state(address)
401
- post[:deliveryAddress][:country] = address[:country] if address[:country]
503
+ post[:deliveryAddress][:country] = get_country(address)
402
504
  end
403
505
  return unless post[:bankAccount]&.kind_of?(Hash) || post[:card]&.kind_of?(Hash)
404
506
 
405
507
  if (address = options[:billing_address] || options[:address]) && address[:country]
406
- post[:billingAddress] = {}
407
- post[:billingAddress][:street] = address[:address1] || 'NA'
408
- post[:billingAddress][:houseNumberOrName] = address[:address2] || 'NA'
409
- post[:billingAddress][:postalCode] = address[:zip] if address[:zip]
410
- post[:billingAddress][:city] = address[:city] || 'NA'
411
- post[:billingAddress][:stateOrProvince] = get_state(address)
412
- post[:billingAddress][:country] = address[:country] if address[:country]
508
+ add_billing_address(post, options, address)
413
509
  end
414
510
  end
415
511
 
512
+ def add_billing_address(post, options, address)
513
+ post[:billingAddress] = {}
514
+ post[:billingAddress][:street] = options[:address_override] == true ? address[:address2] : address[:address1] || 'NA'
515
+ post[:billingAddress][:houseNumberOrName] = options[:address_override] == true ? address[:address1] : address[:address2] || 'NA'
516
+ post[:billingAddress][:postalCode] = address[:zip] if address[:zip]
517
+ post[:billingAddress][:city] = address[:city] || 'NA'
518
+ post[:billingAddress][:stateOrProvince] = get_state(address)
519
+ post[:billingAddress][:country] = get_country(address)
520
+ post[:telephoneNumber] = address[:phone_number] || address[:phone] || ''
521
+ end
522
+
416
523
  def get_state(address)
417
524
  address[:state] && !address[:state].blank? ? address[:state] : 'NA'
418
525
  end
419
526
 
527
+ def get_country(address)
528
+ address[:country].present? ? address[:country] : 'ZZ'
529
+ end
530
+
420
531
  def add_invoice(post, money, options)
421
532
  currency = options[:currency] || currency(money)
422
533
  amount = {
@@ -477,6 +588,17 @@ module ActiveMerchant #:nodoc:
477
588
  post[:card] = card
478
589
  end
479
590
 
591
+ def add_shopper_data(post, payment, options)
592
+ if payment && !payment.is_a?(String)
593
+ post[:shopperName] = {}
594
+ post[:shopperName][:firstName] = payment.first_name
595
+ post[:shopperName][:lastName] = payment.last_name
596
+ end
597
+
598
+ post[:shopperEmail] = options[:email] if options[:email]
599
+ post[:shopperEmail] = options[:shopper_email] if options[:shopper_email]
600
+ end
601
+
480
602
  def capture_options(options)
481
603
  return options.merge(idempotency_key: "#{options[:idempotency_key]}-cap") if options[:idempotency_key]
482
604
 
@@ -508,11 +630,12 @@ module ActiveMerchant #:nodoc:
508
630
  def add_recurring_contract(post, options = {})
509
631
  return unless options[:recurring_contract_type]
510
632
 
511
- recurring = {
512
- contract: options[:recurring_contract_type]
513
- }
514
-
515
- post[:recurring] = recurring
633
+ post[:recurring] = {}
634
+ post[:recurring][:contract] = options[:recurring_contract_type]
635
+ post[:recurring][:recurringDetailName] = options[:recurring_detail_name] if options[:recurring_detail_name]
636
+ post[:recurring][:recurringExpiry] = options[:recurring_expiry] if options[:recurring_expiry]
637
+ post[:recurring][:recurringFrequency] = options[:recurring_frequency] if options[:recurring_frequency]
638
+ post[:recurring][:tokenService] = options[:token_service] if options[:token_service]
516
639
  end
517
640
 
518
641
  def add_application_info(post, options)
@@ -608,10 +731,58 @@ module ActiveMerchant #:nodoc:
608
731
  }
609
732
  end
610
733
 
734
+ def add_fund_source(post, options)
735
+ return unless fund_source = options[:fund_source]
736
+
737
+ post[:fundSource] = {}
738
+ post[:fundSource][:additionalData] = fund_source[:additional_data] if fund_source[:additional_data]
739
+
740
+ if fund_source[:first_name] && fund_source[:last_name]
741
+ post[:fundSource][:shopperName] = {}
742
+ post[:fundSource][:shopperName][:firstName] = fund_source[:first_name]
743
+ post[:fundSource][:shopperName][:lastName] = fund_source[:last_name]
744
+ end
745
+
746
+ if (address = fund_source[:billing_address])
747
+ add_billing_address(post[:fundSource], options, address)
748
+ end
749
+ end
750
+
751
+ def add_metadata(post, options = {})
752
+ return unless options[:metadata]
753
+
754
+ post[:metadata] ||= {}
755
+ post[:metadata].merge!(options[:metadata]) if options[:metadata]
756
+ end
757
+
758
+ def add_header_fields(response)
759
+ return unless @response_headers.present?
760
+
761
+ headers = {}
762
+ headers['response_headers'] = {}
763
+ headers['response_headers']['transient_error'] = @response_headers['transient-error'] if @response_headers['transient-error']
764
+
765
+ response.merge!(headers)
766
+ end
767
+
611
768
  def parse(body)
612
769
  return {} if body.blank?
613
770
 
614
- JSON.parse(body)
771
+ response = JSON.parse(body)
772
+ add_header_fields(response)
773
+ response
774
+ end
775
+
776
+ # Override the regular handle response so we can access the headers
777
+ # set header fields and values so we can add them to the response body
778
+ def handle_response(response)
779
+ @response_headers = response.each_header.to_h if response.respond_to?(:header)
780
+ case response.code.to_i
781
+ when 200...300
782
+ response.body
783
+ else
784
+ raise ResponseError.new(response)
785
+ end
615
786
  end
616
787
 
617
788
  def commit(action, parameters, options)
@@ -626,7 +797,7 @@ module ActiveMerchant #:nodoc:
626
797
  success = success_from(action, response, options)
627
798
  Response.new(
628
799
  success,
629
- message_from(action, response),
800
+ message_from(action, response, options),
630
801
  response,
631
802
  authorization: authorization_from(action, parameters, response),
632
803
  test: test?,
@@ -646,8 +817,14 @@ module ActiveMerchant #:nodoc:
646
817
  end
647
818
 
648
819
  def endpoint(action)
649
- recurring = %w(disable storeToken).include?(action)
650
- recurring ? "Recurring/#{RECURRING_API_VERSION}/#{action}" : "Payment/#{PAYMENT_API_VERSION}/#{action}"
820
+ case action
821
+ when 'disable', 'storeToken'
822
+ "Recurring/#{RECURRING_API_VERSION}/#{action}"
823
+ when 'payout'
824
+ "Payout/#{PAYMENT_API_VERSION}/#{action}"
825
+ else
826
+ "Payment/#{PAYMENT_API_VERSION}/#{action}"
827
+ end
651
828
  end
652
829
 
653
830
  def url(action)
@@ -674,7 +851,7 @@ module ActiveMerchant #:nodoc:
674
851
  end
675
852
 
676
853
  def success_from(action, response, options)
677
- if %w[RedirectShopper ChallengeShopper].include?(response.dig('resultCode')) && !options[:execute_threed] && !options[:threed_dynamic]
854
+ if %w[RedirectShopper ChallengeShopper].include?(response.dig('resultCode')) && !options[:execute_threed] && (!options[:threed_dynamic] || options[:ignore_threed_dynamic])
678
855
  response['refusalReason'] = 'Received unexpected 3DS authentication response, but a 3DS initiation flag was not included in the request.'
679
856
  return false
680
857
  end
@@ -691,18 +868,29 @@ module ActiveMerchant #:nodoc:
691
868
  response['response'] == '[detail-successfully-disabled]'
692
869
  when 'refundWithData'
693
870
  response['resultCode'] == 'Received'
871
+ when 'payout'
872
+ return false unless response['resultCode'] && response['authCode']
873
+
874
+ %[AuthenticationFinished Authorised Received].include?(response['resultCode'])
694
875
  else
695
876
  false
696
877
  end
697
878
  end
698
879
 
699
- def message_from(action, response)
700
- return authorize_message_from(response) if %w(authorise authorise3d authorise3ds2).include?(action.to_s)
701
-
702
- response['response'] || response['message'] || response['result'] || response['resultCode']
880
+ def message_from(action, response, options = {})
881
+ case action.to_s
882
+ when 'authorise', 'authorise3d', 'authorise3ds2'
883
+ authorize_message_from(response, options)
884
+ when 'payout'
885
+ response['refusalReason'] || response['resultCode'] || response['message']
886
+ else
887
+ response['response'] || response['message'] || response['result'] || response['resultCode']
888
+ end
703
889
  end
704
890
 
705
- def authorize_message_from(response)
891
+ def authorize_message_from(response, options = {})
892
+ return raw_authorize_error_message(response) if options[:raw_error_message]
893
+
706
894
  if response['refusalReason'] && response['additionalData'] && (response['additionalData']['merchantAdviceCode'] || response['additionalData']['refusalReasonRaw'])
707
895
  "#{response['refusalReason']} | #{response['additionalData']['merchantAdviceCode'] || response['additionalData']['refusalReasonRaw']}"
708
896
  else
@@ -710,6 +898,14 @@ module ActiveMerchant #:nodoc:
710
898
  end
711
899
  end
712
900
 
901
+ def raw_authorize_error_message(response)
902
+ if response['refusalReason'] && response['additionalData'] && response['additionalData']['refusalReasonRaw']
903
+ "#{response['refusalReason']} | #{response['additionalData']['refusalReasonRaw']}"
904
+ else
905
+ response['refusalReason'] || response['resultCode'] || response['message'] || response['result']
906
+ end
907
+ end
908
+
713
909
  def authorization_from(action, parameters, response)
714
910
  return nil if response['pspReference'].nil?
715
911
 
@@ -731,7 +927,10 @@ module ActiveMerchant #:nodoc:
731
927
  end
732
928
 
733
929
  def error_code_from(response)
734
- STANDARD_ERROR_CODE_MAPPING[response['errorCode']] || response['errorCode']
930
+ response.dig('additionalData', 'refusalReasonRaw').try(:match, /^([a-zA-Z0-9 ]{1,5})(?=:)/).try(:[], 1).try(:strip) ||
931
+ STANDARD_ERROR_CODE_MAPPING[response['errorCode']] ||
932
+ response['errorCode'] ||
933
+ response['refusalReason']
735
934
  end
736
935
 
737
936
  def network_transaction_id_from(response)
@@ -32,7 +32,7 @@ module ActiveMerchant #:nodoc:
32
32
  @client_id = options[:client_id]
33
33
  @client_api_key = options[:client_api_key]
34
34
  super
35
- @access_token = setup_access_token
35
+ @access_token = options[:access_token] || setup_access_token
36
36
  end
37
37
 
38
38
  def purchase(money, card, options = {})
@@ -133,13 +133,27 @@ module ActiveMerchant #:nodoc:
133
133
  'x-client-id' => @client_id,
134
134
  'x-api-key' => @client_api_key
135
135
  }
136
- response = ssl_post(build_request_url(:login), nil, token_headers)
137
- JSON.parse(response)['token']
136
+
137
+ begin
138
+ raw_response = ssl_post(build_request_url(:login), nil, token_headers)
139
+ rescue ResponseError => e
140
+ raise OAuthResponseError.new(e)
141
+ else
142
+ response = JSON.parse(raw_response)
143
+ if (token = response['token'])
144
+ token
145
+ else
146
+ oauth_response = Response.new(false, response['message'])
147
+ raise OAuthResponseError.new(oauth_response)
148
+ end
149
+ end
138
150
  end
139
151
 
140
152
  def build_request_url(action, id = nil)
141
153
  base_url = (test? ? test_url : live_url)
142
- base_url + ENDPOINTS[action].to_s % { id: id }
154
+ endpoint = ENDPOINTS[action].to_s
155
+ endpoint = id.present? ? endpoint % { id: id } : endpoint
156
+ base_url + endpoint
143
157
  end
144
158
 
145
159
  def add_referrer_data(post)
@@ -278,11 +292,11 @@ module ActiveMerchant #:nodoc:
278
292
  pm_options = post.dig('payment_method_options', 'card')
279
293
 
280
294
  external_three_ds = {
281
- 'version': format_three_ds_version(three_d_secure),
282
- 'eci': three_d_secure[:eci]
295
+ version: format_three_ds_version(three_d_secure),
296
+ eci: three_d_secure[:eci]
283
297
  }.merge(three_ds_version_specific_fields(three_d_secure))
284
298
 
285
- pm_options ? pm_options.merge!('external_three_ds': external_three_ds) : post['payment_method_options'] = { 'card': { 'external_three_ds': external_three_ds } }
299
+ pm_options ? pm_options.merge!(external_three_ds: external_three_ds) : post['payment_method_options'] = { card: { external_three_ds: external_three_ds } }
286
300
  end
287
301
 
288
302
  def format_three_ds_version(three_d_secure)
@@ -295,14 +309,14 @@ module ActiveMerchant #:nodoc:
295
309
  def three_ds_version_specific_fields(three_d_secure)
296
310
  if three_d_secure[:version].to_f >= 2
297
311
  {
298
- 'authentication_value': three_d_secure[:cavv],
299
- 'ds_transaction_id': three_d_secure[:ds_transaction_id],
300
- 'three_ds_server_transaction_id': three_d_secure[:three_ds_server_trans_id]
312
+ authentication_value: three_d_secure[:cavv],
313
+ ds_transaction_id: three_d_secure[:ds_transaction_id],
314
+ three_ds_server_transaction_id: three_d_secure[:three_ds_server_trans_id]
301
315
  }
302
316
  else
303
317
  {
304
- 'cavv': three_d_secure[:cavv],
305
- 'xid': three_d_secure[:xid]
318
+ cavv: three_d_secure[:cavv],
319
+ xid: three_d_secure[:xid]
306
320
  }
307
321
  end
308
322
  end
@@ -110,8 +110,18 @@ module ActiveMerchant #:nodoc:
110
110
  'Content-Type' => 'application/x-www-form-urlencoded'
111
111
  }
112
112
 
113
- parsed = parse(ssl_post(url('captura-oauth-provider/oauth/token'), post_data(params), headers))
114
- Response.new(true, parsed[:access_token], parsed)
113
+ begin
114
+ raw_response = ssl_post(url('captura-oauth-provider/oauth/token'), post_data(params), headers)
115
+ rescue ResponseError => e
116
+ raise OAuthResponseError.new(e)
117
+ else
118
+ response = parse(raw_response)
119
+ if (access_token = response[:access_token])
120
+ Response.new(true, access_token, response)
121
+ else
122
+ raise OAuthResponseError.new(response)
123
+ end
124
+ end
115
125
  end
116
126
 
117
127
  def remote_encryption_key(access_token)
@@ -144,9 +154,11 @@ module ActiveMerchant #:nodoc:
144
154
  access_token: access_token,
145
155
  multiresp: multiresp.responses.present? ? multiresp : nil
146
156
  }
147
- rescue ResponseError => error
157
+ rescue ActiveMerchant::OAuthResponseError => e
158
+ raise e
159
+ rescue ResponseError => e
148
160
  # retry to generate a new access_token when the provided one is expired
149
- raise error unless try_again && %w(401 404).include?(error.response.code) && @options[:access_token].present?
161
+ raise e unless retry?(try_again, e, :access_token)
150
162
 
151
163
  @options.delete(:access_token)
152
164
  @options.delete(:encryption_key)
@@ -206,9 +218,11 @@ module ActiveMerchant #:nodoc:
206
218
  multiresp.process { resp }
207
219
 
208
220
  multiresp
221
+ rescue ActiveMerchant::OAuthResponseError => e
222
+ raise OAuthResponseError.new(e)
209
223
  rescue ActiveMerchant::ResponseError => e
210
224
  # Retry on a possible expired encryption key
211
- if try_again && %w(401 404).include?(e.response.code) && @options[:encryption_key].present?
225
+ if retry?(try_again, e, :encryption_key)
212
226
  @options.delete(:encryption_key)
213
227
  commit(action, body, options, false)
214
228
  else
@@ -217,6 +231,10 @@ module ActiveMerchant #:nodoc:
217
231
  end
218
232
  end
219
233
 
234
+ def retry?(try_again, error, key)
235
+ try_again && %w(401 404).include?(error.response.code) && @options[key].present?
236
+ end
237
+
220
238
  def success_from(action, response)
221
239
  case action
222
240
  when 'capture/transaction/refund'