x402sandbox by realandworks.comStoreSalesDocs

Networks

One gateway, two modes. The flip is a setting, not a migration — products, sales history, and stats survive it.

Testnet (default)Mainnet
NetworkBase Sepolia — eip155:84532Base — eip155:8453
Moneytest USDC (free from faucets)real USDC
Facilitatorx402.org — free, no signupCoinbase CDP — free tier 1,000 settlements/mo, then $0.001
CredentialsnoneCDP_API_KEY_ID / CDP_API_KEY_SECRET

Flipping to mainnet

  1. Create a CDP account + Secret API Key at portal.cdp.coinbase.com (leave all Coinbase App/Trade permission toggles OFF — the key only authenticates to the facilitator and can never move funds; Ed25519 signature is fine; no client API key needed)
  2. In Settings: paste the keys, fill the mainnet receive address slot with a self-custody wallet you control, select Mainnet, save
  3. Click Restart server now in the amber banner (or restart however you run it — automatic under systemd)

The testnet receive address lives in its own slot and survives the flip — the two channels never mix.

Refusals that protect you

  • Mainnet without CDP keys → refuses to start, names the missing variables
  • Mainnet with a generated dev wallet as payTo → refused; a throwaway testnet key must never receive real funds

Treat your first mainnet sale as the go-live test: buy from yourself for $0.01 and check the settlement on basescan.org before publicizing the URL.

Going public without a static IP

Bind stays 127.0.0.1; expose via a Cloudflare Tunnel ingress hostname (outbound-only — works behind CGNAT/dynamic IP, zero open ports). Block /admin at the tunnel itself with a path rule ahead of the catch-all:

ingress:
  - hostname: store.example.com
    path: ^/admin.*
    service: http_status:404
  - hostname: store.example.com
    service: http://127.0.0.1:8402
  - service: http_status:404

The gateway already sends cache-control: no-store on every product path, so an edge cache can never serve a stale 402 or leak paid content. Run gateway and tunnel as services (systemd Restart=always); under systemd the settings restart button hands off to the supervisor automatically.