Skip to content
Back to skills

003 Name Skill 2735fccb

ASecurity

Create Python x402 facilitator services that verify and settle payments on-chain. Use when building a FastAPI-based facilitator, implementing the FacilitatorAvmSigner protocol (sign_transaction, sign_group, simulate_group, send_group, confirm_transaction), registering with register_exact_avm_facilitator, creating multi-network facilitators (EVM+SVM+AVM), or setting up facilitator API endpoints (/verify, /settle, /supported). Strong triggers include "create a Python facilitator", "build a paym...

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
developmentpythongobashnodefastapigitapidocumentation

Works with

  • cli
  • api

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 003-name-skill_2735fccb --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 003 Name Skill 2735fccb?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 003 Name Skill 2735fccb
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-003-name-skill-2735fccb/badge)](https://www.skillsdirectory.com/skills/tools-only-003-name-skill-2735fccb)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: create-python-x402-facilitator
description: Create Python x402 facilitator services that verify and settle payments on-chain. Use when building a FastAPI-based facilitator, implementing the FacilitatorAvmSigner protocol (sign_transaction, sign_group, simulate_group, send_group, confirm_transaction), registering with register_exact_avm_facilitator, creating multi-network facilitators (EVM+SVM+AVM), or setting up facilitator API endpoints (/verify, /settle, /supported). Strong triggers include "create a Python facilitator", "build a payment facilitator service", "implement FacilitatorAvmSigner", "set up facilitator endpoints", "verify and settle Algorand payments", "fee payer service".
---

# Creating Python x402 Facilitator Services

Build FastAPI-based facilitator services that verify and settle x402 payments on-chain for Algorand (AVM), with optional multi-network support for EVM and SVM.

## Prerequisites

Before using this skill, ensure:

1. **Python 3.10+** is installed
2. **x402-avm package** is available: `pip install "x402-avm[avm,fastapi]"`
3. **Algorand private key** is available as a Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey) in `AVM_PRIVATE_KEY` environment variable
4. **Algod node access** via AlgoNode (default) or custom node via `ALGOD_SERVER` / `ALGOD_TOKEN`

## Core Workflow: Facilitator Payment Lifecycle

The facilitator sits between the resource server and the blockchain, handling payment verification and settlement:

```
Resource Server              Facilitator                   Blockchain
     |                           |                             |
     |--- /verify (payload) ---->|                             |
     |                           |-- simulate_group() -------->|
     |                           |<-- simulation result -------|
     |<-- {isValid: true} -------|                             |
     |                           |                             |
     |--- /settle (payload) ---->|                             |
     |                           |-- sign_group() ------------>|
     |                           |-- send_group() ------------>|
     |                           |<-- txid --------------------|
     |                           |-- confirm_transaction() --->|
     |<-- {success, txid} -------|                             |
```

## How to Proceed

### Step 1: Install Dependencies

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

This installs `py-algorand-sdk` (algosdk), FastAPI, uvicorn, and the x402-avm SDK.

### Step 2: Implement FacilitatorAvmSigner

The `FacilitatorAvmSigner` protocol defines six methods. Implement all of them using `algosdk`:

```python
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 = [REDACTED](private_key_b64)
        self._address = encoding.encode_address(self._secret_key[32:])
        self._signing_key = [REDACTED](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)
```

### Step 3: Register the Signer with x402Facilitator

```python
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],
)
```

### Step 4: Create FastAPI Endpoints

```python
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)})
```

### Step 5: Run the Service

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

## Important Rules / Guidelines

1. **algosdk encoding boundaries** -- `msgpack_decode()` expects a base64 string, not raw bytes. Always convert: `base64.b64encode(raw_bytes).decode("utf-8")` before decoding. `msgpack_encode()` returns a base64 string; convert back with `base64.b64decode()`.
2. **Private key format** -- `Transaction.sign()` expects a base64-encoded string of the 64-byte key, not raw bytes.
3. **send_group pattern** -- Use `send_raw_transaction(base64.b64encode(b"".join(group_bytes)))` to avoid unnecessary decode/re-encode overhead.
4. **simulate_group pattern** -- Wrap unsigned transactions with `SignedTransaction(txn, None)` and use `allow_empty_signatures=True`.
5. **Address derivation** -- Use `encoding.encode_address(secret_key[32:])` to derive the address from the 64-byte key.
6. **First-class AVM** -- Register AVM unconditionally, never behind `if (env.AVM_*)` guards.
7. **Network constants** -- Import `ALGORAND_TESTNET_CAIP2` and `ALGORAND_MAINNET_CAIP2` from the SDK; do not hardcode genesis hashes in application code.

## Lifecycle Hooks

The `x402Facilitator` supports lifecycle hooks for custom logic:

| Hook | When Called | Use Case |
|------|-----------|----------|
| `on_before_verify` | Before payment verification | Logging, rate limiting |
| `on_after_verify` | After verification completes | Analytics, caching |
| `on_before_settle` | Before settlement submission | Final validation |
| `on_after_settle` | After settlement completes | Notification, receipts |

## Common Errors / Troubleshooting

| Error | Cause | Solution |
|-------|-------|----------|
| `msgpack_decode expects string` | Passing raw bytes to `msgpack_decode` | Wrap with `base64.b64encode(raw).decode()` |
| `Invalid key length` | Wrong private key format | Ensure `AVM_PRIVATE_KEY` is base64-encoded 64 bytes |
| `Simulation failed` | Transaction would fail on-chain | Check balances, asset opt-in, fee amounts |
| `send_raw_transaction expects bytes-like` | Wrong argument type | Use `base64.b64encode(b"".join(group_bytes))` |
| `Transaction not confirmed` | Node timeout or network issue | Increase `rounds` parameter or check node connectivity |
| `send_transactions asserts NOT Transaction` | Passing unsigned `Transaction` to `send_transactions` | Use `send_raw_transaction` instead |

## References / Further Reading

- [REFERENCE.md](./references/REFERENCE.md) -- Detailed API reference for FacilitatorAvmSigner protocol
- [EXAMPLES.md](./references/EXAMPLES.md) -- Complete code examples
- [Facilitator Example](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/python/facilitator)
- [x402-avm AVM Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.