通过 HTTP 402 状态码实现无信任微支付,AI Agent 可在请求/响应循环中直接签署 EIP-712 完成链上支付,无需长期密钥。
当下的 AI Agent 在调用第三方服务(LLM 推理、数据源、算力)时,通常面临两种选择:
嵌入静态 API Key —— 这会造成单点故障,Agent 还必须信任提供商持有的长期凭证。
依赖链下计费 —— 增加延迟,需要管理账户,更重要的是破坏了自主 Agent 应有的"无状态"特性。
x402 规范重新启用了 HTTP 402 Payment Required 状态码,定义了一种轻量、无状态的方案:将 ERC-20 支付(示例中使用 Base 上的 USDC)直接附加到请求/响应周期中。Agent 无需存储长期密钥,只需一个能签署 EIP-712 签名消息的钱包即可。
由于支付是通过签名在链下验证的,实际的 ERC-20 转账可以在 Layer-2(Base)上完成,Gas 成本几乎为零。服务端只需确认转账是否成功——无需为每个请求部署合约。
下面是一个极简的 Express 处理器,用 x402 保护路由。它使用 @coinbase/x402 npm 包(官方参考实现)来生成挑战并验证收据。
// server.ts
import express from 'express';
import { createX402Middleware, X402Config } from '@coinbase/x402';
import { ethers } from 'ethers';
const app = express();
// 1️⃣ 配置 – 根据你的 token/链/收款人调整
const config: X402Config = {
// Base 主网上的 USDC 地址
tokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
// Base 的链 ID (8453)
chainId: 8453,
// 接收付款的钱包地址
recipient: '0xYourAgentServiceWallet',
// 最小单位的金额(USDC 有 6 位小数)
amount: ethers.parseUnits('0.05', 6).toString(), // 每次调用 $0.05
// 可选:nonce 生成器,防止重放
nonce: () => ethers.randomBytes(32).toString('hex'),
};
app.use('/paid-agent', createX402Middleware(config));
// 受保护的端点 – 仅在支付有效后才能访问
app.get('/paid-agent/hello', (req, res) => {
res.json({ message: 'Hello from your paid AI agent!' });
});
app.listen(3000, () => console.log('x402 server listening on :3000'));
createX402Middleware 会检查 x402-Pay 请求头。
如果缺失或无效,则返回 402 状态码及如下 JSON 正文:
{
"scheme": "exact",
"network": "base",
"token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "50000",
"recipient": "0xYourAgentServiceWallet",
"nonce": "a1b2c3d4e5f6..."
}
客户端必须对上述所有字段加上时间戳进行签名,生成一份收据。
Agent 需要一个签名器(例如连接了私钥的 ethers.js Wallet,或通过 MetaMask 连接的硬件钱包)。以下代码展示了一个通用的 fetch 封装,会自动处理 402 挑战。
// agent-client.ts
import { ethers } from 'ethers';
import fetch from 'node-fetch';
// 1️⃣ 设置签名器 – 替换为你自己的密钥管理
const privateKey = process.env.PRIVATE_KEY!; // 生产环境切勿硬编码
const signer = new ethers.Wallet(privateKey);
const provider = new ethers.JsonRpcProvider('https://base.mainnet.rpc.url');
const wallet = signer.connect(provider);
// 2️⃣ 辅助函数:构建 x402 期望的 EIP-712 类型数据
function buildPaymentRequest(challenge: any) {
return {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Payment: [
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'token', type: 'address' },
{ name: 'nonce', type: 'bytes32' },
{ name: 'timestamp', type: 'uint256' },
],
},
domain: {
name: 'x402 Payment',
version: '1',
chainId: challenge.chainId,
verifyingContract: ethers.ZeroAddress, // 无合约,仅签名
},
primaryType: 'Payment',
message: {
recipient: challenge.recipient,
amount: challenge.amount,
token: challenge.token,
nonce: challenge.nonce,
timestamp: Math.floor(Date.now() / 1000),
},
};
}
// 3️⃣ 核心请求函数,带 402 处理逻辑
async function x402Fetch(url: string, init: RequestInit = {}): Promise<Response> {
let response = await fetch(url, init);
// 如果收到 402,解析挑战内容,签名,然后重试
if (response.status === 402) {
const challenge = await response.json();
const payRequest = buildPaymentRequest(challenge);
const signature = await wallet.signTypedData(
payRequest.domain,
payRequest.types,
payRequest.message
);
// 将签名附加为 x402-Pay 请求头
const signedInit = { ...init, headers: { ...init.headers, 'x402-Pay': signature } };
response = await fetch(url, signedInit);
}
return response;
}
// 4️⃣ 示例用法 – 调用付费 Agent 端点
(async () => {
const res = await x402Fetch('http://localhost:3000/paid-agent/hello');
if (!res.ok) {
throw new Error(`Agent call failed: ${res.status} ${res.statusText}`);
}
const data = await res.json();
console.log('Agent replied:', data);
})();
签名器只需签署一条类型消息即可;Agent 不必发送任何链上交易。
实际的 USDC 转账由付款方(Agent 的钱包)在链下执行——服务端授权后通过简单的 transferFrom 完成,或通过服务端轮询的托管合约完成。在许多实现中,服务端会运行一个轻量级的链下验证服务来确认支付收据的有效性。