创建x402Facilitator服务 create-typescript-x402-facilitator

构建验证Algorand支付交易的Facilitator服务,并在链上结算,可选的Bazaar发现扩展用于API目录。

链开发 0 次安装 0 次浏览 更新于 3/4/2026

创建x402 Facilitator服务

构建验证Algorand支付交易的Facilitator服务,并在链上结算,可选的Bazaar发现扩展用于API目录。当构建一个验证支付交易、在链上结算、实现FacilitatorAvmSigner、创建Express.js Facilitator服务器、集成Bazaar发现以索引付费资源或从支付流程中提取发现元数据的Facilitator时使用。强触发词包括“创建x402 Facilitator”、“FacilitatorAvmSigner”、“验证和结算支付”、“Facilitator服务器”、“Bazaar发现”、“x402Facilitator”、“支付结算”、“Facilitator签名者”、“目录付费API”。

先决条件

在使用此技能之前,请确保:

  1. 支持TypeScript的Node.js
  2. 拥有Algorand账户和ALGO,用于支付结算期间的交易费用
  3. 安装algosdk,用于交易签名、模拟和提交

Facilitator核心工作流程:Facilitator的作用

Facilitator是资源服务器和区块链之间的受信任中介。它执行两个操作:

  1. 验证 – 确认支付交易组有效(正确的金额、收款人、签名、时间)而不提交到网络
  2. 结算 – 将验证过的交易组提交到Algorand网络,共同签名支付费用交易
客户端                    资源服务器              Facilitator              Algorand
  |                            |                            |                      |
  |--- 请求+支付 ------>|                            |                      |
  |                            |--- 验证(payload) ------>|                      |
  |                            |<-- { isValid: true } ------|                      |
  |                            |--- 结算(payload) ------>|                      |
  |                            |                            |--- 签名费用txn ---->|
  |                            |                            |--- 发送组 ------>|
  |                            |                            |<-- 确认 ------>|
  |                            |<-- { success, txId } ------|                      |
  |<--- 200 + 内容 ---------|                            |                      |

如何进行

第1步:安装依赖项

npm install @x402-avm/core @x402-avm/avm algosdk express

对于Bazaar发现扩展:

npm install @x402-avm/extensions

第2步:实现FacilitatorAvmSigner

FacilitatorAvmSigner接口将Facilitator连接到Algorand区块链。它处理签名、模拟、提交和确认:

import algosdk from "algosdk";
import type { FacilitatorAvmSigner } from "@x402-avm/avm";

const secretKey = Buffer.from(process.env.AVM_PRIVATE_KEY!, "base64");
const address = algosdk.encodeAddress(secretKey.slice(32));
const algodClient = new algosdk.Algodv2("", "https://testnet-api.algonode.cloud", "");

const facilitatorSigner: FacilitatorAvmSigner = {
  getAddresses: () => [address],

  signTransaction: async (txn: Uint8Array, senderAddress: string) => {
    const decoded = algosdk.decodeUnsignedTransaction(txn);
    const signed = algosdk.signTransaction(decoded, secretKey);
    return signed.blob;
  },

  getAlgodClient: (network: string) => algodClient,

  simulateTransactions: async (txns: Uint8Array[], network: string) => {
    const stxns = txns.map((txnBytes) => {
      try {
        return algosdk.decodeSignedTransaction(txnBytes);
      } catch {
        const txn = algosdk.decodeUnsignedTransaction(txnBytes);
        return new algosdk.SignedTransaction({ txn });
      }
    });
    const request = new algosdk.modelsv2.SimulateRequest({
      txnGroups: [
        new algosdk.modelsv2.SimulateRequestTransactionGroup({ txns: stxns }),
      ],
      allowEmptySignatures: true,
    });
    return algodClient.simulateTransactions(request).do();
  },

  sendTransactions: async (signedTxns: Uint8Array[], network: string) => {
    const combined = Buffer.concat(signedTxns.map((t) => Buffer.from(t)));
    const { txId } = await algodClient.sendRawTransaction(combined).do();
    return txId;
  },

  waitForConfirmation: async (txId: string, network: string, waitRounds = 4) => {
    return algosdk.waitForConfirmation(algodClient, txId, waitRounds);
  },
};

第3步:创建和注册Facilitator

import { x402Facilitator } from "@x402-avm/core/facilitator";
import { registerExactAvmScheme } from "@x402-avm/avm/exact/facilitator";
import { ALGORAND_TESTNET_CAIP2 } from "@x402-avm/avm";

const facilitator = new x402Facilitator();

registerExactAvmScheme(facilitator, {
  signer: facilitatorSigner,
  networks: ALGORAND_TESTNET_CAIP2,
});

第4步:创建Express.js服务器

import express from "express";

const app = express();
app.use(express.json());

app.get("/supported", async (_req, res) => {
  const supported = facilitator.getSupportedNetworks();
  res.json(supported);
});

app.post("/verify", async (req, res) => {
  const { paymentPayload, paymentRequirements } = req.body;
  const result = await facilitator.verify(paymentPayload, paymentRequirements);
  res.json(result);
});

app.post("/settle", async (req, res) => {
  const { paymentPayload, paymentRequirements } = req.body;
  const result = await facilitator.settle(paymentPayload, paymentRequirements);
  res.json(result);
});

app.listen(4000, () => console.log("Facilitator运行在:4000"));

第5步:添加Bazaar发现扩展(可选)

Bazaar扩展允许自动编目x402保护的资源。当资源服务器声明发现元数据时,Facilitator可以索引并提供发现API:

在资源服务器端——声明发现信息:

import { declareDiscoveryExtension } from "@x402-avm/extensions";

const weatherDiscovery = declareDiscoveryExtension({
  input: { city: "San Francisco", units: "metric" },
  inputSchema: {
    properties: {
      city: { type: "string" },
      units: { type: "string", enum: ["metric", "imperial"] },
    },
    required: ["city"],
  },
  output: {
    example: { temperature: 18.5, condition: "Partly Cloudy", humidity: 65 },
  },
});

在Facilitator端——提取和编目:

import { extractDiscoveryInfo, type DiscoveredResource } from "@x402-avm/extensions";

facilitator.onAfterSettle(async (context) => {
  if (context.result.success) {
    const discovered = extractDiscoveryInfo(
      context.paymentPayload,
      context.requirements,
    );

    if (discovered) {
      console.log("编目:", discovered.resourceUrl, discovered.method);
      // 存储在数据库中以供发现API使用
    }
  }
});

第6步:添加生命周期钩子(可选)

facilitator.onBeforeVerify(async (context) => {
  console.log(`验证支付 ${context.requirements.resource}`);
});

facilitator.onAfterSettle(async (context) => {
  if (context.result.success) {
    console.log(`结算:${context.result.txId}`);
  }
});

重要规则/指南

  1. Facilitator需要ALGO — Facilitator地址必须有ALGO以支付结算期间的交易费用
  2. 私钥安全 — 安全存储AVM_PRIVATE_KEY。Facilitator在每个组中共同签名支付费用交易
  3. 结算前的模拟simulateTransactions方法必须用new algosdk.SignedTransaction({ txn })包装未签名的交易,并使用allowEmptySignatures: true
  4. sendTransactions期望签名字节 — 在调用sendRawTransaction之前,使用Buffer.concat()连接所有签名的交易字节
  5. 网络注册 — 使用ALGORAND_TESTNET_CAIP2ALGORAND_MAINNET_CAIP2常量,而不是SDK代码中的字符串字面量
  6. Bazaar是可选的 — Bazaar发现扩展增加了编目能力,但不是Facilitator基本操作所必需的

FacilitatorAvmSigner接口

interface FacilitatorAvmSigner {
  /** 返回此签名者控制的地址列表 */
  getAddresses(): string[];

  /** 为给定的发送者地址签名单个交易 */
  signTransaction(txn: Uint8Array, senderAddress: string): Promise<Uint8Array>;

  /** 为指定的网络获取Algodv2客户端 */
  getAlgodClient(network: string): algosdk.Algodv2;

  /** 模拟交易组(用于不提交的验证) */
  simulateTransactions(txns: Uint8Array[], network: string): Promise<any>;

  /** 将签名的交易发送到网络 */
  sendTransactions(signedTxns: Uint8Array[], network: string): Promise<string>;

  /** 等待交易被确认 */
  waitForConfirmation(
    txId: string,
    network: string,
    waitRounds?: number,
  ): Promise<any>;
}

Bazaar发现架构

资源服务器                        Facilitator                    客户端
     |                                      |                           |
     |-- declareDiscoveryExtension() ------>|                           |
     |   (PaymentRequired中的扩展)            |                           |
     |                                      |                           |
     |                  客户端支付 ------->|                           |
     |                                      |-- extractDiscoveryInfo()  |
     |                                      |  编目资源         |
     |                                      |                           |
     |                                      |<--- /discovery/resources -|
     |                                      |---> 资源列表 --->|

常见错误/故障排除

错误 原因 解决方案
signer not found 没有AVM_PRIVATE_KEY或格式错误 确保Base64编码的64字节密钥
模拟失败 混合签名/未签名交易 new algosdk.SignedTransaction({ txn })包装未签名的
sendRawTransaction失败 交易组未正确连接 使用Buffer.concat(signedTxns.map(t => Buffer.from(t)))
结算超时 网络拥堵或费用低 增加waitRounds参数
No scheme registered 未调用registerExactAvmScheme 在处理请求之前注册
未提取发现 扩展未通过负载传递 确保资源服务器在PaymentRequired中包含扩展

参考资料/进一步阅读