Skip to content

Commit

Permalink
Cybersource Rest: Support normalized 3DS data
Browse files Browse the repository at this point in the history
COMP-80

Adds support for 3DS2 data to the Cybersource Rest gateway.
This follows the standard pattern in AM to add three_d_secure data and
takes inspriation from the legacy Cybersource integration for 3DS.

Docs:https://developer.cybersource.com/api-reference-assets/index.html#payments

Test Summary
Local:
5848 tests, 79233 assertions, 0 failures, 23 errors, 0 pendings, 0 omissions, 0 notifications
99.6067% passed
Tests failing are unrelated to this change
Unit:
32 tests, 165 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
Remote:
49 tests, 150 assertions, 7 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
85.7143% passed
Failing on master too
  • Loading branch information
aenand committed Apr 25, 2024
1 parent 7034274 commit b6d6c11
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Expand Up @@ -153,6 +153,7 @@
* PayTrace: Always send name in billing_address [almalee24] #5086
* StripePI: Update eci format [almalee24] #5097
* Paymentez: Remove reference_id flag [almalee24] #5081
* Cybersource Rest: Add support for normalized three ds [aenand] #5105


== Version 1.135.0 (August 24, 2023)
Expand Down
25 changes: 25 additions & 0 deletions lib/active_merchant/billing/gateways/cyber_source_rest.rb
Expand Up @@ -100,6 +100,30 @@ def scrub(transcript)

private

def add_three_ds(post, payment_method, options)
return unless three_d_secure = options[:three_d_secure]

post[:consumerAuthenticationInformation] ||= {}
if payment_method.brand == 'master'
post[:consumerAuthenticationInformation][:ucafAuthenticationData] = three_d_secure[:cavv]
post[:consumerAuthenticationInformation][:ucafCollectionIndicator] = '2'
else
post[:consumerAuthenticationInformation][:cavv] = three_d_secure[:cavv]
end
post[:consumerAuthenticationInformation][:cavvAlgorithm] = three_d_secure[:cavv_algorithm] if three_d_secure[:cavv_algorithm]
post[:consumerAuthenticationInformation][:paSpecificationVersion] = three_d_secure[:version] if three_d_secure[:version]
post[:consumerAuthenticationInformation][:directoryServerTransactionID] = three_d_secure[:ds_transaction_id] if three_d_secure[:ds_transaction_id]
post[:consumerAuthenticationInformation][:eciRaw] = three_d_secure[:eci] if three_d_secure[:eci]
if three_d_secure[:xid].present?
post[:consumerAuthenticationInformation][:xid] = three_d_secure[:xid]
else
post[:consumerAuthenticationInformation][:xid] = three_d_secure[:cavv]
end
post[:consumerAuthenticationInformation][:veresEnrolled] = three_d_secure[:enrolled] if three_d_secure[:enrolled]
post[:consumerAuthenticationInformation][:paresStatus] = three_d_secure[:authentication_response_status] if three_d_secure[:authentication_response_status]
post
end

def build_void_request(amount = nil)
{ reversalInformation: { amountDetails: { totalAmount: nil } } }.tap do |post|
add_reversal_amount(post, amount.to_i) if amount.present?
Expand All @@ -118,6 +142,7 @@ def build_auth_request(amount, payment, options)
add_business_rules_data(post, payment, options)
add_partner_solution_id(post)
add_stored_credentials(post, payment, options)
add_three_ds(post, payment, options)
end.compact
end

Expand Down
26 changes: 26 additions & 0 deletions test/remote/gateways/remote_cyber_source_rest_test.rb
Expand Up @@ -512,4 +512,30 @@ def test_successful_purchase_in_australian_dollars
assert_nil response.params['_links']['capture']
assert_equal 'AUD', response.params['orderInformation']['amountDetails']['currency']
end

def test_successful_authorize_with_3ds2_visa
@options[:three_d_secure] = {
version: '2.2.0',
cavv: '3q2+78r+ur7erb7vyv66vv\/\/\/\/8=',
eci: '05',
ds_transaction_id: 'ODUzNTYzOTcwODU5NzY3Qw==',
enrolled: 'true',
authentication_response_status: 'Y'
}
auth = @gateway.authorize(@amount, @visa_card, @options)
assert_success auth
end

def test_successful_authorize_with_3ds2_mastercard
@options[:three_d_secure] = {
version: '2.2.0',
cavv: '3q2+78r+ur7erb7vyv66vv\/\/\/\/8=',
eci: '05',
ds_transaction_id: 'ODUzNTYzOTcwODU5NzY3Qw==',
enrolled: 'true',
authentication_response_status: 'Y'
}
auth = @gateway.authorize(@amount, @master_card, @options)
assert_success auth
end
end
52 changes: 52 additions & 0 deletions test/unit/gateways/cyber_source_rest_test.rb
Expand Up @@ -16,6 +16,7 @@ def setup
month: 12,
year: 2031
)
@master_card = credit_card('2222420000001113', brand: 'master')
@apple_pay = network_tokenization_credit_card(
'4111111111111111',
payment_cryptogram: 'AceY+igABPs3jdwNaDg3MAACAAA=',
Expand Down Expand Up @@ -381,6 +382,57 @@ def test_purchase_includes_invoice_number
end.respond_with(successful_purchase_response)
end

def test_mastercard_purchase_with_3ds2
@options[:three_d_secure] = {
version: '2.2.0',
cavv: '3q2+78r+ur7erb7vyv66vv\/\/\/\/8=',
eci: '05',
ds_transaction_id: 'ODUzNTYzOTcwODU5NzY3Qw==',
enrolled: 'true',
authentication_response_status: 'Y',
cavv_algorithm: '2'
}
stub_comms do
@gateway.purchase(100, @master_card, @options)
end.check_request do |_endpoint, data, _headers|
json_data = JSON.parse(data)
assert_equal json_data['consumerAuthenticationInformation']['ucafAuthenticationData'], '3q2+78r+ur7erb7vyv66vv\/\/\/\/8='
assert_equal json_data['consumerAuthenticationInformation']['ucafCollectionIndicator'], '2'
assert_equal json_data['consumerAuthenticationInformation']['cavvAlgorithm'], '2'
assert_equal json_data['consumerAuthenticationInformation']['paSpecificationVersion'], '2.2.0'
assert_equal json_data['consumerAuthenticationInformation']['directoryServerTransactionID'], 'ODUzNTYzOTcwODU5NzY3Qw=='
assert_equal json_data['consumerAuthenticationInformation']['eciRaw'], '05'
assert_equal json_data['consumerAuthenticationInformation']['xid'], '3q2+78r+ur7erb7vyv66vv\/\/\/\/8='
assert_equal json_data['consumerAuthenticationInformation']['veresEnrolled'], 'true'
assert_equal json_data['consumerAuthenticationInformation']['paresStatus'], 'Y'
end.respond_with(successful_purchase_response)
end

def test_visa_purchase_with_3ds2
@options[:three_d_secure] = {
version: '2.2.0',
cavv: '3q2+78r+ur7erb7vyv66vv\/\/\/\/8=',
eci: '05',
ds_transaction_id: 'ODUzNTYzOTcwODU5NzY3Qw==',
enrolled: 'true',
authentication_response_status: 'Y',
cavv_algorithm: '2'
}
stub_comms do
@gateway.authorize(100, @credit_card, @options)
end.check_request do |_endpoint, data, _headers|
json_data = JSON.parse(data)
assert_equal json_data['consumerAuthenticationInformation']['cavv'], '3q2+78r+ur7erb7vyv66vv\/\/\/\/8='
assert_equal json_data['consumerAuthenticationInformation']['cavvAlgorithm'], '2'
assert_equal json_data['consumerAuthenticationInformation']['paSpecificationVersion'], '2.2.0'
assert_equal json_data['consumerAuthenticationInformation']['directoryServerTransactionID'], 'ODUzNTYzOTcwODU5NzY3Qw=='
assert_equal json_data['consumerAuthenticationInformation']['eciRaw'], '05'
assert_equal json_data['consumerAuthenticationInformation']['xid'], '3q2+78r+ur7erb7vyv66vv\/\/\/\/8='
assert_equal json_data['consumerAuthenticationInformation']['veresEnrolled'], 'true'
assert_equal json_data['consumerAuthenticationInformation']['paresStatus'], 'Y'
end.respond_with(successful_purchase_response)
end

def test_adds_application_id_as_partner_solution_id
partner_id = 'partner_id'
CyberSourceRestGateway.application_id = partner_id
Expand Down

0 comments on commit b6d6c11

Please sign in to comment.