Documentation
Everything pay402 does, on one page.
Install, pay, keep the receipt. You should not need to leave this page, let alone this domain, to buy something behind a 402.
Install
pip install "pay402[evm]" # Base pip install "pay402[solana]" # Solana pip install "pay402[all]" # both pip install pay402 # httpx only: quote and rehearse, no signing
Python 3.10+. The bare install depends on
httpx and nothing else, so agent sandboxes accept it;
the chain SDKs (eth-account, solders) are
extras because a buyer on one chain has no reason to carry the
other's library.
Python API
pay()
from pay402 import pay
out = pay(
url, # the checkout URL
private_key=key, # EVM 0x… or Solana base58
max_usd="5.00", # REQUIRED for live payments
network=None, # force a chain, e.g. "eip155:8453"
params=None, # buyer inputs; folded into what you sign
refund_to=None, # a wallet YOU control, on the receipt
test_payer=None, # rehearse a test checkout, no key, no funds
announce=True, # print the receipt URL to stderr on success
)
out.goods # what you bought; binary deliveries arrive as bytes
out.receipt # dict: url, tx_hash, network, amount_usd, …
out.quote # the price that was signed against
The loop inside: request the URL, read the x402 challenge, refuse
if the quoted total exceeds max_usd, sign, retry with
X-PAYMENT, return goods and receipt. Nothing is signed
before the cap check passes.
quote()
from pay402 import quote
q = quote(url) # price, fee, total, chain, payee. Signs nothing.
Errors
| Exception | Meaning |
|---|---|
| PriceTooHigh | The quoted total exceeded
max_usd. Nothing was signed. |
| Pay402Error | Everything else, with the server's own explanation attached: bad key, wrong network, seller-side failure. A seller-side delivery failure means you were not charged. |
CLI
pay402 <url> --quote # price only pay402 <url> --max-usd 5 # pay; key from $PAY402_KEY pay402 <url> --max-usd 5 --json # machine-readable result pay402 <url> --test-payer 0xYou # rehearse a test checkout
| Flag | What it does |
|---|---|
| --key | Buyer private key. Defaults to
$PAY402_KEY, which keeps it out of shell history and
out of the conversation with whatever agent runs the
command. |
| --max-usd | Spend ceiling. Required to pay; nothing is signed above it. |
| --network | Force a chain, e.g.
eip155:8453. |
| --param KEY=VALUE | Buyer input, repeatable. Folded into what you sign: one payment, one exact query. |
| --body | JSON request body for a POST/PUT/PATCH
origin; shorthand for --param body=…. |
| --quote | Print the price and exit. Signs nothing. |
| --test-payer | Pay a TEST-mode checkout as this address; moves no money. |
| --refund-to | A wallet you control, recorded on the
receipt. Defaults to $PAY402_REFUND_TO. |
| --json | Machine-readable output. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Paid; goods on stdout, receipt line printed. |
| 3 | Over your ceiling. Distinct on purpose: an agent retrying a too-expensive call should not be told the same thing as an agent whose key was wrong. |
| 1 | Everything else, explained on stderr. |
Receipts & refunds
Every successful purchase prints
pay402: receipt <url> (stderr), so the proof
survives in the transcript or log of whatever ran the purchase, even
when the caller drops the return value. The receipt page is permanent
and public by its unguessable id: amounts, the split, the on-chain
transaction, a downloadable copy, and a report-a-problem form.
Lost the receipt?
It walks back from the transaction hash: your exchange's withdrawal
history names the wallet you funded, a block explorer names the
payment, and GET /r/by-tx/<tx_hash> on the
settling host redirects to the receipt. Every link in that chain is
permanent.
Refunds
Settlement is non-custodial, so refunds are seller-issued, and the
paying wallet is usually a throwaway the buyer abandons.
refund_to records a wallet you control on the receipt
so a goodwill refund has somewhere real to land. Recorded, never
spent from.
Safety model
Four properties, in the order they protect you:
- A ceiling, or no signature.
max_usdis required for live payments; a payment without a ceiling raises instead of defaulting to permissive. - The transaction is checked before it is signed. On Solana the payee builds the transaction, so pay402 decodes it and refuses unless it does exactly what the 402 advertised: two transfers, quoted wallets, quoted amounts, your token account and no other, no third program.
- A failed delivery is never charged. Settlement runs after delivery, by order of operations rather than refund policy. Written down at /guarantees.
- The key never enters the conversation. The human sets
$PAY402_KEY; the agent runs the command and never sees, reads or forwards the key. Transcripts persist; secrets stay out of them, the same rule that keeps secrets out of commit messages.
Funding a wallet
The safe pattern is a throwaway wallet holding exactly one purchase's worth of USDC: mint, fund, buy, keep the receipt, then let the wallet go. No gas token is ever needed: the facilitator pays gas on Base, a relayer pays fees on Solana.
The buying guide mints one in your browser, shows the funding QR, and checks arrival on-chain with one click.
The other side: selling with checkout402
pay402 is the buyer's half of the x402 loop. The seller's half, a priced URL that answers 402, settles non-custodially and delivers the goods, is checkout402.
To see how pay402 works against it from the seller's seat, or to sell something yourself, start with the checkout402 docs.
Anything answering a spec-correct x402 challenge works too; checkout402 is where this client is proven on mainnet.