# x402 extension

> The LOPW x402 v2 extension on the wire: where the signed offer and receipt travel, the exact JSON shapes, the buyer echo, the handler headers, and why verify and settle are untouched.

*LOPW documentation · Protocol reference.* Canonical page: https://lopw.com/docs/protocol/x402.html · This Markdown twin: https://lopw.com/docs/protocol/x402.md · Index of all pages: https://lopw.com/llms.txt · Everything in one file: https://lopw.com/llms-full.txt

LOPW is an x402 v2 extension named `lopw`. It changes nothing in verify or settle: any conformant facilitator works, and a buyer that ignores the extension still gets a normal x402 purchase. Checked against x402 2.28.0.

| Item | Carried in |
| --- | --- |
| Warranty offer | The 402 `PaymentRequired.extensions.lopw`: `{ info: { version, network, registry, service_id, offer }, schema }`. The buyer echoes it in `PaymentPayload.extensions.lopw` |
| Payment | Standard x402 `exact` scheme, unchanged |
| Service receipt | The settlement response `PAYMENT-RESPONSE.extensions.lopw`: `{ receipt }`, plus `duplicate` only in the DUPLICATE_RECEIPT fault mode of reference services |

## The offer in the 402

```http
HTTP/1.1 402 Payment Required
// PaymentRequired (x402 v2 envelope), with:
"extensions": {
  "lopw": {
    "info": {
      "version": 1,
      "network": "testnet",                 // the binding's network name
      "registry": "C…",                     // Bond Registry contract address
      "service_id": "…64 hex…",
      "offer": "AAAAEQAAAAEAAAAK…"          // base64 ScVal XDR of SignedOffer { offer, signature }
    },
    "schema": { "type": "object" }
  }
}
```

`offer` is declared a dynamic info field (`dynamicInfoFields: ["offer"]`) so per-request nonces do not break x402’s echo validation. The middleware issues an offer only when the service is `Active` and only for the payment requirement whose network matches the binding’s CAIP-2 id; otherwise the 402 carries plain x402 terms.

## The buyer's echo

```http
// PaymentPayload (sent by the buyer with the payment), with:
"extensions": { "lopw": { "info": { …the same info object… }, "schema": { "type": "object" } } }
```

The buyer SDK copies `extensions.lopw` from the requirements into the payload (`enrichPaymentPayload`). The seller only issues receipts for offers it signed itself: it decodes the echoed offer and verifies the signature with its own key before building the receipt.

## The receipt in PAYMENT-RESPONSE

```http
HTTP/1.1 200 OK
PAYMENT-RESPONSE: <base64 settlement response>
// decoded settlement response, with:
"success": true, "transaction": "…64 hex…", "payer": "G…",
"extensions": {
  "lopw": {
    "receipt": "AAAAEQAAAAEAAAAO…",        // base64 ScVal XDR of SignedReceipt { receipt, signature }
    "duplicate": "…"                        // optional; reference services' fault injection only
  }
}
```

The receipt is attached through x402’s `enrichSettlementResponse` hook, after the facilitator reports a successful settlement, using the settlement `transaction` as `payment_reference` and the `payer` as `buyer`. It travels in the standard x402 envelope every client already decodes (`decodePaymentResponseHeader`).

## Handler inputs

| Response header | Read by the middleware as |
| --- | --- |
| `LOPW-Data-As-Of` | `receipt.data_as_of` (Unix seconds; absent means 0) |
| `LOPW-Oracle-Evidence` | `receipt.oracle`: JSON with `oracle_timestamp`, `oracle_price`, `reported_value` as decimal strings; the oracle contract and asset come from the terms |

The middleware reads the HTTP status, the raw request (method, original URL, raw body) and the response body through request-scoped context (`lopwRequestContext()`) and x402’s transport context. The two headers are ordinary response headers that a client may read as well.

## Why LOPW keeps its own receipt format

x402 ships a generic offer-receipt extension that signs JWS or EIP-712 payloads with resource URL, payer and transaction, but no terms, request or response hashes, or oracle evidence, and a Soroban contract cannot cheaply verify JWS. LOPW receipts must be verifiable on-chain, so LOPW keeps XDR plus Ed25519. The two extensions can be served side by side.

## SDK surface on the wire

| Side | Package | Exports |
| --- | --- | --- |
| Seller | `@lopw/seller-middleware` | `createLopwExtension(config)` (returns a `ResourceServerExtension` with `refresh()`), `lopwRequestContext()`, `LOPW_EXTENSION_KEY`, `DATA_AS_OF_HEADER`, `ORACLE_EVIDENCE_HEADER`, types `LopwSellerConfig`, `LopwOfferInfo`, `LopwReceiptInfo`, `ReceiptFaults` |
| Buyer | `@lopw/buyer-sdk` | `LopwBuyer`, `BuyerPolicy`, `WarrantyRecord`, `WarrantedResponse`, `ReceiptStore`, `FileReceiptStore`, `MemoryReceiptStore`, `WarrantyRefused`, `LOPW_EXTENSION_KEY` |
| Both | `@lopw/protocol` | Types, `Predicate`, XDR encoders, `offerHash`/`receiptHash`/`termsHash`/`requestHash`/`responseHash`, sign and verify, base64 codecs, `evaluatePredicate`/`breaches`, `StellarBinding`, `getNetwork`, `CLAIM_ERRORS` |
