From 941c6d2a756d1dcb23619ebe0cb7eb3caee05a1b Mon Sep 17 00:00:00 2001 From: Javier Pedroza Date: Wed, 17 Jul 2024 08:18:48 -0500 Subject: [PATCH] NMI: Adding GooglePay and ApplePay (#5146) Description ------------------------- This commit add support to create transaction with GooglePay and ApplePay. This payment methods are working for nmi_secure. Unit test ------------------------- Finished in 11.283174 seconds. 59 tests, 475 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed 5.23 tests/s, 42.10 assertions/s Remote test ------------------------- Finished in 115.513346 seconds. 55 tests, 199 assertions, 12 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 78.1818% passed 0.48 tests/s, 1.72 assertions/s Rubocop ------------------------- 798 files inspected, no offenses detected Co-authored-by: Javier Pedroza --- lib/active_merchant/billing/gateways/nmi.rb | 14 ++++- test/remote/gateways/remote_nmi_test.rb | 58 +++++++++++++++++---- test/unit/gateways/nmi_test.rb | 46 ++++++++++++++++ 3 files changed, 107 insertions(+), 11 deletions(-) diff --git a/lib/active_merchant/billing/gateways/nmi.rb b/lib/active_merchant/billing/gateways/nmi.rb index bb53c39eedf..8d91472f0d6 100644 --- a/lib/active_merchant/billing/gateways/nmi.rb +++ b/lib/active_merchant/billing/gateways/nmi.rb @@ -134,6 +134,7 @@ def scrub(transcript) gsub(%r((cvv=)\d+), '\1[FILTERED]'). gsub(%r((checkaba=)\d+), '\1[FILTERED]'). gsub(%r((checkaccount=)\d+), '\1[FILTERED]'). + gsub(%r((cavv=)[^&\n]*), '\1[FILTERED]'). gsub(%r((cryptogram=)[^&]+(&?)), '\1[FILTERED]\2') end @@ -166,7 +167,7 @@ def add_payment_method(post, payment_method, options) elsif payment_method.is_a?(NetworkTokenizationCreditCard) post[:ccnumber] = payment_method.number post[:ccexp] = exp_date(payment_method) - post[:token_cryptogram] = payment_method.payment_cryptogram + add_network_token_fields(post, payment_method) elsif card_brand(payment_method) == 'check' post[:payment] = 'check' post[:firstname] = payment_method.first_name @@ -187,6 +188,17 @@ def add_payment_method(post, payment_method, options) end end + def add_network_token_fields(post, payment_method) + if payment_method.source == :apple_pay || payment_method.source == :google_pay + post[:cavv] = payment_method.payment_cryptogram + post[:eci] = payment_method.eci + post[:decrypted_applepay_data] = 1 + post[:decrypted_googlepay_data] = 1 + else + post[:token_cryptogram] = payment_method.payment_cryptogram + end + end + def add_stored_credential(post, options) return unless (stored_credential = options[:stored_credential]) diff --git a/test/remote/gateways/remote_nmi_test.rb b/test/remote/gateways/remote_nmi_test.rb index bd77940790b..ad1f80d48ce 100644 --- a/test/remote/gateways/remote_nmi_test.rb +++ b/test/remote/gateways/remote_nmi_test.rb @@ -10,15 +10,26 @@ def setup routing_number: '123123123', account_number: '123123123' ) - @apple_pay_card = network_tokenization_credit_card( + @apple_pay = network_tokenization_credit_card( '4111111111111111', payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=', month: '01', - year: '2024', + year: Time.new.year + 2, source: :apple_pay, eci: '5', transaction_id: '123456789' ) + + @google_pay = network_tokenization_credit_card( + '4111111111111111', + payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=', + month: '01', + year: Time.new.year + 2, + source: :google_pay, + transaction_id: '123456789', + eci: '05' + ) + @options = { order_id: generate_unique_id, billing_address: address, @@ -130,17 +141,33 @@ def test_failed_purchase_with_echeck assert_equal 'FAILED', response.message end - def test_successful_purchase_with_apple_pay_card - assert @gateway.supports_network_tokenization? - assert response = @gateway.purchase(@amount, @apple_pay_card, @options) + def test_successful_purchase_with_apple_pay + assert @gateway_secure.supports_network_tokenization? + assert response = @gateway_secure.purchase(@amount, @apple_pay, @options) + assert_success response + assert response.test? + assert_equal 'Succeeded', response.message + assert response.authorization + end + + def test_successful_purchase_with_google_pay + assert @gateway_secure.supports_network_tokenization? + assert response = @gateway_secure.purchase(@amount, @google_pay, @options) assert_success response assert response.test? assert_equal 'Succeeded', response.message assert response.authorization end - def test_failed_purchase_with_apple_pay_card - assert response = @gateway.purchase(99, @apple_pay_card, @options) + def test_failed_purchase_with_apple_pay + assert response = @gateway_secure.purchase(1, @apple_pay, @options) + assert_failure response + assert response.test? + assert_equal 'DECLINE', response.message + end + + def test_failed_purchase_with_google_pay + assert response = @gateway_secure.purchase(1, @google_pay, @options) assert_failure response assert response.test? assert_equal 'DECLINE', response.message @@ -482,12 +509,23 @@ def test_check_transcript_scrubbing def test_network_tokenization_transcript_scrubbing transcript = capture_transcript(@gateway) do - @gateway.purchase(@amount, @apple_pay_card, @options) + @gateway.purchase(@amount, @apple_pay, @options) end clean_transcript = @gateway.scrub(transcript) - assert_scrubbed(@apple_pay_card.number, clean_transcript) - assert_scrubbed(@apple_pay_card.payment_cryptogram, clean_transcript) + assert_scrubbed(@apple_pay.number, clean_transcript) + assert_scrubbed(@apple_pay.payment_cryptogram, clean_transcript) + assert_password_scrubbed(clean_transcript) + end + + def test_transcript_scrubbing_with_google_pay + transcript = capture_transcript(@gateway) do + @gateway.purchase(@amount, @google_pay, @options) + end + + clean_transcript = @gateway.scrub(transcript) + assert_scrubbed(@apple_pay.number, clean_transcript) + assert_scrubbed(@apple_pay.payment_cryptogram, clean_transcript) assert_password_scrubbed(clean_transcript) end diff --git a/test/unit/gateways/nmi_test.rb b/test/unit/gateways/nmi_test.rb index f2817a6a38e..adf79d0c1e6 100644 --- a/test/unit/gateways/nmi_test.rb +++ b/test/unit/gateways/nmi_test.rb @@ -29,6 +29,52 @@ def setup descriptor_merchant_id: '120', descriptor_url: 'url' } + + @google_pay = network_tokenization_credit_card( + '4111111111111111', + brand: 'visa', + eci: '05', + month: '02', + year: '2035', + source: :google_pay, + payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=', + transaction_id: '13456789' + ) + + @apple_pay = network_tokenization_credit_card( + '4111111111111111', + brand: 'visa', + eci: '05', + month: '02', + year: '2035', + source: :apple_pay, + payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=', + transaction_id: '13456789' + ) + end + + def test_successful_apple_pay_purchase + stub_comms do + @gateway.purchase(@amount, @apple_pay) + end.check_request do |_endpoint, data, _headers| + assert_match(/type=sale/, data) + assert_match(/ccnumber=4111111111111111/, data) + assert_match(/ccexp=#{sprintf("%.2i", @apple_pay.month)}#{@apple_pay.year.to_s[-2..-1]}/, data) + assert_match(/cavv=EHuWW9PiBkWvqE5juRwDzAUFBAk%3D/, data) + assert_match(/eci=05/, data) + end.respond_with(successful_purchase_response) + end + + def test_successful_google_pay_purchase + stub_comms do + @gateway.purchase(@amount, @google_pay) + end.check_request do |_endpoint, data, _headers| + assert_match(/type=sale/, data) + assert_match(/ccnumber=4111111111111111/, data) + assert_match(/ccexp=#{sprintf("%.2i", @google_pay.month)}#{@google_pay.year.to_s[-2..-1]}/, data) + assert_match(/cavv=EHuWW9PiBkWvqE5juRwDzAUFBAk%3D/, data) + assert_match(/eci=05/, data) + end.respond_with(successful_purchase_response) end def test_successful_authorize_and_capture_using_security_key