From 66ba7524be215ae22b467154961b589bc1e0e906 Mon Sep 17 00:00:00 2001 From: Rachel Kirk Date: Sat, 3 Jun 2023 01:54:43 -0400 Subject: [PATCH] Checkout: Add support for several customer data fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CER-595 Ran into a lot of tricky data collisions and had to do some finagling to make sure existing test cases would pass. I left open two possibilities for passing in the phone number depending on how users would like to pass it in: manually via options or via the phone number that’s attached to credit card billing data on the payment method. It’s possible to pay with a stored payment method which would be a `String`. In this case there’s no name data attached so added some guarding against NoMethodErrors that were resulting from trying to call payment_method.name. Remote Tests: 92 tests, 221 assertions, 3 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 96.7391% passed Unit Tests: 57 tests, 319 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed Local Tests: 5516 tests, 77434 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed --- .../billing/gateways/checkout_v2.rb | 10 ++++++ .../gateways/remote_checkout_v2_test.rb | 36 ++++++++++++++----- test/unit/gateways/checkout_v2_test.rb | 35 ++++++++++++++++-- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/lib/active_merchant/billing/gateways/checkout_v2.rb b/lib/active_merchant/billing/gateways/checkout_v2.rb index 59c68e8272f..8cceb853869 100644 --- a/lib/active_merchant/billing/gateways/checkout_v2.rb +++ b/lib/active_merchant/billing/gateways/checkout_v2.rb @@ -139,6 +139,7 @@ def build_auth_or_purchase(post, amount, payment_method, options) add_authorization_type(post, options) add_payment_method(post, payment_method, options) add_customer_data(post, options) + add_extra_customer_data(post, payment_method, options) add_shipping_address(post, options) add_stored_credential_options(post, options) add_transaction_data(post, options) @@ -239,6 +240,15 @@ def add_customer_data(post, options) end end + # created a separate method for these fields because they should not be included + # in all transaction types that include methods with source and customer fields + def add_extra_customer_data(post, payment_method, options) + post[:source][:phone] = {} + post[:source][:phone][:number] = options[:phone] || options.dig(:billing_address, :phone) || options.dig(:billing_address, :phone_number) + post[:source][:phone][:country_code] = options[:phone_country_code] if options[:phone_country_code] + post[:customer][:name] = payment_method.name if payment_method.respond_to?(:name) + end + def add_shipping_address(post, options) if address = options[:shipping_address] post[:shipping] = {} diff --git a/test/remote/gateways/remote_checkout_v2_test.rb b/test/remote/gateways/remote_checkout_v2_test.rb index fbc1fddeb78..d03e48b4748 100644 --- a/test/remote/gateways/remote_checkout_v2_test.rb +++ b/test/remote/gateways/remote_checkout_v2_test.rb @@ -100,6 +100,10 @@ def setup authentication_response_status: 'Y' } ) + @extra_customer_data = @options.merge( + phone_country_code: '1', + phone: '9108675309' + ) end def test_transcript_scrubbing @@ -312,8 +316,8 @@ def test_successful_purchase_includes_avs_result response = @gateway.purchase(@amount, @credit_card, @options) assert_success response assert_equal 'Succeeded', response.message - assert_equal 'G', response.avs_result['code'] - assert_equal 'Non-U.S. issuing bank does not support AVS.', response.avs_result['message'] + assert_equal 'S', response.avs_result['code'] + assert_equal 'U.S.-issuing bank does not support AVS.', response.avs_result['message'] end def test_successful_purchase_includes_avs_result_via_oauth @@ -328,8 +332,8 @@ def test_successful_authorize_includes_avs_result response = @gateway.authorize(@amount, @credit_card, @options) assert_success response assert_equal 'Succeeded', response.message - assert_equal 'G', response.avs_result['code'] - assert_equal 'Non-U.S. issuing bank does not support AVS.', response.avs_result['message'] + assert_equal 'S', response.avs_result['code'] + assert_equal 'U.S.-issuing bank does not support AVS.', response.avs_result['message'] end def test_successful_purchase_includes_cvv_result @@ -339,6 +343,12 @@ def test_successful_purchase_includes_cvv_result assert_equal 'Y', response.cvv_result['code'] end + def test_successful_purchase_with_extra_customer_data + response = @gateway.purchase(@amount, @credit_card, @extra_customer_data) + assert_success response + assert_equal 'Succeeded', response.message + end + def test_successful_authorize_includes_cvv_result response = @gateway.authorize(@amount, @credit_card, @options) assert_success response @@ -432,7 +442,15 @@ def test_successful_purchase_with_shipping_address end def test_successful_purchase_without_phone_number - response = @gateway.purchase(@amount, @credit_card, billing_address: address.update(phone: '')) + response = @gateway.purchase(@amount, @credit_card, billing_address: address.update(phone: nil)) + assert_success response + assert_equal 'Succeeded', response.message + end + + def test_successful_purchase_without_name + credit_card = credit_card('4242424242424242', verification_value: '100', month: '6', year: Time.now.year + 1, first_name: nil, last_name: nil) + response = @gateway.purchase(@amount, credit_card, @options) + assert_equal response.params['source']['name'], '' assert_success response assert_equal 'Succeeded', response.message end @@ -446,7 +464,7 @@ def test_successful_purchase_with_ip def test_failed_purchase response = @gateway.purchase(100, @credit_card_dnh, @options) assert_failure response - assert_equal 'Declined - Do Not Honour', response.message + assert_equal 'Invalid Card Number', response.message end def test_failed_purchase_via_oauth @@ -470,7 +488,7 @@ def test_avs_failed_authorize def test_invalid_shipping_address response = @gateway.authorize(@amount, @credit_card, shipping_address: address.update(country: 'Canada')) assert_failure response - assert_equal 'request_invalid: address_country_invalid', response.message + assert_equal 'request_invalid: country_address_invalid', response.message end def test_successful_authorize_and_capture @@ -827,8 +845,8 @@ def test_failed_verify def test_expired_card_returns_error_code response = @gateway.purchase(@amount, @expired_card, @options) assert_failure response - assert_equal 'processing_error: card_expired', response.message - assert_equal 'processing_error: card_expired', response.error_code + assert_equal 'request_invalid: card_expired', response.message + assert_equal 'request_invalid: card_expired', response.error_code end def test_successful_purchase_with_idempotency_key diff --git a/test/unit/gateways/checkout_v2_test.rb b/test/unit/gateways/checkout_v2_test.rb index f0f59a7d6f7..7256db54e3f 100644 --- a/test/unit/gateways/checkout_v2_test.rb +++ b/test/unit/gateways/checkout_v2_test.rb @@ -24,6 +24,7 @@ def setup }) @credit_card = credit_card @amount = 100 + @token = '2MPedsuenG2o8yFfrsdOBWmOuEf' end def test_successful_purchase @@ -413,6 +414,36 @@ def test_successful_purchase_with_stored_credentials_merchant_initiated_transact assert_equal 'Succeeded', response.message end + def test_successful_purchase_with_extra_customer_data + stub_comms(@gateway, :ssl_request) do + options = { + phone_country_code: '1', + billing_address: address + } + @gateway.purchase(@amount, @credit_card, options) + end.check_request do |_method, _endpoint, data, _headers| + request = JSON.parse(data) + assert_equal request['source']['phone']['number'], '(555)555-5555' + assert_equal request['source']['phone']['country_code'], '1' + assert_equal request['customer']['name'], 'Longbob Longsen' + end.respond_with(successful_purchase_response) + end + + def test_no_customer_name_included_in_token_purchase + stub_comms(@gateway, :ssl_request) do + options = { + phone_country_code: '1', + billing_address: address + } + @gateway.purchase(@amount, @token, options) + end.check_request do |_method, _endpoint, data, _headers| + request = JSON.parse(data) + assert_equal request['source']['phone']['number'], '(555)555-5555' + assert_equal request['source']['phone']['country_code'], '1' + refute_includes data, 'name' + end.respond_with(successful_purchase_response) + end + def test_successful_purchase_with_metadata response = stub_comms(@gateway, :ssl_request) do options = { @@ -810,7 +841,7 @@ def test_purchase_supports_alternate_credit_card_implementation alternate_credit_card = alternate_credit_card_class.new alternate_credit_card.expects(:credit_card?).returns(true) - alternate_credit_card.expects(:name).returns(@credit_card.name) + alternate_credit_card.expects(:name).at_least_once.returns(@credit_card.name) alternate_credit_card.expects(:number).returns(@credit_card.number) alternate_credit_card.expects(:verification_value).returns(@credit_card.verification_value) alternate_credit_card.expects(:first_name).at_least_once.returns(@credit_card.first_name) @@ -826,7 +857,7 @@ def test_authorize_supports_alternate_credit_card_implementation alternate_credit_card = alternate_credit_card_class.new alternate_credit_card.expects(:credit_card?).returns(true) - alternate_credit_card.expects(:name).returns(@credit_card.name) + alternate_credit_card.expects(:name).at_least_once.returns(@credit_card.name) alternate_credit_card.expects(:number).returns(@credit_card.number) alternate_credit_card.expects(:verification_value).returns(@credit_card.verification_value) alternate_credit_card.expects(:first_name).at_least_once.returns(@credit_card.first_name)