创建Pythonx402Facilitator服务 create-python-x402-facilitator

构建基于FastAPI的Facilitator服务,用于在链上验证和结算Algorand x402支付,支持多网络EVM和SVM。关键词:FastAPI, Facilitator, x402支付,Algorand, EVM, SVM。

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

创建Python x402 Facilitator服务

构建基于FastAPI的Facilitator服务,用于在链上验证和结算x402支付,特别适用于Algorand (AVM),并可选支持EVM和SVM多网络。

前提条件

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

  1. 已安装Python 3.10+
  2. x402-avm包 可用:pip install "x402-avm[avm,fastapi]"
  3. Algorand私钥 作为Base64编码的64字节密钥(32字节种子+32字节公钥)在AVM_PRIVATE_KEY环境变量中可用
  4. **通过AlgoNode(默认)或通过ALGOD_SERVER / ALGOD_TOKEN自定义节点访问Algod节点

核心工作流程:Facilitator支付生命周期

Facilitator位于资源服务器和区块链之间,处理支付验证和结算:

资源服务器              Facilitator                   区块链
     |                           |                             |
     |--- /verify (payload) ---->|                             |
     |                           |-- simulate_group() -------->|
     |                           |<-- 模拟结果 -------|
     |<-- {isValid: true} -------|                             |
     |                           |                             |
     |--- /settle (payload) ---->|                             |
     |                           |-- sign_group() ------------>|
     |                           |-- send_group() ------------>|
     |                           |<-- txid --------------------|
     |                           |-- confirm_transaction() --->|
     |<-- {success, txid} -------|                             |

如何进行

第1步:安装依赖项

pip install "x402-avm[avm,fastapi]"

这将安装py-algorand-sdk (algosdk),FastAPI,uvicorn和x402-avm SDK。

第2步:实现FacilitatorAvmSigner

FacilitatorAvmSigner协议定义了六个方法。使用algosdk实现所有方法:

import os
import base64
from algosdk import encoding, transaction
from algosdk.v2client import algod
from x402.mechanisms.avm.constants import NETWORK_CONFIGS


class AlgorandFacilitatorSigner:
    def __init__(self, private_key_b64: str, algod_url: str = "", algod_token: str = ""):
        self._secret_key = base64.b64decode(private_key_b64)
        self._address = encoding.encode_address(self._secret_key[32:])
        self._signing_key = base64.b64encode(self._secret_key).decode()
        self._clients: dict[str, algod.AlgodClient] = {}
        if algod_url:
            self._default_client = algod.AlgodClient(algod_token, algod_url)
        else:
            self._default_client = None

    def _get_client(self, network: str) -> algod.AlgodClient:
        if network not in self._clients:
            if self._default_client:
                self._clients[network] = self._default_client
            else:
                config = NETWORK_CONFIGS.get(network, {})
                url = config.get("algod_url", "https://testnet-api.algonode.cloud")
                self._clients[network] = algod.AlgodClient("", url)
        return self._clients[network]

    def get_addresses(self) -> list[str]:
        return [self._address]

    def sign_transaction(self, txn_bytes: bytes, fee_payer: str, network: str) -> bytes:
        b64 = base64.b64encode(txn_bytes).decode("utf-8")
        txn_obj = encoding.msgpack_decode(b64)
        signed = txn_obj.sign(self._signing_key)
        return base64.b64decode(encoding.msgpack_encode(signed))

    def sign_group(self, group_bytes, fee_payer, indexes_to_sign, network):
        result = list(group_bytes)
        for i in indexes_to_sign:
            result[i] = self.sign_transaction(group_bytes[i], fee_payer, network)
        return result

    def simulate_group(self, group_bytes, network):
        client = self._get_client(network)
        stxns = []
        for txn_bytes in group_bytes:
            b64 = base64.b64encode(txn_bytes).decode("utf-8")
            obj = encoding.msgpack_decode(b64)
            if isinstance(obj, transaction.SignedTransaction):
                stxns.append(obj)
            else:
                stxns.append(transaction.SignedTransaction(obj, None))
        req = transaction.SimulateRequest(
            txn_groups=[transaction.SimulateRequestTransactionGroup(txns=stxns)],
            allow_empty_signatures=True,
        )
        result = client.simulate_raw_transactions(req)
        for group in result.get("txn-groups", []):
            if group.get("failure-message"):
                raise Exception(f"Simulation failed: {group['failure-message']}")

    def send_group(self, group_bytes, network):
        client = self._get_client(network)
        return client.send_raw_transaction(base64.b64encode(b"".join(group_bytes)))

    def confirm_transaction(self, txid, network, rounds=4):
        client = self._get_client(network)
        transaction.wait_for_confirmation(client, txid, rounds)

第3步:将签名者注册到x402Facilitator

from x402 import x402Facilitator
from x402.mechanisms.avm.exact import register_exact_avm_facilitator
from x402.mechanisms.avm import ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2

signer = AlgorandFacilitatorSigner(
    private_key_b64=os.environ["AVM_PRIVATE_KEY"],
    algod_url=os.environ.get("ALGOD_SERVER", ""),
    algod_token=os.environ.get("ALGOD_TOKEN", ""),
)

facilitator = x402Facilitator()
register_exact_avm_facilitator(
    facilitator,
    signer,
    networks=[ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2],
)

第4步:创建FastAPI端点

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI(title="x402-avm Facilitator Service")

@app.get("/supported")
async def supported():
    return facilitator.get_supported_networks()

@app.post("/verify")
async def verify(request: Request):
    body = await request.json()
    try:
        result = await facilitator.verify(
            body["paymentPayload"], body["paymentRequirements"]
        )
        return result
    except Exception as e:
        return JSONResponse(status_code=400, content={"error": str(e)})

@app.post("/settle")
async def settle(request: Request):
    body = await request.json()
    try:
        result = await facilitator.settle(
            body["paymentPayload"], body["paymentRequirements"]
        )
        return result
    except Exception as e:
        return JSONResponse(status_code=400, content={"error": str(e)})

第5步:运行服务

AVM_PRIVATE_KEY="your-base64-key" uvicorn facilitator_service:app --port 4000

重要规则/指南

  1. algosdk编码边界msgpack_decode()期望一个base64字符串,而不是原始字节。在解码前总是转换:base64.b64encode(raw_bytes).decode("utf-8")msgpack_encode()返回一个base64字符串;用base64.b64decode()转换回来。
  2. 私钥格式Transaction.sign()期望一个base64编码的64字节密钥字符串,而不是原始字节。
  3. send_group模式 – 使用send_raw_transaction(base64.b64encode(b"".join(group_bytes)))避免不必要的解码/重新编码开销。
  4. simulate_group模式 – 用SignedTransaction(txn, None)包装未签名的交易,并使用allow_empty_signatures=True
  5. 地址派生 – 使用encoding.encode_address(secret_key[32:])从64字节密钥派生地址。
  6. 一流的AVM – 无条件注册AVM,绝不在if (env.AVM_*)守卫之后。
  7. 网络常量 – 从SDK导入ALGORAND_TESTNET_CAIP2ALGORAND_MAINNET_CAIP2;不要在应用程序代码中硬编码创世哈希。

生命周期钩子

x402Facilitator支持生命周期钩子,用于自定义逻辑:

钩子 调用时间 用例
on_before_verify 支付验证前 日志记录,速率限制
on_after_verify 验证完成后 分析,缓存
on_before_settle 提交结算前 最终验证
on_after_settle 结算完成后 通知,收据

常见错误/故障排除

错误 原因 解决方案
msgpack_decode expects string msgpack_decode传递原始字节 base64.b64encode(raw).decode()包装
Invalid key length 错误的私钥格式 确保AVM_PRIVATE_KEY是base64编码的64字节
Simulation failed 交易将在链上失败 检查余额,资产选择加入,费用金额
send_raw_transaction expects bytes-like 错误的参数类型 使用base64.b64encode(b"".join(group_bytes))
Transaction not confirmed 节点超时或网络问题 增加rounds参数或检查节点连接
send_transactions asserts NOT Transaction send_transactions传递未签名的Transaction 使用send_raw_transaction代替

参考资料/进一步阅读