# Pay a WebAZ order in USDC with x402 (agents)

**What this is.** A buyer's agent can pay a *Direct Pay* order on WebAZ in USDC on Base using the [x402 v2](https://www.x402.org) HTTP flow: the order's pay endpoint answers `402` with the exact requirement, the agent's wallet signs an EIP-3009 `TransferWithAuthorization`, the agent repeats the request with that signature, and WebAZ settles through a facilitator. Direct Pay is **not** escrow: the money goes to the seller's receiving address; WebAZ records and verifies it.

Everything below is live on `https://webaz.xyz`. Amounts are integer **6-decimal USDC units** (`1 USDC = 1000000`).

## 0. Prerequisites (once, by the human)

1. The account owner opens **Me → Agent autopay (x402)** on webaz.xyz and switches on *"Let my agent pay in USDC (x402)"* with a Passkey. They must set three limits: **per order · rolling 24 h · rolling 30 days** (defaults are prefilled; `POST /api/direct-pay/x402-consent` with `limits:{per_order_units,daily_units,monthly_units}`).
2. The agent holds the account's **api_key** (`Authorization: Bearer …`) — or, better, a **payment-only key** `ak_…` issued by the owner (see §5a). OAuth/grant tokens can only *request* a payment (see §5), never pay.
3. The agent has a wallet with USDC on **Base mainnet** (`eip155:8453`, asset `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`).

`GET /api/direct-pay/x402-consent` (Bearer) returns `active`, the three `limits`, `used_24h_units`, `used_30d_units`.

## 1. Quote → order

Quote first: `webaz_quote_order` (MCP) lists `payment_options` cheapest first; the x402 entry has `rail: "direct_p2p"` and the x402 receiving account's `account_id`. Create the order with that `direct_receive_account_id`. The order enters `direct_pay_window` with a payment deadline.

## 2. Find the pay endpoint

`GET /api/orders/<id>` (Bearer, buyer) → when the order is x402-payable it carries

```json
"payment": { "x402": { "resource_url": "https://webaz.xyz/api/orders/<id>/pay", "method": "POST", "scheme": "exact",
             "network": "eip155:8453", "asset": "0x8335…2913", "amount_units": "12340000", "consent": { "active": true } } }
```

`consent.active:false` comes with `settings_url` and a sentence to show the owner. No `payment.x402` = this order cannot be paid this way (wrong rail, window closed, or a settlement already in flight).

## 3. Get the requirement (402)

`POST <resource_url>` with the Bearer header and **no** `PAYMENT-SIGNATURE`:

- `402` + header `PAYMENT-REQUIRED: <base64(JSON)>` — the x402 v2 *PaymentRequired*: `x402Version: 2`, `accepts: [{ scheme: "exact", network, asset, amount, payTo, maxTimeoutSeconds, extra: { name, version } }]` (`extra` is the USDC EIP-712 domain: `USD Coin` / `2` on Base mainnet), plus `resource` and the payment-identifier extension.
- Everything in the requirement comes from the order's **frozen snapshot**; nothing in your request can change `payTo` or `amount`.

Refusals before a 402 are JSON with `error_code`: `X402_CONSENT_REQUIRED` (owner has not switched autopay on), disclosure not acknowledged, order not payable, or an **autopay limit** (§6).

## 4. Pay

Sign EIP-3009 `TransferWithAuthorization` for exactly `amount` to `payTo`, `validBefore` within `maxTimeoutSeconds`, a fresh 32-byte `nonce`. Build the x402 *PaymentPayload* (`x402Version: 2`, `accepted` = the requirement you accept, echoed field by field, `payload: { authorization, signature }`, the payment-identifier extension) and repeat:

`POST <resource_url>` with `PAYMENT-SIGNATURE: <base64(JSON)>`.

- `200` → `PAYMENT-RESPONSE: <base64(JSON)>` `{ success: true, transaction, network, payer }` and body `{ order_id, status: "accepted", tx_hash, network, payer }`. The order is paid; the seller is notified.
- `400 X402_BAD_PAYLOAD` / mismatch codes → your payload did not echo the requirement exactly; re-read the 402.
- `409` → the order is no longer payable or a settlement is already in flight — **do not sign again**; read §7.

Idempotent: repeating the same signed request returns the same result; a second *different* authorization for the same order is refused (one live-or-successful settlement per order).

## 5. OAuth / grant agents: request, don't pay

With the `payment:request` OAuth scope, `webaz_request_x402_payment` (or `POST /api/agent/orders/<id>/x402-payment-request`) files a bounded request; the **owner** opens the approval page, inspects the frozen order and exact transfer, and pays with their **own** wallet + Passkey. Poll `webaz_approval_requests`. Nothing the agent sends is a signature.

## 5a. Payment-only keys (`ak_…`) — give the agent a key that can only pay

Instead of the account `api_key`, the owner can issue a **payment-only key** from **Me → Agent autopay (x402) → Payment-only keys** (Passkey; the consent in §0 must be on). The key is shown once.

- Accepted by exactly three things: `POST /api/orders` (create a direct-pay order), `GET /api/orders/<id>` and the buyer order reads, and `POST /api/orders/<id>/pay`. Every other call answers `401` as if no credential was sent. MCP: use it as the Bearer on `/mcp` and call `webaz_pay_order`; account tools stay closed.
- Each key has its **own** per-order / 24 h / 30 d limits (each ≤ the account limits in §0). Over a key limit the pay endpoint answers the same `403 X402_*_CAP` with `owner_approval` as in §6. Usage is counted per key, so revoking one agent never changes another agent's budget.
- Revoke any time in the same place (immediate, no Passkey); switching the account consent off disables every key. An unknown or revoked key answers `401 PAY_KEY_INVALID` — stop and ask the owner for a new key.
- Management API (owner session only): `GET / POST / DELETE /api/direct-pay/x402-consent/keys[/<id>]`.

## 6. Autopay limits and over-limit handoff

WebAZ enforces the owner's three limits server-side (usage = what the *agent* paid; owner-approved payments do not count). Over a limit the pay endpoint answers

`403` `{ error_code: "X402_PER_ORDER_CAP" | "X402_DAILY_CAP" | "X402_MONTHLY_CAP", limit, limit_units, used_units, amount_units, owner_approval: { request_id, approval_url, expires_at } }`

and the payment is already waiting for the owner (notified once per order). Tell the user; **do not** split the purchase or retry smaller amounts. The request lives for the order's payment window (max 7 days); a stale one is reopened automatically on the next attempt.

## 7. Settlement state and recovery

- `GET /api/orders/<id>`: `status: "accepted"` = paid. While a settlement is `settling` (facilitator answered late or unknown) the order view says so — this is **not** a failure; the reconciler either confirms it on-chain or, once the authorization is provably unused *and* expired, releases the slot. Never pay again on a timeout.
- Nothing in these responses ever contains your private key or signature after the fact; signatures are not logged.

## 8. Related

- Remote MCP (tools, OAuth): `/docs/REMOTE-MCP.md` · OpenAPI: `/openapi.json` · one-page agent entry: `/llms.txt`
- Prices differ by payment method; the listed price is the card price, non-card methods (PayNow, x402 USDC, escrow) carry the platform's automatic discount. Quote before you pay.
