activemerchant 1.133.0 → 1.137.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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +240 -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 +11 -8
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +4 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +59 -6
  8. data/lib/active_merchant/billing/gateway.rb +9 -0
  9. data/lib/active_merchant/billing/gateways/adyen.rb +162 -43
  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/braintree/token_nonce.rb +65 -20
  21. data/lib/active_merchant/billing/gateways/braintree_blue.rb +226 -73
  22. data/lib/active_merchant/billing/gateways/braintree_orange.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/card_connect.rb +5 -2
  24. data/lib/active_merchant/billing/gateways/card_stream.rb +4 -6
  25. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/cecabank/cecabank_common.rb +36 -0
  27. data/lib/active_merchant/billing/gateways/cecabank/cecabank_json.rb +316 -0
  28. data/lib/active_merchant/billing/gateways/cecabank/cecabank_xml.rb +220 -0
  29. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -240
  30. data/lib/active_merchant/billing/gateways/checkout_v2.rb +238 -34
  31. data/lib/active_merchant/billing/gateways/commerce_hub.rb +63 -6
  32. data/lib/active_merchant/billing/gateways/credorax.rb +3 -5
  33. data/lib/active_merchant/billing/gateways/cyber_source.rb +185 -47
  34. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +102 -58
  35. data/lib/active_merchant/billing/gateways/d_local.rb +26 -15
  36. data/lib/active_merchant/billing/gateways/data_cash.rb +21 -17
  37. data/lib/active_merchant/billing/gateways/datatrans.rb +279 -0
  38. data/lib/active_merchant/billing/gateways/decidir.rb +53 -18
  39. data/lib/active_merchant/billing/gateways/decidir_plus.rb +4 -1
  40. data/lib/active_merchant/billing/gateways/deepstack.rb +382 -0
  41. data/lib/active_merchant/billing/gateways/ebanx.rb +40 -36
  42. data/lib/active_merchant/billing/gateways/efsnet.rb +6 -2
  43. data/lib/active_merchant/billing/gateways/elavon.rb +99 -33
  44. data/lib/active_merchant/billing/gateways/element.rb +36 -7
  45. data/lib/active_merchant/billing/gateways/epay.rb +6 -2
  46. data/lib/active_merchant/billing/gateways/evo_ca.rb +6 -2
  47. data/lib/active_merchant/billing/gateways/eway.rb +4 -2
  48. data/lib/active_merchant/billing/gateways/eway_managed.rb +6 -2
  49. data/lib/active_merchant/billing/gateways/exact.rb +6 -2
  50. data/lib/active_merchant/billing/gateways/fat_zebra.rb +31 -3
  51. data/lib/active_merchant/billing/gateways/federated_canada.rb +6 -2
  52. data/lib/active_merchant/billing/gateways/first_pay/first_pay_common.rb +15 -0
  53. data/lib/active_merchant/billing/gateways/first_pay/first_pay_json.rb +190 -0
  54. data/lib/active_merchant/billing/gateways/first_pay/first_pay_xml.rb +183 -0
  55. data/lib/active_merchant/billing/gateways/first_pay.rb +6 -172
  56. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -2
  57. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +7 -3
  58. data/lib/active_merchant/billing/gateways/flex_charge.rb +347 -0
  59. data/lib/active_merchant/billing/gateways/garanti.rb +4 -2
  60. data/lib/active_merchant/billing/gateways/global_collect.rb +45 -37
  61. data/lib/active_merchant/billing/gateways/hi_pay.rb +286 -0
  62. data/lib/active_merchant/billing/gateways/hps.rb +1 -1
  63. data/lib/active_merchant/billing/gateways/iats_payments.rb +7 -2
  64. data/lib/active_merchant/billing/gateways/inspire.rb +6 -4
  65. data/lib/active_merchant/billing/gateways/instapay.rb +7 -4
  66. data/lib/active_merchant/billing/gateways/ipg.rb +9 -5
  67. data/lib/active_merchant/billing/gateways/iridium.rb +15 -5
  68. data/lib/active_merchant/billing/gateways/itransact.rb +6 -2
  69. data/lib/active_merchant/billing/gateways/iveri.rb +3 -3
  70. data/lib/active_merchant/billing/gateways/ixopay.rb +2 -2
  71. data/lib/active_merchant/billing/gateways/jetpay.rb +4 -2
  72. data/lib/active_merchant/billing/gateways/jetpay_v2.rb +4 -2
  73. data/lib/active_merchant/billing/gateways/kushki.rb +72 -12
  74. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -2
  75. data/lib/active_merchant/billing/gateways/litle.rb +33 -50
  76. data/lib/active_merchant/billing/gateways/mastercard.rb +4 -4
  77. data/lib/active_merchant/billing/gateways/maxipago.rb +2 -2
  78. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +8 -5
  79. data/lib/active_merchant/billing/gateways/merchant_ware.rb +11 -4
  80. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +11 -4
  81. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +19 -3
  82. data/lib/active_merchant/billing/gateways/mercury.rb +6 -2
  83. data/lib/active_merchant/billing/gateways/metrics_global.rb +8 -6
  84. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +1 -0
  85. data/lib/active_merchant/billing/gateways/migs.rb +6 -2
  86. data/lib/active_merchant/billing/gateways/mit.rb +8 -3
  87. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +18 -10
  88. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  89. data/lib/active_merchant/billing/gateways/moneris.rb +9 -3
  90. data/lib/active_merchant/billing/gateways/money_movers.rb +6 -2
  91. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -4
  92. data/lib/active_merchant/billing/gateways/net_registry.rb +6 -2
  93. data/lib/active_merchant/billing/gateways/netbanx.rb +1 -3
  94. data/lib/active_merchant/billing/gateways/netbilling.rb +6 -2
  95. data/lib/active_merchant/billing/gateways/network_merchants.rb +6 -2
  96. data/lib/active_merchant/billing/gateways/nmi.rb +18 -6
  97. data/lib/active_merchant/billing/gateways/ogone.rb +6 -2
  98. data/lib/active_merchant/billing/gateways/openpay.rb +4 -2
  99. data/lib/active_merchant/billing/gateways/opp.rb +1 -2
  100. data/lib/active_merchant/billing/gateways/optimal_payment.rb +6 -2
  101. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +1 -3
  102. data/lib/active_merchant/billing/gateways/orbital.rb +83 -24
  103. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +7 -4
  104. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +6 -2
  105. data/lib/active_merchant/billing/gateways/pay_hub.rb +4 -2
  106. data/lib/active_merchant/billing/gateways/pay_junction.rb +6 -2
  107. data/lib/active_merchant/billing/gateways/pay_secure.rb +6 -2
  108. data/lib/active_merchant/billing/gateways/pay_trace.rb +31 -18
  109. data/lib/active_merchant/billing/gateways/payeezy.rb +19 -8
  110. data/lib/active_merchant/billing/gateways/payex.rb +4 -2
  111. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  112. data/lib/active_merchant/billing/gateways/payflow.rb +1 -3
  113. data/lib/active_merchant/billing/gateways/payment_express.rb +8 -4
  114. data/lib/active_merchant/billing/gateways/paymentez.rb +23 -11
  115. data/lib/active_merchant/billing/gateways/paysafe.rb +12 -11
  116. data/lib/active_merchant/billing/gateways/payscout.rb +7 -4
  117. data/lib/active_merchant/billing/gateways/paystation.rb +7 -3
  118. data/lib/active_merchant/billing/gateways/payway.rb +6 -2
  119. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +2 -2
  120. data/lib/active_merchant/billing/gateways/pin.rb +22 -4
  121. data/lib/active_merchant/billing/gateways/plexo.rb +49 -10
  122. data/lib/active_merchant/billing/gateways/plugnpay.rb +6 -2
  123. data/lib/active_merchant/billing/gateways/priority.rb +6 -5
  124. data/lib/active_merchant/billing/gateways/psigate.rb +6 -2
  125. data/lib/active_merchant/billing/gateways/psl_card.rb +6 -2
  126. data/lib/active_merchant/billing/gateways/qbms.rb +6 -2
  127. data/lib/active_merchant/billing/gateways/quantum.rb +6 -2
  128. data/lib/active_merchant/billing/gateways/quickbooks.rb +6 -5
  129. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +7 -4
  130. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +6 -2
  131. data/lib/active_merchant/billing/gateways/rapyd.rb +148 -46
  132. data/lib/active_merchant/billing/gateways/reach.rb +11 -4
  133. data/lib/active_merchant/billing/gateways/redsys.rb +2 -10
  134. data/lib/active_merchant/billing/gateways/redsys_rest.rb +507 -0
  135. data/lib/active_merchant/billing/gateways/s5.rb +3 -3
  136. data/lib/active_merchant/billing/gateways/safe_charge.rb +36 -16
  137. data/lib/active_merchant/billing/gateways/sage.rb +12 -4
  138. data/lib/active_merchant/billing/gateways/sage_pay.rb +79 -5
  139. data/lib/active_merchant/billing/gateways/sallie_mae.rb +6 -2
  140. data/lib/active_merchant/billing/gateways/secure_net.rb +6 -2
  141. data/lib/active_merchant/billing/gateways/secure_pay.rb +8 -6
  142. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -4
  143. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +6 -2
  144. data/lib/active_merchant/billing/gateways/securion_pay.rb +24 -10
  145. data/lib/active_merchant/billing/gateways/shift4.rb +17 -20
  146. data/lib/active_merchant/billing/gateways/shift4_v2.rb +117 -0
  147. data/lib/active_merchant/billing/gateways/simetrik.rb +17 -11
  148. data/lib/active_merchant/billing/gateways/skip_jack.rb +6 -2
  149. data/lib/active_merchant/billing/gateways/smart_ps.rb +7 -4
  150. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +4 -2
  151. data/lib/active_merchant/billing/gateways/spreedly_core.rb +2 -4
  152. data/lib/active_merchant/billing/gateways/stripe.rb +53 -21
  153. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +199 -50
  154. data/lib/active_merchant/billing/gateways/sum_up.rb +223 -0
  155. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +4 -2
  156. data/lib/active_merchant/billing/gateways/telr.rb +3 -4
  157. data/lib/active_merchant/billing/gateways/trans_first.rb +1 -2
  158. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +8 -16
  159. data/lib/active_merchant/billing/gateways/transact_pro.rb +1 -1
  160. data/lib/active_merchant/billing/gateways/trust_commerce.rb +6 -2
  161. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +9 -8
  162. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +6 -2
  163. data/lib/active_merchant/billing/gateways/vanco.rb +2 -4
  164. data/lib/active_merchant/billing/gateways/vantiv_express.rb +587 -0
  165. data/lib/active_merchant/billing/gateways/verifi.rb +6 -2
  166. data/lib/active_merchant/billing/gateways/viaklix.rb +6 -2
  167. data/lib/active_merchant/billing/gateways/visanet_peru.rb +2 -2
  168. data/lib/active_merchant/billing/gateways/vpos.rb +3 -3
  169. data/lib/active_merchant/billing/gateways/wirecard.rb +7 -3
  170. data/lib/active_merchant/billing/gateways/wompi.rb +5 -0
  171. data/lib/active_merchant/billing/gateways/worldpay.rb +140 -73
  172. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +13 -10
  173. data/lib/active_merchant/billing/gateways/xpay.rb +242 -0
  174. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  175. data/lib/active_merchant/billing/response.rb +2 -2
  176. data/lib/active_merchant/connection.rb +3 -17
  177. data/lib/active_merchant/country.rb +1 -0
  178. data/lib/active_merchant/errors.rb +10 -0
  179. data/lib/active_merchant/version.rb +1 -1
  180. data/lib/support/gateway_support.rb +2 -2
  181. data/lib/support/ssl_verify.rb +4 -4
  182. data/lib/support/ssl_version.rb +6 -6
  183. metadata +30 -9
@@ -161,11 +161,13 @@ module ActiveMerchant #:nodoc:
161
161
  'Content-Type' => 'text/xml; charset=utf-8' }
162
162
  response_string = ssl_post(test? ? self.test_url : self.live_url, soap, headers)
163
163
  response = parse(response_string, soap_action)
164
- return Response.new(response['errorcode'] == '000',
164
+ return Response.new(
165
+ response['errorcode'] == '000',
165
166
  response['errormessage'],
166
167
  response,
167
168
  test: test?,
168
- authorization: response['transaction_id'])
169
+ authorization: response['transaction_id']
170
+ )
169
171
  end
170
172
 
171
173
  def build_soap(request)
@@ -271,11 +271,9 @@ module ActiveMerchant #:nodoc:
271
271
  end
272
272
  end
273
273
 
274
- def build_xml_request(root)
274
+ def build_xml_request(root, &block)
275
275
  builder = Nokogiri::XML::Builder.new
276
- builder.__send__(root) do |doc|
277
- yield(doc)
278
- end
276
+ builder.__send__(root, &block)
279
277
  builder.to_xml
280
278
  end
281
279
 
@@ -7,14 +7,16 @@ module ActiveMerchant #:nodoc:
7
7
  class StripeGateway < Gateway
8
8
  self.live_url = 'https://api.stripe.com/v1/'
9
9
 
10
+ # Docs on AVS codes: https://en.wikipedia.org/w/index.php?title=Address_verification_service&_ga=2.97570079.1027215965.1655989706-2008268124.1655989706#AVS_response_codes
11
+ # possible response values: https://stripe.com/docs/api/payment_methods/object#payment_method_object-card-checks
10
12
  AVS_CODE_TRANSLATOR = {
11
- 'line1: pass, zip: pass' => 'Y',
12
13
  'line1: pass, zip: fail' => 'A',
13
14
  'line1: pass, zip: unchecked' => 'B',
14
- 'line1: fail, zip: pass' => 'Z',
15
+ 'line1: unchecked, zip: unchecked' => 'I',
15
16
  'line1: fail, zip: fail' => 'N',
16
17
  'line1: unchecked, zip: pass' => 'P',
17
- 'line1: unchecked, zip: unchecked' => 'I'
18
+ 'line1: pass, zip: pass' => 'Y',
19
+ 'line1: fail, zip: pass' => 'Z'
18
20
  }
19
21
 
20
22
  CVC_CODE_TRANSLATOR = {
@@ -292,7 +294,9 @@ module ActiveMerchant #:nodoc:
292
294
  gsub(%r(((\[card\]|card)\[number\]=)\d+), '\1[FILTERED]').
293
295
  gsub(%r(((\[card\]|card)\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\3').
294
296
  gsub(%r(((\[bank_account\]|bank_account)\[account_number\]=)\d+), '\1[FILTERED]').
295
- gsub(%r(((\[payment_method_data\]|payment_method_data)\[card\]\[token\]=)[^&]+(&?)), '\1[FILTERED]\3')
297
+ gsub(%r(((\[payment_method_data\]|payment_method_data)\[card\]\[token\]=)[^&]+(&?)), '\1[FILTERED]\3').
298
+ gsub(%r(((\[payment_method_data\]|payment_method_data)\[card\]\[network_token\]\[number\]=)\d+), '\1[FILTERED]').
299
+ gsub(%r(((\[payment_method_options\]|payment_method_options)\[card\]\[network_token\]\[cryptogram\]=)[^&]+(&?)), '\1[FILTERED]')
296
300
  end
297
301
 
298
302
  def supports_network_tokenization?
@@ -577,7 +581,7 @@ module ActiveMerchant #:nodoc:
577
581
 
578
582
  def add_source_owner(post, creditcard, options)
579
583
  post[:owner] = {}
580
- post[:owner][:name] = creditcard.name if creditcard.name
584
+ post[:owner][:name] = creditcard.name if creditcard.respond_to?(:name) && creditcard.name
581
585
  post[:owner][:email] = options[:email] if options[:email]
582
586
 
583
587
  if address = options[:billing_address] || options[:address]
@@ -613,8 +617,21 @@ module ActiveMerchant #:nodoc:
613
617
  post[:radar_options] = radar_options unless radar_options.empty?
614
618
  end
615
619
 
620
+ def add_header_fields(response)
621
+ return unless @response_headers.present?
622
+
623
+ headers = {}
624
+ headers['response_headers'] = {}
625
+ headers['response_headers']['idempotent_replayed'] = @response_headers['idempotent-replayed'] if @response_headers['idempotent-replayed']
626
+ headers['response_headers']['stripe_should_retry'] = @response_headers['stripe-should-retry'] if @response_headers['stripe-should-retry']
627
+
628
+ response.merge!(headers)
629
+ end
630
+
616
631
  def parse(body)
617
- JSON.parse(body)
632
+ response = JSON.parse(body)
633
+ add_header_fields(response)
634
+ response
618
635
  end
619
636
 
620
637
  def post_data(params)
@@ -695,46 +712,46 @@ module ActiveMerchant #:nodoc:
695
712
 
696
713
  def commit(method, url, parameters = nil, options = {})
697
714
  add_expand_parameters(parameters, options) if parameters
698
-
699
715
  return Response.new(false, 'Invalid API Key provided') unless key_valid?(options)
700
716
 
701
717
  response = api_request(method, url, parameters, options)
702
718
  response['webhook_id'] = options[:webhook_id] if options[:webhook_id]
703
719
  success = success_from(response, options)
704
720
 
705
- card = card_from_response(response)
706
- avs_code = AVS_CODE_TRANSLATOR["line1: #{card['address_line1_check']}, zip: #{card['address_zip_check']}"]
707
- cvc_code = CVC_CODE_TRANSLATOR[card['cvc_check']]
708
- Response.new(success,
721
+ card_checks = card_from_response(response)
722
+ avs_code = AVS_CODE_TRANSLATOR["line1: #{card_checks['address_line1_check']}, zip: #{card_checks['address_zip_check'] || card_checks['address_postal_code_check']}"]
723
+ cvc_code = CVC_CODE_TRANSLATOR[card_checks['cvc_check']]
724
+ Response.new(
725
+ success,
709
726
  message_from(success, response),
710
727
  response,
711
728
  test: response_is_test?(response),
712
- authorization: authorization_from(success, url, method, response),
729
+ authorization: authorization_from(success, url, method, response, options),
713
730
  avs_result: { code: avs_code },
714
731
  cvv_result: cvc_code,
715
732
  emv_authorization: emv_authorization_from_response(response),
716
- error_code: success ? nil : error_code_from(response))
733
+ error_code: success ? nil : error_code_from(response)
734
+ )
717
735
  end
718
736
 
719
737
  def key_valid?(options)
720
738
  return true unless test?
721
739
 
722
740
  %w(sk rk).each do |k|
723
- if key(options).start_with?(k)
724
- return false unless key(options).start_with?("#{k}_test")
725
- end
741
+ return false if key(options).start_with?(k) && !key(options).start_with?("#{k}_test")
726
742
  end
727
743
 
728
744
  true
729
745
  end
730
746
 
731
- def authorization_from(success, url, method, response)
732
- return response.fetch('error', {})['charge'] unless success
747
+ def authorization_from(success, url, method, response, options)
748
+ return response.dig('error', 'charge') || response.dig('error', 'setup_intent', 'id') || response['id'] unless success
733
749
 
734
750
  if url == 'customers'
735
751
  [response['id'], response.dig('sources', 'data').first&.dig('id')].join('|')
736
- elsif method == :post && (url.match(/customers\/.*\/cards/) || url.match(/payment_methods\/.*\/attach/))
737
- [response['customer'], response['id']].join('|')
752
+ elsif method == :post && (url.match(/customers\/.*\/cards/) || url.match(/payment_methods\/.*\/attach/) || options[:action] == :store)
753
+ response_id = options[:action] == :store ? response['payment_method'] : response['id']
754
+ [response['customer'], response_id].join('|')
738
755
  else
739
756
  response['id']
740
757
  end
@@ -748,6 +765,18 @@ module ActiveMerchant #:nodoc:
748
765
  !response.key?('error') && response['status'] != 'failed'
749
766
  end
750
767
 
768
+ # Override the regular handle response so we can access the headers
769
+ # set header fields and values so we can add them to the response body
770
+ def handle_response(response)
771
+ @response_headers = response.each_header.to_h if response.respond_to?(:header)
772
+ case response.code.to_i
773
+ when 200...300
774
+ response.body
775
+ else
776
+ raise ResponseError.new(response)
777
+ end
778
+ end
779
+
751
780
  def response_error(raw_response)
752
781
  parse(raw_response)
753
782
  rescue JSON::ParserError
@@ -783,7 +812,10 @@ module ActiveMerchant #:nodoc:
783
812
  end
784
813
 
785
814
  def card_from_response(response)
786
- response['card'] || response['active_card'] || response['source'] || {}
815
+ # StripePI puts the AVS and CVC check significantly deeper into the response object
816
+ response['card'] || response['active_card'] || response['source'] ||
817
+ response.dig('charges', 'data', 0, 'payment_method_details', 'card', 'checks') ||
818
+ response.dig('latest_attempt', 'payment_method_details', 'card', 'checks') || {}
787
819
  end
788
820
 
789
821
  def emv_authorization_from_response(response)
@@ -11,10 +11,15 @@ module ActiveMerchant #:nodoc:
11
11
  CONFIRM_INTENT_ATTRIBUTES = %i[receipt_email return_url save_payment_method setup_future_usage off_session]
12
12
  UPDATE_INTENT_ATTRIBUTES = %i[description statement_descriptor_suffix statement_descriptor receipt_email setup_future_usage]
13
13
  DEFAULT_API_VERSION = '2020-08-27'
14
+ DIGITAL_WALLETS = {
15
+ apple_pay: 'apple_pay',
16
+ google_pay: 'google_pay_dpan',
17
+ untokenized_google_pay: 'google_pay_ecommerce_token'
18
+ }
14
19
 
15
20
  def create_intent(money, payment_method, options = {})
16
21
  MultiResponse.run do |r|
17
- if payment_method.is_a?(NetworkTokenizationCreditCard)
22
+ if payment_method.is_a?(NetworkTokenizationCreditCard) && digital_wallet_payment_method?(payment_method) && options[:new_ap_gp_route] != true
18
23
  r.process { tokenize_apple_google(payment_method, options) }
19
24
  payment_method = (r.params['token']['id']) if r.success?
20
25
  end
@@ -25,9 +30,15 @@ module ActiveMerchant #:nodoc:
25
30
  add_confirmation_method(post, options)
26
31
  add_customer(post, options)
27
32
 
28
- result = add_payment_method_token(post, payment_method, options)
29
- return result if result.is_a?(ActiveMerchant::Billing::Response)
33
+ if new_apple_google_pay_flow(payment_method, options)
34
+ add_digital_wallet(post, payment_method, options)
35
+ add_billing_address(post, payment_method, options)
36
+ else
37
+ result = add_payment_method_token(post, payment_method, options)
38
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
39
+ end
30
40
 
41
+ add_network_token_cryptogram_and_eci(post, payment_method)
31
42
  add_external_three_d_secure_auth_data(post, options)
32
43
  add_metadata(post, options)
33
44
  add_return_url(post, options)
@@ -43,6 +54,8 @@ module ActiveMerchant #:nodoc:
43
54
  add_fulfillment_date(post, options)
44
55
  request_three_d_secure(post, options)
45
56
  add_level_three(post, options)
57
+ add_card_brand(post, options)
58
+ post[:expand] = ['charges.data.balance_transaction']
46
59
 
47
60
  CREATE_INTENT_ATTRIBUTES.each do |attribute|
48
61
  add_whitelisted_attribute(post, options, attribute)
@@ -63,8 +76,12 @@ module ActiveMerchant #:nodoc:
63
76
 
64
77
  def confirm_intent(intent_id, payment_method, options = {})
65
78
  post = {}
66
- result = add_payment_method_token(post, payment_method, options)
67
- return result if result.is_a?(ActiveMerchant::Billing::Response)
79
+ if new_apple_google_pay_flow(payment_method, options)
80
+ add_digital_wallet(post, payment_method, options)
81
+ else
82
+ result = add_payment_method_token(post, payment_method, options)
83
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
84
+ end
68
85
 
69
86
  add_payment_method_types(post, options)
70
87
  CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
@@ -80,22 +97,28 @@ module ActiveMerchant #:nodoc:
80
97
  commit(:post, 'payment_methods', post_data, options)
81
98
  end
82
99
 
100
+ def new_apple_google_pay_flow(payment_method, options)
101
+ return false unless options[:new_ap_gp_route]
102
+
103
+ payment_method.is_a?(NetworkTokenizationCreditCard) && digital_wallet_payment_method?(payment_method)
104
+ end
105
+
83
106
  def add_payment_method_data(payment_method, options = {})
84
107
  post = {
85
108
  type: 'card',
86
109
  card: {
87
- number: payment_method.number,
88
110
  exp_month: payment_method.month,
89
111
  exp_year: payment_method.year
90
112
  }
91
113
  }
92
-
114
+ post[:card][:number] = payment_method.number unless adding_network_token_card_data?(payment_method)
93
115
  post[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
94
116
  if billing = options[:billing_address] || options[:address]
95
117
  post[:billing_details] = add_address(billing, options)
96
118
  end
97
119
 
98
120
  add_name_only(post, payment_method) if post[:billing_details].nil?
121
+ add_network_token_data(post, payment_method, options)
99
122
  post
100
123
  end
101
124
 
@@ -110,8 +133,12 @@ module ActiveMerchant #:nodoc:
110
133
  post = {}
111
134
  add_amount(post, money, options)
112
135
 
113
- result = add_payment_method_token(post, payment_method, options)
114
- return result if result.is_a?(ActiveMerchant::Billing::Response)
136
+ if new_apple_google_pay_flow(payment_method, options)
137
+ add_digital_wallet(post, payment_method, options)
138
+ else
139
+ result = add_payment_method_token(post, payment_method, options)
140
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
141
+ end
115
142
 
116
143
  add_payment_method_types(post, options)
117
144
  add_customer(post, options)
@@ -131,16 +158,25 @@ module ActiveMerchant #:nodoc:
131
158
  r.process do
132
159
  post = {}
133
160
  add_customer(post, options)
134
- result = add_payment_method_token(post, payment_method, options, r)
135
- return result if result.is_a?(ActiveMerchant::Billing::Response)
161
+
162
+ if new_apple_google_pay_flow(payment_method, options)
163
+ add_digital_wallet(post, payment_method, options)
164
+ add_billing_address(post, payment_method, options)
165
+ else
166
+ result = add_payment_method_token(post, payment_method, options, r)
167
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
168
+ end
136
169
 
137
170
  add_metadata(post, options)
138
171
  add_return_url(post, options)
139
172
  add_fulfillment_date(post, options)
140
173
  request_three_d_secure(post, options)
174
+ add_card_brand(post, options)
175
+ add_exemption(post, options)
141
176
  post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
142
177
  post[:usage] = options[:usage] if %w(on_session off_session).include?(options[:usage])
143
178
  post[:description] = options[:description] if options[:description]
179
+ post[:expand] = ['latest_attempt']
144
180
 
145
181
  commit(:post, 'setup_intents', post, options)
146
182
  end
@@ -210,14 +246,16 @@ module ActiveMerchant #:nodoc:
210
246
  # All other types will default to legacy Stripe store
211
247
  def store(payment_method, options = {})
212
248
  params = {}
213
- post = {}
214
249
  # If customer option is provided, create a payment method and attach to customer id
215
250
  # Otherwise, create a customer, then attach
216
- if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
251
+ if new_apple_google_pay_flow(payment_method, options)
252
+ options[:customer] = customer(payment_method, options).params['id'] unless options[:customer]
253
+ verify(payment_method, options.merge!(action: :store))
254
+ elsif payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
217
255
  result = add_payment_method_token(params, payment_method, options)
218
256
  return result if result.is_a?(ActiveMerchant::Billing::Response)
219
257
 
220
- customer_id = options[:customer] || customer(post, payment_method, options).params['id']
258
+ customer_id = options[:customer] || customer(payment_method, options).params['id']
221
259
  options = format_idempotency_key(options, 'attach')
222
260
  attach_parameters = { customer: customer_id }
223
261
  attach_parameters[:validate] = options[:validate] unless options[:validate].nil?
@@ -227,7 +265,8 @@ module ActiveMerchant #:nodoc:
227
265
  end
228
266
  end
229
267
 
230
- def customer(post, payment, options)
268
+ def customer(payment, options)
269
+ post = {}
231
270
  post[:description] = options[:description] if options[:description]
232
271
  post[:expand] = [:sources]
233
272
  post[:email] = options[:email]
@@ -267,8 +306,22 @@ module ActiveMerchant #:nodoc:
267
306
  commit(:post, 'payment_intents', post, options)
268
307
  end
269
308
 
309
+ def supports_network_tokenization?
310
+ true
311
+ end
312
+
270
313
  private
271
314
 
315
+ def digital_wallet_payment_method?(payment_method)
316
+ payment_method.source == :google_pay || payment_method.source == :apple_pay
317
+ end
318
+
319
+ def adding_network_token_card_data?(payment_method)
320
+ return true if payment_method.is_a?(ActiveMerchant::Billing::NetworkTokenizationCreditCard) && payment_method.source == :network_token
321
+
322
+ false
323
+ end
324
+
272
325
  def off_session_request?(options = {})
273
326
  (options[:off_session] || options[:setup_future_usage]) && options[:confirm] == true
274
327
  end
@@ -307,6 +360,14 @@ module ActiveMerchant #:nodoc:
307
360
  post[:metadata][:event_type] = options[:event_type] if options[:event_type]
308
361
  end
309
362
 
363
+ def add_card_brand(post, options)
364
+ return unless options[:card_brand]
365
+
366
+ post[:payment_method_options] ||= {}
367
+ post[:payment_method_options][:card] ||= {}
368
+ post[:payment_method_options][:card][:network] = options[:card_brand] if options[:card_brand]
369
+ end
370
+
310
371
  def add_level_three(post, options = {})
311
372
  level_three = {}
312
373
 
@@ -343,6 +404,72 @@ module ActiveMerchant #:nodoc:
343
404
  return create_payment_method_and_extract_token(post, payment_method, options, responses) if options[:verify]
344
405
 
345
406
  get_payment_method_data_from_card(post, payment_method, options, responses)
407
+ when ActiveMerchant::Billing::NetworkTokenizationCreditCard
408
+ get_payment_method_data_from_card(post, payment_method, options, responses)
409
+ end
410
+ end
411
+
412
+ def add_network_token_data(post_data, payment_method, options)
413
+ return unless adding_network_token_card_data?(payment_method)
414
+
415
+ post_data[:card] ||= {}
416
+ post_data[:card][:last4] = options[:last_4]
417
+ post_data[:card][:network_token] = {}
418
+ post_data[:card][:network_token][:number] = payment_method.number
419
+ post_data[:card][:network_token][:exp_month] = payment_method.month
420
+ post_data[:card][:network_token][:exp_year] = payment_method.year
421
+ post_data[:card][:network_token][:payment_account_reference] = options[:payment_account_reference] if options[:payment_account_reference]
422
+
423
+ post_data
424
+ end
425
+
426
+ def add_network_token_cryptogram_and_eci(post, payment_method)
427
+ return unless adding_network_token_card_data?(payment_method)
428
+
429
+ post[:payment_method_options] ||= {}
430
+ post[:payment_method_options][:card] ||= {}
431
+ post[:payment_method_options][:card][:network_token] ||= {}
432
+ post[:payment_method_options][:card][:network_token][:cryptogram] = payment_method.payment_cryptogram if payment_method.payment_cryptogram
433
+ post[:payment_method_options][:card][:network_token][:electronic_commerce_indicator] = payment_method.eci if payment_method.eci
434
+ end
435
+
436
+ def add_digital_wallet(post, payment_method, options)
437
+ post[:payment_method_data] = {
438
+ type: 'card',
439
+ card: {
440
+ last4: options[:last_4] || payment_method.number[-4..],
441
+ exp_month: payment_method.month,
442
+ exp_year: payment_method.year,
443
+ network_token: {
444
+ number: payment_method.number,
445
+ exp_month: payment_method.month,
446
+ exp_year: payment_method.year
447
+ }
448
+ }
449
+ }
450
+
451
+ add_cryptogram_and_eci(post, payment_method, options) unless options[:wallet_type]
452
+ source = payment_method.respond_to?(:source) ? payment_method.source : options[:wallet_type]
453
+ post[:payment_method_data][:card][:network_token][:tokenization_method] = DIGITAL_WALLETS[source]
454
+ end
455
+
456
+ def add_cryptogram_and_eci(post, payment_method, options)
457
+ post[:payment_method_options] ||= {}
458
+ post[:payment_method_options][:card] ||= {}
459
+ post[:payment_method_options][:card][:network_token] ||= {}
460
+ post[:payment_method_options][:card][:network_token] = {
461
+ cryptogram: payment_method.respond_to?(:payment_cryptogram) ? payment_method.payment_cryptogram : options[:cryptogram],
462
+ electronic_commerce_indicator: format_eci(payment_method, options)
463
+ }.compact
464
+ end
465
+
466
+ def format_eci(payment_method, options)
467
+ eci_value = payment_method.respond_to?(:eci) ? payment_method.eci : options[:eci]
468
+
469
+ if eci_value&.length == 1
470
+ "0#{eci_value}"
471
+ else
472
+ eci_value
346
473
  end
347
474
  end
348
475
 
@@ -384,7 +511,7 @@ module ActiveMerchant #:nodoc:
384
511
  end
385
512
 
386
513
  def get_payment_method_data_from_card(post, payment_method, options, responses)
387
- return create_payment_method_and_extract_token(post, payment_method, options, responses) unless off_session_request?(options)
514
+ return create_payment_method_and_extract_token(post, payment_method, options, responses) unless off_session_request?(options) || adding_network_token_card_data?(payment_method)
388
515
 
389
516
  post[:payment_method_data] = add_payment_method_data(payment_method, options)
390
517
  end
@@ -405,7 +532,7 @@ module ActiveMerchant #:nodoc:
405
532
  end
406
533
 
407
534
  def add_exemption(post, options = {})
408
- return unless options[:confirm]
535
+ return unless options[:confirm] && options[:moto]
409
536
 
410
537
  post[:payment_method_options] ||= {}
411
538
  post[:payment_method_options][:card] ||= {}
@@ -418,63 +545,71 @@ module ActiveMerchant #:nodoc:
418
545
  # the existing logic by default. To be able to utilize this field, you must reach out to Stripe.
419
546
 
420
547
  def add_stored_credentials(post, options = {})
421
- return unless options[:stored_credential] && !options[:stored_credential].values.all?(&:nil?)
548
+ stored_credential = options[:stored_credential]
549
+ return unless stored_credential && !stored_credential.values.all?(&:nil?)
422
550
 
423
551
  post[:payment_method_options] ||= {}
424
552
  post[:payment_method_options][:card] ||= {}
425
- add_stored_credential_transaction_type(post, options) if options[:stored_credential_transaction_type]
426
553
 
427
- stored_credential = options[:stored_credential]
428
- post[:payment_method_options][:card][:mit_exemption] = {}
554
+ card_options = post[:payment_method_options][:card]
555
+ card_options[:mit_exemption] = {}
429
556
 
430
557
  # Stripe PI accepts network_transaction_id and ds_transaction_id via mit field under card.
431
558
  # The network_transaction_id can be sent in nested under stored credentials OR as its own field (add_ntid handles when it is sent in on its own)
432
559
  # If it is sent is as its own field AND under stored credentials, the value sent under its own field is what will send.
433
- post[:payment_method_options][:card][:mit_exemption][:ds_transaction_id] = stored_credential[:ds_transaction_id] if stored_credential[:ds_transaction_id]
434
- post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
560
+ card_options[:mit_exemption][:ds_transaction_id] = stored_credential[:ds_transaction_id] if stored_credential[:ds_transaction_id]
561
+ card_options[:mit_exemption][:network_transaction_id] = stored_credential[:network_transaction_id] if !(options[:setup_future_usage] == 'off_session') && (stored_credential[:network_transaction_id])
562
+
563
+ add_stored_credential_transaction_type(post, options)
435
564
  end
436
565
 
437
566
  def add_stored_credential_transaction_type(post, options = {})
567
+ return unless options[:stored_credential_transaction_type]
568
+
438
569
  stored_credential = options[:stored_credential]
439
570
  # Do not add anything unless these are present.
440
571
  return unless stored_credential[:reason_type] && stored_credential[:initiator]
441
572
 
442
573
  # Not compatible with off_session parameter.
443
574
  options.delete(:off_session)
444
- if stored_credential[:initial_transaction]
445
- # Initial transactions must by CIT
446
- return unless stored_credential[:initiator] == 'cardholder'
447
575
 
448
- initial_transaction_stored_credential(post, stored_credential[:reason_type])
449
- else
450
- # Subsequent transaction
451
- subsequent_transaction_stored_credential(post, stored_credential[:initiator], stored_credential[:reason_type])
452
- end
576
+ stored_credential_type = if stored_credential[:initial_transaction]
577
+ return unless stored_credential[:initiator] == 'cardholder'
578
+
579
+ initial_transaction_stored_credential(post, stored_credential)
580
+ else
581
+ subsequent_transaction_stored_credential(post, stored_credential)
582
+ end
583
+
584
+ card_options = post[:payment_method_options][:card]
585
+ card_options[:stored_credential_transaction_type] = stored_credential_type
586
+ card_options[:mit_exemption].delete(:network_transaction_id) if stored_credential_type == 'setup_on_session'
453
587
  end
454
588
 
455
- def initial_transaction_stored_credential(post, reason_type)
456
- if reason_type == 'unscheduled'
589
+ def initial_transaction_stored_credential(post, stored_credential)
590
+ case stored_credential[:reason_type]
591
+ when 'unscheduled'
457
592
  # Charge on-session and store card for future one-off payment use
458
- post[:payment_method_options][:card][:stored_credential_transaction_type] = 'setup_off_session_unscheduled'
459
- elsif reason_type == 'recurring'
593
+ 'setup_off_session_unscheduled'
594
+ when 'recurring'
460
595
  # Charge on-session and store card for future recurring payment use
461
- post[:payment_method_options][:card][:stored_credential_transaction_type] = 'setup_off_session_recurring'
596
+ 'setup_off_session_recurring'
462
597
  else
463
598
  # Charge on-session and store card for future on-session payment use.
464
- post[:payment_method_options][:card][:stored_credential_transaction_type] = 'setup_on_session'
599
+ 'setup_on_session'
465
600
  end
466
601
  end
467
602
 
468
- def subsequent_transaction_stored_credential(post, initiator, reason_type)
469
- if initiator == 'cardholder'
603
+ def subsequent_transaction_stored_credential(post, stored_credential)
604
+ if stored_credential[:initiator] == 'cardholder'
470
605
  # Charge on-session customer using previously stored card.
471
- post[:payment_method_options][:card][:stored_credential_transaction_type] = 'stored_on_session'
472
- elsif reason_type == 'recurring'
606
+ 'stored_on_session'
607
+ elsif stored_credential[:reason_type] == 'recurring'
473
608
  # Charge off-session customer using previously stored card for recurring transaction
474
- post[:payment_method_options][:card][:stored_credential_transaction_type] = 'stored_off_session_recurring'
609
+ 'stored_off_session_recurring'
475
610
  else
476
611
  # Charge off-session customer using previously stored card for one-off transaction
477
- post[:payment_method_options][:card][:stored_credential_transaction_type] = 'stored_off_session_unscheduled'
612
+ 'stored_off_session_unscheduled'
478
613
  end
479
614
  end
480
615
 
@@ -485,7 +620,7 @@ module ActiveMerchant #:nodoc:
485
620
  post[:payment_method_options][:card] ||= {}
486
621
  post[:payment_method_options][:card][:mit_exemption] = {}
487
622
 
488
- post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = options[:network_transaction_id] if options[:network_transaction_id]
623
+ post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = options[:network_transaction_id]
489
624
  end
490
625
 
491
626
  def add_claim_without_transaction_id(post, options = {})
@@ -501,6 +636,16 @@ module ActiveMerchant #:nodoc:
501
636
  post[:payment_method_options][:card][:mit_exemption][:claim_without_transaction_id] = options[:claim_without_transaction_id]
502
637
  end
503
638
 
639
+ def add_billing_address_for_card_tokenization(post, options = {})
640
+ return unless (billing = options[:billing_address] || options[:address])
641
+
642
+ billing = add_address(billing, options)
643
+ billing[:address].transform_keys! { |k| k == :postal_code ? :address_zip : k.to_s.prepend('address_').to_sym }
644
+
645
+ post[:card][:name] = billing[:name]
646
+ post[:card].merge!(billing[:address])
647
+ end
648
+
504
649
  def add_error_on_requires_action(post, options = {})
505
650
  return unless options[:confirm]
506
651
 
@@ -534,14 +679,18 @@ module ActiveMerchant #:nodoc:
534
679
  post
535
680
  end
536
681
 
537
- def add_billing_address_for_card_tokenization(post, options = {})
538
- return unless (billing = options[:billing_address] || options[:address])
682
+ def add_billing_address(post, payment_method, options = {})
683
+ return if payment_method.nil? || payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(String)
539
684
 
540
- billing = add_address(billing, options)
541
- billing[:address].transform_keys! { |k| k == :postal_code ? :address_zip : k.to_s.prepend('address_').to_sym }
685
+ post[:payment_method_data] ||= {}
686
+ if billing = options[:billing_address] || options[:address]
687
+ post[:payment_method_data][:billing_details] = add_address(billing, options)
688
+ end
542
689
 
543
- post[:card][:name] = billing[:name]
544
- post[:card].merge!(billing[:address])
690
+ unless post[:payment_method_data][:billing_details]
691
+ name = [payment_method.first_name, payment_method.last_name].compact.join(' ')
692
+ post[:payment_method_data][:billing_details] = { name: name }
693
+ end
545
694
  end
546
695
 
547
696
  def add_shipping_address(post, options = {})