Payment acceptance,
without the SaaS tax.
Card. Account-to-account. Stablecoin. One core. Every platform.
OpenPay is a reference payment-acceptance stack in Rust. One safety-critical core compiles to iOS, Android, browser, and Linux and runs the full payment lifecycle: tap-to-pay, vault, on-device fraud, multi-rail routing, double-entry ledger, reconciliation, settlement, refunds, disputes. Apache-2.0. You pay the rails. You do not pay us.
Anatomy
Correctness in the types.
Most payment bugs are not algorithmic. They are mis-stated assumptions about money, about lifecycle, about retries, about who is holding card data. OpenPay puts the invariants in the type system so the assumptions cannot be stated wrong in the first place — and the code that compiles is the code that runs end to end.
Typed money
Money is integer minor units paired with a Currency. There is no floating-point money type. Adding USD to EUR is a compile error, not a runtime check.
Typestate lifecycle
Payment<Created>, Payment<Authorized>, Payment<Captured>, Payment<Refunded> are distinct types. Refunding before capture does not compile. The lifecycle diagram is the type signature.
No PCI scope by default
PaymentMethod has no raw-PAN variant unless the pci-scope feature is enabled. Orchestrators, fraud scorers, and FFI bridges only ever see opaque vault tokens or EMV secure blobs.
Idempotency, not at-most-once-ish
Every intent carries an operator-supplied IdempotencyKey. The orchestrator records the outcome and replays return the recorded result — the rail is never touched a second time. No double charges across retries, restarts, or network blips.
One core, every platform
op-core, op-fraud, op-vault are no_std-friendly and compile to iOS, Android, WASM, and Linux from the same source. The HTTP server lives in its own crate so the device builds never pull tokio or axum.
Rails
Three rails. One orchestrator.
Card networks are no longer the only rail that matters. FedNow, PIX, SEPA Instant clear in seconds at near-zero cost with no chargeback window. A merchant that can route across rails — and across drivers within a rail — captures revenue that a card-only stack drops on the floor.
Card
op-rails-cardEMV · 3DS · interchange
CardAcquirer trait + a Hyperswitch driver out of the box. Stripe-compatible patterns: authorize / capture / refund / void, manual or automatic capture, 3DS / SCA resume on async challenges. Operators ship their own driver against the trait and certify it with op-driver-sdk.
Account-to-account
op-rails-a2aFedNow · PIX · SEPA Instant
Direct ISO 20022 over the rails — pacs.008 for instructions, pacs.002 for status, camt.053 for reconciliation — with no PSP between the merchant and the rail. A2A clears in seconds with no chargeback window, so the fraud decision runs before the rail, on the device, in milliseconds.
Stablecoin
op-rails-cryptoUSDC · Base · EVM
CryptoGateway trait + EvmJsonRpcGateway for USDC on Base, Ethereum, Polygon, Arbitrum. A hand-rolled ERC-20 calldata encoder. LocalKeyEvmSigner ships for hot-wallet operators; production wires Fireblocks / KMS / multisig behind the same EvmSigner trait.
The orchestrator
op-orchestrator- Routes
- Across rails — card to A2A to crypto — by policy, not by code change.
- Retries
- Soft failure on the primary driver falls through to the backup. A hard decline does not.
- Idempotency
- The same intent key, replayed, returns the cached outcome. The rail is never touched twice.
- Resume
- 3DS / SCA challenges land in a resumable state. The client completes; the orchestrator picks up.
Ledger
The ledger is the system of record.
Most stacks treat the ledger as a derived view of PSP webhooks. OpenPay treats it as a first-class append-only graph with bi-temporal history. The webhooks are inputs. The ledger is the answer to every audit question about what happened and when.
Intent
An operator-supplied IdempotencyKey + amount + method enters the orchestrator
Score
On-device fraud scorer runs first — pre-rail, in milliseconds
Route
The router picks a driver per the operator's policy
Settle
The rail returns; the orchestrator records the attempt and the terminal status
Post
Double-entry transaction enters the append-only ledger; entries are never updated
Reconcile
Bank statement (camt.053 or equivalent) diffs against the ledger; matches are deterministic
Append-only double-entry
Entries are never updated or deleted. Corrections are themselves entries. Each transaction lands balanced or it doesn't land at all — the type that represents an unbalanced state is unconstructable.
Bi-temporal time travel
Two clocks per entry: when the event happened, and when the ledger learned of it. “What was account A's balance on May 4th, as we knew it on May 5th?” is a single query, not a six-week archaeology project.
Reconciliation by design
op-reconciliation matches ledger entries against bank statements with deterministic UUID v5 keys. The diff is reproducible; the audit log is the diff history.
One graph, every store
op-graph implements every domain store — ledger, webhooks, reconciliation, refunds, disputes, settlement, subscriptions, idempotency — on a single Minigraf .graph file. Datalog queries over the lot. Bi-temporal history for free.
The Spec
Apache-2.0. Reference, not certified.
OpenPay is published as a reference: the architecture is complete and the code compiles, runs, and tests cleanly across every supported platform. No part of it has been certified by a card scheme, audited by a PCI QSA, or run in regulated production. Use it as a starting point, not as one.
Foundation
Typed money, the typestate payment lifecycle, ISO 20022 message helpers, the EMV TLV parser, and the token-only vault.
Rails & risk
The card / A2A / crypto acquirer traits, the reference drivers, the on-device fraud scorer, and the Stripe-shaped outbound-webhook delivery.
Orchestration
Routing across rails, idempotency, soft-failure fallback, 3DS / SCA resume, append-only double-entry ledger, reconciliation, and the bi-temporal graph.
Deployment
HTTP server, refunds, disputes, batch settlement (NACHA payouts), recurring billing with calendar-aware intervals, FX with banker's rounding, and the driver-SDK conformance harness.
FFI & CLI
Swift, JNI, and WASM bridges from one Rust core; PAN never crosses an FFI boundary. The operator CLI.
The reference implementation
26 Rust crates, Apache-2.0. One safety-critical core compiles to iOS, Android, browser, and Linux. cargo test --workspace is 1124 passing, 0 failing. cargo clippy --workspace --all-targets is zero warnings. Feature-gated paths add 28 EVM and 111 reqwest-transport tests on top.
The conformance harness
op-driver-sdk ships deterministic-mock acquirers and the conformance::run_card / run_a2a / run_crypto harnesses. Operators write their own drivers against the public traits; the harness catches contract violations before the driver sees real money.
The progress log
Phases 0 through 31, each with a NN-name-progress.md capturing design intent, alternatives considered, and the rationale the code can't express. If you want to know why the ledger is graph-backed or why the orchestrator owns idempotency, start there.
Services
The protocol is free. The operations are the offer.
OpenPay is Apache-2.0; the wire format and the right to fork are public. Transaction Science writes the reference implementation and runs the optional hosted services for parties who want the protocol without operating the protocol.
Hosted orchestrator
Run the OpenPay HTTP server — the multi-rail orchestrator, the ledger, the reconciliation engine — as a managed deployment. Operators who don't want to operate stay on the standard; the code that runs is the published code.
Driver authorship
Write a card or A2A or crypto driver against the public traits; we run the conformance harness, audit the integration, and sponsor it through the certification cycle for the underlying rail.
Persistence backends
Ship the LedgerStore / WebhookStore / ReconciliationStore backed by your persistence of choice — PostgreSQL, FoundationDB, Minigraf, Aurora — with regression coverage against the in-tree contract tests.
PCI engagement
When the pci-scope feature is enabled, the engagement to take a deployment through QSA assessment — SAQ-D path or ROC, with the type-level no-PAN-in-default invariants as evidence.
Custody & signing
Operate the stablecoin signer behind the EvmSigner trait: Fireblocks, AWS Nitro, GCP KMS, multisig with co-signers. The reference LocalKeyEvmSigner is for development.