Skip to main content

x402-accept-payments

Example merchant server — clone and run locally in minutes.
This runs an example merchant server that gates a route behind payment — the same pattern you’d use to charge an AI agent for calling an API endpoint or an MCP tool, not just a human-facing page.

Prerequisites

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

The defaults run against a stubbed settlement out of the box — no changes needed to run locally. To test with a real testnet settlement, set 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).
To receive real funds, set DEST_ADDRESS in your .env to your receiving wallet address.

3. Start the server

This runs a single process: the example merchant on 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 get 402 Payment Required with the payment options the merchant accepts (in a PAYMENT-REQUIRED header, mirrored in the JSON body for readability):
To drive a full 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 at src/merchant.ts:
  1. A request arrives at GET /paid.
  2. If no PAYMENT-SIGNATURE header is present → return 402 with the accepted payment options (including the payment-identifier extension as required).
  3. If a header is present → verify the credential, then settle it.
  4. On success → return 200 with the protected content and a PAYMENT-RESPONSE settlement receipt. Key delivery on that receipt’s payment_id.
  5. If settlement is still running → return pending (with PAYMENT-RESPONSE so the payer can read it) and let them retry the same purchase.
In stub mode, verify and settle are short-circuited in-process — no facilitator, gateway, or funds. In real mode the server calls the facilitator’s /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 setting USE_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.

Next steps