Reputation: 636
I'm trying to parse ISO 8583 message and get the individual data elements. Below code I've tried as of now
ISOResponse.java
public class ISOResponse {
private static final String TAG = "ISOResponse";
private static final String ISOResponseMessage = "60010203040210303800000E8002000000000000000031000046741306511212383334363133303034363734313330363534303036323730353532340012910A59218CDAFBBCD2520014";
public void parseISO8583(final Context context) throws ParseException, IOException {
MessageFactory<IsoMessage> messageFactory = new MessageFactory<IsoMessage>();
if (1 == 0) {
ConfigParser.configureFromDefault(messageFactory);
} else {
PrintLog.log(TAG, "Messagefactory is done");
InputStream inputData = context.getResources().openRawResource(R.raw.j8583_config);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputData));
ConfigParser.configureFromReader(messageFactory, bufferedReader);
}
messageFactory.setIgnoreLastMissingField(true);
IsoMessage isoMessage = messageFactory.parseMessage(ISOResponseMessage.getBytes(),10);
if (isoMessage != null) {
PrintLog.log(TAG, "Message type: %04x%n" + isoMessage.getType());
PrintLog.log(TAG, "FIELD TYPE VALUE");
for (int i = 2; i <= 64; i++) {
IsoValue<?> f = isoMessage.getField(i);
if (f != null) {
PrintLog.log(TAG, "%5d %-6s [" + i + f.getType());
PrintLog.log(TAG, f.toString());
PrintLog.log(TAG, "" + ']');
}
}
}
}
}
j8583_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE j8583-config PUBLIC "-//J8583//DTD CONFIG 1.0//EN"
"http://j8583.sourceforge.net/j8583.dtd">
<j8583-config>
<!-- This is a test config file -->
<!-- These are the ISO headers to be prepended to the message types specified -->
<header type="0800"/>
<template type="0280">
<field num="3" type="NUMERIC" length="2">99</field>
</template>
<!-- The server example uses this to read the requests -->
<parse type="0200">
<field num="3" type="NUMERIC" length="6" />
<field num="4" type="AMOUNT" />
<field num="7" type="DATE10" />
<field num="11" type="NUMERIC" length="6" />
<field num="12" type="TIME" />
<field num="13" type="DATE4" />
<field num="15" type="DATE4" />
<field num="17" type="DATE_EXP" />
<field num="32" type="LLVAR" />
<field num="35" type="LLVAR" />
<field num="37" type="NUMERIC" length="12" />
<field num="41" type="ALPHA" length="16" />
<field num="43" type="ALPHA" length="40" />
<field num="48" type="LLLVAR" />
<field num="49" type="ALPHA" length="3" />
<field num="60" type="LLLVAR" />
<field num="61" type="LLLVAR" />
<field num="100" type="LLVAR" />
<field num="102" type="LLVAR" />
</parse>
<!-- The client example uses this to read the responses -->
<parse type="0210">
<field num="3" type="NUMERIC" length="6" />
<field num="4" type="AMOUNT" length="12" />
<field num="11" type="NUMERIC" length="6" />
<field num="12" type="TIME" length="6" />
<field num="13" type="DATE4" length="4" />
<field num="37" type="NUMERIC" length="12" />
<field num="38" type="NUMERIC" length="6" />
<field num="39" type="NUMERIC" length="2" />
<field num="41" type="ALPHA" length="8" />
<field num="55" type="LLLVAR" length="255" />
</parse>
<!-- this is for binary tests (message encoding AND fields) -->
<parse type="0600">
<field num="4" type="AMOUNT" />
<field num="7" type="DATE10" />
<field num="11" type="NUMERIC" length="6" />
<field num="41" type="BINARY" length="8" />
<field num="42" type="BINARY" length="4" />
<field num="43" type="ALPHA" length="40" />
<field num="62" type="LLBIN" />
<field num="63" type="LLBIN" />
<field num="64" type="LLLBIN" />
<field num="65" type="LLLBIN" />
<field num="102" type="LLLVAR" />
</parse>
<parse type="0800">
<field num="3" type="ALPHA" length="6"/>
<field num="12" type="DATE4" length="14"/>
<field num="17" type="DATE4" length="8"/>
<field num="11" type="NUMERIC" length="6"/>
<field num="41" type="LLVAR" length="16"/>
</parse>
<parse type="0810" extends="0800">
<field num="17" type="exclude"/>
<field num="39" type="ALPHA" length="2"/>
</parse>
<parse type="0201">
<field num="3" type="NUMERIC" length="19" />
</parse>
<parse type="0202">
<field num="3" type="NUMERIC" length="22" />
</parse>
<parse type="0280">
<field num="3" type="NUMERIC" length="2" />
</parse>
After this my code is throwing an
Invalid ISO 8583 Header
exception. I tried removing header data and making header length 0 in isoMessage
, still it gives same exception.
Can anyone please help me find error in code. Thanks in advance.
Upvotes: 2
Views: 2124
Reputation: 382
I know this is an old question but I recently ran into a similar problem using this library and just wanted to share this solution so it can help someone else. The j8583 config.xml consists of two parts, the tag that is used to build iso messages and the tag that is used to unpack messages from a string. Each of these has child elements field, type, and sub-elements num, type, and length that serve to define each one of the iso 8583 fields. From experience, the best way to parse iso messages using j8583 is by using the bitmap and generating the configuration file, and then set the message factory. The configuration XML imports a set of defined values in a j8583.dtd and is used as parent tag , this file is stored in a server so you need the internet to download it at runtime or download it yourself and importing as SYSTEM, if you are working with android or the app has trouble communicating with the internet it might be best to add those definitions in an [] rather than using the PUBLIC -path see more here https://www.w3schools.com/xml/xml_dtd.asp. Also important is that when parsing a response, do not include in the response string message its overall length (first 4 characters) or the tdpu header ( next 10 characters after that).
building the response message config file
private void setResponseFactory(String responseMessage) throws IOException {
Log.i(TAG, "in setResponseFactory");
StringBuilder xmlParser = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE j8583-config [\n<!ELEMENT j8583-config ( header*, template*, parse* ) >\n" +
"\n" +
"<!ELEMENT header ( #PCDATA ) >\n" +
"<!ATTLIST header type NMTOKEN #REQUIRED >\n" +
"<!ATTLIST header ref NMTOKEN #IMPLIED >\n" +
"<!ATTLIST header binary ( true | false ) #IMPLIED >\n" +
"\n" +
"<!ELEMENT template ( field* ) >\n" +
"<!ATTLIST template type NMTOKEN #REQUIRED >\n" +
"<!ATTLIST template extends NMTOKEN #IMPLIED >\n" +
"\n" +
"<!ELEMENT parse ( field* ) >\n" +
"<!ATTLIST parse type NMTOKEN #REQUIRED >\n" +
"<!ATTLIST parse extends NMTOKEN #IMPLIED >\n" +
"\n" +
"<!ELEMENT field ( #PCDATA|field )* >\n" +
"<!ATTLIST field num NMTOKEN #REQUIRED >\n" +
"<!ATTLIST field length NMTOKEN #IMPLIED >\n" +
"<!ATTLIST field tz NMTOKEN #IMPLIED >\n" +
"<!ATTLIST field type ( ALPHA | NUMERIC | AMOUNT | DATE4 | DATE6 | DATE10 | DATE12 | DATE14 | DATE_EXP | TIME | LLVAR | LLLVAR | LLLLVAR | BINARY | LLBIN | LLLBIN | LLLLBIN | LLBCDBIN | LLLBCDBIN | LLLLBCDBIN | exclude) #REQUIRED >\n]>\n\n\t\t<!-- This file is generated at runtime, for iso message parsing (unpacking), response message types are request type + 10 -->\n\n");
String messageType = responseMessage.substring(0, 4); // response message type 0210 - sale, 0230 - advice ect...
Log.i(TAG, "response message type " + messageType);
Log.i(TAG, "hex bitmap " + responseMessage.substring(4, 20));
String bitmap = new BigInteger(responseMessage.substring(4, 20), 16).toString(2);
int length = 64 - bitmap.length();
char[] padArray = new char[length];
Arrays.fill(padArray, '0');
String padString = new String(padArray);
bitmap = padString + bitmap; // padd bitmap with zeros to the left
Log.i(TAG, "response bitmap " + bitmap);
HashMap<Integer, Pair<IsoType, Integer>> config = new HashMap<>(); // iso response field dictionary
config.put(Field_Indexes.PRIMARY_ACCOUNT_NUMBER, new Pair<>(IsoType.LLBIN, 0));
config.put(Field_Indexes.PROCESS_CODE, new Pair<>(IsoType.NUMERIC, 6));
config.put(Field_Indexes.TRANSACTION_AMOUNT, new Pair<>(IsoType.NUMERIC, 12));
config.put(Field_Indexes.TRACE_AUDIT_NUMBER, new Pair<>(IsoType.NUMERIC, 6));
config.put(Field_Indexes.LOCAL_TRANSMISSION_TIME, new Pair<>(IsoType.NUMERIC, 6));
config.put(Field_Indexes.LOCAL_TRANSMISSION_DATE, new Pair<>(IsoType.NUMERIC, 4));
config.put(Field_Indexes.POS_ENTRY_MODE, new Pair<>(IsoType.NUMERIC, 4));
config.put(Field_Indexes.NETWORK_INTERNATIONAL_IDENTIFIER, new Pair<>(IsoType.NUMERIC, 4));
config.put(Field_Indexes.POS_CONDITION_CODE, new Pair<>(IsoType.NUMERIC, 2));
config.put(Field_Indexes.RETRIEVAL_REFERENCE_NUMBER, new Pair<>(IsoType.BINARY, 12));
config.put(Field_Indexes.AUTHORIZATION_IDENTIFICATION_RESPONSE, new Pair<>(IsoType.BINARY, 6));
config.put(Field_Indexes.RESPONSE_CODE, new Pair<>(IsoType.BINARY, 2));
config.put(Field_Indexes.CARD_ACCEPTOR_TERMINAL_IDENTIFICATION, new Pair<>(IsoType.BINARY, 8));
config.put(Field_Indexes.ICC_DATA, new Pair<>(IsoType.LLLLBCDBIN, 0));
config.put(Field_Indexes.RN_FIELD_60, new Pair<>(IsoType.LLLLBIN, 0));
config.put(Field_Indexes.RP_FIELD_63, new Pair<>(IsoType.LLLLBCDBIN, 0));
config.put(Field_Indexes.MESSAGE_AUTHENTICATION_CODE_64, new Pair(IsoType.BINARY, 16));
xmlParser.append("<j8583-config>\n\t\t<parse type=\"" + messageType + "\">");
for(int i = 1; i<= 64; i++) // first bitmap if you are using secondary bitmap change to 128 instead of 64
if(bitmap.charAt(i - 1) == '1')
xmlParser.append("\n\t\t\t<field num=\"" + i + "\" type=\"" + config.get(i).first.toString() + "\""
+ ((config.get(i).second > 0) ? " length=\"" + config.get(i).second + "\" ": "") + "/>");
xmlParser.append("\n\t\t</parse>\n</j8583-config>");
wrtieFileOnInternalStorage(xmlParser.toString());
File root = new File(StateMachine.mContext.get().getFilesDir(),"response");
File file = new File(root, "isoresponse.xml");
responseFactory = ConfigParser.createFromClasspathConfig(file.getCanonicalPath());
}
call the message parser
public static void main (String[] args){
setResponseFactory(responseMessage);
responseFactory.setUseBinaryMessages(false);
Log.i(TAG, "in analize response");
HashMap<Integer, String> isoResponseData = SimpleParser.unpackIsoMessage(responseFactory, responseMessage); // remove tag, tdpu header and trailing 0
}
actual unpacking using te simple parser https://github.com/SergeyZyazyulkin/j8583/blob/master/src/main/java/com/solab/iso8583/util/SimpleParser.java
public static HashMap<Integer, String> unpackIsoMessage(MessageFactory mf, String message) throws UnsupportedEncodingException, ParseException {
HashMap<Integer, String> messageValues = new HashMap<>();
Log.i(TAG, "message: " + message);
if (message != null && message.length() > 0) {
IsoMessage m = mf.parseMessage(message.getBytes(), 0);
if (m != null) {
for (int i = 2; i <= 128; i++) {
IsoValue<?> f = m.getField(i);
if (f != null) {
Log.i(TAG, "f value class name " + f.getValue().getClass().getSimpleName());
if(f.getValue().getClass() == byte[].class)
messageValues.put(i, HexCodec.hexEncode((byte[])f.getValue(), 0, ((byte[]) f.getValue()).length));
else if(f.getValue().getClass().getSimpleName().equals("Long")){
messageValues.put(i, f.getValue() + "");
}
else
messageValues.put(i, (String) f.getValue());
Log.i(TAG, "iso value index " + i + " value " + messageValues.get(i));
}
}
}
}
return messageValues;
}
}
other resource http://j8583.sourceforge.net/xmlconf.html
Upvotes: 0
Reputation: 956
I suggest an alternative way to pack and unpack the message. The iso-8583-packer allows you to unpack the message to the following fields
<f>
<f name="Header" val="6001020304" valHex="6001020304"/>
<f name="MTI" val="0210" valHex="0210"/>
<f name="Bitmap" bitmapHex="303800000E800200" bitSet="{3, 4, 11, 12, 13, 37, 38, 39, 41, 55}">
<f name="ProcessingCode" fieldNum="3" val="000000" valHex="000000"/>
<f name="Amount" fieldNum="4" val="000000003100" valHex="000000003100"/>
<f name="SystemTraceAuditNumber" fieldNum="11" val="004674" valHex="004674"/>
<f name="LocalTransactionTimeHHMMSS" fieldNum="12" val="130651" valHex="130651"/>
<f name="LocalTransactionDateMMDD" fieldNum="13" val="1212" valHex="1212"/>
<f name="PosNumber" fieldNum="37" val="8346130046741" valHex="38333436...36373431"/>
<f name="ApprovalCode" fieldNum="38" val="306540" valHex="333036353430"/>
<f name="ActionCode" fieldNum="39" val="06" valHex="3036"/>
<f name="TerminalId" fieldNum="41" val="2705524" valHex="32373035353234"/>
<f name="EMV" fieldNum="55" val="910A59218CDAFBBCD2520014" lenHex="0012" valHex="910A5921...D2520014"/>
</f>
</f>
The message has the following definition
<f type="MSG">
<f type="VAL" name="Header" bodyPacker="LiteralBodyPacker" len="5"/>
<f type="VAL" name="MTI" bodyPacker="BcdBodyPacker" len="2"/>
<f type="BIT_SET" name="Bitmap" bitMapPacker="IfbBitmapPacker">
<f type="VAL" fieldNum="3" name="ProcessingCode" bodyPacker="HexBodyPacker" len="3"/>
<f type="VAL" fieldNum="4" name="Amount" bodyPacker="BcdBodyPacker" len="6"/>
<f type="VAL" fieldNum="11" name="SystemTraceAuditNumber" bodyPacker="BcdBodyPacker" len="3"/>
<f type="VAL" fieldNum="12" name="LocalTransactionTimeHHMMSS" bodyPacker="BcdBodyPacker" len="3"/>
<f type="VAL" fieldNum="13" name="LocalTransactionDateMMDD" bodyPacker="BcdBodyPacker" len="2"/>
<f type="VAL" fieldNum="37" name="PosNumber" bodyPacker="AsciiBodyPacker" len="13"/>
<f type="VAL" fieldNum="38" name="ApprovalCode" bodyPacker="AsciiBodyPacker" len="6"/>
<f type="VAL" fieldNum="39" name="ActionCode" bodyPacker="AsciiBodyPacker" len="2"/>
<f type="VAL" fieldNum="41" name="TerminalId" bodyPacker="AsciiBodyPacker" len="7"/>
<f type="LEN_VAL" fieldNum="55" name="EMV" lengthPacker="BcdLengthPacker" bodyPacker="HexBodyPacker"/>
</f>
</f>
The whole example is located on GitHub. Notice: I'm the author of the iso-8583-packer Java library.
Upvotes: 0
Reputation: 1714
I tried parsing your message and I've got this:
<parse header="6001020304" type="0210" bitmap="303800000E800200">
<field num="3" type="NUMERIC" length="6" value="000000" />
<field num="4" type="AMOUNT" length="12" value="000000003100" />
<field num="11" type="NUMERIC" length="6" value="004674" />
<field num="12" type="TIME" length="6" value="130651" />
<field num="13" type="DATE4" length="4" value="121238" />
<field num="37" type="NUMERIC" length="12" value="346130046741" />
<field num="38" type="NUMERIC" length="6" value="306540" />
<field num="39" type="NUMERIC" length="2" value="06" />
<field num="41" type="ALPHA" length="8" value="2705524\0" />
<field num="55" type="LLLVAR" length="12" value="910A59218CDAFBBCD2520014" />
</parse>
it seems your message header length is 14 including MTI (or 10 without MTI)
Upvotes: 1