activemerchant 1.133.0 → 1.137.0

Sign up to get free protection for your applications and to get access to all the features.
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 = {})