FastAPI server demonstrating advanced x402 patterns including dynamic pricing, payment routing, lifecycle hooks and API discoverability.
from fastapi import FastAPI
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
from x402.http import HTTPFacilitatorClient, FacilitatorConfig, PaymentOption
from x402.http.types import RouteConfig
from x402.server import x402ResourceServer
from x402.mechanisms.evm.exact import ExactEvmServerScheme
from x402.mechanisms.svm.exact import ExactSvmServerScheme
app = FastAPI()
server = x402ResourceServer(HTTPFacilitatorClient(FacilitatorConfig(url=facilitator_url)))
server.register("eip155:84532", ExactEvmServerScheme())
server.register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", ExactSvmServerScheme())
routes = {
"GET /weather": RouteConfig(
accepts=[
PaymentOption(scheme="exact", price="$0.01", network="eip155:84532", pay_to=evm_address),
PaymentOption(scheme="exact", price="$0.01", network="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", pay_to=svm_address),
]
),
}
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
@app.get("/weather")
async def get_weather():
return {"weather": "sunny", "temperature": 70}- Python 3.10+
- uv (install via docs.astral.sh/uv)
- Valid EVM address for receiving payments (Base Sepolia)
- Valid SVM address for receiving payments (Solana Devnet)
- URL of a facilitator supporting the desired payment network, see facilitator list
- Copy
.env-localto.env:
cp .env-local .env- Fill required environment variables:
EVM_ADDRESS- Ethereum address to receive payments (Base Sepolia)SVM_ADDRESS- Solana address to receive payments (Solana Devnet)FACILITATOR_URL- Facilitator endpoint URL (optional, defaults to production)
- Install dependencies:
uv sync- Run the server:
uv run python all_networks.py # All supported networks with optional chain configuration
uv run python hooks.py # Payment lifecycle hooks
uv run python dynamic_price.py # Dynamic pricing
uv run python dynamic_pay_to.py # Dynamic payment routing
uv run python custom_token.py # Custom token parser
uv run python bazaar.py # Bazaar AI discovery
uv run python paywall.py # Browser-based payment UIServer runs at http://localhost:4021
| Endpoint | Payment | Price | Feature |
|---|---|---|---|
GET /health |
No | - | Health check |
GET /weather |
Yes | $0.01 USDC | Static pricing, Bazaar extension |
GET /weather-dynamic |
Yes | $0.001-$0.005 USDC | Dynamic pricing (tier param) |
GET /weather-pay-to |
Yes | $0.001 USDC | Dynamic pay-to (country param) |
GET /premium/* |
Yes | $0.01 USDC | Paywall with browser UI |
$ curl -i http://localhost:4021/weather
HTTP/1.1 402 Payment Required
content-type: application/json
payment-required: <base64-encoded JSON>
{}
The payment-required header contains base64-encoded JSON with payment requirements.
Note: amount is in atomic units (e.g., 10000 = $0.01 USDC, since USDC has 6 decimals):
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "http://localhost:4021/weather"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:84532",
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"amount": "10000",
"payTo": "0x...",
"maxTimeoutSeconds": 300,
"extra": {
"name": "USDC",
"version": "2"
}
}
]
}After payment is verified, the protected endpoint returns the requested data:
HTTP/1.1 200 OK
content-type: application/json
{"report":{"weather":"sunny","temperature":70}}
routes = {
"GET /your-endpoint": RouteConfig(
accepts=[
# EVM payment option
PaymentOption(
scheme="exact",
price="$0.10",
network="eip155:84532",
pay_to=EVM_ADDRESS,
),
# SVM payment option
PaymentOption(
scheme="exact",
price="$0.10",
network="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
pay_to=SVM_ADDRESS,
),
]
),
}
@app.get("/your-endpoint")
async def your_endpoint():
return {"data": "your response"}Two ways to specify price:
# String format (uses default USDC)
price="$0.01"
# AssetAmount object (explicit asset)
price=AssetAmount(
amount="10000", # $0.01 USDC (6 decimals)
asset="0x036CbD53842c5426634e7929541eC2318f3dCF7e",
extra={"name": "USDC", "version": "2"},
)Network identifiers use CAIP-2 format:
EVM Networks:
eip155:84532— Base Sepoliaeip155:8453— Base Mainnet
SVM Networks:
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1— Solana Devnetsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp— Solana Mainnet
Add a browser-based payment interface for human users:
from x402.http.paywall import create_paywall, evm_paywall, svm_paywall
paywall = (
create_paywall()
.with_network(evm_paywall)
.with_network(svm_paywall)
.with_config(app_name="My App", testnet=True)
.build()
)
app.add_middleware(
PaymentMiddlewareASGI,
routes=routes,
server=server,
paywall_provider=paywall,
)Use case: When browser users access a paid endpoint, they see a payment UI instead of raw 402 responses. Supports both EVM (Base) and SVM (Solana) networks.
Enable AI agent discovery with structured input/output schemas:
from x402.extensions.bazaar import declare_discovery_extension, bazaar_resource_server_extension, OutputConfig
server.register_extension(bazaar_resource_server_extension)
RouteConfig(
accepts=[...],
extensions={
**declare_discovery_extension(
input={"city": "San Francisco"},
input_schema={
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
output=OutputConfig(
example={"weather": "sunny", "temperature": 70},
schema={
"properties": {
"weather": {"type": "string"},
"temperature": {"type": "number"},
},
"required": ["weather", "temperature"],
},
),
)
},
)Use case: Making your API discoverable by AI agents and automated clients, enabling programmatic service discovery and integration.
Set price dynamically based on HTTP request context:
def get_dynamic_price(context: HTTPRequestContext) -> str:
tier = context.adapter.get_query_param("tier") or "standard"
return "$0.005" if tier == "premium" else "$0.001"
RouteConfig(
accepts=[
PaymentOption(
scheme="exact",
price=lambda context: get_dynamic_price(context),
network=EVM_NETWORK,
pay_to=EVM_ADDRESS,
),
],
)Use case: Implementing tiered pricing, user-based pricing, content-based pricing or any scenario where the price varies based on the request.
Route payments to different addresses based on request context:
ADDRESS_LOOKUP = {"US": addr_us, "UK": addr_uk, "CA": addr_ca}
def get_dynamic_pay_to(context: HTTPRequestContext) -> str:
country = context.adapter.get_query_param("country") or "US"
return ADDRESS_LOOKUP.get(country, default_address)
RouteConfig(
accepts=[
PaymentOption(
scheme="exact",
pay_to=lambda context: get_dynamic_pay_to(context),
price="$0.001",
network=EVM_NETWORK,
),
],
)Use case: Marketplace applications where payments should go to different sellers, content creators, or service providers based on the resource being accessed.
Add custom logic before/after payment verification and settlement:
from x402 import VerifyContext, SettleResultContext, AbortResult
def before_verify_hook(context: VerifyContext) -> None | AbortResult:
print(f"Verifying payment: {context}")
# Return AbortResult(reason="...") to abort
def after_settle_hook(context: SettleResultContext) -> None:
print(f"Payment settled: {context}")
server.on_before_verify(before_verify_hook)
server.on_after_verify(after_verify_hook)
server.on_verify_failure(on_verify_failure_hook)
server.on_before_settle(before_settle_hook)
server.on_after_settle(after_settle_hook)
server.on_settle_failure(on_settle_failure_hook)Use case: Log payment events to a database or monitoring system, perform custom validation before processing payments, implement retry or recovery logic for failed payments, trigger side effects (notifications, database updates) after successful payments.
Define custom token conversions for specific networks:
from x402.schemas import AssetAmount
def custom_money_parser(amount: float, network: str) -> AssetAmount | None:
if network == "eip155:100": # Gnosis Chain
return AssetAmount(
amount=str(int(amount * 1e18)),
asset="0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", # WXDAI
extra={"token": "Wrapped XDAI"},
)
return None # Fall back to default USDC
evm_scheme = ExactEvmServerScheme()
evm_scheme.register_money_parser(custom_money_parser)
server.register(EVM_NETWORK, evm_scheme)Use case: When you want to accept payments in tokens other than USDC, or use different tokens based on conditions (e.g., DAI for large amounts, custom tokens for specific networks).