> ## 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.

# 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

This setup works on the **free tier** out of the box — no API keys required.

Beyond the free tier the facilitator authenticates merchants with short-lived
Ed25519 JWTs. Create a merchant account at
[merchant.payai.network](https://merchant.payai.network), then add your keys 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>
