Skip to main content
Accept a signed x402 payment authorization from a buyer, submit it to Facilitator Service on Arc testnet, and confirm that USDC settled to your seller address. This quickstart uses the keyless trial, so no Circle account or API key is required.

Prerequisites

Before you begin, ensure that you’ve:
  • Installed Node.js v22.6+
  • Installed viem
  • Created an EVM wallet, which will be your payTo address for receiving USDC payments
  • Obtained the private key controlling payTo, which will be used to sign the seller proof
  • Funded a buyer wallet with Arc testnet USDC from the Circle Faucet

Step 1: Get a signed payment authorization from the buyer

In production, the buyer’s wallet or agent signs the EIP-3009 authorization and sends it to your API in the x402 request. For this quickstart, sign one from a test wallet you control so you can act as both buyer and seller. The authorization signs against the USDC contract on Arc testnet.
sign-authorization.ts
Keep the signature and message fields. You pass them to /settle in Step 3 as payload.signature and payload.authorization.

Step 2: Build the seller proof

The seller proof is a base64url-encoded envelope carrying an EIP-712 signature. It proves you control payTo and binds the request to the purpose and body being sent.
Sign each Facilitator Service call with a proof whose purpose matches the route (verify, settle, or status). For a step-by-step walkthrough and the full signing rules, see Sign a seller proof.
sign-proof.ts

Step 3: Submit the payment

/settle is the authoritative money path. It validates the authorization, screens buyer and seller, records durable payment state, submits the USDC transfer, and returns terminal evidence or a pending response. You can include a payment-identifier extension in the request body for idempotency scoped to your seller account. When supplied, its id must be 16 to 128 characters from [A-Za-z0-9_-]. When omitted, the facilitator uses an internal surrogate. In that case, a retry must reuse the exact same signed authorization, because a fresh authorization without an identifier is treated as a new charge.
body.json
Generate a fresh proof for this call using the sign-proof.ts script from Step 2, then pass it in the Facilitator-Seller-Proof header:

Step 4: Read the settlement response

/settle returns one of two shapes. Terminal success means the transfer confirmed in the HTTP wait window. Facilitator Service responds with:
Pending means confirmation did not arrive in time. Do not fulfill on pending. Facilitator Service responds with a paymentId you poll on /status:
A timeout is not evidence of failure. To retry a pending payment, reuse the same payment-identifier you supplied on the first call. If you did not supply one, retry with the exact same signed buyer authorization. Never re-sign, or you risk charging the buyer twice.

Step 5: Confirm the payment settled

Sign a fresh seller proof for the /status call with purpose: "status", then poll at the interval indicated by retryAfterMs until the payment reaches completed or failed. Substitute the base64url proof from sign-proof.ts:
Facilitator Service responds with completed once the USDC transfer settled to payTo:
You’ve settled a payment through Facilitator Service. In a real x402 integration, this is when your API responds to the buyer with the paid resource.