open-standards/openpay/docs/00-foundation.md
Transaction Science f03b77c1e5 Rust: the repository tracks the latest stable, and says so accurately
This repository had four different answers to "what Rust does this need":

  rust-toolchain.toml  1.98.0   (the pin CI actually installs)
  59 Cargo.toml files  1.97     (rust-version)
  1 Cargo.toml file    1.88     (rust-version)
  INGEST.md            1.95.0   ("CI enforces both")

The floor was enforced in exactly one place -- `wai-quantum-webgpu.yml` ran
`cargo +1.97.0 check` against one crate's one feature. So 59 manifests declared
a minimum nothing verified, and the top-level document told readers a version
that had not been the pin for some time, crediting CI with checking it.

The policy is to track the latest stable. That makes a floor below the pin
something this repository does not promise, so the unenforced check goes, and
the documents stop claiming a version and an enforcement that were not there:

- `wai-quantum-webgpu.yml`: the `MSRV 1.97.0` step, the second toolchain its
  install step existed to fetch, and `MSRV` in the job's name.
- `INGEST.md`: states the pin at the root, that CI installs exactly it, and
  that there is no floor beneath it.
- `openpay/README.md` and `openpay/docs/00-foundation.md`: both named 1.95
  as the toolchain, and the README credited CI with enforcing an MSRV.

A version written in a document is a claim like any other. Stating 1.95 while
the pin is 1.98.0, and "CI enforces both" while one crate was checked, is worse
than stating nothing -- it reads as verified.

**Not done here, deliberately.** The 59 `rust-version = "1.97"` keys and the one
`1.88` stay for now. Removing them is not the small change it looks like: most
member crates inherit via `rust-version.workspace = true`, which fails to parse
the moment the workspace key is gone, so it is a coordinated edit across
hundreds of manifests rather than sixty. The key also has value independent of
any promise, since it turns an old toolchain into a clear message instead of a
compile error. What it must not do is state a floor nobody checks, so the
options are to delete it everywhere or to move it with the pin -- a decision
with consumer-facing consequences, which is its own change.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-03 17:42:20 -05:00

235 lines
13 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.

# OpenPay — Foundation Document
**Status**: Draft v0.1
**Date**: 2026-05-17
**Toolchain**: the stable Rust pinned in the repository root's `rust-toolchain.toml`, edition 2024
**License**: Apache-2.0 (matches Hyperswitch, Fineract, ISO 20022 Rust crates)
---
## 1. Mission
A free, open-source, universal payment-acceptance stack that runs on any modern phone,
tablet, or x86/ARM box, integrates every major payment rail (cards, A2A/instant, wallets,
QR), and ships a single safety-critical Rust core compiled to every platform via cxx,
jni, and wasm-pack.
Not a startup. A reference implementation that any merchant, bank, fintech, or
government can deploy with no per-transaction tax to OpenPay itself.
---
## 2. The Tax Stack We Eliminate (Quantified)
For a merchant doing $1M/yr in card volume on the current US stack:
| Layer | Typical cost | Source |
|---|---|---|
| Interchange (issuer) | 1.5–3.0% | Aeropay 2026 cost analysis |
| Network assessment (Visa/MC) | ~0.13% | Aeropay 2026 cost analysis |
| Processor markup | 0.3–0.5% (often 2.9%+$0.30 blended) | Aeropay 2026 |
| POS SaaS (Toast/Square/Clover) | $69–$300/mo × 12 = $828–$3,600/yr | Vendor pricing pages |
| Hardware lease | $50–$150/mo per terminal | Vendor pricing pages |
| Chargebacks | $15–$100+ per dispute | Aeropay 2026 |
| **Total effective rate** | **3.0–5.0%** | |
| **On $1M volume** | **$30K–$50K/yr** | |
A2A / pay-by-bank rails (FedNow, RTP, PIX, UPI, SEPA Instant) charge cents per
transaction, not percentages. FedNow's 2026 fee schedule increased the per-transaction
limit from $1M to $10M; participation has grown to 1,500+ US financial institutions.
A merchant routing 50% of volume to A2A saves ~$25K/yr on a $1M business before any
SaaS savings.
---
## 3. Hard Constraints (Non-Negotiable Physics)
### 3.1 Tap to Pay on iPhone requires a PSP
Per Apple's developer documentation: "App developers who want to offer Tap to Pay on
iPhone to merchants will first need to integrate with a supported payment service
provider (PSP) who processes the payments read by Tap to Pay on iPhone and provides
the certified terminal configurations that are loaded on the merchant's device."
**Implication**: OpenPay cannot remove the PSP for in-person iPhone card acceptance.
It can (a) treat PSPs as pluggable, swappable drivers and (b) route to non-card rails
wherever the customer permits.
### 3.2 PCI DSS scope is unavoidable for card data
Any code path that touches raw PAN (Primary Account Number) puts the operator in PCI
scope. OpenPay's design must **never see the PAN** in the application layer — only
tokens issued by the PSP or by the device secure element (Apple Secure Enclave,
Android StrongBox, host card emulation).
### 3.3 ISO 20022 is the global messaging substrate
FedNow, PIX, SEPA Instant, India's NPCI (post-IMPS migration), and SWIFT all use
ISO 20022 (PACS for clearing/settlement, PAIN for initiation, CAMT for cash
management). The Rust crate family `open-payments-iso20022-*` v1.0.1 ships full
type-safe serde parsers for every message type. We adopt it directly; we don't
rebuild it.
### 3.4 We are not a money transmitter
OpenPay the project ships software, not regulated money movement. Anyone who deploys
OpenPay to clear funds becomes a money transmitter / payment institution /
authorized payment provider under their own jurisdiction's law and is responsible
for licensing, AML/KYC, and capital requirements. The codebase enforces this with a
clear separation between the `op-core` orchestration layer and `op-rails-*` adapter
crates that must be configured with the operator's own credentials.
---
## 4. Prior Art We Build On (Not Reinvent)
| Project | Role | License | Why we use it |
|---|---|---|---|
| **Hyperswitch** (Juspay) | Payment orchestration server | Apache 2.0 | Rust, PCI-cert, 175M tx/day claimed, 100+ PSP connectors |
| **open-payments-iso20022** | ISO 20022 + FedNow parsers | Apache/MIT | Type-safe pacs/pain/camt in Rust |
| **Apache Fineract** | Core banking / ledger | Apache 2.0 | Account management; we plug in via REST for ledger ops |
| **Mojaloop** | Interoperability switch | Apache 2.0 | Inter-DFSP transfers, Gates Foundation reference |
| **NPCI open source** | UPI reference stack | Apache 2.0 | Distributed payment processing platform |
| **Burn** (tracel-ai) | ML framework | Apache 2.0 / MIT | Cross-platform inference: native, WASM, no_std for embedded |
| **ort** (pykeio) | ONNX Runtime bindings | Apache 2.0 / MIT | Fast inference for production fraud models |
OpenPay's net new contribution is the **edge integration**: a single safety-critical
Rust core that runs on the merchant device (iPhone/Android/web/Linux kiosk), speaks
to Hyperswitch or any PSP for cards, speaks directly ISO 20022 to A2A rails, and
runs fraud inference on-device before any network hop.
---
## 5. Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ MERCHANT DEVICE (any platform) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────┐ │
│ │ iOS shell │ │ Android │ │ Web PWA │ │ Kiosk │ │
│ │ (Swift + │ │ shell │ │ (wasm-bindgen│ │ (Linux │ │
│ │ cxx FFI) │ │ (Kotlin + │ │ + JS/TS UI) │ │ + Rust │ │
│ │ │ │ jni) │ │ │ │ native)│ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └────┬────┘ │
│ └─────────────────┴──────┬──────────┴───────────────┘ │
│ │ │
│ ┌───────────────▼────────────────┐ │
│ │ op-core (Rust 1.95, no_std- │ │
│ │ compatible where possible) │ │
│ │ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ Payment state machine │ │ │
│ │ │ (typestate pattern) │ │ │
│ │ └──────────────────────────┘ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ op-emv: EMV TLV decode │ │ │
│ │ │ op-iso20022: ISO parsers │ │ │
│ │ │ op-vault: tokens only, │ │ │
│ │ │ never raw PAN │ │ │
│ │ │ op-fraud: on-device │ │ │
│ │ │ inference (Burn/ort) │ │ │
│ │ └──────────────────────────┘ │ │
│ └────────┬───────────────────────┘ │
└───────────────────────────┼─────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌───────▼────────┐ ┌───────▼────────┐ ┌───────▼────────┐
│ op-rails-card │ │ op-rails-a2a │ │ op-rails-wallet│
│ │ │ │ │ │
│ adapter trait: │ │ adapter trait: │ │ Apple Pay, │
│ → Hyperswitch │ │ → FedNow │ │ Google Pay, │
│ → Stripe │ │ → RTP (TCH) │ │ host card │
│ → Adyen │ │ → PIX/SPI │ │ emulation │
│ → Finix/Moov │ │ → UPI/NPCI │ │ │
│ (PSP swappable)│ │ → SEPA Inst. │ │ │
│ │ │ (rail-direct) │ │ │
└────────────────┘ └────────────────┘ └────────────────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌───────────▼────────────┐
│ op-ledger │
│ (optional; Fineract │
│ adapter or sled │
│ embedded DB for │
│ edge-only deploys) │
└────────────────────────┘
```
---
## 6. Crate Layout (Cargo Workspace)
```
openpay/
├── Cargo.toml # workspace root
├── crates/
│ ├── op-core/ # state machines, domain types, traits
│ ├── op-iso20022/ # re-export + helpers over open-payments-iso20022
│ ├── op-emv/ # EMV TLV parsing, BER-TLV codec
│ ├── op-vault/ # token-only secure storage abstraction
│ ├── op-fraud/ # on-device inference (Burn primary, ort backend)
│ ├── op-rails-card/ # PSP adapter trait + Hyperswitch driver
│ ├── op-rails-a2a/ # FedNow, PIX, UPI, SEPA Instant drivers
│ ├── op-rails-wallet/ # Apple Pay / Google Pay token handling
│ ├── op-ffi-swift/ # cxx bridge for iOS / macOS
│ ├── op-ffi-jni/ # jni bridge for Android / JVM
│ └── op-wasm/ # wasm-bindgen bridge for web / PWA
├── examples/
│ ├── kiosk-linux/ # standalone binary
│ └── headless-server/ # for testing
└── tests/
├── conformance/ # ISO 20022 message round-trips
├── emv/ # EMV test vectors
└── property/ # proptest invariants on state machine
```
---
## 7. Core Principles (Enforced by the Type System)
1. **No raw PAN in application memory.** The `PaymentMethod` enum carries
`Token(VaultRef)` or `EmvTag(SecureBlob)` variants only. Constructing a
`PaymentMethod::RawPan` requires the `pci-scope` cargo feature and is gated
behind `#[cfg(feature = "pci-scope")]`.
2. **Typestate payment machine.** `Payment<S>` where `S: PaymentState`.
Transitions are functions, not method calls on a mutable struct.
Illegal transitions don't compile.
3. **Money is never f64.** All amounts are `op_core::Money { minor_units: i64, currency: Iso4217 }`.
4. **Errors are sealed enums, never `Box<dyn Error>`.** Each crate exposes
exactly one `Error` type with `thiserror`. Callers exhaustive-match.
5. **No `unsafe` outside `op-ffi-*` and `op-emv` (raw byte parsing).**
Enforced by `#![forbid(unsafe_code)]` at every other crate root.
6. **Every public function is `#[must_use]` when it returns a non-`()` value
that represents a state transition or side-effect.**
7. **Cross-platform via Rust 1.95's `cfg_select!`** for clean target-specific
bodies instead of nested `cfg_if!`.
---
## 8. What Comes Next (Sequenced)
Each phase produces verifiable artifacts; we do not move on until the prior
phase's tests pass.
- **Phase 1** (this commit): foundation doc, workspace skeleton, `op-core`
domain types (`Money`, `Currency`, `PaymentMethod`, typestate machine
scaffolding).
- **Phase 2**: `op-iso20022` wrapper + conformance tests against PACS.008 /
PAIN.001 / CAMT.054 test vectors. <!-- allow-vanity: CAMT.054 is the ISO 20022 message-name, not a count -->
- **Phase 3**: `op-emv` BER-TLV codec with EMVCo test vectors.
- **Phase 4**: `op-rails-card` trait + Hyperswitch driver, mocked against
Hyperswitch's sandbox.
- **Phase 5**: `op-rails-a2a` FedNow driver against FRB test harness.
- **Phase 6**: `op-fraud` Burn model with synthetic transaction stream.
- **Phase 7**: FFI bridges (`op-ffi-swift`, `op-ffi-jni`, `op-wasm`).
- **Phase 8**: example kiosk + headless server + end-to-end test harness.
```