Skip to content

Latest commit

 

History

History
94 lines (75 loc) · 4.86 KB

File metadata and controls

94 lines (75 loc) · 4.86 KB

Protocol: buyer flow + envelope shape + facilitator checks

How a gated request actually plays out on the wire, and what the facilitator validates before it accepts a payment.

The buyer flow

        Server                              Buyer (browser / CLI)
          │                                       │
          │  ◄── GET /odata/v4/prices/Quotes ─────│
          │                                       │
          │ ──── 402 + accepts[0] (price, payTo) ►│
          │                                       │
          │                              ┌────────┴────────┐
          │                              │ build tx        │
          │                              │ sign via CIP-30 │
          │                              │ base64-encode   │
          │                              └────────┬────────┘
          │                                       │
          │  ◄── GET /odata/v4/prices/Quotes ─────│
          │        PAYMENT-SIGNATURE: <base64>    │
          │                                       │
   ┌──────┴──────┐                                │
   │ 6 checks    │                                │
   │ + submit    │                                │
   │ + poll      │                                │
   │ + onAccepted│                                │
   └──────┬──────┘                                │
          │                                       │
          │ ─────── 200 OK + data ───────────────►│
          │   X-PAYMENT-RESPONSE: <base64>        │

PAYMENT-SIGNATURE envelope

The header value is the base64 encoding of:

{
  "x402Version": 2,
  "scheme": "exact",
  "network": "cardano:preprod",
  "payload": {
    "transaction": "<base64-CBOR of signed tx>",
    "nonce":       "<txHash>#<outputIndex>"
  }
}

The nonce references a UTxO that must also appear as an input of the payment tx. Once the tx settles, that UTxO is consumed; replay defense is on-chain, no DB table needed.

Six mandatory facilitator checks

Every accepted payment passes all six (in order):

# Check Code on failure
1 Network matches requirements network_mismatch
2 At least one output to payTo wrong_recipient
3 Sum of payTo outputs for asset ≥ required insufficient_amount
4 Exact policy + asset-name match wrong_asset
5 Nonce UTxO referenced as tx input and unspent on chain nonce_not_referenced / replay_detected
6 Validity-range upper bound still in future expired_ttl

Plus a sanity guard: tx has at least one vkey witness → unsigned_transaction.

Pending settlements and the check-5 grace window

When submit succeeds but the tx is not indexed within the settle poll budget (~60s), the server answers 402 with pending: true + the tx hash. The buyer's contract is to re-send the same PAYMENT-SIGNATURE (never pay again); x402Fetch / x402Axios do this automatically.

A re-send that arrives after the tx got indexed would trip check 5: the nonce is now spent, by this very payment. To avoid rejecting a paid buyer with replay_detected forever, the facilitator applies a fallback: if the envelope's own tx is on chain and its server-observed blockTime is within pendingGraceMs (default 5 minutes, 0 disables), the request is accepted. Inside that window the same envelope is re-servable, an implicit mini-grant, deliberately equivalent in spirit to the X402Grants feature. Consequence for consumers: onAccepted (and the receipts INSERT, which dedupes on txHash) can fire more than once per payment inside the window, so audit callbacks must be idempotent on claim.txHash.

Rejected requests get 402 with an error field of the form "<base> (<code>): <reason>" so clients can parse the code without breaking wire format.

The full list of code values lives in facilitator-protocol.md.

What is x402, briefly

x402 is the dormant HTTP 402 Payment Required status code, revived. Servers respond 402 with a machine-readable body describing the price, asset, and recipient. Clients build, sign, and submit a payment, then retry the request with a PAYMENT-SIGNATURE header. Settlement happens on-chain.

The original x402 spec is Coinbase / EVM-flavoured. The Cardano-x402-v2 spec adapts it to Cardano's UTxO model: cardano:mainnet|preprod|preview (colon-separated) network strings, ${policyId}.${assetNameHex} asset format, and an exact / client | facilitator | server module split. This library is a from-scratch v2 implementation in TypeScript.