创建x402 Facilitator服务
构建验证Algorand支付交易的Facilitator服务,并在链上结算,可选的Bazaar发现扩展用于API目录。当构建一个验证支付交易、在链上结算、实现FacilitatorAvmSigner、创建Express.js Facilitator服务器、集成Bazaar发现以索引付费资源或从支付流程中提取发现元数据的Facilitator时使用。强触发词包括“创建x402 Facilitator”、“FacilitatorAvmSigner”、“验证和结算支付”、“Facilitator服务器”、“Bazaar发现”、“x402Facilitator”、“支付结算”、“Facilitator签名者”、“目录付费API”。
先决条件
在使用此技能之前,请确保:
- 支持TypeScript的Node.js
- 拥有Algorand账户和ALGO,用于支付结算期间的交易费用
- 安装algosdk,用于交易签名、模拟和提交
Facilitator核心工作流程:Facilitator的作用
Facilitator是资源服务器和区块链之间的受信任中介。它执行两个操作:
- 验证 – 确认支付交易组有效(正确的金额、收款人、签名、时间)而不提交到网络
- 结算 – 将验证过的交易组提交到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}`);
}
});
重要规则/指南
- Facilitator需要ALGO — Facilitator地址必须有ALGO以支付结算期间的交易费用
- 私钥安全 — 安全存储
AVM_PRIVATE_KEY。Facilitator在每个组中共同签名支付费用交易 - 结算前的模拟 —
simulateTransactions方法必须用new algosdk.SignedTransaction({ txn })包装未签名的交易,并使用allowEmptySignatures: true - sendTransactions期望签名字节 — 在调用
sendRawTransaction之前,使用Buffer.concat()连接所有签名的交易字节 - 网络注册 — 使用
ALGORAND_TESTNET_CAIP2或ALGORAND_MAINNET_CAIP2常量,而不是SDK代码中的字符串字面量 - 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中包含扩展 |
参考资料/进一步阅读
- REFERENCE.md - 详细的API参考
- EXAMPLES.md - 完整的代码示例
- x402-avm Examples Repository
- x402-avm Documentation