x402 / MPP Header Decoder
Decode and build HTTP 402 payment headers — x402 PAYMENT-REQUIRED, PAYMENT-SIGNATURE and PAYMENT-RESPONSE (v2), their v1 X-PAYMENT equivalents, and MPP's WWW-Authenticate: Payment challenge / Authorization: Payment credential. Base64 → annotated fields, with amounts, networks, and EIP-3009 authorizations explained.
Follows x402 specificationv2 (+ v1 legacy) · MPP draft-httpauth-payment-01·verified Oct 2026· double-checked Oct 2026— exact EVM (EIP-3009, Permit2, ERC-7710) and SVM payloads; batch-settlement channel payloads; MPP challenge, credential and receipt; EIP-712 signer recovery and per-chain USDC domains checked against the reference SDK and eth-account
The three x402 headers
The x402 protocol (see the x402 Flow Simulator for the full handshake) moves all of its payment data in three base64-encoded JSON headers. The server quotes a price in PAYMENT-REQUIRED on a 402 response; the client answers with a signed payment payload in PAYMENT-SIGNATURE (for the exact scheme on EVM, by default an EIP-3009 authorization; on Solana a partially signed transaction); and after settlement the server attaches the result — success flag, network and transaction hash — in PAYMENT-RESPONSE. Protocol v1 used X-PAYMENT / X-PAYMENT-RESPONSE headers and carried the payment requirements in the 402 response body — this decoder handles both generations and tells you which one it detected.
MPP: the other 402
The Machine Payments Protocol (Stripe + Tempo) uses the same status code but a different carrier: the standard HTTP authentication framework. The server's 402 carries WWW-Authenticate: Payment with id, realm, method (tempo, stripe, card, evm, solana, lightning…), intent (charge, session, subscription), expires, and two base64url JSON blobs — request for the payment terms and opaque for server state. The client pays and retries with Authorization: Payment (or Payment-Authorization when the challenge sets header) carrying a credential that echoes the whole challenge, and a successful response carries a Payment-Receipt. Paste any of these and the decoder switches to MPP mode. Like x402, the MPP amount is in base units (the smallest denomination). Spec: draft-httpauth-payment-01.
Reading the decoded fields
Amounts are atomic token units — "10000" means 0.01 USDC at 6 decimals; the decoder resolves the asset contract address against the Stablecoin Reference to convert automatically. Networks use CAIP-2 identifiers in v2 (eip155:8453 = Base) and plain strings in v1 (base). Inside a payment signature, the authorization block is the EIP-3009 TransferWithAuthorization message: validAfter/validBefore bound its lifetime and the 32-byte nonce prevents replay — the token contract burns it on settlement. The decoder rebuilds the EIP-712 digest from accepted.extra (the token’s domain name and version), the network’s chain id and the asset address, and recovers the signer: it must equal from. A wrong extra.name is the classic failure — Circle’s USDC signs as “USD Coin” on Base, Ethereum, Polygon, Arbitrum and Avalanche mainnets but as “USDC” on Base Sepolia.
Common questions
Why does my decoded amount look 10,000× too big?
Amounts are always atomic units, never decimals. USDC has 6 decimals, so divide by 10⁶. Never assume 18 — check the token contract.
Is the PAYMENT-SIGNATURE header sensitive?
An EIP-3009 authorization moves exactly one fixed value to a fixed recipient inside its validAfter–validBefore window, and its nonce dies on first use. Intercepting it does not let an attacker redirect or increase the payment — but treat real headers as confidential anyway, since they can be settled by anyone until they expire.
Which header does my middleware actually read?
The reference x402 middleware (Express, Hono, Next.js) reads PAYMENT-SIGNATURE and falls back to the v1 X-PAYMENT header. If you are debugging a 402 loop, decode both directions here to check the version fields match.
Field layouts follow the x402 v1/v2 specifications (x402 Foundation). All decoding runs in your browser — headers are never uploaded.