Skip to content

Commit

Permalink
Trust Commerce: Support void after purchase
Browse files Browse the repository at this point in the history
Trust Commerce allows voids on purchases, refunds and captures, and
preauths. Preauth uses the 'reversal' function, while purchase, refund,
and capture use the Trust Commerce 'void' function'.

Expand void support for additional transaction types by tracking the
original action associated with the authorization transaction
id. The action take at Trust Commerce (reversal or void) is based on the
original action.

ECS-356

Unit:
12 tests, 41 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

Remote:
17 tests, 64 assertions, 3 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
82.3529% passed

closes activemerchant#3265
  • Loading branch information
jknipp authored and whitby3001 committed Sep 3, 2019
1 parent 6e0eff2 commit 6f7151e
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* Global Collect: Only add name if present [curiousepic] #3268
* HPS: Add Apple Pay raw cryptogram support [slogsdon] #3209
* CardConnect: Fix parsing of level 3 fields [hdeters] #3273
* TrustCommerce: Support void after purchase [jknipp] #3265

== Version 1.95.0 (May 23, 2019)
* Adyen: Constantize version to fix subdomains [curiousepic] #3228
Expand Down
29 changes: 24 additions & 5 deletions lib/active_merchant/billing/gateways/trust_commerce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class TrustCommerceGateway < Gateway
TEST_LOGIN = 'TestMerchant'
TEST_PASSWORD = 'password'

VOIDABLE_ACTIONS = %w(preauth sale postauth credit)

self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :discover, :american_express, :diners_club, :jcb]
self.supported_countries = ['US']
Expand Down Expand Up @@ -179,9 +181,10 @@ def purchase(money, creditcard_or_billing_id, options = {})
# postauth, we preserve active_merchant's nomenclature of capture() for consistency with the rest of the library. To process
# a postauthorization with TC, you need an amount in cents or a money object, and a TC transid.
def capture(money, authorization, options = {})
transaction_id, _ = split_authorization(authorization)
parameters = {
:amount => amount(money),
:transid => authorization,
:transid => transaction_id,
}
add_aggregator(parameters, options)

Expand All @@ -191,9 +194,10 @@ def capture(money, authorization, options = {})
# refund() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
# that you want to refund, and a TC transid for the transaction that you are refunding.
def refund(money, identification, options = {})
transaction_id, _ = split_authorization(identification)
parameters = {
:amount => amount(money),
:transid => identification
:transid => transaction_id
}
add_aggregator(parameters, options)

Expand All @@ -214,18 +218,24 @@ def credit(money, identification, options = {})
# TrustCommerce to allow for reversal transactions before you can use this
# method.
#
# void() is also used to to cancel a capture (postauth), purchase (sale),
# or refund (credit) or a before it is sent for settlement.
#
# NOTE: AMEX preauth's cannot be reversed. If you want to clear it more
# quickly than the automatic expiration (7-10 days), you will have to
# capture it and then immediately issue a credit for the same amount
# which should clear the customers credit card with 48 hours according to
# TC.
def void(authorization, options = {})
transaction_id, original_action = split_authorization(authorization)
action = (VOIDABLE_ACTIONS - ['preauth']).include?(original_action) ? 'void' : 'reversal'

parameters = {
:transid => authorization,
:transid => transaction_id,
}
add_aggregator(parameters, options)

commit('reversal', parameters)
commit(action, parameters)
end

# recurring() a TrustCommerce account that is activated for Citadel, TrustCommerce's
Expand Down Expand Up @@ -416,7 +426,7 @@ def commit(action, parameters)
message = message_from(data)
Response.new(success, message, data,
:test => test?,
:authorization => data['transid'],
:authorization => authorization_from(action, data),
:cvv_result => data['cvv'],
:avs_result => { :code => data['avs'] }
)
Expand Down Expand Up @@ -446,6 +456,15 @@ def message_from(data)
end
end

def authorization_from(action, data)
authorization = data['transid']
authorization = "#{authorization}|#{action}" if authorization && VOIDABLE_ACTIONS.include?(action)
authorization
end

def split_authorization(authorization)
authorization.split('|')
end
end
end
end
12 changes: 12 additions & 0 deletions test/remote/gateways/remote_trust_commerce_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ def test_purchase_with_avs_for_invalid_address
assert_success response
end

# Requires enabling the setting: 'Allow voids to process or settle on processing node' in the Trust Commerce vault UI
def test_purchase_and_void
purchase = @gateway.purchase(@amount, @credit_card, @options)
assert_success purchase

void = @gateway.void(purchase.authorization)
assert_success void
assert_equal 'The transaction was successful', void.message
assert_equal 'accepted', void.params['status']
assert void.params['transid']
end

def test_successful_authorize_with_avs
assert response = @gateway.authorize(@amount, @credit_card, :billing_address => @valid_address)

Expand Down
25 changes: 24 additions & 1 deletion test/unit/gateways/trust_commerce_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_successful_purchase
assert response = @gateway.purchase(@amount, @credit_card)
assert_instance_of Response, response
assert_success response
assert_equal '025-0007423614', response.authorization
assert_equal '025-0007423614|sale', response.authorization
end

def test_unsuccessful_purchase
Expand All @@ -41,6 +41,22 @@ def test_succesful_purchase_with_check
end.respond_with(successful_purchase_response)
end

def test_successful_void_from_purchase
stub_comms do
@gateway.void('1235|sale')
end.check_request do |endpoint, data, headers|
assert_match(%r{action=void}, data)
end.respond_with(successful_void_response)
end

def test_successful_void_from_authorize
stub_comms do
@gateway.void('1235|preauth')
end.check_request do |endpoint, data, headers|
assert_match(%r{action=reversal}, data)
end.respond_with(successful_void_response)
end

def test_amount_style
assert_equal '1034', @gateway.send(:amount, 1034)

Expand Down Expand Up @@ -105,6 +121,13 @@ def unsuccessful_purchase_response
RESPONSE
end

def successful_void_response
<<-RESPONSE
transid=025-0007423828
status=accpeted
RESPONSE
end

def transcript
<<-TRANSCRIPT
action=sale&demo=y&password=password&custid=TestMerchant&shipto_zip=90001&shipto_state=CA&shipto_city=Somewhere&shipto_address1=123+Test+St.&avs=n&zip=90001&state=CA&city=Somewhere&address1=123+Test+St.&cvv=1234&exp=0916&cc=4111111111111111&name=Longbob+Longsen&media=cc&ip=10.10.10.10&email=cody%40example.com&ticket=%231000.1&amount=100
Expand Down

0 comments on commit 6f7151e

Please sign in to comment.