性能优化
1. 缓存预热
在应用启动时预热 Token 检测缓存:复制
const server = new X402Server({ client, facilitator });
// 预热常用 Token(非阻塞)
server.initialize([
"0x25d066c4C68C8A6332DfDB4230263608305Ca991", // USDC
]).then(result => {
if (result.success) {
console.log(`✅ Cached ${result.detected} tokens`);
}
});
2. 快速模式
对于已知支付类型的 Token,使用快速模式跳过检测:复制
const requirements = await server.createRequirements({
asset: "0x25d066c4C68C8A6332DfDB4230263608305Ca991",
maxAmountRequired: "1000000",
paymentType: "permit",
autoDetect: false, // <1ms
});
3. 复用 Requirements
对于固定金额的 API,复用 requirements 对象:复制
// 创建一次,复用多次
const cachedRequirements = await server.createRequirements({
asset: "0xUSDC",
maxAmountRequired: "1000000",
});
app.post("/api", async (req, res) => {
const result = await server.process(
req.headers["x-payment"] as string,
cachedRequirements // 复用
);
// ...
});
安全性
1. 金额限制
客户端必须设置最大支付金额限制:复制
const fetchWithPay = wrapFetchWithPayment(
fetch,
client,
"10000000" // 最多支付 10 USDC
);
多链部署
为不同链创建独立实例
复制
// BSC
const bscClient = createPublicClient({
chain: bsc,
transport: http(),
});
const bscServer = new X402Server({
client: bscClient,
facilitator
});
// Polygon
const polygonClient = createPublicClient({
chain: polygon,
transport: http(),
});
const polygonServer = new X402Server({
client: polygonClient,
facilitator
});
// 根据请求选择
app.post("/api", async (req, res) => {
const network = req.headers["x-network"];
const server = network === "polygon" ? polygonServer : bscServer;
const requirements = await server.createRequirements({
asset: 'xxx',
maxAmountRequired: "1000000",
});
const result = await server.process(
req.headers["x-payment"] as string,
requirements
);
// ...
});
错误处理
根据错误阶段处理
根据错误阶段提供不同的处理策略:复制
const result = await server.process(paymentHeader, requirements);
if (!result.success) {
switch (result.errorStage) {
case "parse":
// 支付头格式错误 - 客户端问题
console.error("Invalid payment header");
return res.status(402).json({
...result.response,
userMessage: "支付信息格式错误,请重试",
});
case "verify":
// 签名验证失败 - 客户端问题
console.error("Signature verification failed");
return res.status(402).json({
...result.response,
userMessage: "支付验证失败,请重新签名",
});
case "settle":
// 链上结算失败 - 服务端问题
console.error("Settlement failed:", result.error);
// 可能需要报警或重试机制
return res.status(500).json({
...result.response,
userMessage: "服务暂时不可用,请稍后重试",
});
}
}
统一错误处理
复制
app.post("/api", async (req, res) => {
try {
const requirements = await server.createRequirements({
asset: "0xUSDC",
maxAmountRequired: "1000000",
});
const result = await server.process(
req.headers["x-payment"] as string,
requirements
);
if (!result.success) {
// 根据错误阶段记录日志
switch (result.errorStage) {
case "parse":
console.error("支付头解析失败:", result.response.error);
break;
case "verify":
console.error("签名验证失败:", result.response.error);
break;
case "settle":
console.error("链上结算失败:", result.error);
break;
}
// 返回对应的状态码
return res.status(result.status).json(result.response);
}
res.json({ data: "success" });
} catch (error) {
console.error("Unexpected error:", error);
res.status(500).json({
error: "Internal server error",
message: error instanceof Error ? error.message : "Unknown error"
});
}
});
客户端错误处理
复制
try {
const response = await fetchWithPay(apiUrl, {
method: "POST",
});
if (!response.ok) {
if (response.status === 402) {
const error = await response.json();
console.error("Payment required:", error);
} else {
console.error("Request failed:", response.status);
}
return;
}
const data = await response.json();
console.log("Success:", data);
} catch (error) {
console.error("Error:", error);
}
服务端错误处理
复制
app.post("/api", async (req, res) => {
try {
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({ data: "content" });
} catch (error) {
console.error("Payment processing error:", error);
res.status(500).json({
error: "Internal server error",
message: error instanceof Error ? error.message : "Unknown error",
});
}
});
常见问题
Q1: 如何选择合适的授权类型?
A: 使用自动检测(默认)或参考以下建议:- EIP-3009:Token 原生支持(如 USDC)- 最高效
- EIP-2612 Permit:标准 ERC-20 - 兼容性好
- Permit2:需要通用授权管理 - 最灵活
Q2: 如何处理不同精度的 Token?
A: 注意使用正确的单位(wei):复制
// USDC (6 decimals): 1 USDC = 1000000
const usdcAmount = "1000000"; // 1 USDC
// DAI (18 decimals): 1 DAI = 1000000000000000000
const daiAmount = "1000000000000000000"; // 1 DAI
Q3: 如何处理支付失败?
A: 根据错误阶段采取不同的处理策略:复制
const result = await server.process(paymentHeader, requirements);
if (!result.success) {
// 检查错误阶段
switch (result.errorStage) {
case "parse":
// 支付头格式错误 - 客户端问题
console.error("Invalid payment header format");
return res.status(402).json(result.response);
case "verify":
// 签名验证失败 - 客户端问题
console.error("Signature verification failed");
return res.status(402).json(result.response);
case "settle":
// 链上结算失败 - 服务端问题
console.error("Settlement failed:", result.error);
// 可能需要报警或重试机制
return res.status(500).json(result.response);
}
}
Q4: 什么时候应该返回 500 而不是 402?
A:- 402: 客户端问题(parse/verify 失败)- 客户端需要修正
- 500: 服务端问题(settle 失败)- 可能是网络问题、gas 不足等