Merchant setup

Accept payments

Configure x402 settlement for a local chain, Base Sepolia or Base mainnet — funds go straight to your wallet.

Payments use x402 v2, scheme exact, on EVM: the buyer signs an EIP-3009 authorisation that names your payTo as recipient, and a facilitator broadcasts it. Funds move buyer → merchant directly on chain. The gateway holds neither the money nor a key.

Optional peers for x402bash
npm install @devlab.group/agent-commerce @x402/core @x402/evm viem

Configure the rail

A private Anvil chain with a mock USDC — deterministic, free, and what the demo and CI use.

bash
npm run chain:start     # Anvil, chain id 84532
npm run chain:deploy    # MockUSDC, funded buyer, writes .deploy/local.json
config.yamlyaml
payments:
  x402:
    enabled: true
    network: ${X402_NETWORK}            # CAIP-2; eip155:84532 locally
    rpcUrl: ${X402_RPC_URL}
    asset: ${X402_ASSET}                # ERC-20 with EIP-3009
    assetName: ${X402_ASSET_NAME}       # EIP-712 domain name
    assetVersion: ${X402_ASSET_VERSION}
    assetDecimals: ${X402_ASSET_DECIMALS}
    payTo: ${MERCHANT_WALLET}           # merchant-controlled. NEVER the gateway's.
    maxTimeoutSeconds: 120
    facilitator:
      mode: local                       # local dev chain only
      signerPrivateKey: ${X402_FACILITATOR_PRIVATE_KEY}
validate and doctor read .deploy/local.json and fill the local X402_* and MERCHANT_WALLET placeholders for you.
The local facilitator key is an Anvil well-known key — local development only, never fund it. Startup refuses it against anything but a local or private RPC.

Put a price on a resource

yaml
    pricing:
      type: fixed
      amount: "0.01"      # decimal string in display units — never a float
      currency: USDC
    payments: [x402]

Amounts are converted to base units inside the provider (6 decimals for USDC). More precision than the asset supports is a configuration error, not a rounding opportunity.

Facilitator authentication

authConfigInstalls
noneauth: { type: none }nothing
bearerauth: { type: bearer, token: ${X402_FACILITATOR_TOKEN} }nothing
cdpauth: { type: cdp, apiKeyId: ${CDP_API_KEY_ID}, apiKeySecret: ${CDP_API_KEY_SECRET} }@coinbase/x402

What an agent receives

An unpaid call to a paid resource answers 402 (HTTP) or an isError tool result (MCP) carrying this envelope:

json
{
  "status": "payment-required",
  "code": "PAYMENT_REQUIRED",
  "requestId": "…",
  "resourceId": "market_report",
  "message": "Payment of 0.01 USDC is required for resource \"market_report\". …",
  "payment": {
    "provider": "x402",
    "version": "2",
    "amount": "0.01",
    "currency": "USDC",
    "destination": "0x…",
    "network": "eip155:84532",
    "asset": "0x…",
    "expiresAt": "…",
    "accepts": [ /* x402 v2 PaymentRequirements, verbatim */ ],
    "envelope": { /* x402 v2 PaymentRequired, verbatim */ }
  }
}

The agent side of that exchange is in Handle a payment.

Where the money went

On startup the gateway prints the effective settlement destination, so an operator can confirm it at a glance:

text
  x402 settlement
    network      eip155:84532  via http://127.0.0.1:8545
    asset        0x… (MockUSDC v2)
    pays to      0x7099…79C8   <- merchant-controlled, not the gateway
    facilitator  local

The same destination is published at GET /.well-known/agent-commerce and printed by doctor.