diff --git a/ISO8583.md b/ISO8583.md new file mode 100644 index 0000000..f2f72a4 --- /dev/null +++ b/ISO8583.md @@ -0,0 +1,114 @@ +# ISO8583 + +## Introduction to ISO8583 + +The specification is available at iso.org and it's gotten really complicated; this is just an introduction to the generalities of the specification. There is actually a [really good article](http://en.wikipedia.org/wiki/ISO_8583) on Wikipedia covering the whole spec and its different versions. + +## Data types + +ISO 8583 specifies a lot of data types, the most common of which are implemented directly in j8583 through the `IsoType` enum. Here is a table of the most common ISO types and their j8583 counterpart: + +| ISO type | j8583 type | Description | +|--------------|----------------|------------------| +| Fixed-length Numeric | NUMERIC | Fixed-width numeric values, padded with zeroes to the left.| +| Fixed-length Alphanumeric | ALPHA | Fixed-width alphanumeric values, padded with spaces to the right.| +| Date | DATE10 | Date in format mmddHHMMSS, fixed width of 10. | +| Date | DATE4 | Date in format mmdd, fixed width of 4. | +| Expiration Date | DATE_EXP | Date in format yymm, fixed width of 4. Used for credit card expiration dates. | +| Time | TIME | Time of day in format HHMMSS, fixed width of 6. | +| Amount | AMOUNT | Currency amount, a positive number expressed in cents, with a fixed width of 12. For example one dollar is encoded as 000000000100 | +| LLVAR | LLVAR | Variable-width alphanumeric value, up to 99 characters long. The length of the value is encoded in the first 2 characters of the value, for example "HEY" is encoded as 03HEY | +| LLLVAR | LLLVAR | Variable-width alphanumeric value, up to 999 characters long. The length of the value is encoded in the first 3 characters of the value, for example "HEY" is encoded as 003HEY | +| Fixed-length binary | BINARY | Similar to ALPHA, but stores byte arrays directly instead of text. +| Binary LLVAR | LLBIN | Similar to LLVAR, but stores byte arrays directly instead of text. +| Binary LLLVAR | LLLBIN | Similar to LLLVAR, but stores byte arrays directly instead of text. + +## Common scenarios + + ISO8583 implementations can vary a lot, depending on the provider, and the type of products and transactions it + is being used for; but there are certain fields which are almost always used in a very similar manner. I will + describe those here. **DISCLAIMER:** This is only something I have learned from experience, and I must say + my experience has been rather limited to mobile carrier providers. Here I focus more on the field number and + type, rather on the usage of the field itself. + +## Message types + + These are some of the most common message types. There is some logic to this, if you look at the message types + as 2-byte hex values: The first byte states the type of operation: 02 are payments, 04 reversals, 08 tests; + the second byte indicates if it's a request or a response. And sometimes repeated requests should end in 1 + instead of 0, for example reversals are 0400 the first time but 0401 the next time you send them. + +| Message Type | Description +| ---: | --- | +| 0200 | A payment or sale request. +| 0210 | A payment or sale response. +| 0400 | A reversal request (to undo a previous 0200 operation). +| 0410 | A reversal response. +| 0600 | A query (to check the status of a previous operation, or check an account's balance, etc). +| 0610 | A query response. +| 0800 | An echo request (just to keep the connection alive and make sure the other side is responsive). +| 0810 | An echo response. + + Some implementations require you to send 0400 for a reversal when you only have the request (so in case of + timeout you use 0400) and 0420 when you did get the response (so in case of some other error in your system). + +## Common fields + +| Field | Description | +| ---: | --- | +| 3 | Operation code, NUMERIC of length 6. | +| 4 | Amount, AMOUNT. | +| 7 | Date, DATE10. | +| 11 | Trace, NUMERIC of length 6. | +| 17 | Transaction date, DATE4. | +| 37 | Reference number, NUMERIC of length 12. | +| 38 | Confirmation number, NUMERIC of length 6. | +| 39 | Response code, NUMERIC of length 2. | +| 41 | Terminal ID, ALPHA of length 8 or 16. | +| 49 | Currency, NUMERIC of length 3. See the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) table of currencies. | +| 128 | MAC (Message Authentication Code). Usually ALPHA or NUMERIC of length 16. | + +## Asynchronous transmission and reception + + When communicating two systems with ISO8583, usually the communication is asynchronous and all messages are + handled through a single connection. Between a terminal and a bigger system the communication will be synchronous. + + In asynchronous communications, the client can send any number of requests to the server, and the server may + respond to those requests in a different order in which they were sent. This is why the trace number (field 11) + is very important and should not be reused; if the client sends requests with traces 123000, 123001 and 123002, + the server may send back first 123001, then 123002, then 123000. The client will know which response corresponds + to which request by checking the trace of the responses. + + The messages can vary in length depending on the message type and the fields it contains. The protocol was + designed so that a message can be read in parts; first the message type, then the primary bitmap, and from there + the rest of the fields (secondary bitmap included) can be read and processed one by one. This is very convenient + for small devices like POS terminals, but is not very efficient in larger systems with a high transaction volume. + In these cases, the sender first encodes the whole message, measures it, and sends the length as a 2- or 4-byte + unsigned integer (most significant bit first). This way the receiver knows to read 2 or 4 bytes, interpret them + as an unsigned integer, and read those many bytes to get an ISO message. Using 2 bytes allows for messages of up + to 65535 bytes, and 4 bytes allows for 2.14 billion bytes long, which is a bit of overkill, given that a message + can be as long as 127290 bytes, if it's encoded in ASCII and has 127 LLLVAR fields each with 999 characters. + +### Message terminator + + The length header that is sent before a message is very useful to separate the reading operation from the parsing + of the message; you can dedicate a thread to read from a socket, and put those buffers on a queue where another + thread parses them. However, a length header only says how many bytes to read and so the reader must read all + those bytes, but what if the message is invalid because it is _longer_ than the length says? To avoid this error, + some systems use a message terminator. This is just a character that must be found at the end of every message. + If messages are encoded as text, a common terminator is ASCII 0x03. The terminator may or may not be counted + as part of the message, that depends on the implementation. You can specify the terminator via the `etx` + property of an IsoMessage, of preferably in the MessageFactory so that all new messages already will have the + property set. To keep the messages from using a terminator, set `etx` to -1. + +## Binary encoding + + Many ISO8583 implementations use plain text for the messages (even if the length header is binary). But some + implementations encode the messages as binary. The main differences are that the message type is only 2 bytes + long instead of 4 (the ASCII version is actually a hex representation of the message type), the bitmap is not + hex-encoded, so it's only 8 bytes long; and all numeric values (including date/time fields) are encoded using + [BCD](http://en.wikipedia.org/wiki/Binary-coded_decimal) (including the headers for LLVAR and LLLVAR fields). + + You can encode messages in binary by setting the `binary` property of IsoMessages, or in the `MessageFactory` + directly by setting the `useBinaryMessages` so that it gets set on all new messages. It must be set in + the `MessageFactory` if you want it to parse binary messages.