Skip to content

Latest commit

 

History

History
188 lines (146 loc) · 4.66 KB

File metadata and controls

188 lines (146 loc) · 4.66 KB

x402 Flask Example Server

Flask server demonstrating how to protect API endpoints with a paywall using the x402 middleware.

from flask import Flask, jsonify
from x402.http.middleware.flask import payment_middleware
from x402.http import HTTPFacilitatorClientSync, FacilitatorConfig, PaymentOption
from x402.http.types import RouteConfig
from x402.server import x402ResourceServerSync
from x402.mechanisms.evm.exact import ExactEvmServerScheme
from x402.mechanisms.svm.exact import ExactSvmServerScheme

app = Flask(__name__)

# Flask is sync, so use sync variants
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url=facilitator_url))
server = x402ResourceServerSync(facilitator)
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),
        ]
    ),
}
payment_middleware(app, routes=routes, server=server)

@app.route("/weather")
def get_weather():
    return jsonify({"weather": "sunny", "temperature": 70})

Prerequisites

  • 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

Setup

  1. Copy .env-local to .env:
cp .env-local .env
  1. 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)
  1. Install dependencies:
uv sync
  1. Run the server:
uv run python main.py

Server runs at http://localhost:4021

Example Endpoints

Endpoint Payment Price
GET /health No -
GET /weather Yes $0.01 USDC
GET /premium/content Yes $0.01 USDC

Response Format

Payment Required (402)

$ 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"
      }
    }
  ]
}

Successful Response (200)

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}}

Extending the Example

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.route("/your-endpoint")
def your_endpoint():
    return jsonify({"data": "your response"})

Price Configuration

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

Network identifiers use CAIP-2 format:

EVM Networks:

  • eip155:84532 — Base Sepolia
  • eip155:8453 — Base Mainnet

SVM Networks:

  • solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 — Solana Devnet
  • solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp — Solana Mainnet