你需要做什么
- 获取收款地址(EIP-7702)
- 创建 PaymentRequirements
- 解析客户端
X-Payment,验证并结算 - 根据结果返回资源或标准 402
前置准备
- Seller 接收地址:前往
app.x402x.ai创建并获取(EIP-7702 部署地址) - Token 使用:
- USD1(BSC):
0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d
- USD1(BSC):
- 安装依赖(服务端):
复制
bun add x402x-server@beta x402x-facilitator@beta express viem
核心流程(服务端)
- 常规
- Express 中间件
- Hono 中间件
创建
server.ts,核心逻辑如下:复制
const client = createPublicClient({ chain: bsc, transport: http() });
const facilitator = new Facilitator({
recipientAddress: RECIPIENT_ADDRESS,
waitUntil: "confirmed",
});
const server = new X402Server({ client, facilitator });
await server.initialize(["0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d"]);
const requirements = await server.createRequirements({
asset: "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d", // USD1
maxAmountRequired: parseEther("0.01").toString(), // 0.01 USD1 (18 decimals)
description: "Premium API",
resource: req.url,
});
const result = await server.process(req.headers["x-payment"] as string, requirements);
if (!result.success) {
// 解析/验证失败:返回标准 402;其他错误按需处理
return res.status(result.status).json(result.response);
}
return res.json({ data, payer: result.data.payer, txHash: result.data.txHash });
查看完整服务端代码(常规)
查看完整服务端代码(常规)
复制
import express from "express";
import { config } from "dotenv";
import { X402Server } from "x402x-server";
import { Facilitator } from "x402x-facilitator";
import { createPublicClient, http } from "viem";
import { parseEther } from "viem";
import { bsc } from "viem/chains";
config();
const app = express();
app.use(express.json());
const PORT = 3939;
const RECIPIENT_ADDRESS=0xSellerRecipientAddress;
async function main() {
// 创建 viem client
const client = createPublicClient({
chain: bsc,
transport: http(),
});
// 创建 facilitator
const facilitator = new Facilitator({
recipientAddress: RECIPIENT_ADDRESS,
waitUntil: "confirmed",
});
// 创建 server
const server = new X402Server({ client, facilitator });
// 预检测当前 token 所支持的支付方案
await server.initialize(["0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d"]);
// 付费 API
app.post("/api/data", async (req, res) => {
try {
// 创建支付要求(可根据业务动态调整金额/描述/资源等)
const requirements = await server.createRequirements({
asset: "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d", // USD1
maxAmountRequired: parseEther("0.01").toString(), // 0.01 USD1
description: "API access",
resource: `http://localhost:${PORT}/api/data`,
});
// 处理支付
const result = await server.process(
req.headers["x-payment"] as string,
requirements
);
if (!result.success) {
return res.status(result.status).json(result.response);
}
// 支付成功,返回数据并可记录对账信息
res.json({
message: "Success!",
data: "This is premium content",
payer: result.data.payer,
txHash: result.data.txHash,
});
} catch (error) {
console.error("Error:", error);
res.status(500).json({
error: "Internal server error",
message: error instanceof Error ? error.message : "Unknown error",
});
}
});
app.listen(PORT, () => {
console.log(`✅ Server running on http://localhost:${PORT}`);
});
}
main();
创建
server.ts,核心逻辑如下:复制
const client = createPublicClient({
chain: bsc,
transport: http(),
});
const facilitator = new Facilitator({
recipientAddress: RECIPIENT_ADDRESS,
});
const server = new X402Server({ client, facilitator });
await server.initialize(["0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d"]);
const paymentMiddleware = createExpressMiddleware({
server,
getToken: () => "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
getAmount: () => parseEther("0.01").toString(),
getConfig: () => ({
description: "Premium API access",
mimeType: "application/json",
}),
});
app.post("/api/data", paymentMiddleware, (req, res) => {
const { payer, txHash } = req.x402!;
res.json({
success: true,
data: "Premium content",
payer,
txHash,
});
});
查看完整服务端代码(Express 中间件)
查看完整服务端代码(Express 中间件)
复制
import express from "express";
import { config } from "dotenv";
import { X402Server, createExpressMiddleware } from "x402x-server";
import { Facilitator } from "x402x-facilitator";
import { createPublicClient, http, parseEther } from "viem";
import { bsc } from "viem/chains";
config();
const app = express();
app.use(express.json());
const PORT = 3939;
async function main() {
const client = createPublicClient({
chain: bsc,
transport: http(),
});
const facilitator = new Facilitator({
recipientAddress: RECIPIENT_ADDRESS,
});
const server = new X402Server({ client, facilitator });
await server.initialize(["0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d"]);
const paymentMiddleware = createExpressMiddleware({
server,
getToken: () => "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
getAmount: () => parseEther("0.01").toString(),
getConfig: () => ({
description: "Premium API access",
mimeType: "application/json",
}),
});
app.get("/health", (_, res) => {
res.json({ status: "ok" });
});
app.post("/api/data", paymentMiddleware, (req, res) => {
const { payer, txHash } = req.x402!;
res.json({
success: true,
data: "Premium content",
payer,
txHash,
});
});
app.listen(PORT, () => {
console.log(`✅ Server running on http://localhost:${PORT}`);
});
}
main();
创建
server.ts,核心逻辑如下:复制
const client = createPublicClient({
chain: bsc,
transport: http(),
});
const facilitator = new Facilitator({
recipientAddress: RECIPIENT_ADDRESS,
});
const server = new X402Server({ client, facilitator });
await server.initialize(["0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d"]);
const paymentMiddleware = createHonoMiddleware({
server,
getToken: () => "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
getAmount: () => parseEther("0.01").toString(),
getConfig: () => ({
description: "Premium API access",
mimeType: "application/json",
}),
});
app.post("/api/data", paymentMiddleware, (c) => {
const { payer, txHash } = c.get("x402");
return c.json({
success: true,
data: "Premium content",
payer,
txHash,
});
});
查看完整服务端代码(Hono 中间件)
查看完整服务端代码(Hono 中间件)
复制
import { config } from "dotenv";
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { createHonoMiddleware, X402Server } from "x402x-server";
import { Facilitator } from "x402x-facilitator";
import { createPublicClient, http, parseEther } from "viem";
import { bsc } from "viem/chains";
config();
// 定义 Hono 环境类型
type Env = {
Variables: {
x402: {
payer: string;
txHash: string;
};
};
};
const ASSET_ADDRESS = '0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d';
const PORT = 3939;
async function main() {
const app = new Hono<Env>();
const client = createPublicClient({
chain: bsc,
transport: http(),
});
const facilitator = new Facilitator({
recipientAddress: RECIPIENT_ADDRESS,
});
const server = new X402Server({ client, facilitator });
await server.initialize([ASSET_ADDRESS]);
const paymentMiddleware = createHonoMiddleware({
server,
getToken: () => ASSET_ADDRESS,
getAmount: () => parseEther("0.01").toString(),
getConfig: () => ({
description: "Premium API access",
mimeType: "application/json",
}),
});
app.get("/health", (c) => {
return c.json({ status: "ok" });
});
app.post("/api/data", paymentMiddleware, (c) => {
const { payer, txHash } = c.get("x402");
return c.json({
success: true,
data: "Premium content",
payer,
txHash,
});
});
serve({
fetch: app.fetch,
port: PORT,
});
console.log(`Server running on port ${PORT}`);
}
main();
推荐做法
- 预热检测缓存:启动时调用
server.initialize(tokens),缩短首次请求耗时 - 快速模式:已知支付类型时可关闭自动检测(
autoDetect: false) - 固定价格复用:对定价接口缓存
requirements并复用,减少创建开销 - 错误分层:parse/verify → 返回 402;settle → 返回 500 并记录日志
常见集成模式
- 固定价格 API:在应用启动时创建并缓存
requirements,请求时直接复用 - 动态计价 API:按请求上下文计算
maxAmountRequired与description
复制
// 固定价格复用示例(启动时)
const cachedRequirements = await server.createRequirements({
asset: "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
maxAmountRequired: parseEther("1").toString(),
description: "Premium API (fixed price)",
resource: "http://localhost:3000/api/data",
mimeType: "application/json",
maxTimeoutSeconds: 3600,
network: "bsc",
});
// 请求时直接使用 cachedRequirements
const result = await server.process(req.headers["x-payment"] as string, cachedRequirements);
错误处理与返回
- 解析/验证失败:直接返回标准 402(
result.status与result.response) - 结算失败或内部异常:返回 500,并记录详细日志(含
payer/txHash如有)