In this post, I hope to show you how you can use the jPOS TransactionManager framework to quickly implement business functionality. You do this by creating and rearranging a series of transaction participants. Once you get a nice library of participants under your control, you’ll see that you can implement new transactions or features or services by re-using and/or re-ordering the participants. Or, you can create new participants by using existing ones as your ‘liftoff’ point.
I’ll give my usual caveat here: none of what I say in these posts replaces the need for you to buy, study and understand the jPOS Programmers’ Guide. For $50, it’s a great investment. It helps keep the jPOS ecosystem flourishing. So, Do the Needful.
This post can be considered to be an extension of Section 15 of the Guide. That section deals with (surprise) the TransactionManager. Think of this post as providing you with some real-world examples of the TransactionManager.
Here’s an example of what I mean…
I recently mentioned to Alejandro of a business need: certain transactions from the point-of-sale are ‘Manager Override’ types of transactions. In a Manager Override, a Manager (duh) at the transaction's origination point has made a decision to either: (a) override a payment card authorization denial response previously relayed by jPOS; or, (b) force accept a card as payment even though the jPOS host is unreachable (the store system software puts the override into a store and forward (‘SAF’) queue); or (c) received phone-based authorization where the jPOS host has previously relayed a ‘Referral’ response from the card issuer or authorizer.
In these cases, we want the TransactionManager to take a different processing path: the Manager Override Authorization request should not be switched out for authorization; rather, it should be 'force-posted' into the transaction log as an approval. On a Manager Override, you must, must, must send an approval back to the transaction originator. Because Overrides are most often shipped from a store system’s SAF queue, if you process in your jPOS host with even a hint of a possibility of a denial, you run the risk of a Store --> Host loop. These situations are to be avoided at all costs.
In this situation, the first step was for me to explain to Alejandro how an incoming transaction is identified as a Manager Override. This particular implementation is a Visa Gen 2 (‘VG2’) interface. We use FSD to parse those messages. So, having understood what a Manager Override looks like, Alejandro put together this short piece of code called IsManagerOverride.java. This is the entire program:
package org.jpos.ev;
import java.io.Serializable;
import org.jpos.ee.Constants;
import org.jpos.transaction.Context;
import org.jpos.transaction.GroupSelector;
import org.jpos.util.FSDMsg;
import org.jpos.core.Configuration;
import org.jpos.core.Configurable;
public class IsManagerOverride
implements GroupSelector, Configurable, Constants
{
public static final String UNKNOWN = "UNKNOWN";
Configuration cfg;
public String select (long id, Serializable ser) {
Context ctx = (Context) ser;
String group = "no";
FSDMsg msg = (FSDMsg) ctx.tget (REQUEST);
if (
"M".equals (msg.get ("card-id-source")) ||
(("S".equals (msg.get ("record-format"))) &&
("9901".equals (msg.get ("tender-attempt"))))
)
{
group = "yes";
}
return cfg.get (group, UNKNOWN);
}
public int prepare (long id, Serializable o) {
return PREPARED | READONLY | NO_JOIN;
}
public void commit (long id, Serializable o) { }
public void abort (long id, Serializable o) { }
public void setConfiguration (Configuration cfg) {
this.cfg = cfg;
You can see in the bolded code that I’ve identified two potential situations in which the incoming transaction is a Manager Override. You can also see that coming out of this routine, the ‘group’ value is either ‘yes’ or ‘no’. You’ll see that we’re setting up a nice little implementation of the TransactionManager’s ‘Group Selector’ concept. [There’s a good discussion of this very handy feature in Section 15.8 of the Programmers’ Guide.]
Now, in the TransactionManager itself, we have this participant:
<participant class="org.jpos.ev.IsManagerOverride">
<property name="yes" value="DummyResponse LogAndReply" />
<property name="no" value="QueryHost LogAndReply" />
</participant>
You can see that we now can alter the course of the transaction if we’ve determined that the transaction is a Manager Override. Specially, in those instances, the transaction in flight is directed to the ‘DummyResponse’ group of participants, which looks like this:
<group name="DummyResponse">
<participant class="org.jpos.ev.SetRC" logger="Q2" realm="dummy-response">
<property name="rc" value="0000" />
</participant>
&svc_response;
</group>
There’s the force approval we need for these instances! [NOTE: In this TransactionManager, svc_response is the participant tasked with building the response to the originator. I’m not discussing those details here.]
By contrast, in all other transactions, we still want to switch out to the remote authorizer for their decision on the transaction. So we have the ‘QueryHost’ group of participants, which looks like this:
<group name="QueryHost">
&query_host_or_reverse;
&svc_response;
</group>
Notice that both decision branches of the IsManagerOverride participant do ‘LogAndReply’ as their final step. That Group of participants looks like this:
<group name="LogAndReply">
&log_and_reply;
&ols_audit;
</group>
We can leave ‘ols_audit’ to separate discussion, but the LogAndReply line references an ‘include’ (‘.inc’) file that contains an important set of ‘end of transaction’ participants: a ‘touch’ of the TransactionManager (to update stats); the transaction ‘close’; the physical sending of the response (back to the origination point); and a safety-catch to ‘ProtectDebugInfo’.
Back to IsManagerOverride for a second, how that particular participant evolved is a testament to the reusability and re-purposing of participants. First, we had this very classic Group Selector, which we put in for a Credit implementation…
<
<participant class="org.jpos.ev.SelectEndPoint">
<property name="FDR" value="QueryFDR LogAndReply" />
<property name="AMEX" value="QueryAMEX LogAndReply" />
<property name="DUPLICATE" value="DuplicateCreditResponse LogAndReply" />
<property name="MANAGER_OVERRIDE" value="DummyCreditResponse LogAndReply" />
<property name="UNKNOWN" value="DeclinedCreditResponse LogAndReply" />
</participant>
</group>
SelectEndPoint reads a ‘BIN Range’ table to determine how to route the transaction; it also contains some additional code to identify Duplicates and our friend, the Manager Override. Additionally, you can see that an UNKNOWN BIN is declined at this point.
For the new TransactionManager we were putting into place, the transactions are identified via a specific message format code in the VG2 request and are all routed to a single remote authorizer. Moreover, the cards aren’t identified in the ‘BIN Range’ table. So, I had the thought we could do something where we just directly re-used SelectEndPoint like so:
<participant class="org.jpos.ev.SelectEndPoint">
<property name="MANAGER_OVERRIDE" value="DummyResponse LogAndReply" />
<property name="UNKNOWN" value="QueryHost LogAndReply" />
</participant>
</group>
And, while that approach would have undoubtedly worked, Alejandro correctly raised some concerns about the long-term supportability of an implementation where the ‘UNKNOWN’ branch result gets treated as a ‘good’ result. You have to admit, the thing looks, um, curious. So, with a small bit of effort, 'IsManagerOverride' was built from 'SelectEndPoint' origins.
I hope you can see how here how jPOS’ TransactionManager concept greatly increases your ability to get rapid ‘Mean Time to Payback’ on implementing new or evolving business requirements. In my particular case, as our library of deployed transaction participants grows, I am finding myself increasingly adept at rapidly fashioning TransactionManagers to meet each incoming business need.
Comments