From 5f83d5de1b1d02e46eef5ed5a8ba702a103a984e Mon Sep 17 00:00:00 2001 From: Javier Pedroza Date: Mon, 16 Sep 2024 10:16:25 -0500 Subject: [PATCH] Nuvei: Add 3DS Global Description ------------------------- This commit enable 3ds Global for Nuvei Unit test ------------------------- Finished in 1.229216 seconds. 19 tests, 98 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed 15.46 tests/s, 79.73 assertions/s Remote test ------------------------- Finished in 106.016738 seconds. 30 tests, 99 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed 0.28 tests/s, 0.93 assertions/s Rubocop ------------------------- 801 files inspected, no offenses detected --- lib/active_merchant/billing/gateways/nuvei.rb | 23 +++++++++++++ test/remote/gateways/remote_nuvei_test.rb | 33 +++++++++++++++++++ test/unit/gateways/nuvei_test.rb | 32 ++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/lib/active_merchant/billing/gateways/nuvei.rb b/lib/active_merchant/billing/gateways/nuvei.rb index 471efa4ad56..b9ced486521 100644 --- a/lib/active_merchant/billing/gateways/nuvei.rb +++ b/lib/active_merchant/billing/gateways/nuvei.rb @@ -34,6 +34,7 @@ def authorize(money, payment, options = {}, transaction_type = 'Auth') build_post_data(post) add_amount(post, money, options) add_payment_method(post, payment, :paymentOption, options) + add_3ds_global(post, options) add_address(post, payment, options) add_customer_ip(post, options) add_stored_credentials(post, payment, options) @@ -204,6 +205,28 @@ def add_customer_names(full_name, payment_method) end end + def add_3ds_global(post, options) + return unless (three_d_secure_options = options[:three_d_secure]) + + card_options = post[:paymentOption][:card] ||= {} + card_options[:threeD] = build_three_d_secure_options(three_d_secure_options, options) + end + + def build_three_d_secure_options(three_d_secure_options, options) + three_d_secure_data = { + externalMpi: { + eci: three_d_secure_options[:eci], + cavv: three_d_secure_options[:cavv], + dsTransID: three_d_secure_options[:ds_transaction_id], + challenge_preference: options[:challenge_preference] + } + }.compact + + three_d_secure_data[:externalMpi][:exemptionRequestReason] = options[:exemption_request_reason] if options[:challenge_preference] == 'ExemptionRequest' + + three_d_secure_data + end + def add_address(post, payment, options) return unless address = options[:billing_address] || options[:address] diff --git a/test/remote/gateways/remote_nuvei_test.rb b/test/remote/gateways/remote_nuvei_test.rb index 739e32a55e8..9d5611a35c1 100644 --- a/test/remote/gateways/remote_nuvei_test.rb +++ b/test/remote/gateways/remote_nuvei_test.rb @@ -11,6 +11,7 @@ def setup @challenge_credit_card = credit_card('2221008123677736', first_name: 'CL-BRW2', last_name: '') @three_ds_amount = 151 # for challenge = 151, for frictionless >= 150 @frictionless_credit_card = credit_card('4000020951595032', first_name: 'FL-BRW2', last_name: '') + @credit_card_3ds = credit_card('4000020951595032') @options = { email: 'test@gmail.com', @@ -38,6 +39,14 @@ def setup } } } + + @three_d_secure_options = @options.merge({ + three_d_secure: { + cavv: 'jJ81HADVRtXfCBATEp01CJUAAAA=', + ds_transaction_id: '97267598-FAE6-48F2-8083-C23433990FBC', + eci: '05' + } + }) end def test_transcript_scrubbing @@ -269,4 +278,28 @@ def test_purchase_using_stored_credentials_merchant_installments_cit assert_success recurring_response assert_match 'SUCCESS', recurring_response.params['status'] end + + def test_failing_purchase_three_d_secure + @three_d_secure_options[:three_d_secure][:cavv] = 'wrong_cavv_value' + assert response = @gateway.purchase(@amount, @credit_card_3ds, @three_d_secure_options) + assert_failure response + assert_equal 'UNEXPECTED SYSTEM ERROR - PLEASE RETRY LATER', response.message + assert_match 'ERROR', response.params['transactionStatus'] + end + + def test_successful_purchase_with_three_d_secure + assert response = @gateway.purchase(@amount, @credit_card_3ds, @three_d_secure_options) + assert_success response + assert response.authorization + assert_equal 'APPROVED', response.message + assert_match 'SUCCESS', response.params['status'] + end + + def test_successful_purchase_three_d_secure_challenge_preference + assert response = @gateway.purchase(@amount, @credit_card_3ds, @three_d_secure_options.merge(challenge_preference: 'ExemptionRequest', exemption_request_reason: 'AccountVerification')) + assert_success response + assert_equal 'APPROVED', response.message + assert_match 'SUCCESS', response.params['status'] + assert response.authorization + end end diff --git a/test/unit/gateways/nuvei_test.rb b/test/unit/gateways/nuvei_test.rb index 30862109d16..79b7ed583e4 100644 --- a/test/unit/gateways/nuvei_test.rb +++ b/test/unit/gateways/nuvei_test.rb @@ -40,6 +40,14 @@ def setup } } + @three_d_secure_options = @options.merge({ + three_d_secure: { + cavv: 'jJ81HADVRtXfCBATEp01CJUAAAA=', + ds_transaction_id: '97267598-FAE6-48F2-8083-C23433990FBC', + eci: '05' + } + }) + @post = { merchantId: 'test_merchant_id', merchantSiteId: 'test_merchant_site_id', @@ -251,6 +259,30 @@ def test_successful_stored_credentials_merchant_recurring end.respond_with(successful_purchase_response) end + def test_add_3ds_global_params + stub_comms do + @gateway.authorize(@amount, @credit_card, @three_d_secure_options) + end.check_request do |_method, _endpoint, data, _headers| + assert_equal 'jJ81HADVRtXfCBATEp01CJUAAAA', JSON.parse(data)['threeD']['cavv'] + assert_equal '97267598-FAE6-48F2-8083-C23433990FBC', JSON.parse(data)['threeD']['dsTransactionId'] + assert_equal '05', JSON.parse(data)['threeD']['eci'] + end.respond_with(successful_authorize_response) + end + + def test_add_3ds_global_params_with_challenge_preference + chellange_preference_params = { + challenge_preference: 'ExemptionRequest', + exemption_request_reason: 'AccountVerification' + } + + stub_comms do + @gateway.purchase(@amount, @credit_card, @three_d_secure_options.merge(chellange_preference_params)) + end.check_request(skip_response: true) do |_method, _endpoint, data, _headers| + assert_equal 'ExemptionRequest', JSON.parse(data)['threeD']['externalMpi']['challenge_preference'] + assert_equal 'AccountVerification', JSON.parse(data)['threeD']['externalMpi']['exemptionRequestReason'] + end + end + private def three_ds_assertions(payment_option_card)