diff --git a/lib/active_merchant/billing/gateways/worldpay.rb b/lib/active_merchant/billing/gateways/worldpay.rb index 48d938acd6b..e41248ed925 100644 --- a/lib/active_merchant/billing/gateways/worldpay.rb +++ b/lib/active_merchant/billing/gateways/worldpay.rb @@ -663,7 +663,7 @@ def add_network_tokenization_card(xml, payment_method, options) end name = card_holder_name(payment_method, options) xml.cardHolderName name if name.present? - xml.cryptogram payment_method.payment_cryptogram unless options[:wallet_type] == :google_pay + xml.cryptogram payment_method.payment_cryptogram if should_send_payment_cryptogram?(options) eci = eci_value(payment_method, options) xml.eciIndicator eci if eci.present? end @@ -671,6 +671,10 @@ def add_network_tokenization_card(xml, payment_method, options) end end + def should_send_payment_cryptogram?(options) + !(wallet_type_google_pay?(options) || (wallet_type_apple_pay?(options) && options[:stored_credential][:initiator] == 'merchant')) + end + def add_card_or_token(xml, payment_method, options) xml.paymentDetails credit_fund_transfer_attribute(options) do if options[:payment_type] == :token @@ -1040,16 +1044,26 @@ def payment_details(payment_method, options = {}) when String token_type_and_details(payment_method) else - type = network_token?(payment_method) || options[:wallet_type] == :google_pay ? :network_token : :credit + type = network_token?(payment_method, options) ? :network_token : :credit { payment_type: type } end end - def network_token?(payment_method) - payment_method.respond_to?(:source) && + def network_token?(payment_method, options) + (payment_method.respond_to?(:source) && payment_method.respond_to?(:payment_cryptogram) && - payment_method.respond_to?(:eci) + payment_method.respond_to?(:eci)) || + wallet_type_google_pay?(options) || + wallet_type_apple_pay?(options) + end + + def wallet_type_apple_pay?(options) + options[:wallet_type] == :apple_pay + end + + def wallet_type_google_pay?(options) + options[:wallet_type] == :google_pay end def token_type_and_details(token) diff --git a/test/remote/gateways/remote_worldpay_test.rb b/test/remote/gateways/remote_worldpay_test.rb index f74e0372ea1..c70f074c193 100644 --- a/test/remote/gateways/remote_worldpay_test.rb +++ b/test/remote/gateways/remote_worldpay_test.rb @@ -115,17 +115,25 @@ def setup sub_tax_id: '987-65-4321' } } - @apple_pay_network_token = network_tokenization_credit_card( - '4895370015293175', + @apple_pay_shared_network_token_options = { month: 10, year: Time.new.year + 2, first_name: 'John', last_name: 'Smith', verification_value: '737', - payment_cryptogram: 'abc1234567890', eci: '07', transaction_id: 'abc123', source: :apple_pay + } + + @apple_pay_network_token = network_tokenization_credit_card( + '4895370015293175', + @apple_pay_shared_network_token_options.merge(payment_cryptogram: 'abc1234567890') + ) + + @apple_pay_network_token_without_payment_cryptogram = network_tokenization_credit_card( + '4895370015293175', + @apple_pay_shared_network_token_options ) @google_pay_network_token = network_tokenization_credit_card( @@ -669,6 +677,29 @@ def test_successful_auth_and_capture_with_gateway_specific_recurring_stored_cred assert_success capture end + def test_successful_recurring_purchase_with_apple_pay_credentials + stored_credential_params = stored_credential(:initial, :recurring, :merchant) + assert auth = @gateway.authorize(@amount, @apple_pay_network_token, @options.merge({ stored_credential: stored_credential_params, wallet_type: :apple_pay })) + assert_success auth + assert auth.authorization + assert auth.params['scheme_response'] + assert auth.params['transaction_identifier'] + + assert capture = @gateway.capture(@amount, auth.authorization, authorization_validated: true) + assert_success capture + + @options[:order_id] = generate_unique_id + @options[:stored_credential] = stored_credential(:used, :recurring, :merchant, network_transaction_id: auth.params['transaction_identifier']) + + assert next_auth = @gateway.authorize(@amount, @apple_pay_network_token_without_payment_cryptogram, @options.merge(wallet_type: :apple_pay)) + assert next_auth.authorization + assert next_auth.params['scheme_response'] + assert next_auth.params['transaction_identifier'] + + assert capture = @gateway.capture(@amount, next_auth.authorization, authorization_validated: true) + assert_success capture + end + def test_successful_authorize_with_3ds_with_normalized_stored_credentials session_id = generate_unique_id stored_credential_params = stored_credential(:initial, :unscheduled, :merchant) diff --git a/test/unit/gateways/worldpay_test.rb b/test/unit/gateways/worldpay_test.rb index ae60b8d8d68..3864ef3cc27 100644 --- a/test/unit/gateways/worldpay_test.rb +++ b/test/unit/gateways/worldpay_test.rb @@ -75,6 +75,18 @@ def setup eci: '05' ) + @apple_pay_network_token_without_payment_cryptogram = network_tokenization_credit_card( + '4895370015293175', + month: 10, + year: Time.new.year + 2, + first_name: 'John', + last_name: 'Smith', + verification_value: '737', + eci: '07', + transaction_id: 'abc123', + source: :apple_pay + ) + @google_pay_network_token_without_eci = network_tokenization_credit_card( '4444333322221111', payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=', @@ -1588,6 +1600,22 @@ def test_authorize_prefers_options_for_ntid assert_success response end + def test_authorize_recurring_apple_pay_with_ntid + stored_credential_params = stored_credential(:used, :recurring, :merchant, network_transaction_id: '3812908490218390214124') + + @options.merge({ + stored_credential: stored_credential_params, + stored_credential_transaction_id: '000000000000020005060720116005060', + wallet_type: :apple_pay + }) + response = stub_comms do + @gateway.authorize(@amount, @apple_pay_network_token_without_payment_cryptogram, @options) + end.check_request do |_endpoint, data, _headers| + assert_match %r(), data + end.respond_with(successful_authorize_response) + assert_success response + end + def test_successful_inquire_with_order_id response = stub_comms do @gateway.inquire(nil, { order_id: @options[:order_id].to_s })