Verify. Settle. Issue receipts.
The x402 payment protocol turns any HTTP endpoint into a paid resource. Users sign once. The facilitator settles on Base. Receipts enable repeat access without re-payment.
How x402 works
Three-call settlement
01
POST /verify
Validate the EIP-3009 authorization. Checks signature, amount match, nonce freshness, expiry, and gas price. Returns valid: true or a structured error code.
02
POST /settle
Execute the on-chain transfer. Facilitator calls transferWithAuthorization on USDC. Returns txHash, payer, and requestId. Gas is paid by the facilitator — not the user.
03
Issue receipt
Bind the txHash to a receipt with a TTL. The receipt ID is presented on repeat requests to skip re-settlement. Reduces cost for high-frequency access.
Receipt reuse
Pay once. Access many.
After settlement, issue a receipt with a TTL. Present the receipt ID on subsequent requests — the server verifies it without triggering a new settlement.
- TTL: Set per-receipt. Controls reuse window.
- Reuse count: Tracked. Visible in dashboard and on the receipt object.
- Revoke: Instant. Revoked receipts return RECEIPT_REVOKED immediately.
- Linked endpoint: Receipt is scoped to a specific resource URL. Cannot be used cross-endpoint.
Receipt verify call
GET /api/v1/receipts/rcpt_01HX.../verify
Authorization: Bearer $P402_API_KEY
# Response (valid):
{
"valid": true,
"reuseCount": 4,
"expiresAt": "2025-01-01T01:00:00Z",
"resource": "https://your-api.com/endpoint"
}
# Response (expired):
{
"valid": false,
"reason": "RECEIPT_EXPIRED",
"requestId": "req_..."
}
Error recovery
Common errors and fixes
AMOUNT_MISMATCH- value in authorization ≠ maxAmountRequired
- Ensure both fields use atomic USDC units. $1.00 = 1000000.
REPLAY_DETECTED- Nonce already used in a previous settlement
- Generate a fresh random bytes32 nonce and re-sign the authorization.
AUTHORIZATION_EXPIRED- validBefore is in the past
- Set validBefore to at least 30 seconds in the future at signing time.
GAS_PRICE_TOO_HIGH- Base network gas > configured limit (50 gwei default)
- Retry after a few seconds. Gas spikes on Base are typically short.
INVALID_SIGNATURE- EIP-712 signature does not match from address
- Verify the EIP-712 domain (chainId: 8453, USDC verifyingContract) and signing wallet.
All errors include a requestId field. Include it when contacting support.
SDK
Drop-in integration
The @p402/sdk handles verify, settle, retry, and receipt reuse automatically. For custom flows, use the REST API directly.
SDK — one line
import { p402Fetch } from '@p402/sdk';
// Drop-in fetch replacement.
// Handles 402 → sign → settle → retry automatically.
const res = await p402Fetch('https://your-api.com/endpoint', {
wallet: yourWallet,
maxAmount: 1_000_000n, // $1.00 USDC
});
// res is the successful endpoint response.
// Receipt is stored automatically for reuse.