x402 Fastify 支付中间件实战:用 HTTP 402 协议为 Fastify API 接入按次付费墙
【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402
导读
本文基于 x402 仓库中的 examples/typescript/servers/fastify/README.md 及其配套源码,完整讲解如何用@x402/fastify中间件为 Fastify 服务端接入 x402 支付协议,实现「付费后才能访问 API 端点」的 HTTP 402 按量付费墙。读完本文,你将掌握paymentMiddleware的路由配置方法、x402ResourceServer与ExactEvmScheme/ExactSvmScheme的注册方式、HTTPFacilitatorClient的对接要点,以及402 Payment Required/PAYMENT-REQUIRED/PAYMENT-RESPONSE头部的完整交互格式,并能在本地把示例服务器与 Fetch、Axios 示例客户端跑通。
示例服务器概览:一个付费的天气 API
示例服务器位于 examples/typescript/servers/fastify,它基于 Fastify(v5,见 package.json)构建,仅暴露一个示例端点GET /weather。访问该端点需要支付0.001 USDC,并支持两条可选支付网络:
- Base Sepolia(
eip155:84532),使用 EVM 精确支付方案; - Solana Devnet(
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1),使用 SVM 精确支付方案。
支付成功后,端点返回一份简单的天气报告:{"report":{"weather":"sunny","temperature":70}}。服务器的监听端口固定为4021(与 HTTP 状态码 402 呼应),具体见 index.ts。
前置条件
运行该示例需要:
- Node.js v20+(建议通过 nvm 安装);
- pnpm v10(workspace 安装与构建依赖 pnpm);
- 有效的 EVM 与 SVM 收款地址(分别对应
EVM_ADDRESS与SVM_ADDRESS); - 支持目标支付网络的 facilitator 服务 URL。
需要说明的是:x402 协议中,facilitator 负责在链上验证并结算支付。示例服务器本身不直接连接链上 RPC 结算,而是把验证与结算委托给 facilitator 完成,因此必须配置一个可用的 facilitator 端点(FACILITATOR_URL)。
环境变量与启动流程
1. 配置环境变量
示例目录下提供了.env-local模板,先复制为.env:
cp .env-local .env然后填入三个必填环境变量:
| 变量 | 说明 |
|---|---|
FACILITATOR_URL | Facilitator 服务端点 URL,负责支付验证与链上结算 |
EVM_ADDRESS | 接收 EVM 支付的以太坊地址(0x前缀) |
SVM_ADDRESS | 接收 Solana 支付的地址 |
从 index.ts 的源码可以看出,这三个变量缺一不可:缺失时程序会打印Missing required environment variables或❌ FACILITATOR_URL environment variable is required并直接process.exit(1)。此外,index.ts通过dotenv的config()在模块加载时自动加载.env,因此启动前务必完成第一步。
2. 安装并构建全部包
示例属于 TypeScript examples 的 pnpm workspace,需要先回到 examples 根目录安装并构建所有依赖包(包括@x402/core、@x402/fastify、@x402/evm、@x402/svm、@x402/extensions,见 package.json):
cd ../../ pnpm install && pnpm build cd servers/fastify3. 启动服务器
pnpm devdev脚本使用tsx index.ts直接运行 TypeScript(见 package.json),无需先编译。启动成功后终端会输出Server listening at http://...:4021。
核心代码逐行拆解
完整示例代码见 index.ts,其骨架如下:
import { config } from "dotenv"; import Fastify from "fastify"; import { paymentMiddleware, x402ResourceServer } from "@x402/fastify"; import { ExactEvmScheme } from "@x402/evm/exact/server"; import { ExactSvmScheme } from "@x402/svm/exact/server"; import { HTTPFacilitatorClient } from "@x402/core/server"; config(); const evmAddress = process.env.EVM_ADDRESS as `0x${string}`; const svmAddress = process.env.SVM_ADDRESS; const facilitatorUrl = process.env.FACILITATOR_URL; // ... 环境变量校验 ... const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl }); const app = Fastify(); paymentMiddleware( app, { "GET /weather": { accepts: [ { scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: evmAddress, }, { scheme: "exact", price: "$0.001", network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", payTo: svmAddress, }, ], description: "Weather data", mimeType: "application/json", }, }, new x402ResourceServer(facilitatorClient) .register("eip155:84532", new ExactEvmScheme()) .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()), ); app.get("/weather", async () => { return { report: { weather: "sunny", temperature: 70 }, }; }); app.listen({ port: 4021 }, (err, address) => { /* ... */ });代码由三部分构成:
HTTPFacilitatorClient(来自@x402/core/server,实现见 httpFacilitatorClient.ts):封装对 facilitator 的 HTTP 通信,仅需传入url。paymentMiddleware(app, routes, resourceServer):注册路由支付配置并注入支付处理逻辑。x402ResourceServer:以链式register(network, schemeServer)注册各网络的支付方案服务端实现——ExactEvmScheme与ExactSvmScheme分别对应 EVM/SVM 的 exact 精确支付方案(服务端实现见 mechanisms/evm/src/exact/server/scheme.ts 与 mechanisms/svm/src/exact/server/scheme.ts)。
注意:原 README 中的示意代码只注册了 EVM 方案,而仓库中真实的 index.ts 同时注册了 EVM 与 SVM 两条链,并以accepts数组的形式为/weather端点声明了两种可接受的支付方式,属于更完整的多网络写法。
路由配置结构(RoutesConfig)
paymentMiddleware的第二个参数是一个以"METHOD /path"为键的路由配置对象,键必须包含 HTTP 方法与路径。单个端点的配置字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
accepts | 对象或数组 | 可接受的支付方式;传数组表示支持多条网络/多种定价 |
scheme | 字符串 | 支付方案,如"exact"(精确支付,对应 upto 的"上限支付"方案) |
price | 字符串 | 人类可读价格,如"$0.001",由中间件解析为对应资产的原子单位 |
network | 字符串 | CAIP-2 网络标识,如eip155:84532、solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 |
payTo | 字符串 | 收款地址(EVM 为0x开头,SVM 为 Base58 地址) |
description | 字符串 | 资源描述,会进入支付要求(payment requirements)展示给客户端 |
mimeType | 字符串 | 资源 MIME 类型,如application/json |
中间件底层如何工作
paymentMiddleware的真正实现位于 typescript/packages/http/fastify/src/index.ts:它内部构造一个x402HTTPResourceServer,随后通过paymentMiddlewareFromHTTPServer在 Fastify 实例上注册两个核心钩子(见同文件 index.ts):
onRequest钩子:把 Fastify 请求包装为FastifyAdapter,判断路径是否需要支付(requiresPayment);若需要,调用processHTTPRequest校验支付头(payment-signature或x-payment)。校验失败返回 402 及支付要求;校验通过则把支付上下文挂到request.x402Context,放行到业务路由。onSend钩子:在响应发送前调用processSettlement完成链上结算,成功后在响应头写入PAYMENT-RESPONSE;若结算失败则改写响应为 402。对未受保护的路由或 4xx/5xx 响应则跳过结算。
其中FastifyAdapter实现了@x402/core的HTTPAdapter接口,负责屏蔽框架差异(头部大小写归一化、URL/路径/查询参数提取、body 获取等),源码见 adapter.ts,其行为由 adapter.test.ts 中的单元测试覆盖,例如getHeader大小写不敏感、getPath会去掉查询串、getUrl拼接protocol://host + url等。
用示例客户端验证付费流程
示例服务器配套了 Fetch 与 Axios 两种客户端,分别位于 examples/typescript/clients/fetch 与 examples/typescript/clients/axios。在各自目录下确认.env已配置后运行:
cd ../clients/fetch pnpm devcd ../clients/axios pnpm dev这些客户端会完整演示 x402 协议的三个标准步骤:
- 首次请求:不带支付信息访问受保护端点,收到
402 Payment Required与支付要求; - 处理支付要求:客户端解析支付要求,签名并构造支付 token(通过 Permit2 等机制授权支付);
- 二次请求:携带支付 token 再次访问,拿到 200 响应与真实业务数据。
这也是 x402「先要价、后付款、再放行」的 HTTP 原生交互模型,客户端与服务器之间不依赖任何私有 SDK 约定。
响应格式详解
未支付:402 Payment Required
未携带有效支付信息时,服务器返回:
HTTP/1.1 402 Payment Required Content-Type: application/json; charset=utf-8 PAYMENT-REQUIRED: <base64-encoded JSON> {}其中PAYMENT-REQUIRED头包含 base64 编码的支付要求 JSON,结构如下(示例来自 README,金额已换算为原子单位):
{ "x402Version": 2, "error": "Payment required", "resource": { "url": "http://localhost:4021/weather", "description": "Weather data", "mimeType": "application/json" }, "accepts": [ { "scheme": "exact", "network": "eip155:84532", "amount": "1000", "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "payTo": "0x1c47E9C085c2B7458F5b6C16cCBD65A65255a9f6", "maxTimeoutSeconds": 300, "extra": { "name": "USDC", "version": "2", "resourceUrl": "http://localhost:4021/weather" } }, { "scheme": "exact", "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", "amount": "1000", "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", "payTo": "FV6JPj6Fy12HG8SYStyHdcecXYmV1oeWERAokrh4GQ1n", "maxTimeoutSeconds": 300, "extra": { "feePayer": "...", "resourceUrl": "http://localhost:4021/weather" } } ] }关键点:
- 金额使用原子单位。例如
amount: "1000"表示 0.001 USDC(USDC 为 6 位小数,1000 / 10^6 = 0.001)。这是price: "$0.001"被方案服务端解析后的结果,开发者对接时切勿把原子单位当作小数直接使用。 accepts是数组,对应配置中的多网络accepts,客户端可任选其一完成支付。asset为代币合约地址(EVM)或代币 Mint 地址(SVM),payTo为收款方。maxTimeoutSeconds: 300表示支付有效期上限。extra中携带协议内部元数据(如资源 URL、SVM 的feePayer),客户端应原样回传。
支付成功:200 OK
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 PAYMENT-RESPONSE: <base64-encoded JSON> {"report":{"weather":"sunny","temperature":70}}PAYMENT-RESPONSE头包含 base64 编码的结算详情 JSON:
{ "success": true, "transaction": "0x...", "network": "eip155:84532", "payer": "0x...", "requirements": { "scheme": "exact", "network": "eip155:84532", "amount": "1000", "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "payTo": "0x...", "maxTimeoutSeconds": 300, "extra": { "name": "USDC", "version": "2", "resourceUrl": "http://localhost:4021/weather" } } }transaction为链上交易哈希,network为实际结算网络,requirements回显了本次支付实际采用的要求(用于对账与审计)。
扩展示例:增加更多付费端点
新增付费端点的模式非常固定:先扩展paymentMiddleware的路由配置,再像普通 Fastify 路由一样定义业务逻辑:
// 1. 在中间件中声明新端点的支付要求 paymentMiddleware( app, { "GET /your-endpoint": { accepts: { scheme: "exact", price: "$0.10", network: "eip155:84532", payTo: evmAddress, }, description: "Your endpoint description", mimeType: "application/json", }, }, resourceServer, ); // 2. 按常规方式实现业务路由 app.get("/your-endpoint", async () => { return { // Your response data }; });路径键同样支持通配符等 Fastify 风格的匹配模式(@x402/fastify包 README 中即展示了"GET /api/premium/*"这类多路由配置,见 typescript/packages/http/fastify/README.md)。
网络标识采用 CAIP-2 格式
network字段遵循 CAIP-2 链标识规范,常用示例:
| 网络标识 | 对应网络 |
|---|---|
eip155:84532 | Base Sepolia(测试网) |
eip155:8453 | Base Mainnet(主网) |
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 | Solana Devnet(测试网) |
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | Solana Mainnet(主网) |
x402ResourceServer 配置
x402ResourceServer采用链式 builder 模式注册各网络的支付方案服务端,声明每个网络应如何被处理:
const resourceServer = new x402ResourceServer(facilitatorClient) .register("eip155:*", new ExactEvmScheme()) // 匹配所有 EVM 链 .register("solana:*", new ExactSvmScheme()); // 匹配所有 SVM 链register的第一个参数是网络标识,支持*通配符批量匹配同一体系的所有链(如eip155:*),示例服务器则使用了精确匹配(eip155:84532);- 第二个参数是
SchemeNetworkServer实现,本示例用到的是 exact(精确支付)方案。
除paymentMiddleware(传入预构建的 resourceServer)外,@x402/fastify还提供两个同族 API(见 typescript/packages/http/fastify/src/index.ts):
paymentMiddlewareFromHTTPServer(app, httpServer, ...):直接传入预配置的x402HTTPResourceServer,适合需要定制 HTTP 层钩子的场景;paymentMiddlewareFromConfig(app, routes, facilitatorClients, schemes, ...):由中间件内部创建 resourceServer,仅需传入 facilitator 客户端与方案注册数组,适合快速上手。
三者共享可选的paywallConfig、paywall(自定义支付页 Provider)与syncFacilitatorOnStart(启动时是否同步 facilitator,默认true)参数。默认情况下,当浏览器(Accept: text/html)访问受保护端点时会返回一个内建的基础 HTML 支付说明页;如需完整的钱包连接与支付 UI,可安装@x402/paywall并传入paywallConfig(如{ appName, appLogo, testnet })。
Facilitator 配置
HTTPFacilitatorClient负责与 facilitator 服务通信,由 facilitator 在链上完成支付验证与结算:
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl }); // 或者传入多个 facilitator 以提升可用性 const facilitatorClient = [ new HTTPFacilitatorClient({ url: primaryFacilitatorUrl }), new HTTPFacilitatorClient({ url: backupFacilitatorUrl }), ];- 单个
HTTPFacilitatorClient只需配置url; - 传入数组即实现多 facilitator 冗余:
x402ResourceServer会按顺序/策略轮询,主服务不可用时回退到备用服务; - 需要鉴权的自建 facilitator 还可以在构造时传入
createAuthHeaders,为 verify 与 settle 请求分别生成请求头(参考 typescript/packages/http/fastify/README.md)。
下一步:进阶能力
本示例演示的是最基础的 exact 付费墙。仓库中的 examples/typescript/servers/advanced 提供了更丰富的进阶场景:
- Bazaar discovery—— 让 API 可被发现(对接 Bazaar 市场发现机制);
- Dynamic pricing—— 基于请求上下文动态定价;
- Dynamic payTo—— 按请求把支付路由到不同收款方;
- Lifecycle hooks—— 在 verify/settle 阶段注入自定义逻辑;
- Custom tokens—— 接受自定义代币支付。
对应的实现细节可在 typescript/packages/extensions 的扩展包与 typescript/packages/mechanisms 的方案实现中找到;x402 协议本身的完整规范参见 specs/x402-specification-v2.md 与 specs/transports-v2/http.md。
小结
通过@x402/fastify,只需三步即可让 Fastify 端点变成按次付费资源:配置accepts支付要求、用x402ResourceServer.register()注册网络方案、通过HTTPFacilitatorClient对接 facilitator。整个支付协商过程完全构建在 HTTP 语义之上(402 状态码 +PAYMENT-REQUIRED/PAYMENT-RESPONSE头部),无需改动路由业务逻辑,客户端也只需遵循标准的三步交互即可完成付款访问。配合多网络accepts数组、通配符方案注册与多 facilitator 冗余,该中间件可以平滑支撑从测试网演示到主网生产的多链付费 API 场景。
【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考