跳转到主要内容

性能优化

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 不足等
结算失败通常是临时性的服务端问题,应该返回 500 并建议用户稍后重试。

下一步