diff --git a/pom.xml b/pom.xml index 9e274e0..c4a4970 100644 --- a/pom.xml +++ b/pom.xml @@ -37,10 +37,6 @@ test - - Java Mexico - http://javamexico.org - chochos diff --git a/src/site/apt/groovy.apt b/src/site/apt/groovy.apt index e69de29..3f691f2 100644 --- a/src/site/apt/groovy.apt +++ b/src/site/apt/groovy.apt @@ -0,0 +1,35 @@ + ----- + Groovy compatibility + ----- + +Isn't ANY Java library compatible with Groovy? + + Yes, of course. You can use j8583 in Groovy just as you would use any other Java library. However, + some j8583 classes have certain methods in them written specifically for usage within Groovy. + +* j8583 short-hand in Groovy + + In Java, you can deal with values in an IsoMessage like this: + ++----- +message.setValue(32, "Some value", IsoType.LLVAR, 0); +IsoValue isoValue = message.getField(32); +String value = (String)message.getObjectValue(32); +Object value2 = message.getField(32).getValue(); ++----- + + In Groovy you can do the same like this + ++----- +message[32] = IsoType.LLVAR("Some value") +message[7] = IsoType.DATE10(new Date()) +message[41] = IsoType.ALPHA("Term", 16) +message[11] = IsoType.NUMERIC(123, 6) +IsoValue isoValue = message[32] +def value = message[32]?.value ++----- + + This is because the IsoMessage class has get <<>> and <<>> methods that work similar + to <<>> and <<>>, and the IsoType class has two <<>> methods, with and without + length, which return IsoValue instances with the proper type and length. The variant that has a length parameter + must be used with NUMERIC and ALPHA types, and the other types can use the variant without length. diff --git a/src/site/apt/guide.apt b/src/site/apt/guide.apt index e69de29..d48a87f 100644 --- a/src/site/apt/guide.apt +++ b/src/site/apt/guide.apt @@ -0,0 +1,82 @@ + ----- + Usage Guide + ----- + +Usage Guide + + This guide explains how to setup j8583. If you want to know more about the ISO8583 protocol, + please read the {{{iso8583.html}corresponding page}}. + +The MessageFactory and IsoMessage classes + + These are the two main classes you need to use to work with ISO8583 messages. An IsoMessage can be + encoded into a byte array or a ByteBuffer to be written to an OutputStream, or it can directly write itself + to an OutputStream. You can set and get the values for each field in an IsoMessage, and it will adjust itself + to use a secondary bitmap if necessary. An IsoMessage has settings to encode itself in binary or ASCII, + to use a secondary bitmap even if it's not necessary, and it can have its own ISO header. + + However, it can be cumbersome to programmatically create IsoMessages all the time. The MessageFactory is a big aid + in IsoMessages with predefined values; also, it can set the date and the trace number in each new message. + +* Configuring the MessageFactory + + There are five main things you need to configure in a MessageFactory: ISO headers, message templates, + parsing templates, TraceNumberGenerator, and custom field encoders. + +* ISO headers + + ISO headers are strings that are associated with a message type. Whenever you ask the message factory + to create an IsoMessage, it will pass the corresponding ISO header (if present) to the new message. + +* Message templates + + A message template is an IsoMessage itself; the MessageFactory can have a template for each message type + it needs to create. When it creates a message and it has a template for that message type, it copies the + fields from the template to the new message before returning it. + +* Parsing templates + + A parsing template is a map containing FieldParseInfo objects as values and the field numbers as the keys. + A FieldParseInfo object contains an IsoType and an optional length; with this information and the field number, + the MessageFactory can parse incoming messages, first analyzing the message type and then using the parsing + template for that type; when parsing a message, the MessageFactory only parses the fields that are specified + in the message's bitmap. For example if the bitmap specifies field 4, the factory will get the FieldParseInfo + stored in the map under key 4, and will attempt to parse the field according to the type and length specified + by the FieldParseInfo. + + A message does not need to contain all the fields specified in a parsing template, but a parsing template must + contain all the fields specified in the bitmap of a message, or the MessageFactory won't be able to parse it + because it has no way of knowing how it should parse that field (and also all subsequent fields). + +* The TraceNumberGenerator + + When creating new messages, they usually need to have a unique trace number, contained in field 11. Also, they + usually need to have the date they were created (or the date the transaction was originated) in field 7. + The MessageFactory can automatically set the current date on all new messages, you just need to set the + <<>> property to <<>>. And it can also assign a new trace number to each message it creates, + but for this it needs a TraceNumberGenerator. + + The TraceNumberGenerator interface defines a <<>> method, which must return a new trace number + between 1 and 999999. It needs to be cyclic, so it returns 1 again after returning 999999. And usually, it + needs to be thread-safe. + + j8583 only defines the interface; in production environments you will usually need to implement your own + TraceNumberGenerator, getting the new trace number from a sequence in a database or some similar mechanism. + As an example, the library includes the SimpleTraceGenerator, which simply increments an in-memory value. + +* Custom field encoders + + Certain implementations of ISO8583 specify fields which contain many subfields. If you only handle strings + in those fields, you'll have to encode all those values before storing them in an IsoMessage, and decode them + when you get them from an IsoMessage. + + In these cases you can implement a CustomField, which is an interface that defines two methods, one for encoding + an object into a String and another for decoding an object from a String. You can pass the MessageFactory a + CustomField for every field where you want to store custom values, so that parsed messages will return the + objects decoded by the CustomFields instead of just strings; and when you set a value in an IsoMessage, you + can specify the CustomField to be used to encode the value as a String. + +* XML configuration + + The easiest way to configure the message templates and parsing templates is by using a + {{{xmlconf.html}XML config file}} and pass it to the MessageFactory. diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt index e69de29..66526cf 100644 --- a/src/site/apt/index.apt +++ b/src/site/apt/index.apt @@ -0,0 +1,46 @@ + ----- + Introduction to j8583 + ----- + +What it j8583? + + Simply put, j8583 is a Java library to generate and read ISO8583 messages. It does not handle sending or + reading them over a network connection, but it does parse the data you have read and can generate the + data you need to write, or write it directly to an OutputStream. + +* What is ISO8583? + + ISO8583 is a message format used for credit card transactions, banking and other commercial interaction between + different systems. It has an ASCII variant and a binary one, and it is somewhat convoluted and difficult to + implement. + + The main format of the ISO8583 is something like this: + ++----- +ISO header (optional)|Message Type|primary bitmap|secondary bitmap (optional)|data fields ++----- + + The ISO header is a string containing some code that can vary according to the message type. + + The message type is a 4-digit number (or 2 bytes when using binary format). + + The bitmap is 64 bits long and it is encoded as 16 hex characters or as 8 bytes when using binary format. + Every bit that is set in the bitmap indicates that the corresponding field is present. If the first bit is set, + then field 1 is present, and so on. + + The fields in the message are numbered from 1 to 64. Field 1 is the secondary bitmap, if present. The secondary + bitmap allows for the message to have fields from 65 to 128. + + You can {{{iso8583.html}check the ISO8583 guide}} for more information; Wikipedia has + {{{http://en.wikipedia.org/wiki/ISO_8583}a very good article}} on the whole specification. + +* How does j8583 work? + + j8583 offers a MessageFactory, which once properly configured, can create different message types with + some values predefined, and can also parse a byte array to create a iso message. Messages are represented + by IsoMessage objects, which store IsoValue instances for their data fields. You can work with the IsoValues + or use the convenience methods of IsoMessage to work directly with the stored values. + + j8583 can directly handle the basic data types: numeric values, strings, and dates. If you want to encode + a custom object in a field, you can implement a custom field encoder. More information is available in the + {{{guide.html}user's guide}}. diff --git a/src/site/apt/iso8583.apt b/src/site/apt/iso8583.apt new file mode 100644 index 0000000..51e518d --- /dev/null +++ b/src/site/apt/iso8583.apt @@ -0,0 +1,144 @@ + ----- + 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 + {{{http://en.wikipedia.org/wiki/ISO_8583}really good article}} 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 <<>> enum. Here is a table of the most common ISO types and their j8583 counterpart: + +*--------------*----------------*-----------------+ +| <> | <> | <> | +*--------------*----------------*-----------------+ +| Numeric | NUMERIC | Fixed-width numeric values, padded with zeroes to the left.| +*--------------*----------------*-----------------+ +| 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 4. 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 | +*--------------*----------------*-----------------+ +ISO 8583 types and their corresponding j8583 types + +* 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. <> 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. + +*------:--+ +| 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 3] Operation code, NUMERIC of length 6. + + [Field 4] Amount, AMOUNT. + + [Field 7] Date, DATE10. + + [Field 11] Trace, NUMERIC of length 6. + + [Field 17] Transaction date, DATE4. + + [Field 37] Reference number, NUMERIC of length 12. + + [Field 38] Confirmation number, NUMERIC of length 6. + + [Field 39] Response code, NUMERIC of length 2. + + [Field 41] Terminal ID, ALPHA of length 8 or 16. + + [Field 49] Currency, NUMERIC of length 3. See the {{{http://en.wikipedia.org/wiki/ISO_4217}ISO 4217}} table of currencies. + + [Field 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 million 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 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 <<>> + 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 <<>> 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 + {{{http://en.wikipedia.org/wiki/Binary-coded_decimal}BCD}} (including the headers for LLVAR and LLLVAR fields). + + You can encode messages in binary by setting the <<>> property of IsoMessages, or in the MessageFactory + directly by setting the <<>> so that it gets set on all new messages. It must be set in + the MessageFactory if you want it to parse binary messages. diff --git a/src/site/apt/spring.apt b/src/site/apt/spring.apt index e69de29..0d20299 100644 --- a/src/site/apt/spring.apt +++ b/src/site/apt/spring.apt @@ -0,0 +1,47 @@ + ----- + Configuring with Spring + ----- + +Configuration via Spring + + You can configure a MessageFactory as a bean in a Spring ApplicationContext, and inject it into any + other components that need to create or parse IsoMessages. + +* Setting up the MessageFactory + + The properties that you want to set up in the Spring config are: + +*------------+---------------------------------+ +| configPath | Specify the path to the XML configuration file with the message and parsing templates. +*------------+---------------------------------+ +| customFields | A map with the CustomField implementations that should be used for each field (the keys are the field numbers). +*------------+---------------------------------+ +| assignDate | Set it to true to have the MessageFactory set field 7 to a DATE10 with the date the message was created. +*------------+---------------------------------+ +| etx | Set it to the numeric value of the message terminator, or -1 if you don't want to use a terminator. +*------------+---------------------------------+ +| forceSecondaryBitmap | If true, then the messages will include the secondary bitmap even if they don't contain any fields above 64. Some providers require this. +*------------+---------------------------------+ +| ignoreLastMissingField | If true, then the factory only logs a warning when parsing incomplete messages. By default it will throw a ParseException. +*------------+---------------------------------+ +| useBinaryMessages | Can be true or false (default false). The factory passes this value to new messages so that they're encoded in binary or ASCII. +*------------+---------------------------------+ +| traceNumberGenerator | Set to an implementation of TraceNumberGenerator so that the factory sets a new trace value in field 11 on new messages. +*------------+---------------------------------+ + + Example: + ++----- + + + + + + + + + + + ++----- diff --git a/src/site/apt/xmlconf.apt b/src/site/apt/xmlconf.apt new file mode 100644 index 0000000..12ec299 --- /dev/null +++ b/src/site/apt/xmlconf.apt @@ -0,0 +1,80 @@ + ----- + XML Configuration + ----- + +XML Configuration + + The MessageFactory can read a XML file to setup message templates, ISO headers by type and parsing templates, + which are the most cumbersome parts to configure programmatically. + + There are three types of main elements that you need to specify in the config file: <<
>>, + <<