The English version of the new site is ready.
git-svn-id: https://j8583.svn.sourceforge.net/svnroot/j8583/j8583@59 db60a039-f72e-0410-b1d2-baa000846441
This commit is contained in:
@@ -37,10 +37,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<organization>
|
||||
<name>Java Mexico</name>
|
||||
<url>http://javamexico.org</url>
|
||||
</organization>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>chochos</id>
|
||||
|
||||
@@ -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 <<<getAt()>>> and <<<putAt()>>> methods that work similar
|
||||
to <<<getField()>>> and <<<setField>>>, and the IsoType class has two <<<call()>>> 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.
|
||||
|
||||
@@ -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
|
||||
<<<assignDate>>> property to <<<true>>>. 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 <<<nextTrace()>>> 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.
|
||||
|
||||
@@ -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}}.
|
||||
|
||||
@@ -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 <<<IsoType>>> enum. Here is a table of the most common ISO types and their j8583 counterpart:
|
||||
|
||||
*--------------*----------------*-----------------+
|
||||
| <<ISO type>> | <<j8583 type>> | <<Description >> |
|
||||
*--------------*----------------*-----------------+
|
||||
| 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. <<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.
|
||||
|
||||
*------:--+
|
||||
| 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 <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
|
||||
{{{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 <<<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.
|
||||
@@ -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:
|
||||
|
||||
+-----
|
||||
<bean id="isoMessageFactory" class="com.solab.iso8583.MessageFactory">
|
||||
<property name="assignDate" value="true" />
|
||||
<property name="etx" value="3" />
|
||||
<property name="ignoreLastMissingField" value="true" />
|
||||
<property name="customFields"><map>
|
||||
<entry key="48"><bean class="your.app.DataProductEncoder" /></entry>
|
||||
<entry key="62"><bean class="your.app.AdditionalDataEncoder" /></entry>
|
||||
</map></property>
|
||||
<!-- It is important to set this AFTER the custom fields
|
||||
if your message templates contain some of those fields -->
|
||||
<property name="configPath" value="/your-j8583.xml" />
|
||||
</bean>
|
||||
+-----
|
||||
|
||||
@@ -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: <<<header>>>,
|
||||
<<<template>>>, and <<<parse>>>. All these must be contained in a single <<<j8583-config>>> element.
|
||||
There is a DTD available at {{{http://j8583.sourceforge.net/j8583.dtd}}}.
|
||||
|
||||
* <<<header>>> element
|
||||
|
||||
Specify a <<<header>>> element for every type of message that needs an ISO header. Only one per message type:
|
||||
|
||||
+-----
|
||||
<header type="0200">ISO015000050</header>
|
||||
<header type="0400">ISO015000050</header>
|
||||
+-----
|
||||
|
||||
* <<<template>>> element
|
||||
|
||||
Each <<<template>>> element defines a message template, with the message type and the fields that the
|
||||
template should include. Every new message of that type that the MessageFactory creates will contain those
|
||||
same values, so this is very useful for defining fixed values, which will be the same in every message.
|
||||
Only one template per type.
|
||||
|
||||
+-----
|
||||
<template type="0200">
|
||||
<field num="3" type="NUMERIC" length="6">650000</field>
|
||||
<field num="32" type="LLVAR">456</field>
|
||||
<field num="35" type="LLVAR">4591700012340000=</field>
|
||||
<field num="43" type="ALPHA" length="40">Fixed-width data</field>
|
||||
<field num="48" type="LLLVAR">Life, the Universe, and Everything|42</field>
|
||||
<field num="49" type="ALPHA" length="3">840</field>
|
||||
<field num="60" type="LLLVAR">B456PRO1+000</field>
|
||||
<field num="61" type="LLLVAR">This field can have a value up to 999 characters long.</field>
|
||||
<field num="100" type="LLVAR">999</field>
|
||||
<field num="102" type="LLVAR">ABCD</field>
|
||||
</template>
|
||||
+-----
|
||||
|
||||
* <<<parse>>> element
|
||||
|
||||
Each <<<parse>>> element defines a parsing template for a message type. It must include all the fields that
|
||||
an incoming message can contain, each field with its type and length (if needed). Only ALPHA and NUMERIC
|
||||
types need to have a length specified. The other types either have a fixed length, or have their length
|
||||
specified as part of the field (LLVAR and LLLVAR).
|
||||
|
||||
+-----
|
||||
<parse type="0210">
|
||||
<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="38" type="NUMERIC" length="6" />
|
||||
<field num="39" type="NUMERIC" length="2" />
|
||||
<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="70" type="ALPHA" length="3" />
|
||||
<field num="90" type="ALPHA" length="42" />
|
||||
<field num="100" type="LLVAR" />
|
||||
<field num="102" type="LLVAR" />
|
||||
<field num="126" type="LLLVAR" />
|
||||
</parse>
|
||||
+-----
|
||||
|
||||
@@ -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 <<<getAt()>>> and <<<putAt()>>> methods that work similar
|
||||
to <<<getField()>>> and <<<setField>>>, and the IsoType class has two <<<call()>>> 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.
|
||||
@@ -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
|
||||
<<<assignDate>>> property to <<<true>>>. 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 <<<nextTrace()>>> 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.
|
||||
@@ -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}}.
|
||||
@@ -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 <<<IsoType>>> enum. Here is a table of the most common ISO types and their j8583 counterpart:
|
||||
|
||||
*--------------*----------------*-----------------+
|
||||
| <<ISO type>> | <<j8583 type>> | <<Description >> |
|
||||
*--------------*----------------*-----------------+
|
||||
| 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. <<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.
|
||||
|
||||
*------:--+
|
||||
| 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 <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
|
||||
{{{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 <<<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.
|
||||
@@ -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:
|
||||
|
||||
+-----
|
||||
<bean id="isoMessageFactory" class="com.solab.iso8583.MessageFactory">
|
||||
<property name="assignDate" value="true" />
|
||||
<property name="etx" value="3" />
|
||||
<property name="ignoreLastMissingField" value="true" />
|
||||
<property name="customFields"><map>
|
||||
<entry key="48"><bean class="your.app.DataProductEncoder" /></entry>
|
||||
<entry key="62"><bean class="your.app.AdditionalDataEncoder" /></entry>
|
||||
</map></property>
|
||||
<!-- It is important to set this AFTER the custom fields
|
||||
if your message templates contain some of those fields -->
|
||||
<property name="configPath" value="/your-j8583.xml" />
|
||||
</bean>
|
||||
+-----
|
||||
@@ -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: <<<header>>>,
|
||||
<<<template>>>, and <<<parse>>>. All these must be contained in a single <<<j8583-config>>> element.
|
||||
There is a DTD available at {{{http://j8583.sourceforge.net/j8583.dtd}}}.
|
||||
|
||||
* <<<header>>> element
|
||||
|
||||
Specify a <<<header>>> element for every type of message that needs an ISO header. Only one per message type:
|
||||
|
||||
+-----
|
||||
<header type="0200">ISO015000050</header>
|
||||
<header type="0400">ISO015000050</header>
|
||||
+-----
|
||||
|
||||
* <<<template>>> element
|
||||
|
||||
Each <<<template>>> element defines a message template, with the message type and the fields that the
|
||||
template should include. Every new message of that type that the MessageFactory creates will contain those
|
||||
same values, so this is very useful for defining fixed values, which will be the same in every message.
|
||||
Only one template per type.
|
||||
|
||||
+-----
|
||||
<template type="0200">
|
||||
<field num="3" type="NUMERIC" length="6">650000</field>
|
||||
<field num="32" type="LLVAR">456</field>
|
||||
<field num="35" type="LLVAR">4591700012340000=</field>
|
||||
<field num="43" type="ALPHA" length="40">Fixed-width data</field>
|
||||
<field num="48" type="LLLVAR">Life, the Universe, and Everything|42</field>
|
||||
<field num="49" type="ALPHA" length="3">840</field>
|
||||
<field num="60" type="LLLVAR">B456PRO1+000</field>
|
||||
<field num="61" type="LLLVAR">This field can have a value up to 999 characters long.</field>
|
||||
<field num="100" type="LLVAR">999</field>
|
||||
<field num="102" type="LLVAR">ABCD</field>
|
||||
</template>
|
||||
+-----
|
||||
|
||||
* <<<parse>>> element
|
||||
|
||||
Each <<<parse>>> element defines a parsing template for a message type. It must include all the fields that
|
||||
an incoming message can contain, each field with its type and length (if needed). Only ALPHA and NUMERIC
|
||||
types need to have a length specified. The other types either have a fixed length, or have their length
|
||||
specified as part of the field (LLVAR and LLLVAR).
|
||||
|
||||
+-----
|
||||
<parse type="0210">
|
||||
<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="38" type="NUMERIC" length="6" />
|
||||
<field num="39" type="NUMERIC" length="2" />
|
||||
<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="70" type="ALPHA" length="3" />
|
||||
<field num="90" type="ALPHA" length="42" />
|
||||
<field num="100" type="LLVAR" />
|
||||
<field num="102" type="LLVAR" />
|
||||
<field num="126" type="LLLVAR" />
|
||||
</parse>
|
||||
+-----
|
||||
|
||||
+7
-3
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="j8583">
|
||||
<bannerLeft>
|
||||
<name>j8583</name>
|
||||
<src>http://sflogo.sourceforge.net/sflogo.php?group_id=194767&type=7</src>
|
||||
<href>http://j8583.sourceforge.net/</href>
|
||||
<name>j8583</name>
|
||||
<href>http://j8583.sourceforge.net/</href>
|
||||
</bannerLeft>
|
||||
<bannerRight>
|
||||
<name>j8583</name>
|
||||
<src>http://sflogo.sourceforge.net/sflogo.php?group_id=194767&type=7</src>
|
||||
<href>http://j8583.sourceforge.net/</href>
|
||||
</bannerRight>
|
||||
<body>
|
||||
<links>
|
||||
@@ -17,6 +19,8 @@
|
||||
<item name="Introduction" href="index.html"/>
|
||||
<item name="API docs" href="apidocs/index.html" />
|
||||
<item name="Beginner's Guide" href="guide.html"/>
|
||||
<item name="ISO8583" href="iso8583.html" />
|
||||
<item name="XML config" href="xmlconf.html" />
|
||||
<item name="Spring Example" href="spring.html"/>
|
||||
<item name="Groovy Compatibility" href="groovy.html" />
|
||||
<item name="Download" href="http://sourceforge.net/projects/j8583/files/j8583/" />
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="j8583">
|
||||
<bannerLeft>
|
||||
<name>j8583</name>
|
||||
<src>http://sflogo.sourceforge.net/sflogo.php?group_id=194767&type=7</src>
|
||||
<href>http://j8583.sourceforge.net/</href>
|
||||
<name>j8583</name>
|
||||
<href>http://j8583.sourceforge.net/</href>
|
||||
</bannerLeft>
|
||||
<bannerRight>
|
||||
<name>j8583</name>
|
||||
<src>http://sflogo.sourceforge.net/sflogo.php?group_id=194767&type=7</src>
|
||||
<href>http://j8583.sourceforge.net/</href>
|
||||
</bannerRight>
|
||||
<body>
|
||||
<links>
|
||||
@@ -17,6 +19,8 @@
|
||||
<item name="Introducción" href="index.html"/>
|
||||
<item name="Documentación API" href="../apidocs/index.html" />
|
||||
<item name="Guía de inicio" href="guide.html"/>
|
||||
<item name="ISO8583" href="iso8583.html" />
|
||||
<item name="XML config" href="xmlconf.html" />
|
||||
<item name="Ejemplo con Spring" href="spring.html"/>
|
||||
<item name="Compatibilidad con Groovy" href="groovy.html" />
|
||||
<item name="Descargar" href="http://sourceforge.net/projects/j8583/files/j8583/" />
|
||||
|
||||
Reference in New Issue
Block a user