iso8583-codec-rs 0.1.0

ISO 8583 message format parser and serializer with JSON-driven field specifications
Documentation

ISO8583

A high-performance Rust library for parsing and serializing ISO 8583 financial messages.

JSON-spec-driven architecture with support for Visa BASE I, Mastercard MIP, and generic ISO 8583 — no code changes needed for new networks.

License: Apache 2.0 Rust Crates.io


ISO8583 is a production-ready Rust library for handling ISO 8583 financial messages used in payment card transactions. Field definitions live entirely in JSON specification files — adding support for new card networks or custom fields requires zero code changes. The library ships with bundled specs for Generic ISO 8583, Visa BASE I, and Mastercard MIP.

Key Features

  • JSON-Spec-Driven: Field definitions, encodings, padding, and validation rules all live in JSON — extend or customize without touching Rust code
  • Multi-Network Support: Bundled specs for Generic ISO 8583:1987, Visa BASE I, and Mastercard MIP with correct encoding defaults
  • Multi-Encoding: ASCII, EBCDIC (CP037), BCD (packed Binary Coded Decimal), and Binary/Hex encodings
  • Composite Fields: Ordered subfields, TLV (EMV ICC data with BER-TLV), Bitmap-based composites, and Mastercard PDS (Private Data Subelements)
  • Non-Blocking Validation: Accumulates all validation errors instead of failing on the first — MTI, mandatory fields, format, length, pattern, and response code checks
  • Bitmap Support: Primary, secondary, and tertiary bitmaps with hex and binary encodings (up to 192 data elements)
  • Response Code Lookup: 87 standard DE 39 response codes with lazy-loaded lookup
  • Performance Optimized: O(1) field lookup via HashMap, zero-copy where possible, minimal allocations
  • 67 Tests: Comprehensive test coverage across all encodings, networks, and composite field types

Architecture

JSON Specification System

The library is driven by JSON spec files that define per-field encoding, prefix type, padding, and validation rules. Each spec has defaults that individual fields can override:

{
  "version": "2003",
  "defaults": {
    "encoding": "ebcdic",
    "prefix_encoding": "bcd",
    "bitmap_encoding": "binary",
    "mti_encoding": "bcd",
    "max_bitmaps": 3
  },
  "fields": {
    "2": {
      "name": "primary_account_number",
      "field_type": "string",
      "prefix": "llvar",
      "max_length": 19
    }
  }
}

Data Flow

Parsing: hex stringdecode MTIdecode bitmapunpack each fieldJSON output

Publishing: JSON inputencode MTIencode bitmappack each fieldhex string

Bundled Specifications

Spec Version Encoding Prefix Bitmap MTI Bitmaps Network
fields.json 1987 ASCII ASCII Hex ASCII 2 Generic ISO 8583
visa_base1.json 2003 EBCDIC BCD Binary BCD 3 Visa BASE I
mastercard_mip.json 2003 EBCDIC BCD Binary ASCII 2 Mastercard MIP

Installation

Add iso8583-codec-rs to your Cargo.toml:

[dependencies]
iso8583-codec-rs = "0.1"

Usage

Basic Parsing and Publishing

use iso8583_codec_rs::{load_spec, parse, publish};
use serde_json::json;

// Load a specification
let spec_json = include_str!("specifications/fields.json");
let spec = load_spec(spec_json).unwrap();

// Build a message as JSON
let message = json!({
    "mti": "0200",
    "fields": {
        "de002_primary_account_number": "4111111111111111",
        "de003_processing_code": "000000",
        "de004_amount_transaction": "000000001000",
        "de011_system_trace_audit_number": "123456",
        "de041_card_acceptor_terminal_id": "TERM0001"
    }
});

// Publish to hex-encoded ISO 8583
let hex_message = publish(&message, &spec).unwrap();

// Parse back to JSON
let parsed = parse(&hex_message, &spec).unwrap();
assert_eq!(parsed["mti"], "0200");
assert_eq!(parsed["fields"]["de002_primary_account_number"], "4111111111111111");

Network-Specific Messages

use iso8583_codec_rs::{load_spec, parse, publish};
use serde_json::json;

// Visa BASE I (EBCDIC + BCD prefix + Binary bitmap + BCD MTI)
let visa_spec = load_spec(include_str!("specifications/visa_base1.json")).unwrap();

let visa_auth = json!({
    "mti": "0100",
    "fields": {
        "de002_primary_account_number": "4111111111111111",
        "de003_processing_code": "000000",
        "de004_amount_transaction": "000000005000",
        "de014_expiration_date": "2512",
        "de022_pos_entry_mode": {
            "pan_entry_mode": "05",
            "pin_entry_capability": "1"
        },
        "de041_card_acceptor_terminal_id": "TERM0001",
        "de049_currency_code_transaction": "840"
    }
});

let hex = publish(&visa_auth, &visa_spec).unwrap();
let parsed = parse(&hex, &visa_spec).unwrap();

// Mastercard MIP (EBCDIC data + ASCII MTI + BCD prefix + Binary bitmap)
let mc_spec = load_spec(include_str!("specifications/mastercard_mip.json")).unwrap();

let mc_auth = json!({
    "mti": "0100",
    "fields": {
        "de002_primary_account_number": "5500000000000004",
        "de003_processing_code": "000000",
        "de004_amount_transaction": "000000002500",
        "de049_currency_code_transaction": "840"
    }
});

let hex = publish(&mc_auth, &mc_spec).unwrap();
let parsed = parse(&hex, &mc_spec).unwrap();

Composite Fields

The library handles four types of composite fields transparently:

use serde_json::json;

// Ordered: subfields concatenated in sequence (e.g., DE 22 POS Entry Mode)
"de022_pos_entry_mode": {
    "pan_entry_mode": "05",
    "pin_entry_capability": "1"
}

// TLV: Tag-Length-Value for EMV data (e.g., DE 55)
// Supports 1-byte, 2-byte, and 3-byte tags with BER-TLV length encoding
"de055_emv_data": {
    "9F26": "AABBCCDD11223344",
    "9F27": "80",
    "9F8101": "FF"
}

// Bitmap: internal bitmap indicating present subfields (e.g., Visa DE 62)
"de062_custom_payment_service": {
    "authorization_characteristics": "A",
    "transaction_id": "123456789012345"
}

// PDS: Mastercard Private Data Subelements (e.g., Mastercard DE 48)
"de048_additional_data_private": {
    "0023": "ABCDE",
    "0043": "12345"
}

Validation

Validation is non-blocking and accumulates all errors:

use iso8583_codec_rs::{load_spec, validate};
use serde_json::json;

let spec = load_spec(include_str!("specifications/fields.json")).unwrap();

let message = json!({
    "mti": "0100",
    "fields": {
        "de002_primary_account_number": "4111111111111111",
        "de003_processing_code": "000000",
        "de004_amount_transaction": "000000005000",
        "de007_transmission_date_time": "0725143052",
        "de011_system_trace_audit_number": "000001",
        "de014_expiration_date": "2512",
        "de022_pos_entry_mode": { "pan_entry_mode": "05", "pin_entry_capability": "1" },
        "de025_pos_condition_code": "00",
        "de041_card_acceptor_terminal_id": "TERM0001",
        "de042_card_acceptor_id_code": "MERCHANT000001 ",
        "de049_currency_code_transaction": "840"
    }
});

let result = validate(&message, &spec);
if result.is_valid() {
    println!("Message is valid");
} else {
    for error in &result.errors {
        println!("DE {}: [{}] {}", error.de, error.rule, error.message);
    }
}

Validation rules include:

  • MTI: Format (4 digits), version (0/1/2/9), class (1-8), function (0-5)
  • Mandatory fields: Per-MTI required field checks
  • Format: Numeric (digits only), binary (hex only), x+n (C/D prefix + digits)
  • Length: Min/max constraints, fixed-length enforcement
  • Pattern: MMDD dates, hhmmss times, YYMM expiry dates
  • Response codes: DE 39 validated against 87 known codes

Response Code Lookup

use iso8583_codec_rs::lookup_response_code;

assert_eq!(lookup_response_code("00"), Some("Approved"));
assert_eq!(lookup_response_code("05"), Some("Do Not Honor"));
assert_eq!(lookup_response_code("51"), Some("Insufficient Funds"));
assert_eq!(lookup_response_code("54"), Some("Expired Card"));

Module Overview

Module Purpose
lib.rs Public API: parse(), publish(), load_spec(), validate(), lookup_response_code()
iso8583.rs Parse/publish pipeline orchestration, composite field dispatch
codec.rs Field-level encode/decode (ASCII, EBCDIC, BCD, Binary), prefix handling, padding
bitmap.rs Primary/secondary/tertiary bitmap handling (hex and binary)
mti.rs Message Type Indicator encode/decode and classification
field_spec.rs LoadedSpec and FieldSpec structs, JSON spec loading
validation.rs Non-blocking validation with accumulated errors
error.rs Iso8583Error enum with structured error variants

Encoding Support

Encoding Data Prefix Bitmap MTI
ASCII String/Numeric fields LLVAR/LLLVAR/LLLLVAR length 16 hex chars per bitmap 4 ASCII chars
EBCDIC (CP037) String/Numeric fields LLVAR/LLLVAR/LLLLVAR length 4 EBCDIC chars
BCD Packed numeric (2 digits/byte) Packed length prefix 2 BCD bytes
Binary/Hex Raw binary data (e.g., PIN block) 8 bytes per bitmap

Testing

# Run all 67 tests
cargo test

# Run a single test
cargo test test_simple_round_trip

# Run tests with output visible
cargo test -- --nocapture

# Run network-specific tests
cargo test visa
cargo test mastercard

Contributing

Contributions are welcome! If you'd like to help, please feel free to fork the repository, make your changes, and submit a pull request. We ask that you ensure test coverage for new features and follow existing code patterns.

About Plasmatic

ISO8583 is developed by Plasmatic, an organization focused on building open-source tools for financial infrastructure. We believe in transparency, security, and performance.

Check out our other projects:

  • SwiftMTMessage: A SWIFT MT message parsing library.
  • MXMessage: An ISO 20022 (MX) message parsing library.
  • Reframe: A SWIFT MT to ISO 20022 (and back) transformation engine.

License

This library is licensed under the Apache License, Version 2.0. See the LICENSE file for details.