Skip to content

Commit

Permalink
Kushki: Enable 3ds2
Browse files Browse the repository at this point in the history
Summary:

Enable 3ds version 2 on the gateway above

SER-625

Unit Test

Finished in 0.019977 seconds.
------------------------------------------------------------------------------------------------------
17 tests, 109 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------

Remote Test

Finished in 82.28609 seconds.
------------------------------------------------------------------------------------------------------
23 tests, 68 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------
  • Loading branch information
jherreraa authored and curiousepic committed Jul 25, 2023
1 parent 3f45193 commit d9c926a
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

== Version 1.134.0 (July 25, 2023)
* Update required Ruby version [almalee24] #4823
* Kushki: Enable 3ds2 [jherreraa] #4832

== Version 1.133.0 (July 20, 2023)
* CyberSource: remove credentials from tests [bbraschi] #4836
Expand Down
40 changes: 36 additions & 4 deletions lib/active_merchant/billing/gateways/kushki.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ def initialize(options = {})
def purchase(amount, payment_method, options = {})
MultiResponse.run() do |r|
r.process { tokenize(amount, payment_method, options) }
r.process { charge(amount, r.authorization, options) }
r.process { charge(amount, r.authorization, options, payment_method) }
end
end

def authorize(amount, payment_method, options = {})
MultiResponse.run() do |r|
r.process { tokenize(amount, payment_method, options) }
r.process { preauthorize(amount, r.authorization, options) }
r.process { preauthorize(amount, r.authorization, options, payment_method) }
end
end

Expand Down Expand Up @@ -89,7 +89,7 @@ def tokenize(amount, payment_method, options)
commit(action, post)
end

def charge(amount, authorization, options)
def charge(amount, authorization, options, payment_method = {})
action = 'charge'

post = {}
Expand All @@ -100,11 +100,12 @@ def charge(amount, authorization, options)
add_metadata(post, options)
add_months(post, options)
add_deferred(post, options)
add_three_d_secure(post, payment_method, options)

commit(action, post)
end

def preauthorize(amount, authorization, options)
def preauthorize(amount, authorization, options, payment_method = {})
action = 'preAuthorization'

post = {}
Expand All @@ -114,6 +115,7 @@ def preauthorize(amount, authorization, options)
add_metadata(post, options)
add_months(post, options)
add_deferred(post, options)
add_three_d_secure(post, payment_method, options)

commit(action, post)
end
Expand Down Expand Up @@ -204,6 +206,36 @@ def add_deferred(post, options)
}
end

def add_three_d_secure(post, payment_method, options)
three_d_secure = options[:three_d_secure]
return unless three_d_secure.present?

post[:threeDomainSecure] = {
eci: three_d_secure[:eci],
specificationVersion: three_d_secure[:version]
}

if payment_method.brand == 'master'
post[:threeDomainSecure][:acceptRisk] = three_d_secure[:eci] == '00'
post[:threeDomainSecure][:ucaf] = three_d_secure[:cavv]
post[:threeDomainSecure][:directoryServerTransactionID] = three_d_secure[:ds_transaction_id]
case three_d_secure[:eci]
when '07'
post[:threeDomainSecure][:collectionIndicator] = '0'
when '06'
post[:threeDomainSecure][:collectionIndicator] = '1'
else
post[:threeDomainSecure][:collectionIndicator] = '2'
end
elsif payment_method.brand == 'visa'
post[:threeDomainSecure][:acceptRisk] = three_d_secure[:eci] == '07'
post[:threeDomainSecure][:cavv] = three_d_secure[:cavv]
post[:threeDomainSecure][:xid] = three_d_secure[:xid]
else
raise ArgumentError.new 'Kushki supports 3ds2 authentication for only Visa and Mastercard brands.'
end
end

ENDPOINT = {
'tokenize' => 'tokens',
'charge' => 'charges',
Expand Down
80 changes: 80 additions & 0 deletions test/remote/gateways/remote_kushki_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,86 @@ def test_failed_authorize
assert_equal 'Monto de la transacción es diferente al monto de la venta inicial', response.message
end

def test_successful_3ds2_authorize_with_visa_card
options = {
currency: 'PEN',
three_d_secure: {
version: '2.2.0',
cavv: 'AAABBoVBaZKAR3BkdkFpELpWIiE=',
xid: 'NEpab1F1MEdtaWJ2bEY3ckYxQzE=',
eci: '07'
}
}
response = @gateway.authorize(@amount, @credit_card, options)
assert_success response
assert_equal 'Succeeded', response.message
assert_match %r(^\d+$), response.authorization
end

def test_successful_3ds2_authorize_with_master_card
options = {
currency: 'PEN',
three_d_secure: {
version: '2.2.0',
cavv: 'AAABBoVBaZKAR3BkdkFpELpWIiE=',
eci: '00',
ds_transaction_id: 'b23e0264-1209-41L6-Jca4-b82143c1a782'
}
}

credit_card = credit_card('5223450000000007', brand: 'master', verification_value: '777')
response = @gateway.authorize(@amount, credit_card, options)
assert_success response
assert_equal 'Succeeded', response.message
end

def test_successful_3ds2_purchase
options = {
three_d_secure: {
version: '2.2.0',
cavv: 'AAABBoVBaZKAR3BkdkFpELpWIiE=',
xid: 'NEpab1F1MEdtaWJ2bEY3ckYxQzE=',
eci: '07'
}
}

response = @gateway.purchase(@amount, @credit_card, options)

assert_success response
assert_equal 'Succeeded', response.message
assert_match %r(^\d+$), response.authorization
end

def test_failed_3ds2_authorize
options = {
currency: 'PEN',
three_d_secure: {
version: '2.2.0',
authentication_response_status: 'Y',
cavv: 'AAABBoVBaZKAR3BkdkFpELpWIiE=',
xid: 'NEpab1F1MEdtaWJ2bEY3ckYxQzE='
}
}
response = @gateway.authorize(@amount, @credit_card, options)
assert_failure response
assert_equal 'K001', response.responses.last.error_code
end

def test_failed_3ds2_authorize_with_different_card
options = {
currency: 'PEN',
three_d_secure: {
version: '2.2.0',
cavv: 'AAABBoVBaZKAR3BkdkFpELpWIiE=',
xid: 'NEpab1F1MEdtaWJ2bEY3ckYxQzE='
}
}
credit_card = credit_card('6011111111111117', brand: 'discover', verification_value: '777')
assert_raise ArgumentError do
@gateway.authorize(@amount, credit_card, options)
end
end

def test_successful_capture
auth = @gateway.authorize(@amount, @credit_card)
assert_success auth
Expand Down

0 comments on commit d9c926a

Please sign in to comment.