From 3d742cdc3f15d7feb0aa2ee9f21b25eba773dd2b Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 14 Apr 2022 11:13:17 -0500 Subject: [PATCH 1/9] Add support for opaque_data w/ Accept.js tokens --- .../billing/gateways/authorize_net_cim.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/active_merchant/billing/gateways/authorize_net_cim.rb b/lib/active_merchant/billing/gateways/authorize_net_cim.rb index fac2913ff37..1eb0876b88e 100644 --- a/lib/active_merchant/billing/gateways/authorize_net_cim.rb +++ b/lib/active_merchant/billing/gateways/authorize_net_cim.rb @@ -760,8 +760,10 @@ def add_payment_profile(xml, payment_profile) xml.tag!('payment') do add_credit_card(xml, payment_profile[:payment][:credit_card]) if payment_profile[:payment].has_key?(:credit_card) add_bank_account(xml, payment_profile[:payment][:bank_account]) if payment_profile[:payment].has_key?(:bank_account) - add_drivers_license(xml, payment_profile[:payment][:drivers_license]) if payment_profile[:payment].has_key?(:drivers_license) # This element is only required for Wells Fargo SecureSource eCheck.Net merchants + add_drivers_license(xml, payment_profile[:payment][:drivers_license]) if payment_profile[:payment].has_key?(:drivers_license) + add_accept_js(xml, payment_profile[:payment][:opaque_data]) if payment_profile[:payment].has_key?(:opaque_data) + # The customer's Social Security Number or Tax ID xml.tag!('taxId', payment_profile[:payment]) if payment_profile[:payment].has_key?(:tax_id) end @@ -851,6 +853,15 @@ def add_drivers_license(xml, drivers_license) end end + def add_accept_js(xml, opaque_data) + return unless opaque_data + # The generic payment data type used to process tokenized payment information + xml.tag!('opaqueData') do + xml.tag!('dataDescriptor', opaque_data[:data_descriptor]) + xml.tag!('dataValue', opaque_data[:data_value]) + end + end + def commit(action, request) url = test? ? test_url : live_url xml = ssl_post(url, request, 'Content-Type' => 'text/xml') From 781d2f87ac7ffe9db489adbf1b7aa4ce46d1bed8 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Wed, 20 Apr 2022 10:01:07 -0500 Subject: [PATCH 2/9] Add simple PORO representing Accept.js token --- lib/active_merchant/billing.rb | 1 + lib/active_merchant/billing/accept_js_token.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 lib/active_merchant/billing/accept_js_token.rb diff --git a/lib/active_merchant/billing.rb b/lib/active_merchant/billing.rb index ea3108597c8..adfa9c41f3c 100644 --- a/lib/active_merchant/billing.rb +++ b/lib/active_merchant/billing.rb @@ -10,6 +10,7 @@ require 'active_merchant/billing/check' require 'active_merchant/billing/payment_token' require 'active_merchant/billing/apple_pay_payment_token' +require 'active_merchant/billing/accept_js_token' require 'active_merchant/billing/response' require 'active_merchant/billing/gateways' require 'active_merchant/billing/gateway' diff --git a/lib/active_merchant/billing/accept_js_token.rb b/lib/active_merchant/billing/accept_js_token.rb new file mode 100644 index 00000000000..8dfd38c4483 --- /dev/null +++ b/lib/active_merchant/billing/accept_js_token.rb @@ -0,0 +1,13 @@ +module ActiveMerchant + module Billing + class AcceptJsToken < PaymentToken + def type + 'accept_js' + end + + def opaque_data + payment_data[:opaque_data] + end + end + end +end From f72ac77e648279c7455935189628c1b87f638eee Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Wed, 20 Apr 2022 10:54:42 -0500 Subject: [PATCH 3/9] Add AuthorizeHelper for Accept.js tokens This helper contains two functions for adding Accept.js (PCI) tokenization support to the test suite. One, `accept_js_token` is a simple dummy/placeholder opaque data structure. The second, `get_sandbox_acceptjs_token_for_credit_card(credit_ard)` takes a test credit card object from ActiveMerchant and constructs an XML API request to the configured Authorize sandbox (in fixtures). This API request (securePaymentContainerRequest) is essentially what the Accept.js javascript code does. Since providing opaque data in the the test suite to validate gateway behavior for tokens requires opaque data from Authorize, we have to ask them for it as if we were using the Accept.js scriptsin a browser. --- test/support/authorize_helper.rb | 126 +++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 test/support/authorize_helper.rb diff --git a/test/support/authorize_helper.rb b/test/support/authorize_helper.rb new file mode 100644 index 00000000000..3a5ea25f060 --- /dev/null +++ b/test/support/authorize_helper.rb @@ -0,0 +1,126 @@ +require 'test_helper' + +module AuthorizeHelper + def acceptjs_token(options = {}) + defaults = { + opaque_data: { + data_value: '1234567890ABCDEF1111AAAA2222BBBB3333CCCC4444DDDD5555EEEE6666FFFF7777888899990000', + data_descriptor: 'COMMON.ACCEPT.INAPP.PAYMENT' + } + }.update(options) + + ActiveMerchant::Billing::AcceptJsToken.new(defaults) + end + + def get_sandbox_acceptjs_token_for_credit_card(credit_card) + sandbox_endpoint = 'https://apitest.authorize.net/xml/v1/request.api' + sandbox_credentials = fixtures(:authorize_net) + + AcceptJsTestToken.new( + credit_card, + endpoint: sandbox_endpoint, + credentials: sandbox_credentials + ).token + end + + class AcceptJsTestToken + attr_reader :credit_card, :options + + def initialize(credit_card, options = {}) + @credit_card = credit_card + @options = { + }.merge(options) + end + + def token + xml = build_xml + response = make_request xml + opaque_data = parse_response response + + ActiveMerchant::Billing::AcceptJsToken.new(opaque_data: opaque_data) + end + + private + + def build_xml + xml = Builder::XmlMarkup.new(:indent => 2) + xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8') + xml.tag!('securePaymentContainerRequest', :xmlns => 'AnetApi/xml/v1/schema/AnetApiSchema.xsd') do + xml.tag!('merchantAuthentication') do + creds = options[:credentials] + xml.tag!('name', creds[:login]) + xml.tag!('transactionKey', creds[:password]) + end + xml.tag!('refId', options[:ref_id]) if options[:ref_id] + xml.tag!('data') do + xml.tag!('type', 'TOKEN') + xml.tag!('id', SecureRandom.uuid) + xml.tag!('token') do + xml.tag!('cardNumber', credit_card.number) + xml.tag!('expirationDate', ('%02d' % credit_card.month) + credit_card.year.to_s) + xml.tag!('cardCode', credit_card.verification_value) + xml.tag!('fullName', "#{credit_card.first_name} #{credit_card.last_name}") + end + end + end + + xml + end + + def make_request(xml) + uri = URI.parse(options[:endpoint]) + req = Net::HTTP::Post.new(uri.path) + req.body = xml.target! + req.content_type = 'text/xml' + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + + resp = http.request(req) + + raise "HTTP #{resp.code} response code tokenizing test card" if resp.code != '200' + raise "HTTP incorrect content type #{resp.content_type} tokenizing test card" if resp.content_type != 'application/xml' + + resp + end + + def parse_response(response) + xml = REXML::Document.new(response.body) + root = REXML::XPath.first(xml, '//securePaymentContainerResponse') || + REXML::XPath.first(xml, '//ErrorResponse') + if root + response = parse_element(root) + end + + raise "Result code #{response['messages']['result_code']}: #{response['messages']['message']['code']}" if response['messages']['result_code'] != 'Ok' + + { + data_descriptor: response['opaque_data']['data_descriptor'], + data_value: response['opaque_data']['data_value'] + } + end + + def parse_element(node) + if node.has_elements? + response = {} + node.elements.each { |e| + key = e.name.underscore + value = parse_element(e) + if response.has_key?(key) + if response[key].is_a?(Array) + response[key].push(value) + else + response[key] = [response[key], value] + end + else + response[key] = parse_element(e) + end + } + else + response = node.text + end + + response + end + end +end From cfd2e65ebf2df062407ead99e6b1e2ec0f565970 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Wed, 20 Apr 2022 11:00:53 -0500 Subject: [PATCH 4/9] Add remote test support for Accept.js tokens --- .../gateways/remote_authorize_net_cim_test.rb | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/test/remote/gateways/remote_authorize_net_cim_test.rb b/test/remote/gateways/remote_authorize_net_cim_test.rb index 124b81c7753..9976b736cee 100644 --- a/test/remote/gateways/remote_authorize_net_cim_test.rb +++ b/test/remote/gateways/remote_authorize_net_cim_test.rb @@ -1,16 +1,21 @@ require 'test_helper' +require 'support/authorize_helper' require 'pp' class AuthorizeNetCimTest < Test::Unit::TestCase + include AuthorizeHelper + def setup Base.mode = :test @gateway = AuthorizeNetCimGateway.new(fixtures(:authorize_net)) @amount = 100 + @customer_profile_id = nil @credit_card = credit_card('4242424242424242') @payment = { :credit_card => @credit_card } + @address = address @profile = { :merchant_customer_id => 'Up to 20 chars', # Optional :description => 'Up to 255 Characters', # Optional @@ -74,10 +79,36 @@ def test_successful_profile_create_get_update_and_delete assert_nil response.authorization assert response = @gateway.get_customer_profile(:customer_profile_id => @customer_profile_id) assert_nil response.params['profile']['merchant_customer_id'] - assert_nil response.params['profile']['description'] + assert_equal 'Up to 255 Characters', response.params['profile']['description'] assert_equal 'new email address', response.params['profile']['email'] end + def test_successful_profile_create_with_acceptjs + @options[:profile][:payment_profiles].delete(:payment) + token = get_sandbox_acceptjs_token_for_credit_card(credit_card) + @options[:profile][:payment_profiles][:payment] = token.payment_data + + assert response = @gateway.create_customer_profile(@options) + @customer_profile_id = response.authorization + + assert_success response + assert response.test? + + assert response = @gateway.get_customer_profile(:customer_profile_id => @customer_profile_id) + assert response.test? + assert_success response + assert_equal @customer_profile_id, response.authorization + assert_equal 'Successful.', response.message + assert response.params['profile']['payment_profiles']['customer_payment_profile_id'] =~ /\d+/, 'The customer_payment_profile_id should be a number' + assert_equal "XXXX#{@credit_card.last_digits}", response.params['profile']['payment_profiles']['payment']['credit_card']['card_number'], "The card number should contain the last 4 digits of the card we passed in #{@credit_card.last_digits}" + assert_equal @profile[:merchant_customer_id], response.params['profile']['merchant_customer_id'] + assert_equal @profile[:description], response.params['profile']['description'] + assert_equal @profile[:email], response.params['profile']['email'] + assert_equal @profile[:payment_profiles][:customer_type], response.params['profile']['payment_profiles']['customer_type'] + assert_equal @profile[:ship_to_list][:phone_number], response.params['profile']['ship_to_list']['phone_number'] + assert_equal @profile[:ship_to_list][:company], response.params['profile']['ship_to_list']['company'] + end + # NOTE - prior_auth_capture should be used to complete an auth_only request # (not capture_only as that will leak the authorization), so don't use this # test as a template. @@ -179,7 +210,36 @@ def test_successful_create_customer_payment_profile_request assert response.test? assert_success response - assert_nil response.authorization + assert customer_payment_profile_id = response.params['customer_payment_profile_id'] + assert customer_payment_profile_id =~ /\d+/, "The customerPaymentProfileId should be numeric. It was #{customer_payment_profile_id}" + end + + def test_get_token_for_credit_card + assert token = get_sandbox_acceptjs_token_for_credit_card(credit_card) + assert_not_nil token.opaque_data[:data_value] + assert token.opaque_data[:data_descriptor] == 'COMMON.ACCEPT.INAPP.PAYMENT' + end + + def test_successful_create_customer_payment_profile_request_with_acceptjs + @options[:profile].delete(:payment_profiles) + assert response = @gateway.create_customer_profile(@options) + @customer_profile_id = response.authorization + + assert response = @gateway.get_customer_profile(:customer_profile_id => @customer_profile_id) + assert_nil response.params['profile']['payment_profiles'] + + token = get_sandbox_acceptjs_token_for_credit_card(credit_card) + + assert response = @gateway.create_customer_payment_profile( + :customer_profile_id => @customer_profile_id, + :payment_profile => { + :customer_type => 'individual', + :payment => token.payment_data + } + ) + + assert response.test? + assert_success response assert customer_payment_profile_id = response.params['customer_payment_profile_id'] assert customer_payment_profile_id =~ /\d+/, "The customerPaymentProfileId should be numeric. It was #{customer_payment_profile_id}" end @@ -218,7 +278,6 @@ def test_successful_create_customer_payment_profile_request_with_bank_account assert response.test? assert_success response - assert_nil response.authorization assert customer_payment_profile_id = response.params['customer_payment_profile_id'] assert customer_payment_profile_id =~ /\d+/, "The customerPaymentProfileId should be numeric. It was #{customer_payment_profile_id}" end @@ -238,7 +297,6 @@ def test_successful_create_customer_shipping_address_request assert response.test? assert_success response - assert_nil response.authorization assert customer_address_id = response.params['customer_address_id'] assert customer_address_id =~ /\d+/, "The customerAddressId should be numeric. It was #{customer_address_id}" end @@ -263,14 +321,13 @@ def test_successful_get_customer_profile_with_multiple_payment_profiles assert response.test? assert_success response - assert_nil response.authorization assert customer_payment_profile_id = response.params['customer_payment_profile_id'] assert customer_payment_profile_id =~ /\d+/, "The customerPaymentProfileId should be numeric. It was #{customer_payment_profile_id}" assert response = @gateway.get_customer_profile(:customer_profile_id => @customer_profile_id) assert_equal 2, response.params['profile']['payment_profiles'].size - assert_equal 'XXXX4242', response.params['profile']['payment_profiles'][0]['payment']['credit_card']['card_number'] - assert_equal 'XXXX1234', response.params['profile']['payment_profiles'][1]['payment']['credit_card']['card_number'] + assert_equal 'XXXX1234', response.params['profile']['payment_profiles'][0]['payment']['credit_card']['card_number'] + assert_equal 'XXXX4242', response.params['profile']['payment_profiles'][1]['payment']['credit_card']['card_number'] end def test_successful_delete_customer_payment_profile_request From 28695483c581b1c773a6af03d03834250d47f0c3 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Wed, 20 Apr 2022 11:06:02 -0500 Subject: [PATCH 5/9] Add unit test support for Accept.js tokens --- test/unit/gateways/authorize_net_cim_test.rb | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/unit/gateways/authorize_net_cim_test.rb b/test/unit/gateways/authorize_net_cim_test.rb index 1d9bd462192..344ebe40148 100644 --- a/test/unit/gateways/authorize_net_cim_test.rb +++ b/test/unit/gateways/authorize_net_cim_test.rb @@ -1,6 +1,8 @@ require 'test_helper' +require 'support/authorize_helper' class AuthorizeNetCimTest < Test::Unit::TestCase + include AuthorizeHelper include CommStub def setup @@ -10,6 +12,7 @@ def setup ) @amount = 100 @credit_card = credit_card + @accept_js_token = acceptjs_token @address = address @customer_profile_id = '3187' @customer_payment_profile_id = '7813' @@ -61,6 +64,18 @@ def test_should_create_customer_profile_request assert_equal 'Successful.', response.message end + def test_should_create_customer_profile_request_with_acceptjs + @gateway.expects(:ssl_post).returns(successful_create_customer_profile_response) + + @options[:profile][:payment_profiles][:payment] = acceptjs_token.payment_data + + assert response = @gateway.create_customer_profile(@options) + assert_instance_of Response, response + assert_success response + assert_equal @customer_profile_id, response.authorization + assert_equal 'Successful.', response.message + end + def test_should_create_customer_payment_profile_request @gateway.expects(:ssl_post).returns(successful_create_customer_payment_profile_response) @@ -79,6 +94,24 @@ def test_should_create_customer_payment_profile_request assert_equal 'This output is only present if the ValidationMode input parameter is passed with a value of testMode or liveMode', response.params['validation_direct_response'] end + def test_should_create_customer_payment_profile_request_with_acceptjs + @gateway.expects(:ssl_post).returns(successful_create_customer_payment_profile_response) + + assert response = @gateway.create_customer_payment_profile( + :customer_profile_id => @customer_profile_id, + :payment_profile => { + :customer_type => 'individual', + :bill_to => @address, + :payment => acceptjs_token.payment_data + }, + :validation_mode => :test + ) + assert_instance_of Response, response + assert_success response + assert_equal @customer_payment_profile_id, response.params['customer_payment_profile_id'] + assert_equal 'This output is only present if the ValidationMode input parameter is passed with a value of testMode or liveMode', response.params['validation_direct_response'] + end + def test_should_create_customer_shipping_address_request @gateway.expects(:ssl_post).returns(successful_create_customer_shipping_address_response) From fc8db28139a3f5b463e1304984404e782cf637b2 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Wed, 20 Apr 2022 11:25:34 -0500 Subject: [PATCH 6/9] Clarify use of opaqueData in request By renaming the helper method `add_accept_js` to `add_opaque_data`, we clarify that any payment details that produce `:opaque_data` are usable as payment data. --- lib/active_merchant/billing/gateways/authorize_net_cim.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_merchant/billing/gateways/authorize_net_cim.rb b/lib/active_merchant/billing/gateways/authorize_net_cim.rb index 1eb0876b88e..4d7a819731e 100644 --- a/lib/active_merchant/billing/gateways/authorize_net_cim.rb +++ b/lib/active_merchant/billing/gateways/authorize_net_cim.rb @@ -762,7 +762,7 @@ def add_payment_profile(xml, payment_profile) add_bank_account(xml, payment_profile[:payment][:bank_account]) if payment_profile[:payment].has_key?(:bank_account) # This element is only required for Wells Fargo SecureSource eCheck.Net merchants add_drivers_license(xml, payment_profile[:payment][:drivers_license]) if payment_profile[:payment].has_key?(:drivers_license) - add_accept_js(xml, payment_profile[:payment][:opaque_data]) if payment_profile[:payment].has_key?(:opaque_data) + add_opaque_data(xml, payment_profile[:payment][:opaque_data]) if payment_profile[:payment].has_key?(:opaque_data) # The customer's Social Security Number or Tax ID xml.tag!('taxId', payment_profile[:payment]) if payment_profile[:payment].has_key?(:tax_id) @@ -853,7 +853,7 @@ def add_drivers_license(xml, drivers_license) end end - def add_accept_js(xml, opaque_data) + def add_opaque_data(xml, opaque_data) return unless opaque_data # The generic payment data type used to process tokenized payment information xml.tag!('opaqueData') do From f384989963ddd133de64896a4e57d3a1ef2ca1b0 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Thu, 21 Apr 2022 12:12:27 -0500 Subject: [PATCH 7/9] Add support for Accept.js tokens to AuthorizeNet gateway --- .../billing/gateways/authorize_net.rb | 48 ++++++++--------- .../gateways/remote_authorize_net_test.rb | 51 +++++++++++++++++++ test/unit/gateways/authorize_net_test.rb | 29 +++++++++++ 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/lib/active_merchant/billing/gateways/authorize_net.rb b/lib/active_merchant/billing/gateways/authorize_net.rb index ccc8794a05a..3e8511a869b 100644 --- a/lib/active_merchant/billing/gateways/authorize_net.rb +++ b/lib/active_merchant/billing/gateways/authorize_net.rb @@ -90,6 +90,7 @@ class AuthorizeNetGateway < Gateway }.freeze APPLE_PAY_DATA_DESCRIPTOR = 'COMMON.APPLE.INAPP.PAYMENT' + ACCEPT_JS_DATA_DESCRIPTOR = 'COMMON.ACCEPT.INAPP.PAYMENT' PAYMENT_METHOD_NOT_SUPPORTED_ERROR = '155' INELIGIBLE_FOR_ISSUING_CREDIT_ERROR = '54' @@ -177,18 +178,18 @@ def credit(amount, payment, options={}) end end - def verify(credit_card, options = {}) + def verify(payment, options = {}) MultiResponse.run(:use_first_response) do |r| - r.process { authorize(100, credit_card, options) } + r.process { authorize(100, payment, options) } r.process(:ignore_result) { void(r.authorization, options) } end end - def store(credit_card, options = {}) + def store(payment, options = {}) if options[:customer_profile_id] - create_customer_payment_profile(credit_card, options) + create_customer_payment_profile(payment, options) else - create_customer_profile(credit_card, options) + create_customer_profile(payment, options) end end @@ -397,6 +398,8 @@ def add_payment_source(xml, source, options, action = nil) add_check(xml, source) elsif card_brand(source) == 'apple_pay' add_apple_pay_payment_token(xml, source) + elsif card_brand(source) == 'accept_js' + add_accept_js_token(xml, source) else add_credit_card(xml, source, action) end @@ -520,8 +523,17 @@ def add_apple_pay_payment_token(xml, apple_pay_payment_token) end end + def add_accept_js_token(xml, accept_js_token) + xml.payment do + xml.opaqueData do + xml.dataDescriptor(accept_js_token.opaque_data[:data_descriptor]) + xml.dataValue(accept_js_token.opaque_data[:data_value]) + end + end + end + def add_market_type_device_type(xml, payment, options) - return if payment.is_a?(String) || card_brand(payment) == 'check' || card_brand(payment) == 'apple_pay' + return if payment.is_a?(String) || %w[check apple_pay accept_js].include?(card_brand(payment)) if valid_track_data xml.retail do xml.marketType(options[:market_type] || MARKET_TYPE[:retail]) @@ -701,23 +713,17 @@ def add_extra_options_for_cim(xml, options) xml.extraOptions("x_delim_char=#{options[:delimiter]}") if options[:delimiter] end - def create_customer_payment_profile(credit_card, options) + def create_customer_payment_profile(payment_source, options) commit(:cim_store_update, options) do |xml| xml.customerProfileId options[:customer_profile_id] xml.paymentProfile do - add_billing_address(xml, credit_card, options) - xml.payment do - xml.creditCard do - xml.cardNumber(truncate(credit_card.number, 16)) - xml.expirationDate(format(credit_card.year, :four_digits) + '-' + format(credit_card.month, :two_digits)) - xml.cardCode(credit_card.verification_value) if credit_card.verification_value - end - end + add_billing_address(xml, payment_source, options) + add_payment_source(xml, payment_source, options) end end end - def create_customer_profile(credit_card, options) + def create_customer_profile(payment_source, options) commit(:cim_store, options) do |xml| xml.profile do xml.merchantCustomerId(truncate(options[:merchant_customer_id], 20) || SecureRandom.hex(10)) @@ -726,15 +732,9 @@ def create_customer_profile(credit_card, options) xml.paymentProfiles do xml.customerType('individual') - add_billing_address(xml, credit_card, options) + add_billing_address(xml, payment_source, options) add_shipping_address(xml, options, 'shipToList') - xml.payment do - xml.creditCard do - xml.cardNumber(truncate(credit_card.number, 16)) - xml.expirationDate(format(credit_card.year, :four_digits) + '-' + format(credit_card.month, :two_digits)) - xml.cardCode(credit_card.verification_value) if credit_card.verification_value - end - end + add_payment_source(xml, payment_source, options) end end end diff --git a/test/remote/gateways/remote_authorize_net_test.rb b/test/remote/gateways/remote_authorize_net_test.rb index 2b239ad18bb..84a87612524 100644 --- a/test/remote/gateways/remote_authorize_net_test.rb +++ b/test/remote/gateways/remote_authorize_net_test.rb @@ -1,6 +1,9 @@ require 'test_helper' +require 'support/authorize_helper' class RemoteAuthorizeNetTest < Test::Unit::TestCase + include AuthorizeHelper + def setup @gateway = AuthorizeNetGateway.new(fixtures(:authorize_net)) @@ -150,6 +153,13 @@ def test_successful_purchase_with_customer assert_equal 'This transaction has been approved', response.message end + def test_successful_purchase_with_acceptjs_token + acceptjs_token = get_sandbox_acceptjs_token_for_credit_card(@credit_card) + response = @gateway.purchase(@amount, acceptjs_token, @options.merge(description: 'Accept.js Store Purchase')) + assert_success response + assert_equal 'This transaction has been approved', response.message + end + def test_failed_purchase response = @gateway.purchase(@amount, @declined_card, @options) assert_failure response @@ -290,6 +300,16 @@ def test_successful_authorization_with_moto_retail_type assert response.authorization end + def test_authorization_and_void_acceptjs + acceptjs_token = get_sandbox_acceptjs_token_for_credit_card(@credit_card) + assert authorization = @gateway.authorize(@amount, acceptjs_token, @options) + assert_success authorization + + assert void = @gateway.void(authorization.authorization) + assert_success void + assert_equal 'This transaction has been approved', void.message + end + def test_successful_verify response = @gateway.verify(@credit_card, @options) assert_success response @@ -312,6 +332,15 @@ def test_successful_store assert_equal '1', response.params['message_code'] end + def test_successful_store_acceptjs + acceptjs_token = get_sandbox_acceptjs_token_for_credit_card(@credit_card) + assert response = @gateway.store(acceptjs_token) + assert_success response + assert response.authorization + assert_equal 'Successful', response.message + assert_equal '1', response.params['message_code'] + end + def test_successful_store_new_payment_profile assert store = @gateway.store(@credit_card) assert_success store @@ -326,6 +355,20 @@ def test_successful_store_new_payment_profile assert_equal '1', response.params['message_code'] end + def test_successful_store_new_payment_profile_acceptjs + assert store = @gateway.store(@credit_card) + assert_success store + assert store.authorization + + customer_profile_id, _, _ = store.authorization.split('#') + acceptjs_token = get_sandbox_acceptjs_token_for_credit_card(@credit_card) + + assert response = @gateway.store(acceptjs_token, customer_profile_id: customer_profile_id) + assert_success response + assert_equal 'Successful', response.message + assert_equal '1', response.params['message_code'] + end + def test_failed_store_new_payment_profile assert store = @gateway.store(@credit_card) assert_success store @@ -628,6 +671,14 @@ def test_successful_credit assert response.authorization end + def test_successful_credit_acceptjs + acceptjs_token = get_sandbox_acceptjs_token_for_credit_card(@credit_card) + response = @gateway.credit(@amount, acceptjs_token, @options.merge(description: 'Accept.js Store Refund')) + assert_success response + assert_equal 'This transaction has been approved', response.message + assert response.authorization + end + def test_successful_echeck_credit response = @gateway.credit(@amount, @check, @options) assert_equal 'The transaction is currently under review', response.message diff --git a/test/unit/gateways/authorize_net_test.rb b/test/unit/gateways/authorize_net_test.rb index 9c444ad6b3a..3abce8b5f0f 100644 --- a/test/unit/gateways/authorize_net_test.rb +++ b/test/unit/gateways/authorize_net_test.rb @@ -1,6 +1,8 @@ require 'test_helper' +require 'support/authorize_helper' class AuthorizeNetTest < Test::Unit::TestCase + include AuthorizeHelper include CommStub BAD_TRACK_DATA = '%B378282246310005LONGSONLONGBOB1705101130504392?' @@ -22,6 +24,7 @@ def setup payment_network: 'Visa', transaction_identifier: 'transaction123' ) + @acceptjs_token = acceptjs_token @options = { order_id: '1', @@ -307,6 +310,22 @@ def test_successful_purchase assert_equal 'CVV not processed', response.cvv_result['message'] end + def test_successful_acceptjs_purchase + response = stub_comms do + @gateway.purchase(@amount, @acceptjs_token) + end.check_request do |endpoint, data, headers| + parse(data) do |doc| + assert_equal @gateway.class::ACCEPT_JS_DATA_DESCRIPTOR, doc.at_xpath('//opaqueData/dataDescriptor').content + assert_equal @acceptjs_token.opaque_data[:data_value], doc.at_xpath('//opaqueData/dataValue').content + end + end.respond_with(successful_purchase_response) + + assert response + assert_instance_of Response, response + assert_success response + assert_equal '508141795', response.authorization.split('#')[0] + end + def test_successful_purchase_with_utf_character stub_comms do @gateway.purchase(@amount, credit_card('4000100011112224', last_name: 'Wåhlin')) @@ -663,6 +682,16 @@ def test_successful_store assert_equal '32506918', store.params['customer_payment_profile_id'] end + def test_successful_acceptjs_store + @gateway.expects(:ssl_post).returns(successful_store_response) + + store = @gateway.store(@acceptjs_token, @options) + assert_success store + assert_equal 'Successful', store.message + assert_equal '35959426', store.params['customer_profile_id'] + assert_equal '32506918', store.params['customer_payment_profile_id'] + end + def test_failed_store @gateway.expects(:ssl_post).returns(failed_store_response) From 78d2bf6ce7aa64333d5ca9f78c7a82cf3c870a63 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Thu, 21 Apr 2022 14:49:36 -0500 Subject: [PATCH 8/9] Add #display_number method --- lib/active_merchant/billing/accept_js_token.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_merchant/billing/accept_js_token.rb b/lib/active_merchant/billing/accept_js_token.rb index 8dfd38c4483..dbd464369c5 100644 --- a/lib/active_merchant/billing/accept_js_token.rb +++ b/lib/active_merchant/billing/accept_js_token.rb @@ -8,6 +8,10 @@ def type def opaque_data payment_data[:opaque_data] end + + def display_number + @metadata[:card_number] + end end end end From f7c5ffb8246213c032f977daa015e6d45147efc0 Mon Sep 17 00:00:00 2001 From: Clint Miller Date: Mon, 25 Apr 2022 16:07:52 -0500 Subject: [PATCH 9/9] Fix merge conflicts --- test/remote/gateways/remote_authorize_net_cim_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/remote/gateways/remote_authorize_net_cim_test.rb b/test/remote/gateways/remote_authorize_net_cim_test.rb index 68656ce27e5..f0459ac5869 100644 --- a/test/remote/gateways/remote_authorize_net_cim_test.rb +++ b/test/remote/gateways/remote_authorize_net_cim_test.rb @@ -110,12 +110,22 @@ def test_successful_profile_create_with_acceptjs end def test_get_customer_profile_with_unmasked_exp_date_and_issuer_info + assert response = @gateway.create_customer_profile(@options) + @customer_profile_id = response.authorization + + assert_success response + assert response.test? + assert response = @gateway.get_customer_profile( customer_profile_id: @customer_profile_id, unmask_expiration_date: true, include_issuer_info: true ) + assert response.test? + assert_success response + assert_equal @customer_profile_id, response.authorization + assert_equal 'Successful.', response.message assert_equal "XXXX#{@credit_card.last_digits}", response.params['profile']['payment_profiles']['payment']['credit_card']['card_number'], "The card number should contain the last 4 digits of the card we passed in #{@credit_card.last_digits}" assert_equal formatted_expiration_date(@credit_card), response.params['profile']['payment_profiles']['payment']['credit_card']['expiration_date'] assert_equal @credit_card.first_digits, response.params['profile']['payment_profiles']['payment']['credit_card']['issuer_number']