x402 协议是一项开放标准,它重新启用了 HTTP 402 Payment Required 状态码,使服务器可以在返回资源之前要求支付,而客户端——通常是自主代理——可以在同一请求周期内完成支付并重试。本文解释了请求/响应机制,展示了如何通过支付中间件为端点设置支付门槛,并逐步介绍了一套可复现的验证和延迟测量流程。它将有文档记录的协议行为与您必须在自己的技术栈上验证的实现选择区分开来,并在剩余决策是选择哪个链端点时指向 RPC Assistant。
x402 协议实际标准化的内容
x402 协议是一项开放、中立的规范,用于构建在长期休眠的 HTTP 402 Payment Required 状态码之上的互联网原生支付。x402 服务器不会将客户端重定向到结账页面,而是对未付费请求返回 402 响应,其响应体包含机器可读的支付指令:支付多少、支付给谁、在哪个网络上以及采用哪种支付方案。客户端构建支付负载,使用授权头重新提交请求,facilitator 在服务器释放资源之前验证并结算支付。
重要的设计特性是支付协商发生在普通 HTTP 内部。基础流程中没有单独的会话、账户创建或 API 密钥配置步骤。这使得 x402 天然适合 API 变现,也适合需要购买少量数据或计算资源而无需人工完成注册表单的 AI 代理。该规范有意保持网络无关性:它旨在支持多条链和多种价值形式,由各个方案定义确切的负载格式。
本指南将协议视为有文档记录的行为加上您必须测试的实现选择。它不声称任何链或提供商的特定吞吐量、费用或最终性数字;这些取决于您选择的支付通道,并且必须在您自己的部署上进行测量。
- 有文档记录:402 质询/响应结构、响应体中的支付指令,以及基于 facilitator 的验证和结算。
- 您的决策:接受哪个网络和方案、信任哪个 facilitator,以及您的用例能容忍多少延迟。
- 您的测量:端到端付费请求延迟、结算确认时间以及负载下的失败率。
构建前的假设和先决条件
下面的演练假设您有一个 Node.js 服务(Express 风格中间件是最常被文档记录的集成方式)、一个在测试网络上已注资的客户端钱包,以及一个 x402 facilitator 端点的访问权限。它还假设您可以访问结算网络的可靠 RPC 端点,因为 facilitator 和您自己的验证逻辑都需要读取链状态。
将机密信息排除在源代码控制之外:从环境变量加载 PRIVATE_KEY 和任何 facilitator 凭据。使用专用的测试钱包,绝不要使用生产密钥。如果您仍在选择结算网络或 RPC 提供商,多链 RPC 端点指南解释了端点质量如何影响确认可见性,而RPC 端点指南涵盖了认证端点的基础知识。
- Node.js 18+ 和一个包管理器。
- 与您的框架兼容的 x402 服务器 SDK 或中间件包。
- 一个测试网络钱包,其中包含少量被接受资产的余额。
- 一个 facilitator URL 和结算网络的 RPC_URL。
- 一个暂存环境,其中失败的支付不会影响真实客户。
将 x402 请求生命周期作为心智模型
将 x402 视为一个四步握手,可以在单次客户端重试内完成。首先,客户端请求受保护的资源而不支付。其次,服务器响应 402 Payment Required 和一个描述可接受支付选项的响应体。第三,客户端选择一个选项,授权支付,并使用支付头重试原始请求。第四,facilitator 验证负载并结算,服务器返回资源。
有两个细节对调试很重要。402 响应不是传统意义上的错误——它是一个结构化的报价,因此其响应体必须可解析且稳定。而且在许多实现中,结算相对于验证是异步的:facilitator 可能在最终链上结算之前确认有效性,这意味着您的服务器应明确决定是在验证时还是结算时释放资源。记录该选择,因为它决定了您对失败或撤销支付的暴露程度。
- 质询:未付费请求收到 402 加上支付要求。
- 授权:客户端签名或构建支付负载并重试。
- 验证:facilitator 根据声明的要求检查负载。
- 结算:价值在所选通道上转移;服务器释放资源。
使用支付中间件为 API 端点设置支付门槛
最小的有用集成声明哪些路由需要支付以及服务器接受什么。下面的配置是说明性的:用您自己的 facilitator 文档中的值替换占位符网络、资产、收款地址和 facilitator URL。其结构——路由映射加上 accepts 数组——是跨 SDK 通用的部分。
请注意,中间件不包含业务逻辑。它拦截未付费请求,发出 402 质询,并委托验证。您的处理程序仅在支付路径成功后运行,这将变现关注点与数据关注点分开。
import express from "express";
import { paymentMiddleware } from "@x402/express";
const app = express();
app.use(
paymentMiddleware(
{
"GET /v1/weather": {
accepts: [
{
scheme: "exact",
network: "base-sepolia",
maxAmountRequired: "10000",
resource: "https://api.example.com/v1/weather",
payTo: process.env.PAY_TO_ADDRESS,
asset: process.env.SETTLEMENT_ASSET,
maxTimeoutSeconds: 60
}
],
description: "Current weather data"
}
},
{
facilitatorUrl: process.env.FACILITATOR_URL,
rpcUrl: process.env.RPC_URL
}
)
);
app.get("/v1/weather", (req, res) => {
res.json({ temperatureC: 21, condition: "clear" });
});
app.listen(3000, () => console.log("listening on :3000"));使用 curl 检查 402 质询
在编写客户端之前,确认服务器发出格式良好的质询。未付费请求应返回状态 402 和一个您可以解析的 JSON 响应体。确切的字段名取决于您的 SDK 实现的方案版本,因此将下面的输出视为与您的 facilitator 规范进行比较的模板,而不是固定契约。
如果您收到 200 而不是 402,则中间件未匹配路由——检查方法和路径字符串,包括尾部斜杠。如果您收到 500,则中间件在构建质询之前失败,这通常意味着缺少环境变量或 facilitator 不可达。
curl -i https://api.example.com/v1/weather
# Expected shape (fields vary by scheme version):
# HTTP/1.1 402 Payment Required
# Content-Type: application/json
#
# {
# "x402Version": 1,
# "accepts": [
# {
# "scheme": "exact",
# "network": "base-sepolia",
# "maxAmountRequired": "10000",
# "payTo": "0xYourAddress",
# "asset": "0xAssetAddress",
# "resource": "https://api.example.com/v1/weather"
# }
# ]
# }构建付费客户端并确认访问
付费客户端必须解析 402 响应体,选择一个可接受的选项,使用其钱包构建支付负载,并使用支付头重试原始请求。下面的客户端使用钱包密钥和 facilitator 的占位符;控制流——请求、解析、支付、重试——是您应该在自己的语言中复现的。
您要寻找的验证结果很简单:重试的请求返回 200 和资源响应体,并且 facilitator 报告该负载的支付已结算或已验证。将 facilitator 返回的支付标识符与您的请求 ID 一起记录,以便以后对账。
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const wallet = createWalletClient({ account, chain: baseSepolia, transport: http(process.env.RPC_URL) });
const url = "https://api.example.com/v1/weather";
const first = await fetch(url);
if (first.status !== 402) throw new Error(`expected 402, got ${first.status}`);
const challenge = await first.json();
const option = challenge.accepts[0];
console.log("paying", option.maxAmountRequired, "to", option.payTo, "on", option.network);
// Build the payment payload using your x402 client SDK, then retry:
// const payload = await buildPaymentPayload({ wallet, option });
// const paid = await fetch(url, { headers: { "X-PAYMENT": payload } });
// console.log(paid.status, await paid.text());可复现的延迟和成功率测量
协议正确性和协议性能是不同的问题。要测量后者,请对暂存环境运行固定数量的付费请求,并记录每个请求的四个时间戳:收到质询、提交支付、返回资源、确认结算。计算每个区间的中位数和第 95 百分位数,而不是报告单一平均值,因为支付流程具有长尾。
将结果记录在类似下表的表格中,填入您自己的数字。不要复制文档或其他部署中的值——费用、最终性和 facilitator 行为因网络和负载而异。在一天中的不同时间至少运行三次会话,并报告分布范围。
- | 指标 | 会话 1 | 会话 2 | 会话 3 | 备注 |
- | --- | --- | --- | --- | --- |
- | 尝试的请求数 | | | | 固定 N,例如 200 |
- | 收到的 402 质询 | | | | 应等于尝试数 |
- | 返回 200 的付费请求 | | | | 成功率 |
- | 质询到资源的中位数(毫秒) | | | | 客户端时钟 |
- | 质询到资源的 p95(毫秒) | | | | 尾部行为 |
- | 提交到结算的中位数(毫秒) | | | | facilitator 报告 |
- | 失败或过期的支付 | | | | 分类原因 |
排查您实际会遇到的故障
大多数 x402 集成问题分为四类:质询从未出现、客户端无法构建有效负载、facilitator 拒绝支付,或资源已释放但结算后来失败。按此顺序逐一处理,因为每个阶段都依赖于前一个阶段。
对于链级症状——读取结算状态超时、过期的 nonce 或不一致的确认——问题可能是您的 RPC 端点而不是协议。区块链 nonce 解释有助于诊断卡住或重复的交易,如果您需要专用连接进行结算监控,请查看专用节点选项。
- 未返回 402:路由不匹配、中间件顺序问题,或代理剥离了状态码。
- 负载格式错误:服务器质询与客户端 SDK 之间的方案或版本不匹配。
- facilitator 拒绝:
maxTimeoutSeconds过期、payTo错误,或网络不受支持。 - 释放后结算失败:您选择了先验证后释放;添加对账或切换到先结算后释放。
- 间歇性超时:在更改协议代码之前检查 RPC 健康状况和 facilitator 可达性。
权衡:x402 不能为您解决的问题
x402 从支付路径中移除了账户配置,但并未移除运营责任。您仍然拥有密钥管理、重放保护、重试请求的幂等性,以及何时释放资源的策略决策。您还继承了所接受支付通道的最终性和费用特征,这些特征应驱动您的超时和重试设置。
还有一个用户体验权衡。人类用户可能会觉得基于钱包的支付不熟悉,因此许多团队将 x402 与常规计费并行运行,而不是取代它。而且由于标准是网络无关的,接受多个选项增加了灵活性,但也倍增了您必须测试的验证路径。从一个测试网络上的一个方案开始,测量它,然后扩展。
最后,facilitator 的选择是一个信任决策。一个验证但不结算、或结算缓慢的 facilitator 会改变您的风险状况。阅读其文档,确认它执行哪些步骤,并独立监控结算,而不是仅依赖其 API 响应。
后续步骤:从暂存到受监控的部署
一旦单个端点通过上述验证流程,将其置于功能标志之后,并在增加流量之前添加可观测性。为质询发出、支付验证、结算确认和资源释放发出结构化日志,全部通过请求 ID 关联。对质询与成功支付的比率设置警报,因为突然下降通常表明客户端或 facilitator 回归,而不是需求变化。
如果您的结算监控依赖于链读取,请选择一个您可以推理的端点。RPC 定价页面解释了计划如何映射到请求量,而OnFinality 的 API 服务描述了跨支持网络的托管访问。对于哪个端点或提供商适合您的结算网络这一具体问题,请使用 RPC Assistant 而不是猜测——它正是为此类简短推荐而构建的。浏览支持的网络以确认您的目标链在承诺某个方案之前已被覆盖。
- 在功能标志后发布一个付费路由;保留未付费路径以供比较。
- 添加幂等键,以便客户端重试不会重复收费。
- 每天将 facilitator 支付 ID 与您自己的请求日志对账。
- 在任何 facilitator、SDK 或 RPC 变更后重新运行延迟表。