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 AFTs #5154

Merged
Merged
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
69 changes: 68 additions & 1 deletion lib/active_merchant/billing/gateways/worldpay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ def credit(money, payment_method, options = {})
payment_details = payment_details(payment_method, options)
if options[:fast_fund_credit]
fast_fund_credit_request(money, payment_method, payment_details.merge(credit: true, **options))
elsif options[:account_funding_transaction]
aft_request(money, payment_method, payment_details.merge(**options))
else
credit_request(money, payment_method, payment_details.merge(credit: true, **options))
end
Expand Down Expand Up @@ -148,7 +150,8 @@ def scrub(transcript)
gsub(%r((<cardNumber>)\d+(</cardNumber>)), '\1[FILTERED]\2').
gsub(%r((<cvc>)[^<]+(</cvc>)), '\1[FILTERED]\2').
gsub(%r((<tokenNumber>)\d+(</tokenNumber>)), '\1[FILTERED]\2').
gsub(%r((<cryptogram>)[^<]+(</cryptogram>)), '\1[FILTERED]\2')
gsub(%r((<cryptogram>)[^<]+(</cryptogram>)), '\1[FILTERED]\2').
gsub(%r((<accountReference accountType="\w+">)\d+(<\/accountReference>)), '\1[FILTERED]\2')
end

private
Expand Down Expand Up @@ -189,6 +192,10 @@ def fast_fund_credit_request(money, payment_method, options)
commit('fast_credit', build_fast_fund_credit_request(money, payment_method, options), :ok, 'PUSH_APPROVED', options)
end

def aft_request(money, payment_method, options)
commit('funding_transfer_transaction', build_aft_request(money, payment_method, options), :ok, 'AUTHORISED', options)
end

def store_request(credit_card, options)
commit('store', build_store_request(credit_card, options), options)
end
Expand Down Expand Up @@ -400,6 +407,66 @@ def build_fast_fund_credit_request(money, payment_method, options)
end
end

def build_aft_request(money, payment_method, options)
build_request do |xml|
xml.submit do
xml.order order_tag_attributes(options) do
xml.description(options[:description].blank? ? 'Account Funding Transaction' : options[:description])
add_amount(xml, money, options)
add_order_content(xml, options)
add_payment_method(xml, money, payment_method, options)
add_shopper(xml, options)
add_sub_merchant_data(xml, options[:sub_merchant_data]) if options[:sub_merchant_data]
add_aft_data(xml, payment_method, options)
end
end
end
end

def add_aft_data(xml, payment_method, options)
xml.fundingTransfer 'type' => options[:aft_type], 'category' => 'PULL_FROM_CARD' do
xml.paymentPurpose options[:aft_payment_purpose] # Must be included for the recipient for following countries, otherwise optional: Argentina, Bangladesh, Chile, Columbia, Jordan, Mexico, Thailand, UAE, India cross-border
xml.fundingParty 'type' => 'sender' do
xml.accountReference options[:aft_sender_account_reference], 'accountType' => options[:aft_sender_account_type]
xml.fullName do
xml.first options.dig(:aft_sender_full_name, :first)
xml.middle options.dig(:aft_sender_full_name, :middle)
xml.last options.dig(:aft_sender_full_name, :last)
end
xml.fundingAddress do
xml.address1 options.dig(:aft_sender_funding_address, :address1)
xml.address2 options.dig(:aft_sender_funding_address, :address2)
xml.postalCode options.dig(:aft_sender_funding_address, :postal_code)
xml.city options.dig(:aft_sender_funding_address, :city)
xml.state options.dig(:aft_sender_funding_address, :state)
xml.countryCode options.dig(:aft_sender_funding_address, :country_code)
end
end
xml.fundingParty 'type' => 'recipient' do
xml.accountReference options[:aft_recipient_account_reference], 'accountType' => options[:aft_recipient_account_type]
xml.fullName do
xml.first options.dig(:aft_recipient_full_name, :first)
xml.middle options.dig(:aft_recipient_full_name, :middle)
xml.last options.dig(:aft_recipient_full_name, :last)
end
xml.fundingAddress do
xml.address1 options.dig(:aft_recipient_funding_address, :address1)
xml.address2 options.dig(:aft_recipient_funding_address, :address2)
xml.postalCode options.dig(:aft_recipient_funding_address, :postal_code)
xml.city options.dig(:aft_recipient_funding_address, :city)
xml.state options.dig(:aft_recipient_funding_address, :state)
xml.countryCode options.dig(:aft_recipient_funding_address, :country_code)
end
if options[:aft_recipient_funding_data]
xml.fundingData do
add_date_element(xml, 'birthDate', options[:aft_recipient_funding_data][:birth_date])
xml.telephoneNumber options.dig(:aft_recipient_funding_data, :telephone_number)
end
end
end
end
end

def add_payment_details_for_ff_credit(xml, payment_method, options)
xml.paymentDetails do
xml.tag! 'FF_DISBURSE-SSL' do
Expand Down
72 changes: 72 additions & 0 deletions test/remote/gateways/remote_worldpay_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,50 @@ def setup
transaction_id: '123456789',
eci: '05'
)

@aft_options = {
account_funding_transaction: true,
aft_type: 'A',
aft_payment_purpose: '01',
aft_sender_account_type: '02',
aft_sender_account_reference: '4111111111111112',
aft_sender_full_name: {
first: 'First',
middle: 'Middle',
last: 'Sender'
},
aft_sender_funding_address: {
address1: '123 Sender St',
address2: 'Apt 1',
postal_code: '12345',
city: 'Senderville',
state: 'NC',
country_code: 'US'
},
aft_recipient_account_type: '03',
aft_recipient_account_reference: '4111111111111111',
aft_recipient_full_name: {
first: 'First',
middle: 'Middle',
last: 'Recipient'
},
aft_recipient_funding_address: {
address1: '123 Recipient St',
address2: 'Apt 1',
postal_code: '12345',
city: 'Recipientville',
state: 'NC',
country_code: 'US'
},
aft_recipient_funding_data: {
telephone_number: '123456789',
birth_date: {
day_of_month: '01',
month: '01',
year: '1980'
}
}
}
end

def test_successful_purchase
Expand Down Expand Up @@ -921,6 +965,34 @@ def test_successful_mastercard_credit_on_cft_gateway
assert_equal 'SUCCESS', credit.message
end

def test_successful_visa_account_funding_transfer
credit = @gateway.credit(@amount, @credit_card, @options.merge(@aft_options))
assert_success credit
assert_equal 'SUCCESS', credit.message
end

def test_successful_visa_account_funding_transfer_via_token
assert store = @gateway.store(@credit_card, @store_options)
assert_success store

credit = @gateway.credit(@amount, store.authorization, @options.merge(@aft_options))
assert_success credit
assert_equal 'SUCCESS', credit.message
end

def test_failed_visa_account_funding_transfer
credit = @gateway.credit(@amount, credit_card('4111111111111111', name: 'REFUSED'), @options.merge(@aft_options))
assert_failure credit
assert_equal 'REFUSED', credit.message
end

def test_failed_visa_account_funding_transfer_acquirer_error
credit = @gateway.credit(@amount, credit_card('4111111111111111', name: 'ACQERROR'), @options.merge(@aft_options))
assert_failure credit
assert_equal 'ACQUIRER ERROR', credit.message
assert_equal '20', credit.error_code
end

# These three fast_fund_credit tests are currently failing with the message: Disbursement transaction not supported
# It seems that the current sandbox setup does not support testing this.

Expand Down
Loading
Loading