Profile
Back to NewsBack
GitHub Trending 20 min
Reader Mode
moov-io/iso8583: A golang implementation to marshal and unmarshal iso8583 message.

moov-io/iso8583: A golang implementation to marshal and unmarshal iso8583 message.

12 hours ago

Moov Banner Logo</a>

Project Documentation · Community · Blog

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

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

- Defining Message Specifications - Working with ISO 8583 Messages - Setting Message Data - Getting Message Data - Partial Message Parsing (MessageScanner) - Inspecting message fields - JSON Encoding and Decoding - Working with Unknown TLV Tags

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 or asking on our #iso8583 Community Slack channel. . Thanks!

Installation

go get github.com/moov-io/iso8583

Quick Start

The following example demonstrates how to:

  • Define message structure using Go types
  • Pack a message for transmission
  • Unpack and parse a received message
package main

import ( "fmt" "os"

"github.com/moov-io/iso8583" "github.com/moov-io/iso8583/examples" )

// Define types for the message fields type Authorization struct { MTI string iso8583:"0" // Message Type Indicator PrimaryAccountNumber string iso8583:"2" // PAN ProcessingCode string iso8583:"3" // Processing code Amount int64 iso8583:"4" // Transaction amount STAN string iso8583:"11" // System Trace Audit Number ExpirationDate string iso8583:"14" // YYMM AcceptorInformation *AcceptorInformation iso8583:"43" // Merchant details }

type AcceptorInformation struct { Name string iso8583:"1" City string iso8583:"2" Country string iso8583:"3" }

func main() { // Pack the message msg := iso8583.NewMessage(examples.Spec)

authData := &Authorization{ MTI: "0100", PrimaryAccountNumber: "4242424242424242", ProcessingCode: "000000", Amount: 2599, ExpirationDate: "2201", AcceptorInformation: &AcceptorInformation{ Name: "Merchant Name", City: "Denver", Country: "US", }, }

// Set the field values err := msg.Marshal(authData) if err != nil { panic(err) }

// Pack the message packed, err := msg.Pack() if err != nil { panic(err) }

// send packed message to the server // ...

// Unpack the message msg = iso8583.NewMessage(examples.Spec) err = msg.Unpack(packed) if err != nil { panic(err) }

// get individual field values var amount int64 err = msg.UnmarshalPath("4", &amount) if err != nil { panic(err) }

fmt.Printf("Amount: %d\n", amount)

// get value of composite subfield var acceptorName string err = msg.UnmarshalPath("43.1", &acceptorName) if err != nil { panic(err) }

fmt.Printf("Acceptor Name: %s\n", acceptorName)

// Get the field values into data structure authData = &Authorization{} err = msg.Unmarshal(authData) if err != nil { panic(err) }

// Print the entire message iso8583.Describe(msg, os.Stdout) }

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.

How to

Defining Message Specifications

Most ISO 8583 implementations use confidential specifications that vary between payment systems, so you'll likely need to create your own specification. We provide example specifications in /specs directory that you can use as a starting point.

Core Concepts

The package maps ISO 8583 concepts to the following types:

  • MessageSpec - Defines the complete message format with fields
  • field.Spec - Defines field's structure and behavior
  • field.Field - Represents an ISO 8583 data element with its value storing and handling logic:
- field.String - For alphanumeric fields - field.Numeric - For numeric fields - field.Binary - For binary data fields - field.Composite - For structured data like TLV/BER-TLV fields or fields with positional subfields

Each field specification consists of these 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, Binary, BCD, LBCD, EBCDIC). | encoding.ASCII | | Pref | Sets the encoding (ASCII, 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') |

Note: While some ISO 8583 specifications do not have field 0 and field 1, we use them for MTI and Bitmap as they are technically regular fields. We use String field for MTI and Bitmap field for the bitmap.

You can also write a spec as a YAML or JSON document and load it at runtime. For more information, see Defining a Spec as a Document.

For more advanced examples including handling of BER-TLV data, positional subfields, and various encoding types, see:

Working with ISO 8583 Messages

The package provides two key operations for working with ISO 8583 messages:

Building Messages

To build a message:

  1. Set message data using Go structs or individual field operations
  2. Pack the message into bytes using Pack()
  3. Send the bytes over the network
Set Data → Pack → Network →

Processing Messages

When receiving a message:

  1. Unpack the received bytes using Unpack()
  2. Get message data using Go structs or individual field operations
  3. Process the data in your application
→ Network → Unpack → Get Data

Setting Message Data

After defining your specification, you can set message data in two ways: working with individual fields or using Go structs. While individual field access is available, using structs provides a cleaner approach.

Using Go Structs (Recommended)

Define a struct that maps your business data to ISO 8583 fields using native Go types and iso8583 tags:

type Authorization struct {
    MTI                  string    iso8583:"0"  // Message Type Indicator
    PrimaryAccountNumber string    iso8583:"2"  // PAN
    ProcessingCode       string    iso8583:"3"  // Processing code
    Amount               int64     iso8583:"4"  // Transaction amount
    STAN                 string    iso8583:"11" // System Trace Audit Number
    LocalTime            string    iso8583:"12" // HHmmss
    LocalDate            string    iso8583:"13" // MMDD
    MerchantType         string    iso8583:"18" // Merchant category code
    AcceptorInfo         *Acceptor iso8583:"43" // Merchant details
}

type Acceptor struct { Name string iso8583:"1" City string iso8583:"2" Country string iso8583:"3" }

Then create and populate your message:

// Create new message
msg := iso8583.NewMessage(Spec)

// Prepare transaction data auth := &Authorization{ MTI: "0100", PrimaryAccountNumber: "4242424242424242", ProcessingCode: "000000", Amount: 9999, STAN: "000123", LocalTime: "152059", LocalDate: "0205", MerchantType: "5411", AcceptorInfo: &Acceptor{ Name: "ACME Store", City: "New York", Country: "US", }, }

// Marshal data into message err := msg.Marshal(auth) if err != nil { panic(err) }

// Pack for transmission data, err := msg.Pack() if err != nil { panic(err) } // data is ready to be sent

If you want empty values to be included in the message, you can use the keepzero option in the iso8583 field tag:

type Authorization struct {
    // ...
    AdditionalData       string    iso8583:"48,keepzero" // Additional data
}

For such fields, the field bit will be set in the bitmap, but the value will be empty. You should set padding for such fields to ensure the field length is correct.

Working with Individual Fields

Click to show individual field operations

For simple operations, you can set fields directly:

msg := iso8583.NewMessage(spec)

// Set MTI msg.MTI("0100")

// Set field values as strings err := msg.Field(2, "4242424242424242") err = msg.Field(3, "000000") err = msg.Field(4, "9999")

// For binary fields err = msg.BinaryField(52, []byte{0x1A, 0x2B, 0x3C, 0x4D})

// Pack for transmission data, err := msg.Pack()

Note: Individual field operations are limited to string or []byte values. The underlying field handles type conversion.

Legacy: Using Field Types

Click to show legacy field type usage

In previous versions, it was common to use package-specific field types. While still supported, we recommend using native Go types instead:

type Authorization struct {
    MTI         *field.String iso8583:"0"
    PAN         *field.String iso8583:"2"
    Amount      *field.Numeric iso8583:"4"
    LocalTime   *field.String iso8583:"12"
}

auth := &Authorization{ MTI: field.NewStringValue("0100"), PAN: field.NewStringValue("4242424242424242"), Amount: field.NewNumericValue(9999), LocalTime: field.NewStringValue("152059"), }

msg.Marshal(auth)

Getting Message Data

When you receive a packed ISO 8583 message, you can unpack and access its data in two ways: using Go structs or accessing individual fields. Using structs provides a cleaner approach to working with message data.

Using Go Structs (Recommended)

Define a struct matching your expected message format using native Go types:

type Authorization struct {
    MTI                  string    iso8583:"0"  // Message Type Indicator
    PrimaryAccountNumber string    iso8583:"2"  // PAN
    ProcessingCode       string    iso8583:"3"  // Processing code
    Amount              int64     iso8583:"4"  // Transaction amount
    STAN                string    iso8583:"11" // System Trace Audit Number
    LocalTime           string    iso8583:"12" // HHmmss
    LocalDate           string    iso8583:"13" // MMDD
    MerchantType        string    iso8583:"18" // Merchant category code
    AcceptorInfo        *Acceptor iso8583:"43" // Merchant details
}

type Acceptor struct { Name string iso8583:"1" City string iso8583:"2" Country string iso8583:"3" }

Then unpack and parse your message:

// Create message with appropriate spec
msg := iso8583.NewMessage(specs.Spec87ASCII)

// Unpack received bytes err := msg.Unpack(receivedData) if err != nil { panic(err) }

// Parse into struct var auth Authorization err = msg.Unmarshal(&auth) if err != nil { panic(err) }

// Work with parsed data fmt.Printf("Transaction amount: %d\n", auth.Amount) fmt.Printf("Merchant: %s, %s\n", auth.AcceptorInfo.Name, auth.AcceptorInfo.City)

// Print full message contents (with sensitive data masked) iso8583.Describe(msg, os.Stdout)

Working with Individual Fields

Click to show individual field operations

For quick access to specific fields:

msg := iso8583.NewMessage(spec)
err := msg.Unpack(receivedData)

// Get MTI mti, err := msg.GetMTI()

// Get field values as strings pan, err := msg.GetString(2) proc, err := msg.GetString(3) amount, err := msg.GetString(4)

// Get binary field values pinData, err := msg.GetBytes(52)

Note: Individual field access is limited to string or []byte values.

Legacy: Using Field Types

Click to show legacy field type usage

Previously, it was common to use package-specific field types. While still supported, we recommend using native Go types instead:

type LegacyAuthorization struct {
    MTI         *field.String  iso8583:"0"
    PAN         *field.String  iso8583:"2"
    Amount      *field.Numeric iso8583:"4"
    LocalTime   *field.String  iso8583:"12"
}

var auth LegacyAuthorization msg.Unmarshal(&auth)

fmt.Println(auth.MTI.Value()) fmt.Println(auth.Amount.Value())

Partial Message Parsing (MessageScanner)

When you only need to inspect a few fields from a raw message — for example, reading the MTI to decide how to route, or extracting the STAN for logging — you can use MessageScanner instead of unpacking the entire message.

MessageScanner is a forward-only cursor that parses fields on demand. It consumes bytes sequentially but only returns the fields you ask for, making it lightweight and suitable for proxies and routers.

s := iso8583.NewMessageScanner(spec, rawBytes)

// Scan MTI to decide how to handle the message f, err := s.ScanField(0) if err != nil { // handle error }

mti, err := f.String() if err != nil { // handle error }

if mti == "0800" { // Network management message — forward raw bytes to a // dedicated handler without further parsing handleNetworkMessage(rawBytes) return }

// For other messages, continue scanning to get STAN f, err = s.ScanField(11) if err != nil { // handle error }

stan, err := f.String() if err != nil { // handle error }

// Route or log based on MTI and STAN fmt.Printf("MTI: %s, STAN: %s\n", mti, stan)

Key behaviors:

  • Forward-only: fields must be scanned in ascending order. Scanning a field at or before the current position returns an error.
  • Minimal allocations: fields between the current position and the target are consumed from the byte stream but discarded. Only the requested field is allocated and returned.

Inspecting 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=72472400000000000300XXXX020200099010=*==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 and Decoding

You can serialize message into JSON format:

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

Working with Unknown TLV Tags

Composite fields in ISO 8583 messages often use TLV (Tag-Length-Value) encoding. In practice, network specifications may include tags that you are not interested in and don't want to define in your Go specification. The library provides a way to skip, store, and inspect such unknown tags.

Skipping Unknown Tags

To skip unknown tags during unpacking, set SkipUnknownTLVTags: true in the TagSpec of the composite field. The behavior depends on the TLV encoding type:

  • BER-TLV fields: The parser knows how to read the tag and length of unknown tags automatically, so no additional configuration is needed.
Tag: &field.TagSpec{
      Enc:                encoding.BerTLVTag,
      Sort:               sort.StringsByHex,
      SkipUnknownTLVTags: true, // skip tags not in Subfields
  },
  • Non-BER TLV fields: You must specify PrefUnknownTLV so the parser knows how to read the length of unknown tags. This only works when all tags in the composite field use a consistent tag length and length prefix format.
Tag: &field.TagSpec{
      Length:             2,             // tag length in bytes
      Enc:                encoding.ASCII,
      Sort:               sort.Strings,
      SkipUnknownTLVTags: true,
      PrefUnknownTLV:     prefix.ASCII.L, // length prefix format for unknown tags
  },

Without SkipUnknownTLVTags, the parser returns an error when it encounters a tag not defined in Subfields.

Storing Unknown Tags

By default, skipped tags are discarded. If you want to preserve them — for example, to re-pack the message with all original data intact — enable StoreUnknownTLVTags:

Tag: &field.TagSpec{
    Enc:                 encoding.BerTLVTag,
    Sort:                sort.StringsByHex,
    SkipUnknownTLVTags:  true,
    StoreUnknownTLVTags: true, // keep unknown tags in the message
},

When enabled, unknown tags are stored as Binary fields inside the composite. Packing the message again will include them in the output, preserving the original data.

You can also manually set unknown tags on a composite field using MarshalPath. This is useful when you want to set a packable unknown field on the composite — for example, when reconstructing a message or adding tags that are not defined in the spec. The following conditions must be met:

  • Both SkipUnknownTLVTags and StoreUnknownTLVTags are enabled in the TagSpec
  • The value implements field.Field (e.g., *field.Binary)
// Set an unknown tag on field 55
err := msg.MarshalPath("55.9F36", &field.Binary{...})

Finding Unknown Tags with UnknownTags

If you store unknown tags, you can use the UnknownTags helper to retrieve them from a message after unpacking. It returns a map of unknown fields keyed by their dot-separated path (e.g., "55.9F36" for unknown tag 9F36 inside field 55):

msg := iso8583.NewMessage(spec)
err := msg.Unpack(rawData)
if err != nil {
    // handle error
}

unknownTags := iso8583.UnknownTags(msg) for path, f := range unknownTags { val, _ := f.Bytes() fmt.Printf("Unknown tag at %s: %X\n", path, val) }

If you're working with a standalone *field.Composite (not a full message), use UnknownCompositeTags instead:

composite := field.NewComposite(spec)
_, err := composite.Unpack(rawData)
if err != nil {
    // handle error
}

unknownTags := iso8583.UnknownCompositeTags(composite) for path, f := range unknownTags { val, _ := f.Bytes() fmt.Printf("Unknown tag at %s: %X\n", path, val) }

Note: UnknownTags and UnknownCompositeTags only return results when StoreUnknownTLVTags is enabled in the composite field specs. If unknown tags are skipped but not stored, they are discarded during unpacking and cannot be retrieved.

Sending and Receiving Messages

While this package handles message formatting and parsing, for network operations we recommend using our companion package moov-io/iso8583-connection. It provides robust client/server communication with features like:

  • Message sending and receiving
  • Request/response matching
  • Connection management
  • Support for both acquiring and issuing services
  • Testing utilities

Network Headers

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 or any other custom header format. We provide a network.Header interface to simplify the reading and writing of the network header.

Following network headers implementations are available in network package:

  • 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=72472400000000000300XXXX020200099010=*==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 more

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.

Chat with me