# x402 Policy Integration

x402 payments are policy-sensitive because the account may sign an authorization that a facilitator settles later. Policy enforcement therefore must happen before the signature leaves the account, and settlement must be reconciled afterward.

## Required Pre-Sign Checks

- Merchant is registered and active.
- Service is registered, active, and belongs to the merchant.
- Service metadata hash matches the fetched service catalog.
- Facilitator is registered and active.
- Token and network are accepted by the service.
- Quote hash is canonical.
- x402 payment payload hash matches the quote.
- Payment amount is within per-payment and daily policy.
- Payment hash has not already been recorded.

## Current Contract Support

`PolicyModule.setPaymentPolicy` configures per-account signed-payment budgets by:

- merchant address
- token
- facilitator
- max per payment
- max per day
- allowed flag

Use the zero address as the merchant address to authorize purchases from any merchant for the configured token and facilitator. If both an any-merchant policy and an exact merchant policy exist, the exact merchant policy wins. This lets an agent owner grant broad purchasing authority while revoking or tightening one merchant separately.

`PolicyModule.recordSignedPayment` records a signed authorization hash and rejects replayed payment hashes.

`CommerceRegistry.commitQuote` binds the quote to the `x402PayloadHash`. This gives agents a stable value to compare against the payment requirement returned by an x402 server before signing.

## Normalizer and Verifier

The hosted API now exposes:

```
POST /x402/normalize
```

The normalizer accepts a payment requirement object or a container with `accepts[]`, maps common x402 field names into a Cortex canonical envelope, canonicalizes JSON key order, and returns the hash agents should compare against the quote.

Canonical envelope:

```json
{
  "schema": "cortex.x402-payment-requirement.v1",
  "scheme": "exact",
  "network": "base-sepolia",
  "pay_to": "0x...",
  "asset": "0x...",
  "amount": "1000000",
  "resource": "https://merchant.example/api/report",
  "method": "POST",
  "facilitator_url": "https://facilitator.example",
  "nonce": "quote-001"
}
```

Agent flow:

1. Fetch the merchant quote response.
2. Extract the payment requirement returned by the x402 endpoint or facilitator.
3. Call `/x402/normalize` locally or through the hosted API.
4. Compare `x402_payload_hash` with the quote's `x402PayloadHash`.
5. Run policy checks for merchant, service, token, facilitator, amount, daily budget, and replay.
6. Sign only when the hash and policy both pass.

## Scheme-Specific Verification

```
POST /x402/verify
```

After an authorization is signed (or when a facilitator presents one), the verifier
recovers the EIP-712 signer and checks the authorized terms against the payment
requirement. It is stateless — no chain calls, no persistence — so it can run
locally in the SDK or through the hosted API. Two schemes are supported:

- **`eip3009`** — `TransferWithAuthorization` / `ReceiveWithAuthorization`, signed
  over the **token contract's** own EIP-712 domain (the USDC "exact" rail). The
  caller supplies the token domain (`name`, `version`, `chainId`, `verifyingContract`)
  because those live on-chain in the token. Checks: signature recovers to
  `authorization.from`, recipient equals `pay_to`, token equals `asset`, chain
  matches, transferred `value` equals the required amount (exact), and the
  `validAfter`/`validBefore` window contains `now`.
- **`permit2`** — Uniswap Permit2 `PermitTransferFrom`, signed over the canonical
  Permit2 domain (`0x000000000022D473030F116dDEE9F6B43aC78BA3`). Checks: signature
  recovers to `owner`, `permitted.token` equals `asset`, chain matches, `spender`
  matches the expected settler when supplied, `permitted.amount` covers the required
  amount (a ceiling, not an exact match), and the `deadline` has not passed.

The request takes either a full `payment_requirement_json` (normalized internally,
which also yields the hash for quote binding) or a lean `expected` object
(`chain_id`, `asset`, `pay_to`, `amount`, `spender`). Pass `quote.x402_payload_hash`
to additionally assert the requirement is the one bound to the quote. The response
is `{ scheme, valid, signer, checks[], expected, quote_hash_match }`, where each
check is `{ name, passed, detail }` so a failing authorization explains exactly
which constraint it violated.

## Remaining x402 Work

- Add facilitator settlement reconciliation so a receipt can prove which facilitator actually submitted payment.
- Add production allowlists for facilitator domains and settlement addresses.
