
X402 Payments
- 16 installs
- 9 repo stars
- Updated August 4, 2026
- aznatkoiny/zai-skills
x402-payments is a Claude skill for building applications with Coinbase's x402 protocol, which embeds USDC stablecoin payments into HTTP using the 402 status code.
About
x402-payments is a skill for building applications with Coinbase's x402 protocol, an open standard for HTTP-native stablecoin payments that uses the HTTP 402 status code. A developer uses it to add USDC pay-per-request billing to an API, or to build clients and AI agents that pay for x402-protected resources. It covers server middleware for Express, Hono, and Next.js, buyer-side fetch and axios clients, and both Base (EVM) and Solana payment flows.
- Implements Coinbase's x402 protocol for HTTP-native USDC stablecoin payments via the 402 status code
- Provides seller-side payment middleware for Express, Hono, and Next.js plus buyer-side fetch/axios clients
- Supports both Base (EVM, EIP-3009) and Solana (SVM, SPL) payment flows with testnet and mainnet facilitators
X402 Payments by the numbers
- 16 all-time installs (skills.sh)
- Ranked #274 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
x402-payments capabilities & compatibility
Free on testnet (x402.org facilitator, Base Sepolia); mainnet needs CDP facilitator API keys plus USDC and gas.
- Capabilities
- payment middleware · stablecoin checkout · agent payments
- Works with
- stripe
- Use cases
- api development
- Pricing
- Bring your own API key
What x402-payments says it does
x402 embeds stablecoin payments into HTTP by using the 402 "Payment Required" status code.
npx skills add https://github.com/aznatkoiny/zai-skills --skill x402-paymentsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 16 |
|---|---|
| repo stars | ★ 9 |
| Last updated | August 4, 2026 |
| Repository | aznatkoiny/zai-skills ↗ |
What it does
Add per-request USDC stablecoin payments to an API or build agents that pay for x402-protected resources.
Who is it for?
Adding pay-per-request USDC billing to an API, or building AI agents and clients that pay for x402-protected resources.
Skip if: Traditional card checkout, fiat subscriptions, or payment flows that do not involve stablecoins on Base or Solana.
When should I use this skill?
Creating APIs that require USDC payments per request, or building buyers or agents that pay for x402-protected resources.
What you get
An API endpoint gated by a signed USDC payment, or a client that automatically pays and retrieves x402-protected resources.
- payment-gated API endpoints
- x402 buyer client wrappers for fetch or axios
By the numbers
- 6-step payment flow (request, 402, sign, resubmit, verify, settle)
- supports 3 server frameworks: Express, Hono, Next.js
Files
x402 Protocol Skill
Protocol Overview
x402 embeds stablecoin payments into HTTP by using the 402 "Payment Required" status code. A server responds with payment requirements; the client signs a payment authorization, resubmits the request, and gets the resource after verification and settlement.
Payment flow: 1. Client sends HTTP request → Server returns 402 + PAYMENT-REQUIRED header (base64 JSON) 2. Client reads requirements, creates signed payment payload 3. Client resubmits request with PAYMENT-SIGNATURE header (base64 JSON) 4. Server verifies payment via facilitator POST /verify 5. Server performs work, settles via facilitator POST /settle 6. Server returns 200 + resource + PAYMENT-RESPONSE header (contains txHash)
Key concepts:
- Facilitators verify and settle payments without holding funds. Use
https://x402.org/facilitatorfor testnet, CDP facilitator for mainnet. - Schemes:
exact(fixed price per request) is the production scheme.uptoanddeferredare proposed. - Networks: Identified by CAIP-2 format —
eip155:84532(Base Sepolia),eip155:8453(Base Mainnet),solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1(Solana Devnet). - EVM uses EIP-3009 gasless
TransferWithAuthorization. Solana uses SPL token transfers.
Quick-Start: Protect an API Endpoint (Seller)
npm install @x402/express @x402/core @x402/evmimport express from "express";
import { paymentMiddleware } from "@x402/express";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";
const app = express();
const payTo = process.env.PAY_TO!;
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:84532", payTo },
],
description: "Get current weather data",
mimeType: "application/json",
},
},
server,
),
);
app.get("/weather", (req, res) => {
res.json({ weather: "sunny", temperature: 70 });
});
app.listen(4021, () => console.log("Server on :4021"));Quick-Start: Pay for x402 Resources (Buyer/Agent)
npm install @x402/fetch @x402/core @x402/evm viemimport { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("http://localhost:4021/weather");
const data = await response.json();
console.log(data);
// Read payment receipt
const httpClient = new x402HTTPClient(client);
const receipt = httpClient.getPaymentSettleResponse(
(name) => response.headers.get(name),
);
console.log("Tx:", receipt?.txHash);Decision Tree
| Decision | Choice | Packages |
|---|---|---|
| Server: Express | paymentMiddleware from @x402/express | @x402/express @x402/core @x402/evm |
| Server: Next.js | paymentProxy from @x402/next | @x402/next @x402/core @x402/evm |
| Server: Hono | paymentMiddleware from @x402/hono | @x402/hono @x402/core @x402/evm |
| Client: fetch | wrapFetchWithPayment | @x402/fetch @x402/core @x402/evm viem |
| Client: axios | wrapAxiosWithPayment | @x402/axios @x402/core @x402/evm viem axios |
| Client: manual | x402Client + x402HTTPClient from @x402/core | @x402/core @x402/evm viem |
| Chain: EVM | registerExactEvmScheme | @x402/evm + viem |
| Chain: Solana | registerExactSvmScheme | @x402/svm + @solana/kit @scure/base |
| Chain: both | Register both schemes on same client/server | All chain deps |
| Env: testing | Facilitator https://x402.org/facilitator | Base Sepolia / Solana Devnet |
| Env: production | CDP facilitator + API keys | Base Mainnet / Solana Mainnet |
| Agent: MCP | MCP server with @x402/axios | See references/agentic-patterns.md |
| Agent: Anthropic | Tool-use with @x402/fetch | See references/agentic-patterns.md |
Reference File Navigation
| Task | Read this file |
|---|---|
| Headers, payloads, CAIP-2 IDs, facilitator API, V1→V2 changes | references/protocol-spec.md |
| Express / Hono / Next.js middleware, multi-route, dynamic pricing | references/server-patterns.md |
| Fetch / axios client, wallet setup, lifecycle hooks, error handling | references/client-patterns.md |
| AI agent payments, MCP server, tool discovery, budget controls | references/agentic-patterns.md |
| Testnet→mainnet migration, CDP keys, faucets, security, sessions | references/deployment.md |
Critical Implementation Notes
1. Register schemes before wrapping fetch/axios — order matters. 2. Two equivalent registration APIs:
- Function:
registerExactEvmScheme(server)/registerExactEvmScheme(client, { signer }) - Method:
server.register("eip155:84532", new ExactEvmScheme())
3. V2 headers (current): PAYMENT-REQUIRED, PAYMENT-SIGNATURE, PAYMENT-RESPONSE. V1 headers (legacy): X-PAYMENT, X-PAYMENT-RESPONSE. SDK is backward-compatible. 4. Price format: "$0.001" (dollar string) — SDK converts to atomic units (6 decimals for USDC). 5. Python SDK uses V1 patterns only. Use TypeScript or Go for V2. 6. Node.js v24+ required for the TypeScript SDK. 7. Repo: https://github.com/coinbase/x402 — canonical examples in examples/typescript/. 8. Docs: https://docs.cdp.coinbase.com/x402/welcome and https://x402.gitbook.io/x402.
Agentic Payment Patterns
Table of Contents
1. MCP Server for Claude Desktop 2. Anthropic Tool-Use Agent 3. Bazaar Discovery Agent 4. Budget-Controlled Agent Wallet 5. Multi-Agent Payment Orchestration
MCP Server for Claude Desktop
Source: examples/typescript/clients/mcp/
Build an MCP (Model Context Protocol) server that wraps x402-protected APIs as tools for Claude Desktop.
npm install @modelcontextprotocol/sdk @x402/axios @x402/core @x402/evm @x402/svm viem @solana/kit @scure/base axiosimport { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import axios from "axios";
import { x402Client, wrapAxiosWithPayment } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { base58 } from "@scure/base";
const EVM_PRIVATE_KEY = process.env.EVM_PRIVATE_KEY as `0x${string}`;
const SVM_PRIVATE_KEY = process.env.SVM_PRIVATE_KEY!;
const RESOURCE_SERVER_URL = process.env.RESOURCE_SERVER_URL || "http://localhost:4021";
// Create x402 client with payment schemes
const client = new x402Client();
// Register EVM scheme
const evmSigner = privateKeyToAccount(EVM_PRIVATE_KEY);
registerExactEvmScheme(client, { signer: evmSigner });
// Register SVM scheme (optional — omit if EVM only)
const svmSigner = await createKeyPairSignerFromBytes(base58.decode(SVM_PRIVATE_KEY));
registerExactSvmScheme(client, { signer: svmSigner });
// Create Axios instance with payment handling
const api = wrapAxiosWithPayment(
axios.create({ baseURL: RESOURCE_SERVER_URL }),
client,
);
// Define MCP server with tools
const mcpServer = new McpServer({
name: "x402-weather",
version: "1.0.0",
});
mcpServer.tool("get_weather", "Get weather (costs $0.001 USDC)", {}, async () => {
const response = await api.get("/weather");
return {
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
};
});
mcpServer.tool(
"get_forecast",
"Get 5-day forecast (costs $0.01 USDC)",
{ city: { type: "string", description: "City name" } },
async ({ city }) => {
const response = await api.get(`/forecast?city=${encodeURIComponent(city)}`);
return {
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
};
},
);
// Start stdio transport
const transport = new StdioServerTransport();
await mcpServer.connect(transport);Claude Desktop Configuration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or ~/.config/claude/claude_desktop_config.json (Linux):
{
"mcpServers": {
"x402-weather": {
"command": "node",
"args": ["--experimental-modules", "/path/to/mcp-server.js"],
"env": {
"EVM_PRIVATE_KEY": "0x...",
"SVM_PRIVATE_KEY": "...",
"RESOURCE_SERVER_URL": "http://localhost:4021"
}
}
}
}Anthropic Tool-Use Agent
Build an AI agent that autonomously decides when to call paid APIs using Anthropic's tool-use.
npm install @anthropic-ai/sdk @x402/fetch @x402/core @x402/evm viemimport Anthropic from "@anthropic-ai/sdk";
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
// Set up x402 payment client
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const x402 = new x402Client();
registerExactEvmScheme(x402, { signer });
const fetchWithPayment = wrapFetchWithPayment(fetch, x402);
// Define tools for the agent
const tools: Anthropic.Messages.Tool[] = [
{
name: "get_weather",
description: "Get current weather data. Costs $0.001 USDC per call.",
input_schema: {
type: "object" as const,
properties: {
city: { type: "string", description: "City name" },
},
required: ["city"],
},
},
];
// Execute tool calls — payment handled automatically
async function executeTool(name: string, input: Record<string, unknown>) {
switch (name) {
case "get_weather": {
const response = await fetchWithPayment(
`http://localhost:4021/weather?city=${encodeURIComponent(input.city as string)}`,
);
return await response.json();
}
default:
throw new Error(`Unknown tool: ${name}`);
}
}
// Agent loop
const anthropic = new Anthropic();
async function runAgent(userMessage: string) {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: userMessage },
];
while (true) {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
tools,
messages,
});
if (response.stop_reason === "end_turn") {
const text = response.content.find((b) => b.type === "text");
return text?.text;
}
// Process tool uses
const toolUses = response.content.filter((b) => b.type === "tool_use");
messages.push({ role: "assistant", content: response.content });
const toolResults: Anthropic.Messages.ToolResultBlockParam[] = [];
for (const toolUse of toolUses) {
if (toolUse.type !== "tool_use") continue;
const result = await executeTool(
toolUse.name,
toolUse.input as Record<string, unknown>,
);
toolResults.push({
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify(result),
});
}
messages.push({ role: "user", content: toolResults });
}
}
const answer = await runAgent("What's the weather like in Tokyo?");
console.log(answer);Bazaar Discovery Agent
Discover available x402 services, evaluate pricing, and call them dynamically.
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
interface BazaarResource {
resource: string;
type: string;
accepts: Array<{ maxAmountRequired: string; network: string }>;
metadata: { description: string };
}
async function discoverServices(facilitatorUrl: string): Promise<BazaarResource[]> {
const response = await fetch(`${facilitatorUrl}/discovery/resources`);
const data = await response.json();
return data.items;
}
async function findAffordable(services: BazaarResource[], maxCostUSDC: number) {
return services
.map((s) => ({
...s,
costUSDC: parseInt(s.accepts[0].maxAmountRequired) / 1_000_000,
}))
.filter((s) => s.costUSDC <= maxCostUSDC)
.sort((a, b) => a.costUSDC - b.costUSDC);
}
// Discover → Filter → Call
const services = await discoverServices("https://x402.org/facilitator");
const affordable = await findAffordable(services, 0.01); // Max $0.01
for (const service of affordable) {
console.log(`Calling ${service.resource} ($${service.costUSDC})`);
const response = await fetchWithPayment(service.resource);
console.log(await response.json());
}Budget-Controlled Agent Wallet
Enforce spending limits for autonomous agents.
class AgentWallet {
private totalSpent = 0;
private txLog: Array<{ url: string; amount: number; ts: Date }> = [];
constructor(
private dailyBudget: number,
private perCallMax: number,
) {}
approve(url: string, amountAtomic: string): boolean {
const usd = parseInt(amountAtomic) / 1_000_000;
if (usd > this.perCallMax) {
console.warn(`Rejected: $${usd} exceeds per-call max $${this.perCallMax}`);
return false;
}
if (this.totalSpent + usd > this.dailyBudget) {
console.warn(`Rejected: daily budget exhausted`);
return false;
}
this.totalSpent += usd;
this.txLog.push({ url, amount: usd, ts: new Date() });
return true;
}
summary() {
return {
spent: this.totalSpent,
remaining: this.dailyBudget - this.totalSpent,
calls: this.txLog.length,
};
}
resetDaily() { this.totalSpent = 0; }
}
// Wire into x402 client via lifecycle hooks
const wallet = new AgentWallet(1.0, 0.10); // $1/day, $0.10/call
client.onBeforePayment(async (details) => {
return wallet.approve(details.resource, details.amount);
});
client.onAfterPayment(async () => {
console.log("Budget:", wallet.summary());
});Multi-Agent Payment Orchestration
Separate wallets and budgets per agent role.
function createAgent(name: string, keyEnvVar: string, budgetUSDC: number) {
const signer = privateKeyToAccount(process.env[keyEnvVar] as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const wallet = new AgentWallet(budgetUSDC, budgetUSDC / 10);
client.onBeforePayment(async (details) => {
const ok = wallet.approve(details.resource, details.amount);
if (ok) console.log(`[${name}] Approved: ${details.resource}`);
return ok;
});
return {
client,
wallet,
fetch: wrapFetchWithPayment(fetch, client),
};
}
const research = createAgent("research", "RESEARCH_KEY", 5.0);
const data = createAgent("data", "DATA_KEY", 2.0);
const analysis = createAgent("analysis", "ANALYSIS_KEY", 1.0);Client-Side Implementation Patterns
Table of Contents
1. Fetch Client 2. Axios Client 3. Multi-Chain Client (EVM + Solana) 4. Manual Client (Core Only) 5. EVM Wallet Setup 6. Solana Wallet Setup 7. Lifecycle Hooks 8. Network Preferences 9. Error Handling 10. Environment Variables Template
Fetch Client
Source: examples/typescript/clients/fetch/
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
// Create signer from private key
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
// Create x402 client and register EVM scheme
const client = new x402Client();
registerExactEvmScheme(client, { signer });
// Wrap native fetch — 402 responses handled automatically
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("http://localhost:4021/weather", {
method: "GET",
});
const data = await response.json();
console.log("Response:", data);
// Read payment settlement receipt from headers
if (response.ok) {
const httpClient = new x402HTTPClient(client);
const paymentResponse = httpClient.getPaymentSettleResponse(
(name) => response.headers.get(name),
);
console.log("Tx hash:", paymentResponse?.txHash);
}Axios Client
Source: examples/typescript/clients/axios/
import { x402Client, wrapAxiosWithPayment, x402HTTPClient } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import axios from "axios";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
// Wrap an Axios instance
const api = wrapAxiosWithPayment(
axios.create({ baseURL: process.env.RESOURCE_SERVER_URL }),
client,
);
const response = await api.get(process.env.ENDPOINT_PATH!);
console.log("Response:", response.data);
// Read settlement receipt
const httpClient = new x402HTTPClient(client);
const paymentResponse = httpClient.getPaymentSettleResponse(
(name) => response.headers[name.toLowerCase()],
);
console.log("Settled:", paymentResponse);Multi-Chain Client
Source: examples/typescript/clients/mcp/ pattern
Handle both EVM and Solana endpoints with one client — the SDK auto-selects based on server requirements.
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { base58 } from "@scure/base";
// EVM signer
const evmSigner = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
// Solana signer (64-byte secret key in base58)
const svmSigner = await createKeyPairSignerFromBytes(
base58.decode(process.env.SOLANA_PRIVATE_KEY!),
);
// Register both schemes
const client = new x402Client();
registerExactEvmScheme(client, { signer: evmSigner });
registerExactSvmScheme(client, { signer: svmSigner });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
// Client auto-selects correct scheme based on server's network in PAYMENT-REQUIRED
const evmResponse = await fetchWithPayment("https://evm-api.example.com/data");
const svmResponse = await fetchWithPayment("https://solana-api.example.com/data");Manual Client
Source: examples/typescript/clients/custom/
Full control over the 402 flow using only @x402/core. No interceptors.
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const httpClient = new x402HTTPClient(client);
// Step 1: Make initial request
const response = await fetch("http://localhost:4021/weather");
if (response.status === 402) {
// Step 2: Parse payment requirements from header
const requirements = httpClient.getPaymentRequirements(
(name) => response.headers.get(name),
);
// Step 3: Select a requirement and create payment
const selectedRequirement = requirements[0];
const paymentPayload = await client.createPayment(selectedRequirement);
// Step 4: Encode as header value
const paymentHeader = httpClient.encodePaymentHeader(paymentPayload);
// Step 5: Retry with payment
const paidResponse = await fetch("http://localhost:4021/weather", {
headers: { "PAYMENT-SIGNATURE": paymentHeader },
});
// Step 6: Read settlement receipt
const receipt = httpClient.getPaymentSettleResponse(
(name) => paidResponse.headers.get(name),
);
console.log("Paid! Tx:", receipt?.txHash);
return paidResponse.json();
}EVM Wallet Setup
Requires viem peer dependency.
import { privateKeyToAccount } from "viem/accounts";
// From 0x-prefixed hex private key
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
// The signer provides:
// - signer.address: string (e.g., "0x1234...")
// - signer.signTypedData(domain, types, message): Promise<string>Generate a new wallet for testing:
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
console.log("Address:", account.address);
console.log("Private key:", privateKey); // Save this securelySolana Wallet Setup
Requires @solana/kit and @scure/base peer dependencies.
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { base58 } from "@scure/base";
// From base58-encoded 64-byte secret key (private key + public key)
const signer = await createKeyPairSignerFromBytes(
base58.decode(process.env.SOLANA_PRIVATE_KEY!),
);
// The signer provides:
// - signer.address: Address (base58 string)
// - signer.signTransaction(tx): Promise<SignedTransaction>Lifecycle Hooks
Source: examples/typescript/clients/advanced/
Inject custom logic before/after payment creation.
const client = new x402Client();
registerExactEvmScheme(client, { signer });
// Before payment: validate, log, or reject
client.onBeforePayment((details) => {
console.log(`About to pay ${details.amount} to ${details.payTo}`);
// Return true to proceed, false to abort
return details.amount < maxAllowed;
});
// After payment: track receipts
client.onAfterPayment((result) => {
console.log(`Payment created: ${JSON.stringify(result)}`);
metrics.increment("x402.payments_created");
});
// On error: custom error handling
client.onPaymentError((error) => {
console.error(`Payment failed: ${error.message}`);
alerting.notify("x402-payment-failure", error);
});Network Preferences
Source: examples/typescript/clients/advanced/
Configure preferred networks with fallbacks. SDK picks the best match from server's accepts array.
const client = new x402Client();
registerExactEvmScheme(client, { signer: evmSigner });
registerExactSvmScheme(client, { signer: svmSigner });
// Prefer Base Sepolia, fall back to any EVM, then Solana
client.setNetworkPreferences([
"eip155:84532", // First choice
"eip155:*", // Any EVM network
"solana:*", // Any Solana network
]);Error Handling
Common error patterns when interacting with x402-protected endpoints.
try {
const response = await fetchWithPayment("https://api.example.com/paid");
if (!response.ok && response.status !== 402) {
throw new Error(`Server error: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.message?.includes("insufficient funds")) {
console.error("Wallet needs more USDC. Fund via Circle faucet for testnet.");
} else if (error.message?.includes("No matching scheme")) {
console.error("Client doesn't support any of the server's accepted networks.");
} else if (error.message?.includes("signature")) {
console.error("Payment signature was rejected — check private key and network.");
} else if (error.message?.includes("timeout")) {
console.error("Payment or settlement timed out.");
}
throw error;
}Checking if an endpoint requires payment (preflight):
const checkResponse = await fetch(url, { method: "HEAD" });
if (checkResponse.status === 402) {
const requirementsHeader = checkResponse.headers.get("PAYMENT-REQUIRED");
const requirements = JSON.parse(atob(requirementsHeader!));
const costUSDC = parseInt(requirements.accepts[0].maxAmountRequired) / 1_000_000;
console.log(`This endpoint costs $${costUSDC} USDC`);
}Environment Variables Template
# Wallet keys
EVM_PRIVATE_KEY=0x... # 0x-prefixed hex
SOLANA_PRIVATE_KEY=... # base58, 64 bytes
# Target server
RESOURCE_SERVER_URL=http://localhost:4021
ENDPOINT_PATH=/weatherDeployment Guide
Table of Contents
1. Testnet Development Setup 2. USDC Faucets 3. Testnet to Mainnet Migration 4. CDP Facilitator Setup 5. Private Key Security 6. Session Cookies (Skip Re-payment) 7. Monitoring and Observability 8. Error Response Reference
Testnet Development Setup
Testnet requires no API keys. Use these defaults:
| Setting | Testnet Value |
|---|---|
| Facilitator URL | https://x402.org/facilitator |
| EVM Network | eip155:84532 (Base Sepolia) |
| Solana Network | solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 (Devnet) |
| USDC | Test tokens from Circle faucet |
Minimum testnet server `.env`:
PAY_TO=0xYourEthereumAddressMinimum testnet client `.env`:
EVM_PRIVATE_KEY=0xYourPrivateKey
RESOURCE_SERVER_URL=http://localhost:4021USDC Faucets
Base Sepolia (EVM)
- Circle Faucet: https://faucet.circle.com — Select "Base Sepolia" and "USDC". Grants 20 USDC every 2 hours per address.
- Also need Base Sepolia ETH for wallet creation (not for x402 payments — those are gasless). Use any Base Sepolia faucet.
Solana Devnet
- Circle Faucet: https://faucet.circle.com — Select "Solana Devnet" and "USDC".
- Devnet USDC mint:
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU - Also need Devnet SOL:
solana airdrop 2via Solana CLI.
Testnet to Mainnet Migration
Three changes — no code restructuring needed.
Step 1: Change network identifiers
// Before (testnet)
network: "eip155:84532" // Base Sepolia
network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" // Solana Devnet
// After (mainnet)
network: "eip155:8453" // Base Mainnet
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" // Solana MainnetStep 2: Switch facilitator URL
// Before (testnet)
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
// After (mainnet — CDP hosted)
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://api.cdp.coinbase.com/platform/v2/x402",
});Step 3: Add CDP API credentials
CDP_API_KEY_ID=your-api-key-id
CDP_API_KEY_SECRET=your-api-key-secretRegister mainnet scheme on server:
const server = new x402ResourceServer(facilitatorClient)
.register("eip155:8453", new ExactEvmScheme()); // Base mainnetCDP Facilitator Setup
The Coinbase Developer Platform facilitator handles verification and settlement for production.
1. Sign up at https://cdp.coinbase.com 2. Create a project in the CDP dashboard 3. Generate API credentials (API Key ID + Secret) 4. Set environment variables:
CDP_API_KEY_ID=your-api-key-id
CDP_API_KEY_SECRET=your-api-key-secretPricing:
- Free tier: 1,000 transactions per month
- After free tier: $0.001 per transaction
- No percentage-based fees
Facilitator URL: https://api.cdp.coinbase.com/platform/v2/x402
Server configuration with auth headers:
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://api.cdp.coinbase.com/platform/v2/x402",
createAuthHeaders: () => ({
"X-CDP-API-KEY-ID": process.env.CDP_API_KEY_ID!,
"X-CDP-API-KEY-SECRET": process.env.CDP_API_KEY_SECRET!,
}),
});Private Key Security
Never hardcode private keys.
Development
# .env file (add to .gitignore!)
EVM_PRIVATE_KEY=0x...
SOLANA_PRIVATE_KEY=...Production
- Use cloud secret managers (AWS Secrets Manager, GCP Secret Manager, Vault)
- Fund agent wallets with limited amounts only
- Use separate wallets per agent and per environment
- Rotate keys periodically
- Monitor wallet balances with alerts for unusual spending
Generate a dedicated agent wallet
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const key = generatePrivateKey();
const account = privateKeyToAccount(key);
console.log("Agent address:", account.address);
console.log("Private key:", key);
// Fund this address with only the USDC needed for operationSession Cookies
Source: examples/typescript/fullstack/next-advanced/
Issue a session cookie after first payment to avoid charging on every request.
import { serialize } from "cookie";
app.get("/premium", async (req, res) => {
// Check for existing session
const sessionToken = req.cookies?.["x402-session"];
if (sessionToken && isValidSession(sessionToken)) {
return res.json(await generateContent());
}
// No session — require payment
const paymentHeader = req.headers["payment-signature"] as string;
if (!paymentHeader) {
return res.status(402).json({ error: "Payment required" });
}
const verifyResult = await server.verify(paymentHeader, paymentConfig);
if (!verifyResult.isValid) {
return res.status(402).json({ error: verifyResult.invalidReason });
}
const settleResult = await server.settle(paymentHeader, paymentConfig);
// Issue session cookie (valid 24 hours)
const token = generateSessionToken(settleResult.txHash);
res.setHeader(
"Set-Cookie",
serialize("x402-session", token, {
httpOnly: true,
secure: true,
maxAge: 86400,
path: "/",
}),
);
res.set("PAYMENT-RESPONSE", settleResult.encoded);
res.json(await generateContent());
});Auth-Based Pricing
Source: examples/typescript/fullstack/auth_based_pricing/
Charge different prices based on authentication (SIWE + JWT).
// Authenticated users: $0.01, Anonymous users: $0.10
app.use((req, res, next) => {
const jwt = req.headers.authorization?.replace("Bearer ", "");
req.x402Price = jwt && verifyJWT(jwt) ? "$0.01" : "$0.10";
next();
});Monitoring
Track x402 payments with lifecycle hooks.
client.onBeforePayment((details) => {
metrics.increment("x402.payment_required");
metrics.histogram("x402.amount_usd", parseInt(details.amount) / 1_000_000);
return true;
});
client.onAfterPayment(() => metrics.increment("x402.payment_success"));
client.onPaymentError((err) => {
metrics.increment("x402.payment_error");
console.error(`[x402] ${err.message}`);
});Key metrics
x402.payment_required— Endpoints hit without paymentx402.payment_success/x402.payment_error— Client-side outcomesx402.settlement_success/x402.settlement_failure— Server-side outcomesx402.settlement_duration— End-to-end time (~2 seconds typical)x402.amount_usd— Payment size distribution
Error Response Reference
| HTTP Status | Meaning | Client Action |
|---|---|---|
402 + PAYMENT-REQUIRED header | Payment needed | Parse requirements, sign, retry |
402 + invalidReason in body | Payment rejected | Check key, network, amount |
| 400 | Malformed payment header | Verify base64 encoding |
| 500 | Settlement failed on-chain | Retry after delay |
200 + PAYMENT-RESPONSE header | Success | Extract txHash for receipt |
Common failure causes:
- Insufficient USDC balance in payer wallet
- Wrong network (client on testnet, server expects mainnet)
- Expired
validBeforetimestamp in payment authorization - Private key doesn't match the
fromaddress - Facilitator unreachable or experiencing downtime
x402 Protocol Specification
Table of Contents
1. Payment Flow with Headers 2. Header Structures 3. Payment Schemes 4. CAIP-2 Network Identifiers 5. Facilitator API 6. V1 vs V2 Differences 7. Data Types Reference
Payment Flow with Headers
1. Initial request (no payment)
GET /weather HTTP/1.1
Host: api.example.com2. Server returns 402 with payment requirements
HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: <base64-encoded JSON PaymentRequired object>
Content-Type: application/json
{"error": "Payment required"}3. Client retries with signed payment
GET /weather HTTP/1.1
Host: api.example.com
PAYMENT-SIGNATURE: <base64-encoded JSON PaymentPayload object>4. Server returns resource with settlement receipt
HTTP/1.1 200 OK
PAYMENT-RESPONSE: <base64-encoded JSON SettlementResponse object>
Content-Type: application/json
{"weather": "sunny", "temperature": 70}Header Structures
PAYMENT-REQUIRED (base64 JSON)
{
x402Version: 2,
accepts: [
{
scheme: "exact",
network: "eip155:84532", // CAIP-2 identifier
maxAmountRequired: "1000", // Atomic units (0.001 USDC = 1000)
resource: "/weather",
description: "Weather data API",
mimeType: "application/json",
payTo: "0x1234...abcd",
maxTimeoutSeconds: 60,
asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract
extra: { name: "USD Coin", version: "2" },
outputSchema: null // Optional JSON schema
}
],
error: "X-PAYMENT header is required" // Optional error message
}PAYMENT-SIGNATURE (base64 JSON) — EVM Exact Scheme
{
x402Version: 2,
scheme: "exact",
network: "eip155:84532",
payload: {
signature: "0x...", // EIP-712 typed data signature
authorization: {
from: "0xPayerAddress",
to: "0xRecipientAddress",
value: "1000",
validAfter: "0",
validBefore: "1735689600", // Unix timestamp
nonce: "0x..." // Random 32-byte hex nonce
}
}
}PAYMENT-RESPONSE (base64 JSON)
{
success: true,
txHash: "0xabc123...",
networkId: "eip155:84532"
}Payment Schemes
exact — Production scheme
Pay a predetermined fixed amount per request. Uses EIP-3009 TransferWithAuthorization on EVM (gasless, signature-based). Uses SPL token transfers on Solana.
- Nonces: Random 32-byte values (not sequential) — enables concurrent payments.
- EVM asset: USDC contract implementing EIP-3009.
- Solana asset: SPL token mint address.
upto — Proposed
Pay up to a maximum amount based on actual consumption. Designed for usage-based billing (e.g., LLM token pricing). Not yet implemented in production.
deferred — Proposed by Cloudflare
Batched/delayed settlement for micropayments. Designed for high-frequency, low-value transactions like pay-per-crawl. Settlement happens in aggregate rather than per-request. Supports both stablecoin and traditional payment rails.
CAIP-2 Network Identifiers
| Network | CAIP-2 Identifier | Environment |
|---|---|---|
| Base Sepolia | eip155:84532 | Testnet |
| Base Mainnet | eip155:8453 | Production |
| Ethereum Mainnet | eip155:1 | Production |
| Polygon Mainnet | eip155:137 | Production |
| Avalanche Mainnet | eip155:43114 | Production |
| Arbitrum Mainnet | eip155:42161 | Production |
| Solana Devnet | solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 | Testnet |
| Solana Mainnet | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | Production |
USDC contract addresses (EVM):
- Base Sepolia:
0x036CbD53842c5426634e7929541eC2318f3dCF7e - Base Mainnet:
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
USDC mint (Solana):
- Devnet:
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU - Mainnet:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Facilitator API
Facilitators handle verification and settlement so resource servers don't need blockchain infrastructure.
POST /verify
Verify a payment is valid without settling it.
// Request
{
x402Version: 2,
paymentHeader: "<base64 PAYMENT-SIGNATURE value>",
paymentRequirements: { /* PaymentRequirements object */ }
}
// Response
{
isValid: boolean,
invalidReason: string | null
}POST /settle
Submit payment to the blockchain.
// Request (same as /verify)
{
x402Version: 2,
paymentHeader: "<base64 PAYMENT-SIGNATURE value>",
paymentRequirements: { /* PaymentRequirements object */ }
}
// Response
{
success: boolean,
txHash: string | null, // Blockchain transaction hash
networkId: string | null, // CAIP-2 network
error: string | null
}GET /supported
List supported scheme+network pairs.
// Response
{
kinds: [
{ scheme: "exact", network: "eip155:84532" },
{ scheme: "exact", network: "eip155:8453" },
{ scheme: "exact", network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" }
]
}GET /discovery/resources (Bazaar extension)
Discover x402-protected resources.
// Response
{
x402Version: 2,
items: [
{
resource: "https://api.example.com/weather",
type: "http",
accepts: [ /* PaymentRequirements */ ],
metadata: { description: "...", input: {...}, output: {...} }
}
],
pagination: { limit: 20, offset: 0, total: 42 }
}Facilitator URLs:
- Testnet:
https://x402.org/facilitator(free, no auth, Base Sepolia + Solana Devnet) - Mainnet (CDP):
https://api.cdp.coinbase.com/platform/v2/x402(requires CDP API keys)
V1 vs V2 Differences
| Aspect | V1 | V2 (Current) |
|---|---|---|
| Payment header | X-PAYMENT | PAYMENT-SIGNATURE |
| Requirements | Response body JSON | PAYMENT-REQUIRED header |
| Response header | X-PAYMENT-RESPONSE | PAYMENT-RESPONSE |
| Network format | String ("base-sepolia") | CAIP-2 ("eip155:84532") |
| SDK architecture | Monolithic packages (x402-express) | Modular scoped packages (@x402/express) |
| Scheme registration | Automatic | Plugin-driven (registerExactEvmScheme()) |
| Multi-chain | Manual per-network setup | Native wildcard (eip155:*) |
| Extensions | None | Bazaar discovery, lifecycle hooks, modular paywall |
| Multi-facilitator | Single | SDK selects best match based on preferences |
V2 SDK is fully backward-compatible with V1 payloads. V1 package names (x402-express, x402-axios) are deprecated but still functional.
Data Types Reference
PaymentRequirements
{
scheme: string; // "exact"
network: string; // CAIP-2 identifier
maxAmountRequired: string; // Atomic units as string (uint256)
resource: string; // URL path of protected resource
description: string; // Human-readable description
mimeType: string; // Response MIME type
payTo: string; // Recipient wallet address
maxTimeoutSeconds: number; // Max server response time
asset: string; // Token contract/mint address
extra: object | null; // Scheme-specific data
outputSchema?: object | null; // Optional JSON schema for response
}PaymentPayload (PAYMENT-SIGNATURE content)
{
x402Version: number;
scheme: string;
network: string;
payload: object; // Scheme-dependent (see exact scheme above)
}Server-Side Implementation Patterns
Table of Contents
1. Express Middleware 2. Next.js Middleware 3. Hono Middleware 4. Multi-Route Configuration 5. Multi-Chain Server (EVM + Solana) 6. Dynamic Pricing (Without Middleware) 7. Delayed Settlement 8. Facilitator Configuration 9. Bazaar Discovery Extension 10. Environment Variables Template
Express Middleware
Source: examples/typescript/servers/express/
import express from "express";
import { paymentMiddleware } from "@x402/express";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";
const app = express();
const payTo = process.env.PAY_TO!;
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:84532", payTo },
],
description: "Get current weather data",
mimeType: "application/json",
},
},
server,
),
);
app.get("/weather", (req, res) => {
res.json({ report: { weather: "sunny", temperature: 70 } });
});
app.listen(4021);Alternative registration style (from CDP docs — equivalent):
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const server = new x402ResourceServer(facilitatorClient)
.register("eip155:84532", new ExactEvmScheme());Next.js Middleware
Source: examples/typescript/fullstack/mainnet/
// middleware.ts
import { paymentProxy } from "@x402/next";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";
const payTo = process.env.PAY_TO!;
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
export const middleware = paymentProxy(
{
"/api/protected": {
accepts: [
{ scheme: "exact", price: "$0.01", network: "eip155:84532", payTo },
],
description: "Access to protected content",
mimeType: "application/json",
},
},
server,
);
export const config = {
matcher: ["/api/protected/:path*"],
};Hono Middleware
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware } from "@x402/hono";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";
const app = new Hono();
const payTo = process.env.PAY_TO!;
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
app.use(
paymentMiddleware(
{
"/data": {
accepts: [
{ scheme: "exact", price: "$0.10", network: "eip155:84532", payTo },
],
description: "Premium data endpoint",
},
},
server,
),
);
app.get("/data", (c) => c.json({ message: "Premium content" }));
serve({ fetch: app.fetch, port: 3000 });Multi-Route Configuration
Route keys use "METHOD /path" format. Supports wildcards.
app.use(
paymentMiddleware(
{
"GET /free": null, // Explicitly no payment
"GET /cheap": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:84532", payTo },
],
},
"GET /premium": {
accepts: [
{ scheme: "exact", price: "$0.10", network: "eip155:84532", payTo },
],
},
"POST /api/*": {
accepts: [
{ scheme: "exact", price: "$0.01", network: "eip155:84532", payTo },
],
},
},
server,
),
);Multi-Chain Server
Source: Accept payments on both EVM and Solana.
import { registerExactEvmScheme } from "@x402/evm/exact/server";
import { registerExactSvmScheme } from "@x402/svm/exact/server";
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
registerExactSvmScheme(server);
app.use(
paymentMiddleware(
{
"GET /multi": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: evmAddress },
{
scheme: "exact",
price: "$0.001",
network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
payTo: solanaAddress,
},
],
},
},
server,
),
);Dynamic Pricing
Source: examples/typescript/servers/advanced/
Handle pricing without middleware for full control over verify/settle flow.
app.get("/dynamic-price", async (req, res) => {
const multiplier = parseInt(req.query.multiplier as string) || 1;
const basePrice = 0.001;
const price = `$${(basePrice * multiplier).toFixed(6)}`;
const paymentConfig = {
accepts: [{ scheme: "exact", price, network: "eip155:84532", payTo }],
description: "Dynamic pricing endpoint",
};
const paymentHeader = req.headers["payment-signature"] as string;
if (!paymentHeader) {
res.status(402).set(
"PAYMENT-REQUIRED",
Buffer.from(JSON.stringify({ x402Version: 2, ...paymentConfig })).toString("base64"),
);
return res.json({ error: "Payment required" });
}
const verifyResult = await server.verify(paymentHeader, paymentConfig);
if (!verifyResult.isValid) {
return res.status(402).json({ error: verifyResult.invalidReason });
}
// Perform work BEFORE settling (delayed settlement pattern)
const result = await expensiveOperation(multiplier);
const settleResult = await server.settle(paymentHeader, paymentConfig);
res.set("PAYMENT-RESPONSE", settleResult.encoded);
res.json({ result, txHash: settleResult.txHash });
});Delayed Settlement
Source: examples/typescript/servers/advanced/
Verify first, do work, then settle. Useful when immediate response matters more than payment guarantee.
app.get("/delayed", async (req, res) => {
const paymentHeader = req.headers["payment-signature"] as string;
if (!paymentHeader) { /* return 402 */ }
// Step 1: Verify only
const verifyResult = await server.verify(paymentHeader, paymentConfig);
if (!verifyResult.isValid) { /* return 402 */ }
// Step 2: Do expensive work
const result = await generateContent();
// Step 3: Settle after work completes
const settleResult = await server.settle(paymentHeader, paymentConfig);
res.set("PAYMENT-RESPONSE", settleResult.encoded);
res.json(result);
});Facilitator Configuration
// Testnet (no auth, free)
const testnetFacilitator = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
// Mainnet via CDP (requires API keys)
const mainnetFacilitator = new HTTPFacilitatorClient({
url: "https://api.cdp.coinbase.com/platform/v2/x402",
createAuthHeaders: () => ({
Authorization: `Bearer ${process.env.CDP_API_KEY}`,
}),
});
// Environment-based selection
const facilitatorClient = new HTTPFacilitatorClient({
url:
process.env.NODE_ENV === "production"
? "https://api.cdp.coinbase.com/platform/v2/x402"
: "https://x402.org/facilitator",
});Bazaar Discovery
Expose endpoints for automated discovery by agents and facilitators.
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:84532", payTo },
],
description: "Real-time weather data for any city",
mimeType: "application/json",
extensions: {
bazaar: {
discoverable: true,
category: "weather",
tags: ["forecast", "real-time"],
},
},
},
},
server,
),
);Environment Variables Template
# Server wallet (receives payments)
PAY_TO=0xYourEthereumWalletAddress
PAY_TO_SOLANA=YourSolanaWalletAddress
# Facilitator
FACILITATOR_URL=https://x402.org/facilitator
# CDP API keys (mainnet only)
CDP_API_KEY_ID=your-api-key-id
CDP_API_KEY_SECRET=your-api-key-secret
# Server
PORT=4021
NODE_ENV=developmentRelated skills
FAQ
Which frameworks does x402-payments support on the server side?
It provides payment middleware for Express, Hono, and Next.js, using paymentMiddleware for Express and Hono and paymentProxy for Next.js.
Which blockchains does x402-payments work with?
It supports Base (EVM) using EIP-3009 gasless TransferWithAuthorization and Solana (SVM) using SPL token transfers.