Public networks
Base mainnet (PayAI)
Real funds through a public facilitator that takes no credential.
Settles x402 v2 exact payments on Base through PayAI's public facilitator, with auth.type: none. PayAI's pricing guide says ordinary exact payments need no API key within the free credit allowance. Credits are counted per receiving wallet for its lifetime; shared hosts and IPs also draw from a shared pool, which may run out sooner. Beyond the allowance, PayAI requires credits and a key. The gateway still needs its x402 peers, but not @coinbase/x402.
node -e "fetch('https://facilitator.payai.network/supported').then(r=>r.json())
.then(d=>console.log(d.kinds.filter(k=>k.network==='eip155:8453')))"Confirm the output includes protocol v2 and the exact scheme.
version: 1
merchant:
id: base-mainnet-payai-example
name: Base Mainnet (PayAI) Example
publicBaseUrl: ${GATEWAY_PUBLIC_BASE_URL:-http://localhost:8080}
server:
port: ${GATEWAY_PORT:-8080}
host: 0.0.0.0
allowedOrigins: []
storage:
receipts:
driver: sqlite
path: ${RECEIPT_STORE_PATH:-./data/receipts.sqlite}
protocols:
http:
enabled: true
mcp:
enabled: true
mountPath: /mcp
resources:
premium_report:
name: Premium Report
description: One paid endpoint, settled in USDC on Base.
input:
type: object
properties: {}
additionalProperties: false
backend:
type: http
method: GET
url: ${MERCHANT_API_BASE_URL:-http://localhost:3000}/api/report
timeoutMs: 10000
pricing:
type: fixed
amount: "0.01"
currency: USDC
expose: [http, mcp]
payments: [x402]
payments:
x402:
enabled: true
network: eip155:8453
rpcUrl: ${X402_RPC_URL:-https://base.drpc.org}
asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
assetName: "USD Coin"
assetVersion: "2"
assetDecimals: 6
payTo: ${MERCHANT_WALLET}
maxTimeoutSeconds: 600
# required, and required to be `true`. Mainnet is never a default
allowMainnet: ${ALLOW_X402_MAINNET}
# A separate decision from allowMainnet: every payment authorization goes
# to PayAI without credentials, and PayAI applies its own limits
allowUnauthenticatedFacilitator: true
facilitator:
mode: remote
url: https://facilitator.payai.network
auth:
type: noneTwo acknowledgments, not one
allowMainnet: true # I meant to use real money
allowUnauthenticatedFacilitator: true # I accept THIS counterpartyNeither implies the other, and dropping either fails config loading:
FAIL CONFIG_INVALID: payments.x402: no credential is configured for facilitator https://facilitator.payai.network on mainnet. Set payments.x402.allowUnauthenticatedFacilitator: true to accept anonymous access and the facilitator's limits, or configure facilitator.auth.
details: {"path":"payments.x402.allowUnauthenticatedFacilitator"}doctor reports it as WARN, not a pass, for as long as it stays this way.
What you are exposed to
Not your money: an EIP-3009 authorization names its recipient, amount and chain, so the facilitator can broadcast exactly that transfer or nothing. At risk: privacy (it sees each authorization, with the payer, amount and timing) and availability — once the payTo wallet's allowance or the shared pool is exhausted, unauthenticated paid calls fail closed. For sustained use, choose a facilitator the gateway can authenticate to (bearer or cdp), or run your own.
Run it
ALLOW_X402_MAINNET=true MERCHANT_WALLET=0xYourWallet \
npm run agent-commerce -- validate --config examples/base-mainnet-payai/config.yaml
# to actually settle: each successful x402 run spends real USDC (default 0.01)
export ALLOW_X402_MAINNET=true
export X402_SMOKE_TARGETS=base
export X402_MAINNET_BUYER_PRIVATE_KEY=0x... # funded with USDC on Base
export X402_MAINNET_MERCHANT_ADDRESS=0x...
export X402_FACILITATOR_URL=https://facilitator.payai.network
npm run test:mainnetThe smoke test reads the USDC Transfer from the transaction receipt on Base to confirm settlement; see smoke tests.
Source: examples/base-mainnet-payai