Payments
Agent-to-agent payments via x402 protocol. Charge for your services or pay other agents. USDC on Base, gasless for the payer.
How x402 works
x402 is an HTTP protocol for micropayments. When a client requests a paid resource, the server responds with HTTP 402 (Payment Required) plus payment details. The client signs an EIP-3009 authorization (a gasless USDC transfer) and retries. A facilitator settles on-chain.
Key feature: The payer never sends ETH or gas. EIP-3009 is a signed authorization — the facilitator pays gas and collects a small fee. Instant, cheap, and trustless.
| Property | Value |
|---|---|
| Token | USDC (Circle) |
| Network | Base mainnet (eip155:8453) |
| Settlement | Via facilitator (gasless for payer) |
| Protocol | x402 v2 (CAIP-2 network IDs) |
| Signing | EIP-3009 (signed USDC transfer authorization) |
| Min amount | 1 base unit = 0.000001 USDC |
USDC amount encoding
USDC has 6 decimal places. Amounts are always in base units (strings):
Payment Client (paying agents)
import { createPaymentClient } from "@a3stack/payments";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const payer = createPaymentClient({
account,
chains: ["eip155:8453"], // supported chains (Base default)
// maxAmount: "1000000", // optional: max USDC to auto-pay (safety cap)
});payer.fetch()
Auto-pays x402 requirements. Drop-in replacement for fetch.
// Auto-pays x402 requirements transparently
const response = await payer.fetch("https://api.paidagent.ai/tool");
const data = await response.json();
// Or target a specific agent by ERC-8004 ID
// (auto-resolves payment wallet from on-chain registration)
const paidFetch = await payer.fetchForAgent("eip155:8453:0x8004...#2376");
const response = await paidFetch("https://mcp.paidagent.ai/tool");payer.getBalance()
// Check your USDC balance on Base
const balance = await payer.getBalance("eip155:8453");
// {
// amount: 1500000n, // raw USDC (6 decimals)
// formatted: "1.500000", // human-readable
// symbol: "USDC",
// }payer.decodeReceipt()
// After a paid request, decode the payment receipt
const receipt = payer.decodeReceipt(response);
// {
// txHash?: "0x...",
// from: "0x...",
// to: "0x...",
// amount: "1000",
// asset: "0x833589...", // USDC
// chain: "eip155:8453",
// timestamp: 1733000000,
// }payer.checkPaymentRequirements()
// Check if an endpoint requires payment before connecting
const check = await payer.checkPaymentRequirements("https://api.agent.ai/tool");
// {
// requiresPayment: true,
// amount: "1000",
// asset: "0x833589...",
// network: "eip155:8453",
// payTo: "0x1be93C..."
// }Payment Server (receiving agents)
import { createPaymentServer } from "@a3stack/payments";
import { USDC_BASE } from "@a3stack/payments";
const receiver = createPaymentServer({
payTo: "0x1be93C...", // your wallet — receives USDC
amount: "100000", // 0.10 USDC in base units (6 decimals)
asset: USDC_BASE, // USDC address on Base
chain: "eip155:8453",
description: "My AI tool, 0.10 USDC per call",
// facilitator?: "https://...", // optional custom facilitator URL
});receiver.middleware()
Express.js middleware. Verifies payment before your handler runs.
import express from "express";
const app = express();
app.use("/tool", receiver.middleware(), (req, res) => {
// ✓ Payment verified at this point
const payment = res.locals.payment; // PaymentDetails
console.log(`Received ${payment.amount} USDC from ${payment.from}`);
res.json({ result: "premium content" });
});
app.listen(3000);Manual verification
// Manual: check if a request carries valid payment
const { valid, payment, error } = await receiver.verify(req);
if (!valid) {
const reqs = receiver.buildRequirements("https://api.myagent.ai/tool");
res.status(402).set("X-PAYMENT-REQUIRED", reqs).end();
return;
}
// Payment valid — serve the responseDirect class usage
import { PaymentClient } from "@a3stack/payments";
// Direct class instantiation (same as createPaymentClient)
const client = new PaymentClient({ account });
const balance = await client.getBalance("eip155:8453");Constants
import { USDC_BASE, USDC_ETHEREUM, USDC_ARBITRUM } from "@a3stack/payments";
// Base mainnet USDC
console.log(USDC_BASE);
// "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"Protocol flow (annotated)
// The x402 payment flow in detail:
// 1. Client makes a normal HTTP request
GET /tool HTTP/1.1
// 2. Server returns 402 with payment requirements
HTTP/1.1 402 Payment Required
X-PAYMENT-REQUIRED: { "accepts": [{ "scheme": "exact", "network": "eip155:8453", ... }] }
// 3. Client signs EIP-3009 authorization (gasless — no ETH needed, just USDC)
// Authorization: Transfer USDC from client to agent's payTo address
// 4. Client retries with X-PAYMENT header
GET /tool HTTP/1.1
X-PAYMENT: <base64-encoded EIP-3009 signature>
// 5. Server verifies signature and responds
HTTP/1.1 200 OK
X-PAYMENT-RESPONSE: { "success": true, "txHash": "..." }
// 6. Facilitator settles the USDC transfer on-chain
// Client: -0.001 USDC Agent: +0.001 USDCKey addresses
| Contract | Address | Chain |
|---|---|---|
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | Base |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Base |