How It Works
The x402 payment protocol follows a four-phase flow: Challenge, Payment, Authorization, and Receipt.
Complete Flow Diagram
┌─────────────┐
│ Client │
└──────┬──────┘
│
│ 1. Initial Request (no auth)
│ GET /api/weather
↓
┌──────────────────────────────────────┐
│ Provider API + SDK Middleware │
└──────┬───────────────────────────────┘
│
│ 2. Forward to Gateway
↓
┌──────────────────────────────────────┐
│ Preimage Research Gateway │
│ - Check authorization │
│ - Generate macaroon │
│ - Create payment challenge │
└──────┬───────────────────────────────┘
│
│ 3. Return 402 Payment Required
│ + x402 challenge JSON
↓
┌─────────────┐
│ Client │
│ Parses: │
│ - Macaroon │
│ - Payment │
│ details │
└──────┬──────┘
│
│ 4. Sign USDC payments
│ (EIP-3009 transferWithAuthorization)
│
│ 5. Retry request with:
│ Authorization: x402 <macaroon>:<payload1>:<payload2>
↓
┌──────────────────────────────────────┐
│ Provider API + SDK Middleware │
└──────┬───────────────────────────────┘
│
│ 6. Forward authorization to Gateway
↓
┌──────────────────────────────────────┐
│ Preimage Gateway │
│ - Verify macaroon │
│ - Verify USDC payment signatures │
│ - Submit to blockchain (via facilitator)
│ - Generate JWS receipt │
└──────┬───────────────────────────────┘
│
│ 7. Return 200 OK + receipt
↓
┌──────────────────────────────────────┐
│ Provider API + SDK Middleware │
│ - Attach receipt to response │
│ - Call API handler │
└──────┬───────────────────────────────┘
│
│ 8. Return data + receipt
│ Preimage-Research-Receipt: <JWS>
↓
┌─────────────┐
│ Client │
│ Success! │
└─────────────┘Phase 1: Challenge
When a client makes a request to a protected endpoint without authorization:
Request
GET /api/weather?city=SF HTTP/1.1
Host: api.example.comResponse: 402 Payment Required
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "950000",
"resource": "https://api.example.com/api/weather",
"description": "Provider payment",
"payTo": "0xPROVIDER_WALLET",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
},
{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "50000",
"resource": "https://api.example.com/api/weather",
"description": "Platform fee (5% - Tier 1)",
"payTo": "0xPLATFORM_WALLET",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
],
"macaroon": "AgETd2FsbGV0LWFkZHJlc3MFIDBFOTRBRjREOTg3MzI3MDFBRDZBQTA5RTI3NTNEOEU4QjcwMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAhZjYXZlYXQ6ZW5kcG9pbnQtaWQFHGEwZjZhMmYyLTdlMDEtNDE3YS04ZTVhLWZhYjE2ZDkxNTg4YgACFmNhdmVhdDpwcm92aWRlci1pZAUgZWQ4OGUzNGUtYzI0ZC00NjI2LWJhYTMtYjc3YWE0MzI2NDk3AAITamF2ZWF0OmV4cGlyeQUKMTczMjYzMDQzMgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"providerId": "prov_abc123",
"endpointId": "ep_xyz789",
"requestId": "req_unique123"
}The macaroon is a time-limited authorization token. It contains the provider ID, endpoint ID, and expiry time, all cryptographically signed by the Gateway.
Challenge Components
accepts: Array of payment requirements (provider + platform)macaroon: Base64-encoded authorization token with caveatsproviderId: Your provider identifierendpointId: The specific endpoint being accessedrequestId: Unique request identifier for idempotency
Phase 2: Payment
The client signs two USDC payments using EIP-3009 (transferWithAuthorization):
Payment 1: Provider
// Sign transfer authorization for provider payment
const signature1 = await wallet.signTypedData({
domain: {
name: 'USD Coin',
version: '2',
chainId: 8453,
verifyingContract: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
},
types: {
TransferWithAuthorization: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'validAfter', type: 'uint256' },
{ name: 'validBefore', type: 'uint256' },
{ name: 'nonce', type: 'bytes32' }
]
},
message: {
from: '0xBUYER_WALLET',
to: '0xPROVIDER_WALLET',
value: 950000n, // 0.95 USDC (95% for $1 payment - Tier 1)
validAfter: 0n,
validBefore: Math.floor(Date.now() / 1000) + 3600,
nonce: randomBytes32()
}
})Payment 2: Platform Fee
// Sign transfer authorization for platform fee
const signature2 = await wallet.signTypedData({
// ... same structure
message: {
from: '0xBUYER_WALLET',
to: '0xPLATFORM_WALLET',
value: 50000n, // 0.05 USDC (5% for $1 payment - Tier 1)
// ...
}
})Dynamic Fee Split: The actual split varies by payment tier:
- $0.005-$0.009: 90/10 split (Ultra-Micro: 10% fee)
- $0.01-$0.99: 95/5 split (Micropayments: 5% fee)
- $1-$9.99: ~92-96% to provider (Standard: 3% + $0.05)
- $10+: ~97% to provider (Large: 2.5% + $0.05)
No funds leave your wallet yet! These are just cryptographic signatures. The actual USDC transfers happen on-chain only after the Gateway verifies and submits them.
Phase 3: Authorization
The client retries the request with the x402 Authorization header:
Request with Authorization
GET /api/weather?city=SF HTTP/1.1
Host: api.example.com
Authorization: x402 AgETd2FsbGV0LWFkZHJlc3MFIDBFOTRBRjREOTg3MzI3...:0xBUYER,0xPROVIDER,950000,0,1234567890,0xABCD...,0xSIG1...:0xBUYER,0xPLATFORM,50000,0,1234567890,0xEF01...,0xSIG2...Authorization Header Format
Authorization: x402 <macaroon>:<payload1>:<payload2>Where:
<macaroon>: Base64 macaroon from challenge<payload1>: Provider payment (comma-separated: from,to,value,validAfter,validBefore,nonce,signature)<payload2>: Platform payment (same format)
Gateway Verification
The Gateway:
- ✅ Verifies macaroon - Checks signature, expiry, provider ID, endpoint ID
- ✅ Verifies payment signatures - EIP-712 signature validation
- ✅ Checks payment amounts - Ensures payment >= required amount
- ✅ Submits to blockchain - Via x402 facilitator (non-custodial)
- ✅ Generates receipt - JWS-signed proof of payment
Phase 4: Receipt
If verification succeeds, the Gateway returns 200 OK with a signed receipt:
Response
HTTP/1.1 200 OK
Content-Type: application/json
Preimage-Receipt: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXltZW50SWQiOiJwYXlfYWJjMTIzIiwicHJvdmlkZXJJZCI6InByb3ZfeHl6Nzg5IiwiZW5kcG9pbnRJZCI6ImVwX3dlYXRoZXIiLCJ0b3RhbFVzZGMiOiIxMDAwIiwicHJvdmlkZXJVc2RjIjoiOTgwIiwicGxhdGZvcm1Vc2RjIjoiMjAiLCJ0aW1lc3RhbXAiOiIyMDI0LTAxLTE1VDEwOjMwOjAwWiJ9.Xq2Nm8hf9k3...
{
"city": "SF",
"temperature": 62,
"conditions": "Partly cloudy"
}Receipt Contents
Decode the JWT receipt to get:
{
"paymentId": "pay_abc123",
"providerId": "prov_xyz789",
"endpointId": "ep_weather",
"totalUsdc": "1000000",
"providerUsdc": "950000",
"platformUsdc": "50000",
"tier": "micropayment",
"effectiveRate": "0.05",
"timestamp": "2024-01-15T10:30:00Z",
"transactionHash": "0x789..."
}Store receipts for accounting! They're cryptographically signed proof of payment, perfect for reconciliation and auditing.
Mock Mode
For testing without real blockchain transactions, all SDKs support mock mode:
Provider SDK (Mock Enabled)
createX402Middleware({
gatewayUrl: 'http://localhost:8080',
providerId: 'prov_test',
mockPayments: true // Skip real Gateway
})Client Mock Request
// In mock mode, any request with this header succeeds
Authorization: x402 mock:mockMock mode:
- ✅ Returns fake challenges
- ✅ Accepts any
x402 mock:mockauthorization - ✅ Generates mock receipts
- ✅ No network calls to Gateway
- ✅ No blockchain transactions
Mock mode is for testing only! Never use it in production. It bypasses all payment verification.
Security
Macaroons
Macaroons are like cookies but with:
- Caveats - Embedded restrictions (expiry, endpoint, provider)
- Cryptographic signatures - Can't be tampered with
- Attenuation - Can add more restrictions, never remove them
EIP-3009
transferWithAuthorization enables:
- Gasless transfers - Receiver pays gas, not sender
- Expiry times - Signatures expire automatically
- Nonce-based replay protection - Each signature is unique
JWS Receipts
Receipts are signed with ES256 (ECDSA P-256):
- Verifiable - Anyone can verify with public key
- Immutable - Can't be altered without detection
- Non-repudiable - Gateway can't deny issuing them