iso8583-codec-rs 0.1.0

ISO 8583 message format parser and serializer with JSON-driven field specifications
Documentation
<div align="center">
  <img src="https://avatars.githubusercontent.com/u/207296579?s=200&v=4" alt="Plasmatic Logo" width="120" height="120">

# 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](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
  [![Rust](https://img.shields.io/badge/rust-2021+-orange.svg)](https://www.rust-lang.org)
  [![Crates.io](https://img.shields.io/crates/v/iso8583-codec-rs.svg)](https://crates.io/crates/iso8583-codec-rs)

  <p>
    <a href="https://github.com/GoPlasmatic">Organization</a> &bull;
    <a href="https://docs.rs/iso8583-codec-rs">Documentation</a> &bull;
    <a href="https://github.com/GoPlasmatic/ISO8583/issues">Issues</a>
  </p>
</div>

-----

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:

```json
{
  "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 string` &rarr; `decode MTI` &rarr; `decode bitmap` &rarr; `unpack each field` &rarr; `JSON output`

**Publishing:** `JSON input` &rarr; `encode MTI` &rarr; `encode bitmap` &rarr; `pack each field` &rarr; `hex 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`:

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

## Usage

### Basic Parsing and Publishing

```rust
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

```rust
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:

```rust
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:

```rust
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

```rust
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

```bash
# 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](https://github.com/GoPlasmatic), an organization focused on building open-source tools for financial infrastructure. We believe in transparency, security, and performance.

Check out our other projects:

  - [SwiftMTMessage]https://github.com/GoPlasmatic/SwiftMTMessage: A SWIFT MT message parsing library.
  - [MXMessage]https://github.com/GoPlasmatic/MXMessage: An ISO 20022 (MX) message parsing library.
  - [Reframe]https://github.com/GoPlasmatic/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](LICENSE) file for details.

-----

<div align="center">
<p>Built with care by the <a href="https://github.com/GoPlasmatic">Plasmatic</a> team</p>
</div>