open-standards/openpay/docs/02-iso20022-progress.md
Transaction Science 70ecd6caa0 Initial import — three open standards under one shelf
Transaction Science open standards monorepo. Each subdirectory is a
self-contained reference protocol with its own README and licence.

- openpay/    Apache-2.0  payment-acceptance stack (Rust, 26 crates)
- smart-byte/ CC-BY-4.0   content-addressed value carrier (spec)
- eoc/        CC-BY-4.0   energy-optimised compute (spec)

The wire format and the right to fork are public. The optional hosted
services are the offer; the protocols are not.
2026-05-23 07:58:09 -04:00

93 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 2 — `op-iso20022` Complete
**Status**: Draft v0.2
**Date**: 2026-05-17
## What shipped
`op-iso20022` is a typed, validated facade over the upstream `open-payments-iso20022`
v1.0.10 and `open-payments-fednow` v1.0.10 crates. It does three things the upstream
doesn't:
1. **Idiomatic constructors** via `CreditTransferBuilder<P: Profile>` — callers
describe a payment in OpenPay terms; the builder produces a profile-conformant
message.
2. **Per-rail profiles** — `FedNow`, `Rtp`, `SepaInstant`, `Pix` each pin the right
ISO 20022 version, accepted status codes, and identifier formats.
3. **Round-trip codec** with `quick-xml` and a conformance test suite that
reads real-shaped sample XML and verifies our schema model.
## Modules
| Module | Responsibility |
|---|---|
| `error` | Sealed `Error` enum, exhaustive match at every callsite. |
| `message` | `MessageKind` (pacs / pain / camt / admi families) + version-agnostic `Message` wrapper. |
| `status` | `TransactionStatus` (ACTC/ACSC/RJCT/PDNG/RCVD/ACCP) + `StatusReason` (AC01/AC03/AC04/AC06/AG01/AM04/AM05/FRAD/...) with unknown-code preservation via `Other(String)`. |
| `bah` | Business Application Header (head.001.001.02) with strict format validation. |
| `codec` | `from_xml` / `to_xml` over quick-xml + `round_trip_canonical` for conformance tests. |
| `profile` | Per-rail rules: FedNow, RTP, SEPA Instant, PIX. |
| `builder` | High-level `CreditTransferBuilder<P>` bridging op-core domain types to ISO 20022. |
## Verified ground truth
Every fact in this crate that comes from outside our codebase was cross-referenced
against a primary source before being encoded:
| Claim | Source |
|---|---|
| FedNow uses `pacs.008.001.08` | FedNow Service Operating Procedures v3.2, June 2025, §15 |
| FedNow status codes are ACTC/ACSC/RJCT/PDNG | Same, §14 + Post-Implementation FAQ |
| FedNow BAH required on every value message | Same, §13 |
| RTP allows up to 8 agents in pacs.008 | Payments Canada RTR ISO 20022 Spec v1.4, May 2025, §3.1 |
| SEPA Instant uses BIC identifiers | EPC SCT Inst Rulebook 2024 |
| PIX uses 8-digit ISPB clearing-system codes | Banco Central do Brasil PIX manuals |
| ABA routing checksum is weighted (3,7,1) × 3 mod 10 | Federal Reserve ABA spec |
| UETR is lowercase UUID v4 | SWIFT GPI specification |
| Unstructured remittance capped at 140 chars | EPC SCT Inst Rulebook + FedNow ISO 20022 Quick Reference |
## Test coverage
| Module | Tests |
|---|---|
| `message` | 4 |
| `status` | 6 |
| `bah` | 6 (including 5 real-world ABA numbers cross-verified by Python script) |
| `profile` | 13 |
| `builder` | 10 (UETR format cases independently verified) |
| `conformance.rs` (integration) | 4 |
| **Total Phase 2** | **43** |
| **Cumulative (Phases 1 + 2)** | **62** |
## Conformance vectors
Three real-shaped sample XML messages live in `crates/op-iso20022/vectors/`:
- `fednow_pacs008_v08_minimal.xml` — customer credit transfer, USD $100,
Chase (021000021) → BofA (026009593), with valid UETR.
- `fednow_pacs002_v10_acsc.xml` — ACSC settlement status response.
- `fednow_pacs002_v10_rjct.xml` — RJCT with AC04 (closed account) reason.
Every field name, status code, and routing number in these samples was checked
against the FedNow Operating Procedures and the Federal Reserve's ABA registry
(by checksum, since the routing numbers are real). Account numbers, message IDs,
and UETRs are synthetic.
## What is NOT yet implemented (deferred to later phases)
- The full mapping from `BuiltCreditTransfer<P>` to the upstream `Document`
enum's variant for each pacs.008 version. We have the validated intermediate
form; binding it to the upstream serde types is mechanical and will be done
once we run a real `cargo check` against the v1.0.10 crate (the field names
in v08 of pacs.008 are stable but I'd rather verify than guess).
- The full ISO 20022 ExternalStatusReason1Code list (~200 codes). We have the
~14 most common; unknown codes round-trip via `StatusReason::Other(String)`.
- `pacs.009` (FI credit transfer / liquidity), `pain.013` (RFP), `camt.056`
(return request) builders. The infrastructure is in place; each adds 50–100
lines of profile-validated builder code.
## Next: Phase 3 — `op-emv`
EMV BER-TLV codec for contact, contactless, and Tap-to-Pay payload parsing.
This is what `op-rails-card` and `op-ffi-swift` will consume to read the
`ProximityReader` payload from iOS and the equivalent NFC payload from Android.