pay.sh docs
SDKPython

Frameworks

The Flask, FastAPI, and Django shims — each translates the framework-agnostic core into its own request/response idioms.

solana_pay_kit carries no web-framework dependency in the base install. Each shim lives in an optional submodule and delegates protocol dispatch and 402-challenge assembly to the host-neutral core, translating only the outcome into the framework's idioms. A verified Payment is attached to the request and its settlement headers are merged onto the success response.

pip install "solana-pay-kit[flask]" — a @require_payment view decorator plus is_paid / payment accessors.

from flask import Flask, jsonify
from solana_pay_kit.flask import payment, require_payment

app = Flask(__name__)


@app.get("/report")
@require_payment(report_gate)
def report():
    return jsonify(ok=True, tx=payment().transaction)

pip install "solana-pay-kit[fastapi]" — an app-level paywall that reads FastAPI route metadata, plus decorator and dependency forms for route-specific gates.

Use install_paywall(...) when your app already has a route table and you want route metadata to be the source of truth. Pass a PayConfig or a plain mapping loaded from TOML/YAML/env:

install_paywall(    app,    {        "enabled": True,        "network": "solana_localnet",        "price_usd": "0.01",        "preflight": False,        "signer_env": None,    },    paid_tags=("paid",),)@app.post("/v1/chat/completions", tags=["paid"])async def chat_completions(request: Request) -> dict[str, object]:    verified = payment(request)    return {"ok": True, "tx": verified.transaction if verified else None}@app.get("/health")async def health() -> dict[str, bool]:    return {"ok": True}

Routes tagged with paid are gated. Public routes, like /health, stay unpaid. Set default_policy="paid" to gate every matched route unless it is marked with @pay_not_required() or a public tag.

Use @pay_required(...) when one endpoint needs its own gate instead of the app default:

from solana_pay_kit import usd
from solana_pay_kit.fastapi import pay_not_required, pay_required


@app.post("/priority")
@pay_required(usd("0.05"))
async def priority():
    return {"ok": True}


@app.get("/health")
@pay_not_required()
async def health():
    return {"ok": True}

For lower-level setup, install from an explicit PaywallConfig:

from solana_pay_kit import usd
from solana_pay_kit.fastapi import PaywallConfig, install_paywall_from_config

install_paywall_from_config(app, PaywallConfig(gate_ref=usd("0.01"), config=cfg))

Use this form when you already called solana_pay_kit.configure(...) with custom MppConfig or X402Config, or when you want to pass a prebuilt Config directly.

Use RequirePayment for a one-off dependency:

from fastapi import Depends, FastAPI
from solana_pay_kit.fastapi import Payment, RequirePayment, install

app = FastAPI()
install(app)

require_report = Depends(RequirePayment(report_gate))


@app.get("/report")
async def report(payment: Payment = require_report):
    return {"ok": True, "via": payment.protocol.value}

FastAPI also exposes RequireUsage (metered upto) and RequireSession — see Schemes.

pip install "solana-pay-kit[django]" — a require_payment view decorator and an optional PaymentMiddleware stack form.

from django.http import JsonResponse
from solana_pay_kit.django import payment, require_payment


@require_payment(report_gate)
def report(request):
    return JsonResponse({"ok": True, "tx": payment(request).transaction})

Framework-agnostic core

For imperative gating inside any handler, the trio is importable from the top level:

FunctionPurpose
require_payment(request)Returns the verified Payment; raises PaymentRequiredError if unpaid.
is_paid(request)Predicate, never raises.
get_payment(request)The verified Payment, None until paid.

On this page