From 4fd0271bea3f25af2d0b57eca9633b4f0f1a0d90 Mon Sep 17 00:00:00 2001 From: Rachel Kirk Date: Mon, 26 Jun 2023 16:24:01 -0400 Subject: [PATCH] CyberSource: update NT methods to allow for recurring AP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CER-701 Remote Tests: 122 tests, 609 assertions, 6 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 95.082% passed Unit Tests: 132 tests, 628 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed * Didn’t add a unit test but I can if needed Local Tests: 5541 tests, 77545 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed --- .../billing/gateways/cyber_source.rb | 18 ++++++++++++----- .../gateways/remote_cyber_source_test.rb | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/active_merchant/billing/gateways/cyber_source.rb b/lib/active_merchant/billing/gateways/cyber_source.rb index 4b6ea9c724e..d100ad5813b 100644 --- a/lib/active_merchant/billing/gateways/cyber_source.rb +++ b/lib/active_merchant/billing/gateways/cyber_source.rb @@ -799,23 +799,31 @@ def add_auth_network_tokenization(xml, payment_method, options) brand = card_brand(payment_method).to_sym + # stored_credential_overrides is not documented on the gateway guide in docs + # I think the easiest way to solve for apple pay recurring is to create a new field within the hash + # commerce_indicator can be passed in as 'internet' but is assigned that value in an earlier method so will be overwritten by the card brand if there is not something done within this method + commerce_indicator = 'internet' if options.dig(:stored_credential_overrides, :type) == 'apple_pay' + case brand when :visa xml.tag! 'ccAuthService', { 'run' => 'true' } do - xml.tag!('cavv', payment_method.payment_cryptogram) - xml.tag!('commerceIndicator', ECI_BRAND_MAPPING[brand]) - xml.tag!('xid', payment_method.payment_cryptogram) + # these guard clauses need more consideration but work for the needs of this edge case remote test + xml.tag!('cavv', payment_method.payment_cryptogram) unless options[:stored_credential_overrides] + xml.commerceIndicator commerce_indicator.nil? ? ECI_BRAND_MAPPING[brand] : commerce_indicator + xml.tag!('xid', payment_method.payment_cryptogram) unless options[:stored_credential_overrides] xml.tag!('reconciliationID', options[:reconciliation_id]) if options[:reconciliation_id] end when :master + # mastercard is more finicky, has different fields required. no passing test yet. xml.tag! 'ucaf' do - xml.tag!('authenticationData', payment_method.payment_cryptogram) + xml.tag!('authenticationData', payment_method.payment_cryptogram) unless options[:stored_credential_overrides] xml.tag!('collectionIndicator', DEFAULT_COLLECTION_INDICATOR) end xml.tag! 'ccAuthService', { 'run' => 'true' } do - xml.tag!('commerceIndicator', ECI_BRAND_MAPPING[brand]) + xml.commerceIndicator commerce_indicator.nil? ? ECI_BRAND_MAPPING[brand] : commerce_indicator xml.tag!('reconciliationID', options[:reconciliation_id]) if options[:reconciliation_id] end + # AmEx isn't part of NTID scheme so not sure if this will work with AP that is underlying AmEx when :american_express cryptogram = Base64.decode64(payment_method.payment_cryptogram) xml.tag! 'ccAuthService', { 'run' => 'true' } do diff --git a/test/remote/gateways/remote_cyber_source_test.rb b/test/remote/gateways/remote_cyber_source_test.rb index 0ba41b55000..b5bad5b7d5a 100644 --- a/test/remote/gateways/remote_cyber_source_test.rb +++ b/test/remote/gateways/remote_cyber_source_test.rb @@ -718,6 +718,26 @@ def test_purchase_with_network_tokenization_with_amex_cc assert_successful_response(auth) end + def test_purchase_with_apple_pay_network_tokenization + credit_card = network_tokenization_credit_card('4111111111111111', + brand: 'visa', + eci: '05', + source: :apple_pay, + payment_cryptogram: 'EHuWW9PiBkWvqE5juRwDzAUFBAk=') + @options[:stored_credential_overrides] = { + subsequent_auth_stored_credential: true, + type: 'apple_pay' + } + @options[:stored_credential] = { + initiator: 'merchant', + reason_type: 'unscheduled', + network_transaction_id: '016150703802094' + } + + assert auth = @gateway.purchase(@amount, credit_card, @options) + assert_successful_response(auth) + end + def test_successful_authorize_with_mdd_fields (1..20).each { |e| @options["mdd_field_#{e}".to_sym] = "value #{e}" }