Giter VIP home page Giter VIP logo

iso8583's Introduction

Moov Banner Logo

Project Documentation · Community · Blog

GoDoc Build Status Coverage Status Go Report Card Repo Size Apache 2 License Slack Channel GitHub Stars Twitter

moov-io/iso8583

Moov's mission is to give developers an easy way to create and integrate bank processing into their own software products. Our open source projects are each focused on solving a single responsibility in financial services and designed around performance, scalability, and ease of use.

ISO8583 implements an ISO 8583 message reader and writer in Go. ISO 8583 is an international standard for card-originated financial transaction messages that defines both message format and communication flow. It's used by major card networks around the globe including Visa, Mastercard, and Verve. The standard supports card purchases, withdrawals, deposits, refunds, reversals, balance inquiries, inter-account transfers, administrative messages, secure key exchanges, and more.

Table of contents

Project status

Moov ISO8583 is a Go package that's been thoroughly tested and trusted in the real world. The project has proven its reliability and robustness in real-world, high-stakes scenarios. Please let us know if you encounter any missing feature/bugs/unclear documentation by opening up an issue. Thanks!

Go library

This project uses Go Modules. See Golang's install instructions for help in setting up Go. You can download the source code and we offer tagged and released versions as well. We highly recommend you use a tagged release for production.

Go version support policy

Always up-to-date, never left behind

While we strive to embrace the latest language enhancements, we also appreciate the need for a certain degree of backward compatibility. We understand that not everyone can update to the latest version immediately. Our philosophy is to move forward and embrace the new, but without leaving anyone immediately behind.

Which versions do we support now?

As of today, we are supporting the following versions as referenced in the setup-go action step:

  • stable (which points to the current Go version)
  • oldstable (which points to the previous Go version)

The setup-go action automatically manages versioning, allowing us to always stay aligned with the latest and preceding Go releases.

What does this mean for you?

Whenever a new version of Go is released, we will update our systems and ensure that our project remains fully compatible with it. At the same time, we will continue to support the previous version. However, once a new version is released, the 'previous previous' version will no longer be officially supported.

Continuous integration

To ensure our promise of support for these versions, we've configured our GitHub CI actions to test our code with both the current and previous versions of Go. This means you can feel confident that the project will work as expected if you're using either of these versions.

Installation

go get github.com/moov-io/iso8583

How to

Define your specification

Currently, we have defined the following ISO 8583 specifications:

  • Spec87ASCII - 1987 version of the spec with ASCII encoding
  • Spec87Hex - 1987 version of the spec with Hex encoding

Spec87ASCII is suitable for the majority of use cases. Simply instantiate a new message using specs.Spec87ASCII:

isomessage := iso8583.NewMessage(specs.Spec87ASCII)

If this spec does not meet your needs, we encourage you to modify it or create your own using the information below.

First, you need to define the format of the message fields that are described in your ISO8583 specification. Each data field has a type and its own spec. You can create a NewBitmap, NewString, or NewNumeric field. Each individual field spec consists of a few elements:

Element Notes Example
Length Maximum length of field (bytes, characters or digits), for both fixed and variable lengths. 10
Description Describes what the data field holds. "Primary Account Number"
Enc Sets the encoding type (ASCII, Hex, Binary, BCD, LBCD, EBCDIC). encoding.ASCII
Pref Sets the encoding (ASCII, Hex, Binary, BCD, EBCDIC) of the field length and its type as fixed or variable (Fixed, L, LL, LLL, LLLL). The number of 'L's corresponds to the number of digits in a variable length. prefix.ASCII.Fixed
Pad (optional) Sets padding direction and type. padding.Left('0')

While some ISO8583 specifications do not have field 0 and field 1, we use them for MTI and Bitmap. Because technically speaking, they are just regular fields. We use field specs to describe MTI and Bitmap too. We currently use the String field for MTI, while we have a separate Bitmap field for the bitmap.

The following example creates a full specification with three individual fields (excluding MTI and Bitmap):

spec := &iso8583.MessageSpec{
	Fields: map[int]field.Field{
		0: field.NewString(&field.Spec{
			Length:      4,
			Description: "Message Type Indicator",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.Fixed,
		}),
		1: field.NewBitmap(&field.Spec{
			Description: "Bitmap",
			Enc:         encoding.Hex,
			Pref:        prefix.Hex.Fixed,
		}),

		// Message fields:
		2: field.NewString(&field.Spec{
			Length:      19,
			Description: "Primary Account Number",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.LL,
		}),
		3: field.NewNumeric(&field.Spec{
			Length:      6,
			Description: "Processing Code",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.Fixed,
			Pad:         padding.Left('0'),
		}),
		4: field.NewString(&field.Spec{
			Length:      12,
			Description: "Transaction Amount",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.Fixed,
			Pad:         padding.Left('0'),
		}),
	},
}

The following example creates a full specification with three individual fields (excluding MTI and Bitmap). It differs from the example above, by showing the expandability of the bitmap field. This is useful for specs that define both a primary and secondary bitmap.

spec := &iso8583.MessageSpec{
	Fields: map[int]field.Field{
		0: field.NewString(&field.Spec{
			Length:      4,
			Description: "Message Type Indicator",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.Fixed,
		}),
		1: field.NewBitmap(&field.Spec{
			Description: "Bitmap",
			Enc:         encoding.Hex,
			Pref:        prefix.Hex.Fixed,
		}),

		// Message fields:
		2: field.NewString(&field.Spec{
			Length:      19,
			Description: "Primary Account Number",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.LL,
		}),
		3: field.NewNumeric(&field.Spec{
			Length:      6,
			Description: "Processing Code",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.Fixed,
			Pad:         padding.Left('0'),
		}),
		4: field.NewString(&field.Spec{
			Length:      12,
			Description: "Transaction Amount",
			Enc:         encoding.ASCII,
			Pref:        prefix.ASCII.Fixed,
			Pad:         padding.Left('0'),
		}),
        // Pulled from the 1993 spec
        67: field.NewNumeric(&field.Spec{
            Length: 2,
            Description: "Extended Payment Data",
            Enc: encoding.ASCII,
            Pref: prefix.ASCII.Fixed,
            Pad: padding.Left('0'),
        }),
	},
}

Build and pack the message

After the specification is defined, you can build a message. Having a binary representation of your message that's packed according to the provided spec lets you send it directly to a payment system!

Notice in the examples below, you do not need to set the bitmap value manually, as it is automatically generated for you during packing.

Setting values of individual fields

If you need to set few fields, you can easily set them using message.Field(id, string) or message.BinaryField(id, []byte) like this:

// create message with defined spec
message := NewMessage(spec)

// set message type indicator at field 0
message.MTI("0100")

// set all message fields you need as strings

err := message.Field(2, "4242424242424242")
// handle error

err = message.Field(3, "123456")
// handle error

err = message.Field(4, "100")
// handle error

// generate binary representation of the message into rawMessage
rawMessage, err := message.Pack()

// now you can send rawMessage over the wire

Working with individual fields is limited to two types: string or []byte. Underlying field converts the input into its own type. If it fails, then error is returned.

Setting values using data struct

Accessing individual fields is handy when you want to get value of one or two fields. When you need to access a lot of them and you want to work with field types, using structs with message.Marshal(data) is more convenient.

First, you need to define a struct with fields you want to set. Fields should correspond to the spec field types. Here is an example:

// list fields you want to set, add `index` tag with field index or tag (for
// composite subfields) use the same types from message specification
type NetworkManagementRequest struct {
	MTI                  *field.String `index:"0"`
	TransmissionDateTime *field.String `index:"7"`
	STAN                 *field.String `index:"11"`
	InformationCode      *field.String `index:"70"`
}

message := NewMessage(spec)

// now, pass data with fields into the message
err := message.Marshal(&NetworkManagementRequest{
	MTI:                  field.NewStringValue("0800"),
	TransmissionDateTime: field.NewStringValue(time.Now().UTC().Format("060102150405")),
	STAN:                 field.NewStringValue("000001"),
	InformationCode:      field.NewStringValue("001"),
})

// pack the message and send it to your provider
requestMessage, err := message.Pack()

Parse the message and access the data

When you have a binary (packed) message and you know the specification it follows, you can unpack it and access the data. Again, you have two options for data access: access individual fields or populate struct with message field values.

Getting values of individual fields

You can access values of individual fields using message.GetString(id), message.GetBytes(id) like this:

message := NewMessage(spec)
message.Unpack(rawMessage)

mti, err := message.GetMTI() // MTI: 0100
// handle error

pan, err := message.GetString(2) // Card number: 4242424242424242
// handle error

processingCode, err := message.GetString(3) // Processing code: 123456
// handle error

amount, err := message.GetString(4) // Transaction amount: 100
// handle error

Again, you are limited to a string or a []byte types when you get values of individual fields.

Getting values using data struct

To get values of multiple fields with their types just pass a pointer to a struct for the data you want into message.Unmarshal(data) like this:

// list fields you want to set, add `index` tag with field index or tag (for
// composite subfields) use the same types from message specification
type NetworkManagementRequest struct {
	MTI                  *field.String `index:"0"`
	TransmissionDateTime *field.String `index:"7"`
	STAN                 *field.String `index:"11"`
	InformationCode      *field.String `index:"70"`
}

message := NewMessage(spec)
// let's unpack binary message
err := message.Unpack(rawMessage)
// handle error

// create pointer to empty struct
data := &NetworkManagementRequest{}

// get field values into data struct
err = message.Unmarshal(data)
// handle error

// now you can access field values
data.MTI.Value() // "0100"
data.TransmissionDateTime.Value() // "220102103212"
data.STAN.Value() // "000001"
data.InformationCode.Value() // "001"

For complete code samples please check ./message_test.go.

Inspect message fields

There is a Describe function in the package that displays all message fields in a human-readable way. Here is an example of how you can print message fields with their values to STDOUT:

// print message to os.Stdout
iso8583.Describe(message, os.Stdout)

and it will produce the following output:

MTI........................................: 0100
Bitmap.....................................: 000000000000000000000000000000000000000000000000
Bitmap bits................................: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
F000 Message Type Indicator................: 0100
F002 Primary Account Number................: 4242****4242
F003 Processing Code.......................: 123456
F004 Transaction Amount....................: 100
F020 PAN Extended Country Code.............: 4242****4242
F035 Track 2 Data..........................: 4000****0506=2512111123400001230
F036 Track 3 Data..........................: 011234****3445=724724000000000****00300XXXX020200099010=********************==1=100000000000000000**
F045 Track 1 Data..........................: B4815****1896^YATES/EUGENE L^^^356858      00998000000
F052 PIN Data..............................: 12****78
F055 ICC Data – EMV Having Multiple Tags...: ICC  ... Tags

by default, we apply iso8583.DefaultFilters to mask the values of the fields with sensitive data. You can define your filter functions and redact specific fields like this:

filterAll = func(in string, data field.Field) string {
	runesInString := utf8.RuneCountInString(in)

	return strings.Repeat("*", runesInString)
}

// filter only value of the field 2
iso8583.Describe(message, os.Stdout, filterAll(2, filterAll))

// outputs:
// F002 Primary Account Number................: ************

If you want to view unfiltered values, you can use no-op filters iso8583.DoNotFilterFields that we defined:

// display unfiltered field values
iso8583.Describe(message, os.Stdout, DoNotFilterFields()...)

JSON encoding

You can serialize message into JSON format:

message := iso8583.NewMessage(spec)
message.MTI("0100")
message.Field(2, "4242424242424242")
message.Field(3, "123456")
message.Field(4, "100")

jsonMessage, err := json.Marshal(message)

it will produce the following JSON (bitmap is not included, as it's only used to unpack message from the binary representation):

{
   "0":"0100",
   "2":"4242424242424242",
   "3":123456,
   "4":"100"
}

Also, you can unmarshal JSON into iso8583.Message:

input := `{"0":"0100","2":"4242424242424242","4":"100"}`

message := NewMessage(spec)
if err := json.Unmarshal([]byte(input), message); err != nil {
    // handle err
}

// access indidual fields or using struct

Network Header

All messages between the client/server (ISO host and endpoint) have a message length header. It can be a 4 bytes ASCII or 2 bytes BCD encoded length. We provide a network.Header interface to simplify the reading and writing of the network header.

Following network headers are supported:

  • Binary2Bytes - message length encoded in 2 bytes, e.g, {0x00 0x73} for 115 bytes of the message
  • ASCII4Bytes - message length encoded in 4 bytes ASCII, e.g., 0115 for 115 bytes of the message
  • BCD2Bytes - message length encoded in 2 bytes BCD, e.g, {0x01, 0x15} for 115 bytes of the message
  • VMLH (Visa Message Length Header) - message length encoded in 2 bytes + 2 reserved bytes

You can read network header from the network connection like this:

header := network.NewBCD2BytesHeader()
_, err := header.ReadFrom(conn)
if err != nil {
	// handle error
}

// Make a buffer to hold message
buf := make([]byte, header.Length())
// Read the incoming message into the buffer.
read, err := io.ReadFull(conn, buf)
if err != nil {
	// handle error
}
if reqLen != header.Length() {
	// handle error
}

message := iso8583.NewMessage(specs.Spec87ASCII)
message.Unpack(buf)

Here is an example of how to write network header into network connection:

header := network.NewBCD2BytesHeader()
packed, err := message.Pack()
if err != nil {
	// handle error
}
header.SetLength(len(packed))
_, err = header.WriteTo(conn)
if err != nil {
	// handle error
}
n, err := conn.Write(packed)
if err != nil {
	// handle error
}

CLI

CLI suports following command:

  • display to display ISO8583 message in a human-readable format

Installation

iso8583 CLI is available as downloadable binaries from the releases page for MacOS, Windows and Linux.

Here is an example how to install MacOS version:

wget -O ./iso8583 https://github.com/moov-io/iso8583/releases/download/v0.4.6/iso8583_0.4.6_darwin_amd64 && chmod +x ./iso8583

Now you can run CLI:

➜ ./iso8583
Work seamlessly with ISO 8583 from the command line.

Usage:
  iso8583 <command> [flags]

Available commands:
  describe: display ISO 8583 file in a human-readable format

Display

To display ISO8583 message in a human-readable format

Example:

➜ ./bin/iso8583 describe msg.bin
MTI........................................: 0100
Bitmap.....................................: 000000000000000000000000000000000000000000000000
Bitmap bits................................: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
F000 Message Type Indicator................: 0100
F002 Primary Account Number................: 4242****4242
F003 Processing Code.......................: 123456
F004 Transaction Amount....................: 100
F020 PAN Extended Country Code.............: 4242****4242
F035 Track 2 Data..........................: 4000****0506=2512111123400001230
F036 Track 3 Data..........................: 011234****3445=724724000000000****00300XXXX020200099010=********************==1=100000000000000000**
F045 Track 1 Data..........................: B4815****1896^YATES/EUGENE L^^^356858      00998000000
F052 PIN Data..............................: 12****78
F055 ICC Data – EMV Having Multiple Tags...: ICC  ... Tags

You can specify which of the built-in specs to use to the describe message via the spec flag:

➜ ./bin/iso8583 describe -spec spec87ascii msg.bin

You can also define your spec in JSON format and describe message using the spec file with spec-file flag:

➜ ./bin/iso8583 describe -spec-file ./examples/specs/spec87ascii.json msg.bin

Please, check the example of the JSON spec file spec87ascii.json.

Learn about ISO 8583

Getting help

channel info
Project Documentation Our project documentation available online.
Twitter @moov You can follow Moov.io's Twitter feed to get updates on our project(s). You can also tweet us questions or just share blogs or stories.
GitHub Issue If you are able to reproduce a problem please open a GitHub Issue under the specific project that caused the error.
moov-io slack Join our slack channel (#iso8583) to have an interactive discussion about the development of the project.

Contributing

While Spec87ASCII is appropriate for most users, we hope to see improvements and variations of this specification for different systems by the community. Please do not hesitate to contribute issues, questions, or PRs to cover new use cases. Tests are also appreciated if possible!

Please review our Contributing guide and Code of Conduct to get started! Check out our issues for first time contributors for something to help out with.

This project uses Go Modules and Go v1.18 or newer. See Golang's install instructions for help setting up Go. You can download the source code and we offer tagged and released versions as well. We highly recommend you use a tagged release for production.

Related projects

As part of Moov's initiative to offer open source fintech infrastructure, we have a large collection of active projects you may find useful:

  • Moov ACH provides ACH file generation and parsing, supporting all Standard Entry Codes for the primary method of money movement throughout the United States.

  • Moov Watchman offers search functions over numerous trade sanction lists from the United States and European Union.

  • Moov Fed implements utility services for searching the United States Federal Reserve System such as ABA routing numbers, financial institution name lookup, and FedACH and Fedwire routing information.

  • Moov Wire implements an interface to write files for the Fedwire Funds Service, a real-time gross settlement funds transfer system operated by the United States Federal Reserve Banks.

  • Moov ImageCashLetter implements Image Cash Letter (ICL) files used for Check21, X.9 or check truncation files for exchange and remote deposit in the U.S.

  • Moov Metro2 provides a way to easily read, create, and validate Metro 2 format, which is used for consumer credit history reporting by the United States credit bureaus.

License

Apache License 2.0 - See LICENSE for details.

iso8583's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

iso8583's Issues

Upper Hex.

My friends, I have a problem because the bitmap is being generated in capital letters.

I need it:

0800A2380001004000080400000000000000000000060223031106022320031106020400890000000000000000000501.00301

But what comes out of my "Go" program is this:

0800a2380001004000080400000000000000000000060223031106022320031106020400890000000000000000000501.00301

notice that the "A" became lowercase "a".

Is there any way to change this?

Implement REST API

Here is the draft of the API. I'm open to discuss it

POST /specs - creates and stores specification in memory, returns specification ID or error

{
   "spec":{
      "fields":{
         "0":{
            "type":"String",
            "length":4,
            "description":"Message Type Indicator",
            "enc":"ASCII",
            "pref":"ASCII.Fixed"
         },
         "1":{
            "type":"Bitmap",
            "description":"Bitmap",
            "enc":"Hex",
            "pref":"Hex.Fixed"
         },
         "2":{
            "type":"Numeric",
            "length":19,
            "description":"Primary Account Number",
            "enc":"ASCII",
            "pref":"ASCII.LL"
         },
         "3":{
            "type":"Numeric",
            "length":19,
            "description":"Transaction Amount",
            "enc":"ASCII",
            "pref":"ASCII.Fixed",
            "pad":{
               "type":"left",
               "padder":"0"
            }
         }
      }
   }
}

POST /specs/:id/message/encode

Request:

{
   "fields":{
      "2":"do not send plain card number here :)",
      "3":123456,
      "4":100,
      "5":"whatever value"
   }
}

Response:

{
   "message":"base64-encoded-binary-message"
}

POST /specs/:id/message/decode

Request:

{
   "message":"base64-encoded-binary-message"
}

Response:

{
   "fields":{
      "2":"we need to figure out how to (not) expose card number",
      "3":123456,
      "4":100,
      "5":"whatever value"
   }
}

Utility Pack & Unpack from network connection

A network connection to a processing network (visa) has a two-byte message length plus the unpacked or packed ISO8583 message.

To send a message you must iso8583.pack() the iso message and check that you received the bytes without an error. Then you use binary.size to get the length of the message. You write a bugger of length size and add the size and the packed data before it is submitted to the network.

To receive a message on a net connection you read the first two bytes, throw them away, and then unpack the bytes into an ISO message.

I would like to see the ability to send/parse a message over a go socket connection. It seems like this has to be a common pattern for anyone using this library on a network. Based on other comments it seems like each network has a different header length. The length of the header probably needs to be configurable.

field constraints support

some fields not only must have iso8583 types, but also must conform to some other stuff like len of field, or number of spaces or digits , may be cool to add Validate func(field.Field) error to spec?
So after we fill the fields and Validate func not nil we can call it and get reasonable error if it present?
Also we can create predefined funcs as example for date time fields for example...
i think that this will be cool

Helps to understand the message Unpack

I get the message from the server "01360210723804010AC000081641341100081378100030000000000006630624185210000001185210062405104008911751800000196POS1111100000000000"

but the unpack result doesn't seem correct to me : 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30

Below is the example code I set up

package main

import (
"fmt"
"github.com/moov-io/iso8583"
"github.com/moov-io/iso8583/encoding"
"github.com/moov-io/iso8583/field"
"github.com/moov-io/iso8583/padding"
"github.com/moov-io/iso8583/prefix"
)

func NewSpec() *iso8583.MessageSpec {
return &iso8583.MessageSpec{
Fields: map[int]field.Field{
0: field.NewString(&field.Spec{
Length: 8,
Description: "Message Type Indicator",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
1: field.NewBitmap(&field.Spec{
Description: "Bitmap",
Enc: encoding.Hex,
Pref: prefix.Hex.Fixed,
}),
// 2: field.NewString(&field.Spec{
// Length: 19,
// Description: "Primary Account Number",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.LL,
// }),
3: field.NewNumeric(&field.Spec{
Length: 6,
Description: "Processing Code",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
4: field.NewNumeric(&field.Spec{
Length: 12,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
7: field.NewNumeric(&field.Spec{
Length: 10,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
11: field.NewNumeric(&field.Spec{
Length: 6,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
12: field.NewNumeric(&field.Spec{
Length: 6,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
13: field.NewNumeric(&field.Spec{
Length: 4,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
//14: field.NewNumeric(&field.Spec{
// Length: 4,
// Description: "Transaction Amount",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.Fixed,
// Pad: padding.Left('0'),
//}),
22: field.NewNumeric(&field.Spec{
Length: 3,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
//23: field.NewNumeric(&field.Spec{
// Length: 3,
// Description: "Transaction Amount",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.Fixed,
// Pad: padding.Left('0'),
//}),
32: field.NewString(&field.Spec{
Length: 11,
Description: "Field 32",
Enc: encoding.ASCII,
Pref: prefix.ASCII.LL,
}),
35: field.NewString(&field.Spec{
Length: 37,
Description: "Field 32",
Enc: encoding.ASCII,
Pref: prefix.ASCII.LL,
}),
37: field.NewNumeric(&field.Spec{
Length: 12,
Description: "NSU",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
41: field.NewString(&field.Spec{
Length: 8,
Description: "TERMINAL",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
//39: field.NewString(&field.Spec{
// Length: 2,
// Description: "Field 39",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.Fixed,
// Pad: padding.Left('0'),
//}),
42: field.NewString(&field.Spec{
Length: 15,
Description: "Field 42",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
//45: field.NewString(&field.Spec{
// Length: 76,
// Description: " 1",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.LL,
//}),
47: field.NewString(&field.Spec{
Length: 999,
Description: "DADOS ADD",
Enc: encoding.ASCII,
Pref: prefix.ASCII.LLL,
}),
//48: field.NewString(&field.Spec{
// Length: 999,
// Description: "47",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.LLL,
//}),
49: field.NewNumeric(&field.Spec{
Length: 3,
Description: "Transaction Amount",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
52: field.NewString(&field.Spec{
Length: 16,
Description: "Field 52",
Enc: encoding.Binary,
Pref: prefix.ASCII.Fixed,
}),
//55: field.NewString(&field.Spec{
// Length: 999,
// Description: "CHIP EMV",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.LLL,
//}),
61: field.NewString(&field.Spec{
Length: 999,
Description: " ADC",
Enc: encoding.ASCII,
Pref: prefix.ASCII.LLL,
//}),
//70: field.NewNumeric(&field.Spec{
// Length: 3,
// Description: "Transaction Amount",
// Enc: encoding.ASCII,
// Pref: prefix.ASCII.Fixed,
// Pad: padding.Left('0'),
}),
},
}

}

func main() {
test()
}

func test() {
want := "01360210723804010AC000081641341100081378100030000000000006630624185210000001185210062405104008911751800000196POS1111100000000000"

spec := NewSpec()

message := iso8583.NewMessage(spec)

message.Unpack([]byte(want))

message.GetMTI() 
message.GetString(3) 
message.GetString(4) 
message.GetString(7) 
message.GetString(11)
message.GetString(12)
message.GetString(13)


// set your values
c, err := message.Pack()
if err != nil {
	panic(err)

}
fmt.Printf("% x\n", c)

}

add integration validation feature

we need to add

  • validation for message types (MTI)
  • message types through specification file
  • to specify mandatory fields of each message type

fields vs fieldsMap

In Message when do you use fields vs fieldsMap? Can someone document this in message.go? Although these struck fields are not exported it would be nice if they had comments.

type Message struct {
	fields    map[int]field.Field
	spec      *MessageSpec
	data      interface{}
	fieldsMap map[int]struct{}
	bitmap    *field.Bitmap
}

ISO7813 functions for reading/writing card track data

Utility functions for reading/writing card track data. This should be in its own package.

Create functions for reading and writing magnetic track data. The track data is present in the ISO8583 fields.

Data field Type Usage
35 z ..37 Track 2 data
36 n ...104 Track 3 data
45 an ..76 Track 1 data

ISO 7813 (tracks 1 and 2) and ISO 4909 (track 3) are the formats of the three magnetic tracks in standard financial transaction cards. (Credit and Debit Cards)

Information about the track data.

Echo Test

Hello, do you have any example of echo test (0800), sent via tcp ip?

I read the examples but didn't quite understand.
My echo test
De3
De7
De11
De12
De13
De32
De39
De42
De61
De70

failed to decode PAN in incoming way4 iso8583 message

raw bytes:

484950482426010211422713000000110022669967516623611614500000000519171716623103231795193839617315208101611149495151484948544955545555545556565148484848485053495249485053323232323232323232323232846832658584796879773232323232323232323232323232626576776584893232323232323275900145574856484850485357494848484950574951484849505749574848491185752544848494957535648485753484949484849494857535448494949484948484948484848535753574849556765826895866982737073676584737978097565151484853555055484856524948504965767765848944836584806569866532574847504956525748495567658268958669827370736765847379783152099159391128159671600003161614021159261593615926214959542215431561159554155210408010861051159732681019810511614581029325172012800236765826895866982737073676584737978140240126202884875548515250502039845048504948515048205258450494851495348484848484848484848484849565453485620417847897117116105105117115821019912199108101114206158452505451525142424242485455522072844920928449210484515756211284492122844821318846765826895866982737073676584737978

way4 message log:

 Type: 0120
                               000:INTERNAL.ISO
                               002:4786740000395353
                               2.01:47
                               003:000000
                               3.01:00
                               3.02:00
                               3.03:00
                               004:000000015900
                               007:0513110344
                               011:000053
                               012:170343
                               013:0513
                               014:2303
                               018:5169
                               019:398
                               022:071
                               22.01:07
                               22.02:1
                               023:001
                               025:00
                               032:0038
                               035:4786740000395353=23032011872900000166
                               037:113385105350
                               038:768200
                               039:00
                               041:25001684
                               042:25001684      
                               043:TOO GRAND S             >ALMATY       KZ
                               43.01:TOO GRAND S             >
                               43.02:ALMATY      
                                43.03:KZ
                               049:398
                               065:L13385017COU                                112:F075CA09TFCC42698CB09T20210320CD19T201126000000000000182612CE0FT478674****5353CF02T1D102T1D204T398D302T0D402T0D507T01000R
                               114:29014KAZYBAEVA D 264 OF 22100572700
                               124:CARC:2 CBR:VISA CFMT:0200 CVCR:2 DEV:1000F2000A0000000 MPD:00100100003 SICG:T TCA:01E68084C0002D02B1000000 TCH:v UPD:06003 VPD:05
                               ```

PAN encoded via BCD in LVAR numeric with max 19 symbols

JSON marshaling panics on specs with empty extended bitmap

The official ISO8583 spec consists of an extended bitmap in DE 65. In practice, this is left empty in the vast majority of the use cases. JSON marshaling panics on a nil pointer reference on specs with this field defined.

In particular, it panics here because the underlying utils.Bitmap is nil at runtime:

 func (f *Bitmap) Bytes() ([]byte, error) {                                                                                                                                                                                                                                                                                                                                      
     return f.bitmap.Bytes(), nil                                                                                                                                                              
 } 

Adding a nil check before referencing the underlying bitmap fixes this issue.

Does this library have support for extended bitmaps?

JSON encoding and decoding for iso8583 Spec

If you want to use iso8583 CLI, currently it supports only built-in specs. We have only one spec defined (ASCII 87).

I think that loading specs from JSON when you use CLI will make it easy to use for developers who do not want to modify and build binaries (who wants? :))

For CLI it will look like this:

iso8583 describe message.bin --spec-file 2000.json

JSON format of the specification will try to follow the go struct as close as possible.

Dump ISO8583 data to string output for debugging.

Create a function that dumps the iso8583 message into labels for debugging and logging.

<Property Name> <Field Length> <tab> :<Field Value>

isomessage = iso8583.NewMessage(iso8583.Spec87)
err = isomessage.Unpack(Unpack([]byte("0800A23800000080000004000000000000000000000706082419005835082419070620390059000"))
if err != nil {
    t.Errorf("failed to pack unpacked message: %v", err)
}
isomessage.stringDump()
asciiMessage                  :0800A23800000080000004000000000000000000000706082419005835082419070620390059000
Header                  :
MessageType                   :0800
Bitmap                        :A2380000008000000400000000000000 bits:10100010001110000000000000000000000000001000000000000000000000000000010000000000000000000000000000000000000000000000000000000000
F03_ProcessingCode            :000000
F07_TransmissionDateTime      :0706082419
F11_SystemTraceAuditNumber    :005835
F12_DateTimeLocalTransaction  :082419
F13_DateLocalTxn              :0706
F41_CardAcceptorTerminalIdenti:20390059
F70_AuthInstCntryCode         :000

I would like to name this "stringDump" or some variation so that we could add hexDump() and others in the future.

This function plus a simple string input CLI would be a nice tool utility tool. This would be like the achcli for describing a file. This should be a separate issue.
https://github.com/moov-io/ach#command-line

subfield decoding support

some fields have TLV stuff, do you have plans to add ability to define subfield and parsing capability?

Upgrade assert testing framework

Project is currently dependent on github.com/stretchr/testify/assert which is a deprecated testing framework. The test's should be updated to use gopkg.in/check.v1

runtime error: slice bounds out of range

Running the fuzzer there were some crashing input files:

panic: runtime error: slice bounds out of range [:-85]
goroutine 1 [running]:
github.com/moov-io/iso8583/pkg/lib.(*Element).characterDecoding(0xc0000b4fc0, 0x7f59ae53c040, 0x3, 0x3, 0xc0000bc0c8, 0x565661, 0xc0000b4fc0)
	/go/src/github.com/moov-io/iso8583/pkg/lib/element.go:249 +0x599
github.com/moov-io/iso8583/pkg/lib.(*Element).Load(0xc0000b4fc0, 0x7f59ae53c040, 0x3, 0x3, 0xc000058780, 0x0, 0x0)
	/go/src/github.com/moov-io/iso8583/pkg/lib/element.go:85 +0x263
github.com/moov-io/iso8583/pkg/lib.(*isoMessage).createElement(0xc0000822c0, 0x3d, 0x40, 0x7f59ae53c000, 0x43, 0x43, 0x8, 0x0, 0x0)
	/go/src/github.com/moov-io/iso8583/pkg/lib/message.go:305 +0x298
github.com/moov-io/iso8583/pkg/lib.(*isoMessage).Load(0xc0000822c0, 0x7f59ae53c000, 0x43, 0x43, 0x5e3040, 0xc0000822c0, 0x0)
	/go/src/github.com/moov-io/iso8583/pkg/lib/message.go:178 +0x4a4
github.com/moov-io/iso8583/test/fuzz-reader.Fuzz(0x7f59ae53c000, 0x43, 0x43, 0x4)
	/go/src/github.com/moov-io/iso8583/test/fuzz-reader/reader.go:51 +0x1f8
go-fuzz-dep.Main(0xc00003ff70, 0x1, 0x1)
	go-fuzz-dep/main.go:36 +0x1ad
main.main()
	github.com/moov-io/iso8583/test/fuzz-reader/go.fuzz.main/main.go:15 +0x52
exit status 2

This was the input: 0000000360030000000000000000000000000000000-3

MessageSpec Name

I would like to be able to programmatically know which message spec is being used.

message := iso8583.NewMessage(iso8583.Spec87)

When you pass in a specification it is explicit but you can never get access programmatically that you are using Spec87 or a custom spec.

In order to debug and issue I would like to be able to find this spec.

func (m *Message) stringDump() {
	fmt.Printf("Parsing with specification: %s\n", m.spec.name)
}

I am not sure if this should only be internal or if it should be exported?

Numeric with non-nil padder causes '0' value unpacking to panic

Numeric.Unpack currently calls Unpad on a non-nil padder and trims off bytes which match a particular pattern.

Moov's Spec87 only left pads numerics with '0' chars as such:

          4: field.NewString(&field.Spec{                                                                                                                                                                             
              Length:      12,                                                                                                                                                                                        
              Description: "Transaction Amount",                                                                                                                                                                      
              Enc:         encoding.ASCII,                                                                                                                                                                            
              Pref:        prefix.ASCII.Fixed,                                                                                                                                                                        
              Pad:         padding.Left('0'),                                                                                                                                                                         
          }),                                                                                                                                                                                                         
          5: field.NewString(&field.Spec{                                                                                                                                                                             
              Length:      12,                                                                                                                                                                                        
              Description: "Settlement Amount",                                                                                                                                                                       
              Enc:         encoding.ASCII,                                                                                                                                                                            
              Pref:        prefix.ASCII.Fixed,                                                                                                                                                                        
              Pad:         padding.Left('0'),                                                                                                                                                                         
          }),                                                                                                                                                                                                         
          6: field.NewString(&field.Spec{                                                                                                                                                                             
              Length:      12,                                                                                                                                                                                        
              Description: "Billing Amount",                                                                                                                                                                          
              Enc:         encoding.ASCII,                                                                                                                                                                            
              Pref:        prefix.ASCII.Fixed,                                                                                                                                                                        
              Pad:         padding.Left('0'),                                                                                                                                                                         
          }),   

Our own internal spec (which goes to subfield granularity) also does the same. This leads to an issue where if an 'amount' (for example) is '00000000', then the entire input is trimmed out to an empty string. This results in strconv.Atoi(string(raw)) panicking as it's being called with empty string "". I would classify such an error as a bug in its current state.

We have not found a use case to right-pad numeric values as of yet. As such, we feel its a safe assumption that one doesn't exist. We have also not found a use case to pad numeric fields with non-numeric values. strconv.Atoi(...) supports left padded values.

strconv.Atoi("00005") returns 5.

Given all of the above, we feel the following snippet of code should be removed from Numeric's Unpack method:

      if f.spec.Pad != nil {                                                                                                                                                                    
          raw = f.spec.Pad.Unpad(raw)                                                                                                                                                           
      } 

input bytes does not match output bytes ?

I would expect the bytes that I pass into Unpack to match the bytes that I get from Pack

func TestBytesMatch(t *testing.T) {
	msg := iso8583.NewMessage(iso8583.Spec87)
	wantMsg := []byte("01007000000000000000164242424242424242123456000000000100")
	err := msg.Unpack(wantMsg)
	if err != nil {
		t.Errorf("failed to pack unpacked message: %v", err)
	}
	rawmsg, err := msg.Pack()
	if err != nil {
		t.Errorf("failed to pack message: %v", err)
	}

	res := bytes.Equal(wantMsg, rawmsg)

	if  !res  {
		t.Errorf("input message does not match output")
	}
}
--- FAIL: TestBytesMatch (0.00s)
    /Users/wadearnold/Documents/GitHub/wadearnold/iso8583/test/issues/issue69_test.go:38: input message does not match output
FAIL

Thoughts on why they don't match? If there is always a mutation then is there a way to get the input bytes. This would be useful when Unpack and Pack fail to know what caused the issue.

Documentation should use default spec87

If you read the readme for this project it is assumed that you need to build your own spec. The majority of people will be able to simply use the default spec87 and then have typed iso messages. We should migrate the docs from assuming you need to build a spec to specify that most use cases are fine with spec87 and if the spec does not meet your needs then show how we can modify or create your own.

Creating a message in documentation should always have this simple instantiation other than learning how to modify or build your own spec.

isomessage := iso8583.NewMessage(iso8583.Spec87)

We should encourage others to contribute back their specs for different systems.

Interactive CLI describe input from terminal

The CLI currently allows for a file to be read into the cli and the iso8583 message to be described.

I would like to run the cli as an interactive session. Could we add a -i for interactive or -interactive

➜ ./bin/iso8583 -i describe -spec spec87ascii
➜ message: 0200323A40010841801038000000000000000004200508050113921208050420042251320720000010000001156040800411        01251146333156336000299

ISO 8583 Message:
MTI.............................: 0100
Bitmap..........................: A2380000008000000400000000000000
Bitmap bits.....................: 10100010 10100010 10100010 10100010 10100010 10100010 10100010 10100010
001 Processing Code.............: 000001
011 System Trace Audit Number...: 005835

➜ message: 

support missing data types

Support data type "x" and "z"
x: character “C” or “D” to indicate “credit” or “debit” value of a dollar amount
z: magnetic stripe track-2 or track-3 data

Support for EBCDIC character set

The official ISO8583-1987 spec references EBCDIC as the character set of choice to encode messages. Certain card schemes also require this.

Could we extend support for this character set?

How far has ASCII taken users of this lib when integrating with card schemes with regards to message flows?

Am I correct in thinking that it only requires implementation of the encoding/Encoder and prefix/Prefixer interfaces to support this?

EMV TLV(Tag Length Value) decoder/encoder. aka. Parse Field 55(DE55)

Create the ability to read and write EMV TLV data elements. This should be its own package and used for field 55 in iso8583.

EMV Chip and Tag - EMV TLV - ISO8583 Data Element Field 55 - iso7816-4

"EMV Tags" are usually mile-long alpha-numeric strings in a BER-TLV format. "TLV" or as it might be referred to as "SIMPLE-TLV" stands for "Tag Length Value". BER-TLV is an "expanded" version of "TLV" that allows supplying tags with variable length, include one "TLV" inside another and provides few extra capabilities (for more details please refer to stackoverflow post).

Complete list of Field 55:ICC data
https://developer-eu.elavon.com/docs/eisop/field-descriptions/field-55-icc-data

EMV Tag Search
https://emvlab.org/emvtags/all/
https://neapay.com/online-tools/emv-tags-list.html

This application decodes EMV records encoded in tag-length-value (TLV) format
https://emvlab.org/tlvutils/
https://neapay.com/online-tools/emv-tlv-decoder.html

Complete list of Field 55:ICC data
https://developer-eu.elavon.com/docs/eisop/field-descriptions/field-55-icc-data

TLV also knows as ANS.1 BER (Basic Encoding Rules) from X.208
https://github.com/mercari/go-emv-code
https://github.com/binaryfoo/emv-bertlv
https://golang.org/src/encoding/asn1/asn1.go
https://github.com/skythen/bertlv
https://github.com/hexdigest/bertlv
https://stackoverflow.com/questions/18853800/simple-tlv-vs-ber-tlv

improvement using configuration file

These data fields are slightly different based on the network, so we would like to use a JSON configuration file for parsing the data elements rather than hard coding the logic for every use case.

The goal is to allow for ascii, XML, or JSON requests into the library and transpose the message data.
Example: https://github.com/kamshory/ISO8583-JSON-XML

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.