x402-accept-payments
Example merchant server — clone and run locally in minutes.
Prerequisites
Node.js 20+
Node.js 20+
npm is included with Node.js.Verify:
Prerequisites for real settlement
The stub needs no funds. When you settle for real, the merchant is a pure recipient — it holds no keys and moves no funds, so it only needs a valid receive address on the destination chain — see Prerequisites.1. Clone and install
2. Configure
USE_STUB_FACILITATOR=false and point FACILITATOR_URL at the gateway’s facilitator prefix (https://payment-gw.production-testnet.atum.xyz/x402/v1) — a base URL including /x402/v1, since the example appends /verify and /settle to it. The corridor’s contract addresses are then resolved automatically from the gateway’s /v1/defaults, so you never set them in .env (see Choose an environment).
3. Start the server
localhost:4020, with an in-process stub standing in for Atum’s x402 facilitator — so the full 402 → pay → 200 flow runs locally with no separate facilitator service, no gateway, and no funds.
You should see:
GET /paid is a sample route in the example app (not an Atum API) standing in for whatever you’re gating — an API endpoint, an MCP tool, a dataset. Once it’s running, the merchant is ready to receive requests from a human’s browser, a script, or an AI agent’s tool call.
4. Test the flow
Request the gated route without paying, and you get402 Payment Required with the payment options the merchant accepts (in a PAYMENT-REQUIRED header, mirrored in the JSON body for readability):
402 → pay → 200, run the x402-make-payments client against this server — a plain curl can’t produce the signed credential a payment needs. The exact challenge shape, headers, and amounts are in src/merchant.ts.
What’s happening
Looking atsrc/merchant.ts:
- A request arrives at
GET /paid. - If no
PAYMENT-SIGNATUREheader is present → return402with the accepted payment options (including thepayment-identifierextension as required). - If a header is present → verify the credential, then settle it.
- On success → return
200with the protected content and aPAYMENT-RESPONSEsettlement receipt. Key delivery on that receipt’spayment_id. - If settlement is still running → return pending (with
PAYMENT-RESPONSEso the payer can read it) and let them retry the same purchase.
/verify and /settle instead. Either way it holds no funds and no keys, and the same code path serves a human, a script, or an agent.
Going to testnet
Settling for real means settingUSE_STUB_FACILITATOR=false and your DEST_ADDRESS, then pointing FACILITATOR_URL at https://payment-gw.production-testnet.atum.xyz/x402/v1 — the corridor’s contract addresses are fetched from the gateway’s /v1/defaults automatically, so you don’t paste them by hand. See the example README for .env values and on-chain verification — its Going to testnet / mainnet section walks through that app. New to Atum’s environments? See Choose an environment for which one to target and what to expect from production-testnet. Run through the pre-flight checklist before your first real payment.