

Here's a source of confusion that arises again and again in our payment systems discussions: the difference between a Return and a Reversal. [A 'Return' can also go by the names 'Merchandise Return' or 'Refund.']
What a Return/Refund is emphatically not: a Reversal.
There are two types of reversals (I'm looking at this from the Acquirer perspective): a Terminal-based reversal; and a Host-based reversal. Terminal-based reversals have a "close cousin" in the Void (also referred to a 'cancellation').
We hear things from our OLS.Switch customers, prospects, partners and trial-drivers like:
"Don't we need to keep 60 days of our tranlog online in case returns come in?"
No, you don't. Returns are not tied to originals. They are standalone transactions.
"How often/reliably does an original invoice # get passed back in a message for a return?"
Exactly 0% of the time. Returns are not tied to originals. They are standalone transactions.
"What are our key match-up fields for returns?"
There are none. Returns are not tied to originals. They are standalone transactions.
"Why didn't the issuer reject that return?"
Because most times they don't even see the auth request (I'll show you this further on in the post). Returns are not tied to originals. They are standalone transactions.
In each case, the questioner has conflated the concepts of the Return and the Reversal.
But enough words, the best thing I can do is to show you how these concepts get implemented so you can see the differences in Real Payment Systems.
Here's a group from a TransactionManager that does Return transaction processing for a large acquirer:
<group name="CreditReturn"><participant class="org.jpos.ev.PopulateCreditTranLog"
logger="Q2" realm="populate-credit-tranlog">
<property name="itc" value="03000" />
<property name="cardType" value="CR" />
<property name="checkpoint" value="populate-credit-tranlog" />
</participant>
&validate_terminal;
<participant class="org.jpos.ev.FindDuplicate">
<property name="itc" value="03000" />
<property name="dupe-check-window" value="2700" />
<property name="checkpoint" value="find-duplicate" />
</participant>
<participant class="org.jpos.ev.SelectEndPoint">
<property name="EP1" value="CreditReturnResponse LogAndReply" />
<property name="EP2" value="CreditReturnResponse LogAndReply" />
<property name="EP3" value="CreditReturnResponse LogAndReply" />
<property name="EP4" value="QueryEP4Return LogAndReply" />
<property name="DUPLICATE" value="DupCreditReturnResponse LogAndReply" />
<property name="MANAGER_OVERRIDE" value="DummyCreditResponse LogAndReply" />
<property name="UNKNOWN" value="DeclinedCreditResponse LogAndReply" />
</participant>
</group>
There's some masking there of the actual endpoints in play but rest assured, these are major players in the payment systems industry that we're talking to. Take note of two things:
- There's no attempt to match to an original. [You can read about 'FindDuplicate' - an entirely different concept - here.]
- Only in one of four cases does the issuer actually want to authorize the Return online. It's the smallest of the four authorizing bodies. In each of the three other cases (Endpoints 1 through 3), the first the issuer learns of the return is in the extract file sent later that evening.
Now, let's compare that to the implementation of a Terminal Reversal group of participants:
<group name="FSASaleReversal">
<participant class="org.jpos.ev.PopulateFSACreditTranLog"
logger="Q2" realm="populate-fsa-credit-tranlog">
<property name="itc" value="05301" />
<property name="cardType" value="FS" />
<property name="checkpoint" value="populate-fsa-credit-tranlog" />
</participant>
&validate_terminal;
<!-- We need this special 'select' that doesn't put Dup or Override in play -->
<participant class="org.jpos.ev.SelectEndPointForReversal">
<property name="EP1" value="FSASaleReversal_Continue LogAndReply" />
<property name="EP2" value="FSASaleReversalLocal LogAndReply" />
<property name="EP3" value="FSASaleReversalLocal LogAndReply" />
<property name="EP4" value="FSASaleReversalLocal LogAndReply" />
<property name="UNKNOWN" value="DummyFSAResponse LogAndReply" />
</participant>
</group>
<group name="FSASaleReversal_Continue">
&find_original_purchase;
<!-- If we made it this far without aborting, it means we found an original
and we now need to decide if we SAF its reversal or void. We need to be
careful here: only SAF if the orginal was remotely authorized. Use APR's
IsManagerOverride model to create WasRemoteAuthorized (use of 'was' meaning
we refer to the original, not the transaction in flight -->
<participant class="org.jpos.ev.WasRemoteAuthorized">
<property name="yes" value="FSASaleReversalSAF" />
<property name="no" value="FSASaleReversalLocal" />
</participant>
</group>
<!-- If SAF-ing the FSA reversal, do this group -->
<group name="FSASaleReversalSAF">
<participant class="org.jpos.ev.CreateEP1Request"
logger="Q2" realm="create-ep1-request">
<property name="mti" value="0400" />
<property name="pcode" value="000000" />
<property name="template" value="cfg/ep1-credit-template.xml" />
<property name="space" value="jdbm:ep1-stan" />
<property name="checkpoint" value="create-ep1-request" />
</participant>
&store_and_forward;
<participant class="org.jpos.ev.FlagReversal">
<property name="reversal-class" value="T" />
</participant>
&force_approval;
&fsa_response;
</group>
Lots of concepts are thrown together in there. I don't want to touch them all here, but - in general - you can see that the reversal treatment is more complex than the return. I chose to show the FSA group here instead of straight credit because the bar is higher in terms of real-time notification of the endpoint. The highlights:
- For Endpoint 1 ('EP1'), we go through a group that is going to notify the issuer via Store and Forward (near real-time) that a reversal got done.
- Note that we try to find the original purchase!
- We always use SAF to send reversals.
- We use FlagReversal to tag the original as being reversed (revInd = 'T').
- Protect the Originator (!) by ensuring Reversals and Voids always get an Approval ('force_approval').
Note that Voids get routed through the same group. At the point-of-sale, voids differ from reversals in that Voids require an overt clerk or administrator action - e.g., canceling the transaction - whereas reversals are system-generated (e.g., when no response is received to certain transaction class - Debit, EBT, for example - within a proscribed transaction timeout period).
Also note that Host-based Timeouts are intrinsically different than the Terminal-based Timeouts described above. The 'H' varieties are handled via the 'query host' participant of the original. Here's a real-time example:
<participant class="org.jpos.ep1.QueryEP1Host"
logger="Q2" realm="query-remote-host">
<property name="mux" value="ep1-mux" />
<property name="saf" value="saf" />
<property name="timeout" value="25000" />
<property name="threshold" value="12000" />
<property name="checkpoint" value="query-host-or-reverse" />
<property name="reverse-on-timeout" value="true" />
</participant>
<participant class="org.jpos.ev.FlagReversal">
<property name="reversal-class" value="H" />
</participant>
There, we're giving the endpoint 25 seconds to respond to the Mux. If we don't see a response in that time, we flag the original as being host-reversed (revInd = 'H') and place a corresponding 0420 (reversal) in the endpoint's SAF queue.
One important conceptual difference between terminal-based and host-based reversal scenarios is this: in terminal-based scenario, you end up with two cross-linked records on the tranlog (the original and the reversal); in the host-based scenario, there's only one transaction (the original).
Hi Andy. What actions are taken on your switch when, processing a reverse, the target transaction is still in progress?
Posted by: Federico | Tuesday, August 04, 2009 at 13:18
Hi Federico -
I assume you're talking about a host-based reversal here...
Yes, the target transaction is still in process.
When the QueryHost participant times out, the Mux takes the request off the "awaiting response" list. We log the transaction as timed-out (a special result code of ours) and send the appropriate response back to the originator.
If the transaction requires a reversal, we SAF it at that point.
If the authorizer comes in with a late response, our 'Late Reply Handler' posts a warning message to our Syslog.
Posted by: Andy Orrock | Wednesday, August 05, 2009 at 14:33
Andy, is there some indication on what Message Type should be used for a Void transaction.
I was contemplating between a 0400 or 0420 but your statement that voids should be treated as force approval gives me the feeling it needs to be a 0420. Is my understanding correct?
Posted by: chhil | Thursday, October 15, 2009 at 01:03