Skip to content

Commit

Permalink
Worldpay: Update Stored Credentials
Browse files Browse the repository at this point in the history
    Update add_stored_credential_using_normalized_fields to
    only send usage: 'USED' with merchantInitiatedReason and
    only send reason if it's recurring or installment.

    Unit:
    133 tests, 741 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
    100% passed

    Remote:
    114 tests, 489 assertions, 3 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
    97.3684% passed
  • Loading branch information
Alma Malambo committed Nov 20, 2024
1 parent 95c09a8 commit 88bdf59
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
* Litle: Success codes added to valid responses [jherreraa] #5339
* CommerceHub: Update Production URL [almalee24] #5340
* CommerceHub: Add Network Token support [almalee24] #5331
* Worldpay: Update Stored Credentials [almalee24] #5330

== Version 1.137.0 (August 2, 2024)
* Unlock dependency on `rexml` to allow fixing a CVE (#5181).
Expand Down
16 changes: 9 additions & 7 deletions lib/active_merchant/billing/gateways/worldpay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ def add_card(xml, payment_method, options)
def add_stored_credential_options(xml, options = {})
if options[:stored_credential]
add_stored_credential_using_normalized_fields(xml, options)
else
elsif options[:stored_credential_usage]
add_stored_credential_using_gateway_specific_fields(xml, options)
end
end
Expand All @@ -798,23 +798,21 @@ def add_stored_credential_using_normalized_fields(xml, options)
stored_credential_params = generate_stored_credential_params(is_initial_transaction, reason, options[:stored_credential][:initiator])

xml.storedCredentials stored_credential_params do
xml.schemeTransactionIdentifier network_transaction_id(options) if network_transaction_id(options) && subsequent_non_cardholder_transaction?(options, is_initial_transaction)
xml.schemeTransactionIdentifier network_transaction_id(options) if send_network_transaction_id?(options)
end
end

def add_stored_credential_using_gateway_specific_fields(xml, options)
return unless options[:stored_credential_usage]

is_initial_transaction = options[:stored_credential_usage] == 'FIRST'
stored_credential_params = generate_stored_credential_params(is_initial_transaction, options[:stored_credential_initiated_reason])

xml.storedCredentials stored_credential_params do
xml.schemeTransactionIdentifier options[:stored_credential_transaction_id] if options[:stored_credential_transaction_id] && subsequent_non_cardholder_transaction?(options, is_initial_transaction)
xml.schemeTransactionIdentifier options[:stored_credential_transaction_id] if options[:stored_credential_transaction_id] && !is_initial_transaction
end
end

def subsequent_non_cardholder_transaction?(options, is_initial_transaction)
!is_initial_transaction && options&.dig(:stored_credential, :initiator) != 'cardholder'
def send_network_transaction_id?(options)
network_transaction_id(options) && !options.dig(:stored_credential, :initial_transaction) && options.dig(:stored_credential, :initiator) != 'cardholder'
end

def add_shopper(xml, options)
Expand Down Expand Up @@ -1164,6 +1162,10 @@ def generate_stored_credential_params(is_initial_transaction, reason = nil, init

stored_credential_params = {}
stored_credential_params['usage'] = is_initial_transaction ? 'FIRST' : 'USED'

return stored_credential_params unless %w(RECURRING INSTALMENT).include?(reason)
return stored_credential_params if customer_or_merchant == 'customerInitiatedReason' && stored_credential_params['usage'] == 'USED'

stored_credential_params[customer_or_merchant] = reason if reason
stored_credential_params
end
Expand Down
25 changes: 22 additions & 3 deletions test/unit/gateways/worldpay_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ def test_authorize_passes_stored_credential_options
response = stub_comms do
@gateway.authorize(@amount, @credit_card, options)
end.check_request do |_endpoint, data, _headers|
assert_match(/<storedCredentials usage\=\"USED\" merchantInitiatedReason\=\"UNSCHEDULED\"\>/, data)
assert_match(/<storedCredentials usage\=\"USED\"\>/, data)
assert_match(/<schemeTransactionIdentifier\>000000000000020005060720116005060\<\/schemeTransactionIdentifier\>/, data)
end.respond_with(successful_authorize_response)
assert_success response
Expand All @@ -437,7 +437,7 @@ def test_authorize_with_nt_passes_stored_credential_options
response = stub_comms do
@gateway.authorize(@amount, @nt_credit_card, options)
end.check_request do |_endpoint, data, _headers|
assert_match(/<storedCredentials usage\=\"USED\" merchantInitiatedReason\=\"UNSCHEDULED\"\>/, data)
assert_match(/<storedCredentials usage\=\"USED\"\>/, data)
assert_match(/<schemeTransactionIdentifier\>000000000000020005060720116005060\<\/schemeTransactionIdentifier\>/, data)
end.respond_with(successful_authorize_response)
assert_success response
Expand All @@ -448,7 +448,7 @@ def test_authorize_with_nt_passes_standard_stored_credential_options
response = stub_comms do
@gateway.authorize(@amount, @nt_credit_card, @options.merge({ stored_credential: stored_credential_params }))
end.check_request do |_endpoint, data, _headers|
assert_match(/<storedCredentials usage\=\"USED\" merchantInitiatedReason\=\"UNSCHEDULED\"\>/, data)
assert_match(/<storedCredentials usage\=\"USED\"\>/, data)
assert_match(/<schemeTransactionIdentifier\>20005060720116005060\<\/schemeTransactionIdentifier\>/, data)
end.respond_with(successful_authorize_response)
assert_success response
Expand Down Expand Up @@ -1704,6 +1704,25 @@ def test_authorize_prefers_options_for_ntid
assert_success response
end

def test_authorize_stored_credentials_for_subsequent_customer_transaction
options = @options.merge!({
stored_credential: {
network_transaction_id: '3812908490218390214124',
initial_transaction: false,
reason_type: 'recurring',
initiator: 'cardholder'
}
})

response = stub_comms do
@gateway.authorize(@amount, @credit_card, options)
end.check_request do |_endpoint, data, _headers|
assert_match(/<storedCredentials usage\=\"USED\"\>/, data)
assert_not_match(/<schemeTransactionIdentifier\>000000000000020005060720116005060\<\/schemeTransactionIdentifier\>/, data)
end.respond_with(successful_authorize_response)
assert_success response
end

def test_authorize_recurring_apple_pay_with_ntid
stored_credential_params = stored_credential(:used, :recurring, :merchant, network_transaction_id: '3812908490218390214124')

Expand Down

0 comments on commit 88bdf59

Please sign in to comment.