> ## Documentation Index
> Fetch the complete documentation index at: https://docs.payai.network/llms.txt
> Use this file to discover all available pages before exploring further.

> Add x402 payment protection to a Python Flask server and settle payments through the PayAI facilitator.

# Flask

## Getting started with Flask

Start accepting x402 payments in your Flask server in 2 minutes.

<Note>You can find the full code for this example [here](https://github.com/x402-foundation/x402/tree/main/examples/python/servers/flask).</Note>

### Step 1: Install dependencies

```bash theme={null}
pip install 'x402[flask,httpx,evm,svm]' 'solana<0.40' python-dotenv
```

<Note>
  `solana<0.40` is a temporary pin. `x402[svm]` requires `solana>=0.36.0` with no
  upper bound, but `solana` 0.40 removed `solana.rpc.api` and `rpc.types.TxOpts`,
  which `x402`'s SVM signer still imports — so an unpinned install resolves to a
  version the SDK cannot use. A fix is proposed upstream in
  [x402-foundation/x402#3071](https://github.com/x402-foundation/x402/pull/3071) —
  drop the pin once it ships in an `x402` release.
</Note>

### Step 2: Set your environment variables

Your `.env` file should look like this:

```
EVM_ADDRESS=0x...       # EVM wallet address to receive payments
SVM_ADDRESS=...         # Solana wallet address to receive payments
FACILITATOR_URL=https://facilitator.payai.network
```

### Step 3: Create a new Flask app

```python theme={null}
import os

from dotenv import load_dotenv
from flask import Flask, jsonify

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

load_dotenv()

# Config
EVM_ADDRESS = os.getenv("EVM_ADDRESS")
SVM_ADDRESS = os.getenv("SVM_ADDRESS")
EVM_NETWORK: Network = "eip155:84532"  # Base Sepolia
SVM_NETWORK: Network = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"  # Solana Devnet
FACILITATOR_URL = os.getenv("FACILITATOR_URL", "https://x402.org/facilitator")

if not EVM_ADDRESS or not SVM_ADDRESS:
    raise ValueError("Missing required environment variables")


# App
app = Flask(__name__)


# x402 Middleware
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url=FACILITATOR_URL))
server = x402ResourceServerSync(facilitator)
server.register(EVM_NETWORK, ExactEvmServerScheme())
server.register(SVM_NETWORK, ExactSvmServerScheme())

routes = {
    "GET /weather": RouteConfig(
        accepts=[
            PaymentOption(
                scheme="exact",
                pay_to=EVM_ADDRESS,
                price="$0.01",
                network=EVM_NETWORK,
            ),
            PaymentOption(
                scheme="exact",
                pay_to=SVM_ADDRESS,
                price="$0.01",
                network=SVM_NETWORK,
            ),
        ],
        mime_type="application/json",
        description="Weather report",
    ),
    "GET /premium/*": RouteConfig(
        accepts=[
            PaymentOption(
                scheme="exact",
                pay_to=EVM_ADDRESS,
                price=AssetAmount(
                    amount="10000",  # $0.01 USDC
                    asset="0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                    extra={"name": "USDC", "version": "2"},
                ),
                network=EVM_NETWORK,
            ),
            PaymentOption(
                scheme="exact",
                pay_to=SVM_ADDRESS,
                price="$0.01",
                network=SVM_NETWORK,
            ),
        ],
        mime_type="application/json",
        description="Premium content",
    ),
}
payment_middleware(app, routes=routes, server=server)


# Routes
@app.route("/health")
def health_check():
    return jsonify({"status": "ok"})


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


@app.route("/premium/content")
def get_premium_content():
    return jsonify({"content": "This is premium content"})


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=4021, debug=False)
```

### Step 4: Run the server

```bash theme={null}
flask run
```

<Check>
  Your server is now accepting x402 payments!
</Check>

### Step 5: Test the server

You can test payments against your server locally by following the [httpx example](https://github.com/x402-foundation/x402/tree/main/examples/python/clients/httpx) or the [requests example](https://github.com/x402-foundation/x402/tree/main/examples/python/clients/requests) from the x402 repository.

Just set your environment variables to match your local server, install the dependencies, and run the examples.

## Going to production

The examples above use testnets. For Mainnet, update the route's network and any explicit scheme registrations together, use the matching asset and funded buyer, and verify the paid path. The [Solana Mainnet example](/x402/solana-mainnet-express) provides a complete tested configuration; adding credentials alone does not switch networks.

This setup can serve ordinary exact mainnet payments in production within the
[free allowance](/x402/facilitators/pricing) — no API key or portal signup is
required. Production readiness still includes normal controls such as secret
management, monitoring, and failure handling.

Beyond the allowance the facilitator authenticates merchants with short-lived
Ed25519 JWTs. An autonomous agent can [buy credits and receive an API key over
x402](/x402/facilitators/agent-api-keys); PayAI creates a wallet-owned agent
account programmatically. You can also use the optional [merchant
dashboard](https://merchant.payai.network). Add the resulting credentials to
your `.env`:

```env theme={null}
PAYAI_API_KEY_ID=your-key-id
PAYAI_API_KEY_SECRET=payai_sk_your-api-key-secret
```

There is no PayAI package for Python — the x402 SDK takes an `auth_provider`, so
signing the token is all you need. Install `cryptography` and drop this file
beside your `main.py`:

```bash theme={null}
pip install cryptography
```

```python theme={null}
import base64, json, time, uuid
from cryptography.hazmat.primitives.serialization import load_der_private_key
from x402.http.facilitator_client_base import AuthHeaders


class PayAIAuthProvider:
    """Signs short-lived EdDSA JWTs for the PayAI facilitator.

    Implements the x402 AuthProvider protocol, so the facilitator client
    attaches the token to every request without further wiring.
    """

    def __init__(self, api_key_id: str, api_key_secret: str, ttl: int = 120):
        self._kid = api_key_id
        secret = api_key_secret.strip()
        if secret.startswith("payai_sk_"):
            secret = secret[len("payai_sk_"):]
        self._key = load_der_private_key(base64.b64decode(secret), password=None)
        self._ttl = ttl
        self._token: str | None = None
        self._renew_at = 0.0

    @staticmethod
    def _b64(raw: bytes) -> str:
        return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")

    def _mint(self) -> str:
        now = int(time.time())
        header = self._b64(json.dumps(
            {"alg": "EdDSA", "typ": "JWT", "kid": self._kid},
            separators=(",", ":")).encode())
        payload = self._b64(json.dumps(
            {"sub": self._kid, "iss": "payai-merchant", "iat": now,
             "exp": now + self._ttl, "jti": str(uuid.uuid4())},
            separators=(",", ":")).encode())
        message = f"{header}.{payload}"
        self._token = f"{message}.{self._b64(self._key.sign(message.encode()))}"
        # refresh a little early so an in-flight request never carries a token
        # that expires mid-lifetime
        self._renew_at = now + self._ttl - 30
        return self._token

    def _jwt(self) -> str:
        if self._token and time.time() < self._renew_at:
            return self._token
        return self._mint()

    def get_auth_headers(self) -> AuthHeaders:
        h = {"Authorization": f"Bearer {self._jwt()}"}
        return AuthHeaders(verify=h, settle=h, supported=h, bazaar=h)
```

Then pass it to the facilitator client:

```python theme={null}
from payai_auth import PayAIAuthProvider

facilitator = HTTPFacilitatorClient(
    FacilitatorConfig(
        url=FACILITATOR_URL,
        auth_provider=PayAIAuthProvider(
            os.environ["PAYAI_API_KEY_ID"],
            os.environ["PAYAI_API_KEY_SECRET"],
        ),
    )
)
```

The provider caches each token and re-signs 30 seconds before expiry, so a
long-running server signs roughly once every two minutes rather than on every
request. See [Facilitator Pricing](/x402/facilitators/pricing) for tier details
and [Facilitator Authentication](/x402/facilitators/authentication) for the
protocol reference.

## x402 reference

For a deeper dive into message shapes, headers, verification and settlement responses, see the [x402 Reference](/x402/reference).

## Need help?

<Card title="Join our Community" icon="discord" href="https://discord.gg/eWJRwMpebQ">
  Have questions or want to connect with other developers? Join our Discord server.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.