Skip to content

Commit

Permalink
Nuvei: Add Google and Apple pay (#5289)
Browse files Browse the repository at this point in the history
Description
-------------------------
[SER-1344](https://spreedly.atlassian.net/browse/SER-1344)

Unit test
-------------------------
Finished in 0.010805 seconds.

14 tests, 47 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

1295.70 tests/s, 4349.84 assertions/s

Remote test
-------------------------
Finished in 73.320303 seconds.

24 tests, 73 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

0.33 tests/s, 1.00 assertions/s

Rubocop
-------------------------
801 files inspected, no offenses detected

Co-authored-by: Javier Pedroza <[email protected]>
  • Loading branch information
javierpedrozaing and Javier Pedroza authored Oct 18, 2024
1 parent 4d0d1da commit 9bc26ed
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
* Nuvei: Add partial approval feature [javierpedrozaing] #5250
* Nuvei: Add ACH support [javierpedrozaing] #5269
* Nuvei: Add GSF for verify method [javierpedrozaing] #5278
* Nuvei: Add Google and Apple pay [javierpedrozaing] #5289

== Version 1.137.0 (August 2, 2024)
* Unlock dependency on `rexml` to allow fixing a CVE (#5181).
Expand Down
25 changes: 21 additions & 4 deletions lib/active_merchant/billing/gateways/nuvei.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class NuveiGateway < Gateway
init_payment: '/initPayment'
}

NETWORK_TOKENIZATION_CARD_MAPPING = {
'apple_pay' => 'ApplePay',
'google_pay' => 'GooglePay'
}

def initialize(options = {})
requires!(options, :merchant_id, :merchant_site_id, :secret_key)
super
Expand Down Expand Up @@ -150,7 +155,8 @@ def scrub(transcript)
gsub(%r(("merchantId\\?":\\?")\d+), '\1[FILTERED]').
gsub(%r(("merchantSiteId\\?":\\?")\d+), '\1[FILTERED]').
gsub(%r(("merchantKey\\?":\\?")\d+), '\1[FILTERED]').
gsub(%r(("accountNumber\\?":\\?")\d+), '\1[FILTERED]')
gsub(%r(("accountNumber\\?":\\?")\d+), '\1[FILTERED]').
gsub(%r(("cryptogram\\?":\\?")[^"\\]*)i, '\1[FILTERED]')
end

private
Expand Down Expand Up @@ -203,10 +209,21 @@ def add_bank_account(post, payment, options)
}
end

def add_payment_method(post, payment, key = :paymentOption, options = {})
payment_data = payment.is_a?(CreditCard) ? credit_card_hash(payment) : payment
def add_payment_method(post, payment, key, options = {})
payment_data = payment.is_a?(CreditCard) || payment.is_a?(NetworkTokenizationCreditCard) ? credit_card_hash(payment) : payment
if payment.is_a?(NetworkTokenizationCreditCard)
payment_data[:brand] = payment.brand.upcase

external_token = {}
external_token[:externalTokenProvider] = NETWORK_TOKENIZATION_CARD_MAPPING[payment.source.to_s]
external_token[:cryptogram] = payment.payment_cryptogram if payment.payment_cryptogram
external_token[:eciProvider] = payment.eci if payment.eci

payment_data.slice!(:cardNumber, :expirationMonth, :expirationYear, :last4Digits, :brand, :CVV)

post[:paymentOption] = { card: payment_data.merge(externalToken: external_token) }

if payment.is_a?(CreditCard)
elsif payment.is_a?(CreditCard)
post[key] = key == :paymentOption ? { card: payment_data } : payment_data
elsif payment.is_a?(Check)
post[:userTokenId] = options[:user_token_id]
Expand Down
33 changes: 33 additions & 0 deletions test/remote/gateways/remote_nuvei_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ def setup
}

@bank_account = check(account_number: '111111111', routing_number: '999999992')

@apple_pay_card = network_tokenization_credit_card(
'5204245250460049',
payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=',
month: '12',
year: Time.new.year + 2,
source: :apple_pay,
verification_value: 111,
eci: '5'
)

@google_pay_card = network_tokenization_credit_card(
'4761344136141390',
payment_cryptogram: 'YwAAAAAABaYcCMX/OhNRQAAAAAA=',
month: '12',
year: Time.new.year + 2,
source: :google_pay,
eci: '5'
)
end

def test_transcript_scrubbing
Expand Down Expand Up @@ -292,4 +311,18 @@ def test_successful_authorize_with_bank_account
assert_success response
assert_match 'PENDING', response.message
end

def test_successful_purchase_with_apple_pay
response = @gateway.purchase(@amount, @apple_pay_card, @options)
assert_success response
assert_equal 'APPROVED', response.message
assert_not_nil response.params[:paymentOption][:userPaymentOptionId]
end

def test_successful_purchase_with_google_pay
response = @gateway.purchase(@amount, @google_pay_card, @options)
assert_success response
assert_equal 'APPROVED', response.message
assert_not_nil response.params[:paymentOption][:userPaymentOptionId]
end
end
44 changes: 44 additions & 0 deletions test/unit/gateways/nuvei_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ def setup
}

@bank_account = check()

@apple_pay_card = network_tokenization_credit_card(
'5204 2452 5046 0049',
payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=',
month: '12',
year: Time.new.year,
source: :apple_pay,
verification_value: 111,
eci: '5'
)

@google_pay_card = network_tokenization_credit_card(
'4761209980011439',
payment_cryptogram: 'YwAAAAAABaYcCMX/OhNRQAAAAAA=',
month: '11',
year: '2022',
source: :google_pay,
verification_value: 111,
eci: '5'
)
end

def test_calculate_checksum_authenticate
Expand Down Expand Up @@ -291,6 +311,30 @@ def test_successful_verify
end
end

def test_successful_purchase_with_apple_pay
stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @apple_pay_card, @options)
end.check_request do |_method, endpoint, data, _headers|
if /payment/.match?(endpoint)
json_data = JSON.parse(data)
assert_equal 'ApplePay', json_data['paymentOption']['card']['externalToken']['externalTokenProvider']
assert_not_nil json_data['paymentOption']['card']['externalToken']['cryptogram']
end
end.respond_with(successful_purchase_response)
end

def test_successful_purchase_with_google_pay
stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @google_pay_card, @options)
end.check_request do |_method, endpoint, data, _headers|
if /payment/.match?(endpoint)
json_data = JSON.parse(data)
assert_equal 'GooglePay', json_data['paymentOption']['card']['externalToken']['externalTokenProvider']
assert_not_nil json_data['paymentOption']['card']['externalToken']['cryptogram']
end
end.respond_with(successful_purchase_response)
end

private

def three_ds_assertions(payment_option_card)
Expand Down

0 comments on commit 9bc26ed

Please sign in to comment.