pay402 / docs

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

ExceptionMeaning
PriceTooHighThe quoted total exceeded max_usd. Nothing was signed.
Pay402ErrorEverything 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
FlagWhat it does
--keyBuyer 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-usdSpend ceiling. Required to pay; nothing is signed above it.
--networkForce a chain, e.g. eip155:8453.
--param KEY=VALUEBuyer input, repeatable. Folded into what you sign: one payment, one exact query.
--bodyJSON request body for a POST/PUT/PATCH origin; shorthand for --param body=….
--quotePrint the price and exit. Signs nothing.
--test-payerPay a TEST-mode checkout as this address; moves no money.
--refund-toA wallet you control, recorded on the receipt. Defaults to $PAY402_REFUND_TO.
--jsonMachine-readable output.

Exit codes

CodeMeaning
0Paid; goods on stdout, receipt line printed.
3Over 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.
1Everything 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:

  1. A ceiling, or no signature. max_usd is required for live payments; a payment without a ceiling raises instead of defaulting to permissive.
  2. 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.
  3. A failed delivery is never charged. Settlement runs after delivery, by order of operations rather than refund policy. Written down at /guarantees.
  4. 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.