Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Worldpay: Support apple pay recurring #5268

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions lib/active_merchant/billing/gateways/worldpay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -663,14 +663,24 @@ def add_network_tokenization_card(xml, payment_method, options)
end
name = card_holder_name(payment_method, options)
xml.cardHolderName name if name.present?
xml.cryptogram payment_method.payment_cryptogram unless options[:wallet_type] == :google_pay
xml.cryptogram payment_method.payment_cryptogram if should_send_payment_cryptogram?(options, payment_method)
eci = eci_value(payment_method, options)
xml.eciIndicator eci if eci.present?
end
add_stored_credential_options(xml, options)
end
end

def should_send_payment_cryptogram?(options, payment_method)
!(
wallet_type_google_pay?(options) ||
(
payment_method_apple_pay?(payment_method) &&
options.dig(:stored_credential, :initiator) == 'merchant'
)
)
end

def add_card_or_token(xml, payment_method, options)
xml.paymentDetails credit_fund_transfer_attribute(options) do
if options[:payment_type] == :token
Expand Down Expand Up @@ -1040,16 +1050,28 @@ def payment_details(payment_method, options = {})
when String
token_type_and_details(payment_method)
else
type = network_token?(payment_method) || options[:wallet_type] == :google_pay ? :network_token : :credit
type = network_token?(payment_method, options) ? :network_token : :credit

{ payment_type: type }
end
end

def network_token?(payment_method)
payment_method.respond_to?(:source) &&
def network_token?(payment_method, options)
(payment_method.respond_to?(:source) &&
payment_method.respond_to?(:payment_cryptogram) &&
payment_method.respond_to?(:eci)
payment_method.respond_to?(:eci)) ||
wallet_type_google_pay?(options) ||
payment_method_apple_pay?(payment_method)
end

def payment_method_apple_pay?(payment_method)
return false unless payment_method.is_a?(NetworkTokenizationCreditCard)

payment_method.source == :apple_pay
end

def wallet_type_google_pay?(options)
options[:wallet_type] == :google_pay
end

def token_type_and_details(token)
Expand Down
24 changes: 24 additions & 0 deletions test/remote/gateways/remote_worldpay_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def setup
sub_tax_id: '987-65-4321'
}
}

@apple_pay_network_token = network_tokenization_credit_card(
'4895370015293175',
month: 10,
Expand Down Expand Up @@ -669,6 +670,29 @@ def test_successful_auth_and_capture_with_gateway_specific_recurring_stored_cred
assert_success capture
end

def test_successful_recurring_purchase_with_apple_pay_credentials
stored_credential_params = stored_credential(:initial, :recurring, :merchant)
assert auth = @gateway.authorize(@amount, @apple_pay_network_token, @options.merge({ stored_credential: stored_credential_params }))
assert_success auth
assert auth.authorization
assert auth.params['scheme_response']
assert auth.params['transaction_identifier']

assert capture = @gateway.capture(@amount, auth.authorization, authorization_validated: true)
assert_success capture

@options[:order_id] = generate_unique_id
@options[:stored_credential] = stored_credential(:used, :recurring, :merchant, network_transaction_id: auth.params['transaction_identifier'])
KenderBolivarT marked this conversation as resolved.
Show resolved Hide resolved

assert next_auth = @gateway.authorize(@amount, @apple_pay_network_token, @options)
assert next_auth.authorization
assert next_auth.params['scheme_response']
assert next_auth.params['transaction_identifier']

assert capture = @gateway.capture(@amount, next_auth.authorization, authorization_validated: true)
assert_success capture
end

def test_successful_authorize_with_3ds_with_normalized_stored_credentials
session_id = generate_unique_id
stored_credential_params = stored_credential(:initial, :unscheduled, :merchant)
Expand Down
17 changes: 17 additions & 0 deletions test/unit/gateways/worldpay_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,23 @@ def test_authorize_prefers_options_for_ntid
assert_success response
end

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

@options.merge({
stored_credential: stored_credential_params,
stored_credential_transaction_id: '000000000000020005060720116005060',
wallet_type: :apple_pay
})
response = stub_comms do
@gateway.authorize(@amount, @apple_play_network_token, @options)
end.check_request do |_endpoint, data, _headers|
assert_match %r(<EMVCO_TOKEN-SSL type="APPLEPAY">), data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add a check to ensure the cryptogram is not sent? Maybe something like this:

Suggested change
assert_match %r(<EMVCO_TOKEN-SSL type="APPLEPAY">), data
refute_match %r(<cryptogram>), data

refute_match %r(<cryptogram>), data
end.respond_with(successful_authorize_response)
assert_success response
end

def test_successful_inquire_with_order_id
response = stub_comms do
@gateway.inquire(nil, { order_id: @options[:order_id].to_s })
Expand Down
Loading