Paying with x402
Three steps: ask, sign, retry. No account is created at any point, and nothing is held on your behalf between calls.
1. Ask, and get a price
Call the endpoint normally. If your request is malformed you get a
400 — you are never quoted a price for a request that could not have
succeeded. Otherwise you get a 402 carrying the challenge:
HTTP/1.1 402 Payment Required
content-type: application/json
www-authenticate: x402 realm="business-invoice-calculate", scheme="exact", network="eip155:196"
{
"status": "payment_required",
"x402Version": 2,
"service_id": "business-invoice-calculate",
"price_usd": "0.010",
"accepts": [
{
"scheme": "exact",
"network": "eip155:196",
"amount": "<atomic units of the asset>",
"maxAmountRequired": "<same value, both spellings are emitted>",
"asset": "0x… settlement token contract",
"assetSymbol": "USDG",
"decimals": 6,
"payTo": "0x… the provider's receiving address",
"resource": "https://api.dana-edu.pp.ua/v1/business/invoice-calculate",
"maxTimeoutSeconds": 300
}
]
}
Both amount and maxAmountRequired are present with the
same value. Two official OKX pages disagree about which spelling is current, so
both are emitted — a client reading either gets a correct challenge instead of a
failed payment.
2. Sign an authorisation
Sign an EIP-3009 transferWithAuthorization for exactly the amount,
asset and recipient named in the challenge. Base64-encode this payload and send it
in the X-PAYMENT header:
{
"x402Version": 2,
"accepted": {
"scheme": "exact",
"network": "eip155:196",
"amount": "<the amount from the challenge, unchanged>",
"asset": "<the asset from the challenge>",
"payTo": "<the payTo from the challenge>",
"maxTimeoutSeconds": 300
},
"payload": {
"signature": "0x… your EIP-3009 signature",
"authorization": {
"from": "0x… your wallet",
"to": "<the payTo from the challenge>",
"value": "<the amount from the challenge>",
"validAfter": "0",
"validBefore": "<unix seconds>",
"nonce": "0x… 32 random bytes, never reused"
}
}
} 3. Retry
curl -i -X POST "https://api.dana-edu.pp.ua/v1/business/invoice-calculate" \
-H "content-type: application/json" \
-H "X-PAYMENT: <base64 of the payload above>" \
-d '{ ... }'
On success you get 200 with the result and an
X-PAYMENT-RESPONSE header containing the settlement receipt.
What gets checked, and why
| If | You get | Why it is refused |
|---|---|---|
| The same authorisation is presented twice | PAYMENT_REPLAY_DETECTED | An authorisation is a bearer instrument until spent. If one could be replayed, every endpoint would be priced at "pay once, call forever". Sign a new one with a fresh nonce. |
| The amount differs from the challenge | PAYMENT_WRONG_AMOUNT | Refused in both directions. An amount that does not match means you and the provider disagree about the price, and guessing which is right is worse than saying so. |
| A different recipient | PAYMENT_WRONG_RECIPIENT | Compared case-insensitively, so EIP-55 checksum casing never causes a false rejection. |
| A different chain or token | PAYMENT_WRONG_NETWORK / PAYMENT_WRONG_ASSET | Atomic units are meaningless without the token they belong to. 3000 units of a 6-decimal stablecoin is $0.003; 3000 units of an 18-decimal token is dust. |
validBefore has passed | PAYMENT_EXPIRED | Sign a new authorisation. |
| An unsupported scheme | PAYMENT_UNSUPPORTED_SCHEME | The error lists the schemes that would have worked for that endpoint. |
Order of operations
Input is validated before payment is demanded, so a malformed request is never billed. Settlement happens after the work succeeds, so a request that errors is not charged for. Both are deliberate and both are covered by tests.
Batched settlement
Some sub-cent endpoints also accept the aggr_deferred scheme. A
per-call on-chain settlement costs more than a $0.003 call earns, so batching is the
only way those endpoints make sense — but it is advertised only where the broker
actually supports it, because offering a scheme that will then be rejected is worse
than not offering it. Each endpoint's page lists the schemes it accepts.