Skip to content

Commit

Permalink
BT: Add support for Network Tokens
Browse files Browse the repository at this point in the history
ECS-2899

Braintree supports bring your own Network Tokens via their SDK
starting in Major version 4.0. This commit adds logic to update the BT gem
version used by the CI/CD tests to 4.0 and adds support for using Network
Tokens on the Braintree Blue gateway.

Remote Tests:
104 tests, 554 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

Remove android pay from BT
  • Loading branch information
aenand committed Aug 1, 2023
1 parent 0ff6eaa commit 23a917c
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 73 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Adyen: Add option to elect which error message [aenand] #4843
* Reach: Update list of supported countries [jcreiff] #4842
* Paysafe: Truncate address fields [jcreiff] #4841
* Braintree: Support third party Network Tokens [aenand] #4775

== Version 1.134.0 (July 25, 2023)
* Update required Ruby version [almalee24] #4823
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ gem 'rubocop', '~> 0.72.0', require: false

group :test, :remote_test do
# gateway-specific dependencies, keeping these gems out of the gemspec
gem 'braintree', '>= 3.0.0', '<= 3.0.1'
gem 'braintree', '>= 4.12.0'
gem 'jose', '~> 1.1.3'
gem 'jwe'
gem 'mechanize'
Expand Down
104 changes: 68 additions & 36 deletions lib/active_merchant/billing/gateways/braintree_blue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -835,54 +835,86 @@ def add_stored_credential_data(parameters, credit_card_or_vault_id, options)

def add_payment_method(parameters, credit_card_or_vault_id, options)
if credit_card_or_vault_id.is_a?(String) || credit_card_or_vault_id.is_a?(Integer)
if options[:payment_method_token]
parameters[:payment_method_token] = credit_card_or_vault_id
options.delete(:billing_address)
elsif options[:payment_method_nonce]
parameters[:payment_method_nonce] = credit_card_or_vault_id
else
parameters[:customer_id] = credit_card_or_vault_id
end
add_third_party_token(parameters, credit_card_or_vault_id, options)
else
parameters[:customer].merge!(
first_name: credit_card_or_vault_id.first_name,
last_name: credit_card_or_vault_id.last_name
)
if credit_card_or_vault_id.is_a?(NetworkTokenizationCreditCard)
if credit_card_or_vault_id.source == :apple_pay
parameters[:apple_pay_card] = {
number: credit_card_or_vault_id.number,
expiration_month: credit_card_or_vault_id.month.to_s.rjust(2, '0'),
expiration_year: credit_card_or_vault_id.year.to_s,
cardholder_name: credit_card_or_vault_id.name,
cryptogram: credit_card_or_vault_id.payment_cryptogram,
eci_indicator: credit_card_or_vault_id.eci
}
elsif credit_card_or_vault_id.source == :android_pay || credit_card_or_vault_id.source == :google_pay
Braintree::Version::Major < 3 ? pay_card = :android_pay_card : pay_card = :google_pay_card
parameters[pay_card] = {
number: credit_card_or_vault_id.number,
cryptogram: credit_card_or_vault_id.payment_cryptogram,
expiration_month: credit_card_or_vault_id.month.to_s.rjust(2, '0'),
expiration_year: credit_card_or_vault_id.year.to_s,
google_transaction_id: credit_card_or_vault_id.transaction_id,
source_card_type: credit_card_or_vault_id.brand,
source_card_last_four: credit_card_or_vault_id.last_digits,
eci_indicator: credit_card_or_vault_id.eci
}
case credit_card_or_vault_id.source
when :apple_pay
add_apple_pay(parameters, credit_card_or_vault_id)
when :google_pay
add_google_pay(parameters, credit_card_or_vault_id)
else
add_network_tokenization_card(parameters, credit_card_or_vault_id)
end
else
parameters[:credit_card] = {
number: credit_card_or_vault_id.number,
cvv: credit_card_or_vault_id.verification_value,
expiration_month: credit_card_or_vault_id.month.to_s.rjust(2, '0'),
expiration_year: credit_card_or_vault_id.year.to_s,
cardholder_name: credit_card_or_vault_id.name
}
add_credit_card(parameters, credit_card_or_vault_id)
end
end
end

def add_third_party_token(parameters, payment_method, options)
if options[:payment_method_token]
parameters[:payment_method_token] = payment_method
options.delete(:billing_address)
elsif options[:payment_method_nonce]
parameters[:payment_method_nonce] = payment_method
else
parameters[:customer_id] = payment_method
end
end

def add_credit_card(parameters, payment_method)
parameters[:credit_card] = {
number: payment_method.number,
cvv: payment_method.verification_value,
expiration_month: payment_method.month.to_s.rjust(2, '0'),
expiration_year: payment_method.year.to_s,
cardholder_name: payment_method.name
}
end

def add_apple_pay(parameters, payment_method)
parameters[:apple_pay_card] = {
number: payment_method.number,
expiration_month: payment_method.month.to_s.rjust(2, '0'),
expiration_year: payment_method.year.to_s,
cardholder_name: payment_method.name,
cryptogram: payment_method.payment_cryptogram,
eci_indicator: payment_method.eci
}
end

def add_google_pay(parameters, payment_method)
Braintree::Version::Major < 3 ? pay_card = :android_pay_card : pay_card = :google_pay_card
parameters[pay_card] = {
number: payment_method.number,
cryptogram: payment_method.payment_cryptogram,
expiration_month: payment_method.month.to_s.rjust(2, '0'),
expiration_year: payment_method.year.to_s,
google_transaction_id: payment_method.transaction_id,
source_card_type: payment_method.brand,
source_card_last_four: payment_method.last_digits,
eci_indicator: payment_method.eci
}
end

def add_network_tokenization_card(parameters, payment_method)
parameters[:credit_card] = {
number: payment_method.number,
expiration_month: payment_method.month.to_s.rjust(2, '0'),
expiration_year: payment_method.year.to_s,
cardholder_name: payment_method.name,
network_tokenization_attributes: {
cryptogram: payment_method.payment_cryptogram,
ecommerce_indicator: payment_method.eci
}
}
end

def bank_account_errors(payment_method, options)
if payment_method.validate.present?
payment_method.validate
Expand Down
32 changes: 13 additions & 19 deletions test/remote/gateways/remote_braintree_blue_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ def setup
},
ach_mandate: ach_mandate
}

@nt_credit_card = network_tokenization_credit_card('4111111111111111',
brand: 'visa',
eci: '05',
source: :network_token,
payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=')
end

def test_credit_card_details_on_store
Expand All @@ -54,6 +60,13 @@ def test_successful_authorize
assert_equal 'authorized', response.params['braintree_transaction']['status']
end

def test_successful_authorize_with_nt
assert response = @gateway.authorize(@amount, @nt_credit_card, @options)
assert_success response
assert_equal '1000 Approved', response.message
assert_equal 'authorized', response.params['braintree_transaction']['status']
end

def test_successful_authorize_with_nil_and_empty_billing_address_options
credit_card = credit_card('5105105105105100')
options = {
Expand Down Expand Up @@ -682,25 +695,6 @@ def test_authorize_and_capture_with_apple_pay_card
assert_success capture
end

def test_authorize_and_capture_with_android_pay_card
credit_card = network_tokenization_credit_card(
'4111111111111111',
payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=',
month: '01',
year: '2024',
source: :android_pay,
transaction_id: '123456789',
eci: '05'
)

assert auth = @gateway.authorize(@amount, credit_card, @options)
assert_success auth
assert_equal '1000 Approved', auth.message
assert auth.authorization
assert capture = @gateway.capture(@amount, auth.authorization)
assert_success capture
end

def test_authorize_and_capture_with_google_pay_card
credit_card = network_tokenization_credit_card(
'4111111111111111',
Expand Down
31 changes: 14 additions & 17 deletions test/unit/gateways/braintree_blue_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ def test_apple_pay_card
assert_equal 'transaction_id', response.authorization
end

def test_android_pay_card
def test_google_pay_card
Braintree::TransactionGateway.any_instance.expects(:sale).
with(
amount: '1.00',
Expand All @@ -1038,15 +1038,15 @@ def test_android_pay_card
brand: 'visa',
eci: '05',
payment_cryptogram: '111111111100cryptogram',
source: :android_pay,
source: :google_pay,
transaction_id: '1234567890'
)

response = @gateway.authorize(100, credit_card, test: true, order_id: '1')
assert_equal 'transaction_id', response.authorization
end

def test_google_pay_card
def test_network_token_card
Braintree::TransactionGateway.any_instance.expects(:sale).
with(
amount: '1.00',
Expand All @@ -1055,27 +1055,24 @@ def test_google_pay_card
first_name: 'Longbob', last_name: 'Longsen' },
options: { store_in_vault: false, submit_for_settlement: nil, hold_in_escrow: nil },
custom_fields: nil,
google_pay_card: {
credit_card: {
number: '4111111111111111',
expiration_month: '09',
expiration_year: (Time.now.year + 1).to_s,
cryptogram: '111111111100cryptogram',
google_transaction_id: '1234567890',
source_card_type: 'visa',
source_card_last_four: '1111',
eci_indicator: '05'
cardholder_name: 'Longbob Longsen',
network_tokenization_attributes: {
cryptogram: '111111111100cryptogram',
ecommerce_indicator: '05'
}
}
).
returns(braintree_result(id: 'transaction_id'))

credit_card = network_tokenization_credit_card(
'4111111111111111',
brand: 'visa',
eci: '05',
payment_cryptogram: '111111111100cryptogram',
source: :google_pay,
transaction_id: '1234567890'
)
credit_card = network_tokenization_credit_card('4111111111111111',
brand: 'visa',
eci: '05',
source: :network_token,
payment_cryptogram: '111111111100cryptogram')

response = @gateway.authorize(100, credit_card, test: true, order_id: '1')
assert_equal 'transaction_id', response.authorization
Expand Down

0 comments on commit 23a917c

Please sign in to comment.