Rail adapters
Stipendex does not execute settlement itself — existing payment systems do, through
adapters. The protocol fixes the adapter interface and the evidence format in
Proof; everything else (licenses, KYC, currency conversion, anti-fraud) is the
rail's domain.
Adapter interface (in the wallet)
RailAdapter {
scheme: string // "account" | "stripe-mpp" | "stripe-connect" | "x402" | …
pay(quote, railParams) → evidence // service: immediate capture
hold(quote, railParams) → evidence // checkout: pre-auth of estimate+tolerance
capture(evidence, amountFinal) → evidence' // checkout: charge ≤ hold
void(evidence) → void // release the hold without charging
refund(evidence, amount) → evidence' // extension
}
Receiver-side, symmetrically: verify(proof) → ok | invalid (locally or by
querying the facilitator/chain).
account — facilitator ledger (the out-of-the-box default)
A prepaid ledger at a hosted operator. Goal: zero entry barrier — the developer touches neither cards, nor crypto, nor regulation.
- The principal tops up the account the usual way (card/invoice); the wallet gets a scoped token for charges within the mandate.
pay= a ledger entry signed by the facilitator;evidence= the facilitator's JWS.- Receiver verification = one facilitator request (or offline verification of the facilitator's signature if the receiver trusts it).
- Tab sessions settle inside the ledger for free — micropayments carry no network fees at all. Mechanically a tab is a payment channel: opening holds the cap (one call), each payment is an offline wallet-signed increment the receiver verifies locally, and settlement is a channel close — the receiver claims with the LAST increment proof wrapped in a RECEIVER-SIGNED settlement (the receiver key is pinned at open — without this, the payer, who holds every increment, could front-run the close with an early low claim), gets its running total, and the rest of the hold refunds to the account. Unclaimed holds refund in full after the tab TTL (settle before expiry).
- Funds are held by a licensed operator; the wallet and SDKs never touch funds (the money-transmission boundary).
The facilitator additionally provides: the HITL pending queue, velocity anti-fraud, mandate revocation, receiver payouts (scheduled/threshold).
stripe-mpp — fiat machine payments
A bridge into the Stripe Machine Payments Protocol: session-aggregated billing on
fiat rails. The natural choice for receivers already living in Stripe. evidence =
the MPP session/charge identifier; Stipendex tab sessions map onto MPP sessions almost
one-to-one.
stripe-connect — the checkout profile
The goods-purchase rail (the 4agents order-flow v2.1 model, decision D1):
hold= a PaymentIntent withcapture_method=manualforestimate + tolerance;capture(amountFinal)= partial capture of the confirmed amount (≤ hold);- receiver/platform split = direct charges, or destination charges with
on_behalf_of, + application fee — the platform fee is withheld atomically and the seller stays merchant of record. Plain destination charges withouton_behalf_ofmake the platform the merchant of record — a misconfiguration under this profile; void= intent cancellation without a charge;- the platform MUST stay out of possession/control of funds in every flow — including refunds, disputes, and manual-capture holds; funds sit with the licensed PSP. This is what keeps the platform outside money-transmitter licensing.
Taxes, reserves, payouts, MoR are the orchestrating platform's configuration on top of Connect, outside Stipendex (see checkout-profile.md).
x402 — crypto per-call
For full autonomy and receivers without banking:
pay= a stablecoin (USDC) transfer per thequote.rails[]parameters (network,asset,to);evidence= tx hash + signed payload.- Verification — by an x402 facilitator or by the receiver itself on-chain.
hold/captureare absent in base x402 → the rail is unsuitable for the checkout profile (onlyamount,capturemode).
Compatibility is deliberate: a Stipendex Quote carries the same scheme fields as x402
paymentRequirements, so a receiver with a working x402 stack connects by renaming
fields, not rewriting.
Rail selection by the wallet
- Intersect
quote.rails[]with configured adapters. - Priority from wallet configuration (default:
account→stripe-*→x402). - Empty →
denied(no_common_rail)— the agent can report to the receiver/principal.
Requirements for any rail (normative)
- R1.
evidenceMUST be verifiable by a third party (signature/chain/API) — the wallet's "word of honor" is not evidence. - R2. Idempotency: repeating
pay/capturewith the same inputs creates no second money movement. - R3. capture ≤ hold, always; a rail without partial capture does not declare checkout-profile support.
- R4. Rail errors return to the wallet machine-readable; the wallet never blindly retries money operations (retries — idempotent operations only).