Public networks
Base mainnet (PayAI)
The cheapest mainnet proving run: an unauthenticated public facilitator.
The cheapest way to prove the mainnet path end to end. PayAI's public facilitator serves x402 v2 exact on eip155:8453 and takes no credential — no account, no SDK, no @coinbase/x402.
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
# "I meant to use real money."
allowMainnet: ${ALLOW_X402_MAINNET}
# "I accept THIS counterparty, with no account and no terms."
allowUnauthenticatedFacilitator: true
facilitator:
mode: remote
url: https://facilitator.payai.network
auth:
type: noneTwo acknowledgements, 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: facilitator https://facilitator.payai.network
takes no credential, and this is a mainnet deployment. It will see every
payment authorisation you handle, with no account, terms or support behind
it. Set payments.x402.allowUnauthenticatedFacilitator: true to accept that,
or configure facilitator.auth.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 authorisation names its recipient, amount and chain, so the facilitator can broadcast exactly that transfer or nothing. At risk: availability (no SLA — every paid call fails closed if it disappears), your payment graph (it sees every authorisation), and continuity. Fine for a proving run; think harder before production.
Run it
ALLOW_X402_MAINNET=true MERCHANT_WALLET=0xYourWallet \
npm run agent-commerce -- validate --config examples/base-mainnet-payai/config.yaml
# to actually settle — spends real USDC every run
export ALLOW_X402_MAINNET=true
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:mainnetSource: examples/base-mainnet-payai