Reversal and Duplicates Scenarios

Entirely TMI on Reversals and Duplicates

Readers of this blog know that I spend a lot of time talking about the intricacies of the various payment processing (acquirer-side) reversal scenarios and the importance of getting the reversal model right

Here’s another take at reversal processing – entirely TMI (Too Much Information) as the title of this piece suggests, plus some other thoughts on a ‘close cousin’ of reversal processing: finding and quashing duplicate transactions that could be tossed your way from the transaction’s origination point.

jPOS implementations can efficiently support the reversal and ‘find duplicate’ models of our clients. 
On the reversal side of an implementation, Acquirers need to support both host- and terminal-based reversals.  The host-based reversal side follows a very standard approach:

  1. A timeout parameter is applied to all remote queries.
  2. If no response is received prior to the expiration of the timeout, a reversal is generated (only if required – credit interfaces rely on settlement files to post and typically do not want to see reversals…even of the ‘courtesy’ variety).
  3. The transaction is logged to the jPOS application’s ‘tranlog’ with a non-zero internal result code (‘irc’) and a ‘reversal indicator’ of ‘H’ reflecting a host-based reversal.

To show how this is implemented within a jPOS Transaction Manager, we provide here an example of two participants used to perform a ‘query host or reverse’ operation from an Acquirer to an FDR Debit/EBT financial gateway:

  <participant class=”org.jpos.ev.QueryHost”
     logger=”Q2″ realm=”query-remote-host”>
   <property name=”mux”     value=”fdr-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> 

Terminal-based reversal processing is more of a challenge because the jPOS processing engine must be aligned to match up to the proprietary reversal model used by our customers.  We urge our clients to get their terminal-based reversal model details on the table Day 1 so we can get all the nuances worked out right away.  This means getting ‘origination point’ systems personnel to explain how they expect a reversal to be tied an original and then propagating that model into your jPOS implementation. 

We make special note of this point in order to pass along a tough lesson learned recently…our standard ‘point of acquisition’ timeout reversal model implementation assumed that the Primary Account Number (‘PAN’) was the linchpin around which we should base our reversal logic.  That turned out to be a faulty assumption; the store systems folks explained their front-end implementation and described a rather logical sequence of events where one card could be substituted for another (within the same customer session), the result being that a reversal may in fact have a different card number.

The important thing is that this isn’t a discussion on whether that front-end model is right, wrong, flawed, etc.  The reality is:  it’s reflective of a specific business model; it’s typically been proven right and improved/hardened over time; and – most importantly – the goal of any financial switch implementation should be to get into production without inflicting any type of change on a store systems implementation.  Since store systems changes most often manifest themselves in chain-wide rollouts, they should be avoided at all costs.

The terminal-based reversal is implemented via a ‘FindOriginal’ participant.  For example, here is the group of participants called at one jPOS client location for a terminal-based Debit Reversal transaction:

<group name=”DebitSaleReversal”>
  <participant class=”org.jpos.ev.PopulateDebitTranLog”
     logger=”Q2″ realm=”populate-debit-tranlog”>
   <property name=”itc” value=”05301″ />
   <property name=”cardType” value=”DB” />
   <property name=”checkpoint” value=”populate-debit-tranlog” />
  </participant>

  &validate_terminal;

  <participant class=”org.jpos.ev.FindOriginal”>
   <property name=”original-itc” value=”05300″ />
   <property name=”original-card-type” value=”DB” />
   <property name=”reversal-window” value=”86400″ />
  </participant>
  <participant class=”org.jpos.ev.CreateFDRRequest”
     logger=”Q2″ realm=”create-fdr-request”>
   <property name=”mti”        value=”0400″ />
   <property name=”pcode”      value=”009000″ />
   <property name=”template”   value=”cfg/fdr-template.xml” />
   <property name=”space”      value=”jdbm:fdr-stan” />
   <property name=”checkpoint” value=”create-fdr-request” />
  </participant>

  &store_and_forward;

  <participant class=”org.jpos.ev.FlagReversal”>
   <property name=”reversal-class” value=”T” />
  </participant>
  &force_approval;
  &debit_response;
</group>

In that participant flow (noting highlights here only)…

  • The Debit Reversal (identified in a ‘switch’ participant prior to the flow shown here) is assigned an internal tran code (‘itc’) of 05301.  [NOTE:  These itc values aren't a jPOS standard; it's a Transaction Code numbering scheme I invented and have propagated throughout various jPOS-based solution implementations.]
  • FindOriginal searches the tranlog for a corresponding original (making use of tranlog indices put in place by DBAs in support of this specific, important action).
  • If an original was located, a reversal (0400 in this implementation) is formatted and placed into the appropriate store and forward (‘SAF’) queue.
  • If an original was located, the FlagReversal participant tags the original with a ‘T’ (to signify a terminal-based reversal) in the Reversal Indicator field and cross-links the original and reversal in the tranlog.
  • The ‘force_approval’ step ensures that if a corrupt, non-processable reversal is received from the origination point, we respond with an Approval in order to prevent a hard loop between terminal and host.
  • The ‘debit_response’ step formats a message back to the origination point.  We take special care to respond to all reversal attempts – regardless of the internal result – with an Approval.  To do anything else is to invite the possibility of endless loops and, ultimately, manual intervention and queue clearance at the origination point.

In order to be reversed by FindOriginal:

  • There needs to be a match (between an original and a reversal) on the fields designated by the implementer as the “reversal match-up fields.”  For example, at one jPOS/OLS.Switch client location, this match-up logic is implemented via a Hibernate call like this:

Criteria crit = db.session().createCriteria (DebitTranLog.class).
    add (Expression.eq (“storeNumber”, rev.getStoreNumber())).
    add (Expression.eq (“registerNumber”, rev.getRegisterNumber())).
    add (Expression.eq (“registerTranId”, rev.getRegisterTranId())).
    add (Expression.eq (“tenderNumber”, rev.getTenderNumber())).
    add (Expression.eq (“internalTranCode”, originalTranCode)).
    add (Expression.eq (“internalResultCode”, TRAN_APPROVED)).
    add (Expression.eq (“reconId”, new Long (0L))).
    add (Expression.isNull (“revInd”)).
    addOrder (Order.desc (“id”)).
    setMaxResults (1);

  • The original transaction must have been approved.
  • The original transaction must not have already been reversed.
  • The original transaction must not have already been extracted in the nightly reconciliation process.

For further reading on reversal challenges and our team’s real-world experiences in this area, please refer to the following in-depth discussions…

Handling Acquirer-side Reversals in a Payment Switch

Get the Reversal Model right

Linking Originals and Reversals

The action of filtering out duplicates can be seen as a very ‘close cousin’ of the terminal-based reversal.  In both cases, the host implementation bears the responsibility of doing a “FindOriginal.”  In reversal processing, it is a reversal that prompts a look for an original (i.e., a financial request like a Purchase/Sale or Merchandise Return).  By contrast, in a ‘dup-check’ implementation every financial request will perform a FindOriginal-like step. 

For example, in the Debit Sale (Purchase), we execute (at one client site) this flow of participants:

<group name=”DebitSale”>
  <participant class=”org.jpos.ev.PopulateDebitTranLog”
     logger=”Q2″ realm=”populate-debit-tranlog”>
   <property name=”itc” value=”05300″ />
   <property name=”cardType” value=”DB” />
   <property name=”checkpoint” value=”populate-debit-tranlog” />
  </participant>

  &validate_terminal;

  <participant class=”org.jpos.ev.FindDuplicate”>
   <property name=”itc” value=”05300″ />
   <property name=”checkpoint” value=”find-duplicate” />
  </participant>
  <participant class=”org.jpos.ev.HasEntry”>
   <property name=”name” value=”DUPLICATE_TRANLOG” />
   <property name=”yes”  value=”DuplicateDebitResponse LogAndReply” />
   <property name=”no”   value=”DebitSale_Response LogAndReply” />
  </participant>
</group>

<group name=”DebitSale_Response”>
  &translate_pin;

  <participant class=”org.jpos.ev.CreateFDRRequest”
     logger=”Q2″ realm=”create-fdr-request”>
   <property name=”mti”        value=”0200″ />
   <property name=”pcode”      value=”009000″ />
   <property name=”template”   value=”cfg/fdr-template.xml” />
   <property name=”space”      value=”jdbm:fdr-stan” />
   <property name=”checkpoint” value=”create-fdr-request” />
  </participant>
  &query_fdr_host_or_reverse;
  &debit_response;
</group>

Behind the scenes, that ‘FindDuplicate’ participant is modeled very closely on ‘FindOriginal.’  The logic is very easy to follow here:

If ‘FindDuplicate’ locates a previous attempt (one that was approved and not subsequently reversed), it places a “DUPLICATE TRANLOG” entry into the transaction context.  The ‘HasEntry’ participant re-directs the transaction flow based upon whether the transaction in flight is determined to be a duplicate.  If it is not, the transactions proceeds as intended (here, the PIN is translated and external authorization is sought).  If it is, the transaction is given an internal result code (‘irc’) to reflect it as a Duplicate.  Of course, the origination point is told this transaction is an approval (the internal logic provides the approval code from the original).

For the record, the DuplicateDebitResponse participant is the referenced example looks like this: 

<group name=”DuplicateDebitResponse”>
  <participant class=”org.jpos.ev.SetRC” logger=”Q2″ realm=”duplicate-debit-response-set-rc”>
   <property name=”rc” value=”4001″ />
  </participant>
  &debit_response;
</group>

Tinggalkan Balasan

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Ubah )

Twitter picture

You are commenting using your Twitter account. Log Out / Ubah )

Facebook photo

You are commenting using your Facebook account. Log Out / Ubah )

Connecting to %s