Skip to content

Commit

Permalink
Cybersource: auto void r230
Browse files Browse the repository at this point in the history
ECS-2870

Cybersource transactions that fail with a reasonCode of `230` are
in a state where the gateway advises the merchant to decline but has
not declined it themselves. Instead the transaction is pending capture which
can create a mismatch in reporting.

This commit attempts to auto void transactions that have this response code
and a flag that indicates the merchants would like to auto void these kinds
of transactions.

Remote:
121 tests, 611 assertions, 5 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
95.8678% passed

5 test failures on master as well
  • Loading branch information
aenand committed Jun 6, 2023
1 parent ef8d6a7 commit a710327
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
* Braintree: Update mapping for billing address phone number [jcreiff] #4779
* CommerceHub: Enabling multi-use public key encryption [jherreraa] #4771
* Ogone: Enable 3ds Global for Ogone Gateway [javierpedrozaing] #4776
* Borgun change default TrCurrencyExponent and MerchantReturnUrl [naashton] #4
* Worldpay: Fix Google Pay [almalee24] #4774
* Borgun change default TrCurrencyExponent and MerchantReturnUrl [naashton] #4788
* CyberSource: Enable auto void on r230 [aenand] #4794

== Version 1.129.0 (May 3rd, 2023)
* Adyen: Update selectedBrand mapping for Google Pay [jcreiff] #4763
Expand Down
10 changes: 10 additions & 0 deletions lib/active_merchant/billing/gateways/cyber_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,8 @@ def commit(request, action, amount, options)
message = message_from(response)
authorization = success || in_fraud_review?(response) ? authorization_from(response, action, amount, options) : nil

message = auto_void?(authorization_from(response, action, amount, options), response, message, options)

Response.new(success, message, response,
test: test?,
authorization: authorization,
Expand All @@ -1061,6 +1063,14 @@ def commit(request, action, amount, options)
cvv_result: response[:cvCode])
end

def auto_void?(authorization, response, message, options = {})
return message unless response[:reasonCode] == '230' && options[:auto_void_230]

response = void(authorization, options)
response&.success? ? message += ' - transaction has been auto-voided.' : message += ' - transaction could not be auto-voided.'
message
end

# Parse the SOAP response
# Technique inspired by the Paypal Gateway
def parse(xml)
Expand Down
41 changes: 41 additions & 0 deletions test/unit/gateways/cyber_source_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,38 @@ def test_invalid_field
assert_equal 'c:billTo/c:postalCode', response.params['invalidField']
end

def test_cvv_mismatch_successful_auto_void
@gateway.expects(:ssl_post).returns(cvv_mismatch_response)
@gateway.expects(:void).once.returns(ActiveMerchant::Billing::Response.new(true, 'Transaction successful'))

response = @gateway.authorize(@amount, credit_card, @options.merge!(auto_void_230: true))

assert_failure response
assert_equal '230', response.params['reasonCode']
assert_equal 'The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check - transaction has been auto-voided.', response.message
end

def test_cvv_mismatch
@gateway.expects(:ssl_post).returns(cvv_mismatch_response)
@gateway.expects(:void).never

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

assert_failure response
assert_equal '230', response.params['reasonCode']
assert_equal 'The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check', response.message
end

def test_cvv_mismatch_auto_void_failed
@gateway.expects(:ssl_post).returns(cvv_mismatch_response)
@gateway.expects(:void)
response = @gateway.purchase(@amount, credit_card, @options.merge!(auto_void_230: true))

assert_failure response
assert_equal '230', response.params['reasonCode']
assert_equal 'The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check - transaction could not be auto-voided.', response.message
end

def test_able_to_properly_handle_40bytes_cryptogram
long_cryptogram = "NZwc40C4eTDWHVDXPekFaKkNYGk26w+GYDZmU50cATbjqOpNxR/eYA==\n"
credit_card = network_tokenization_credit_card('4111111111111111',
Expand Down Expand Up @@ -1822,6 +1854,15 @@ def invalid_field_response
XML
end

def cvv_mismatch_response
<<~XML
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-1918753692"><wsu:Created>2019-09-05T14:10:46.665Z</wsu:Created></wsu:Timestamp></wsse:Security></soap:Header><soap:Body><c:replyMessage xmlns:c="urn:schemas-cybersource-com:transaction-data-1.155"><c:requestID>5676926465076767004068</c:requestID><c:decision>REJECT</c:decision><c:reasonCode>230</c:reasonCode><c:invalidField>c:billTo/c:postalCode</c:invalidField><c:requestToken>AhjzbwSTM78uTleCsJWkEAJRqivRidukDssiQgRm0ky3SA7oegDUiwLm</c:requestToken></c:replyMessage></soap:Body></soap:Envelope>
XML
end

def invalid_xml_response
"What's all this then, govna?</p>"
end
Expand Down

0 comments on commit a710327

Please sign in to comment.