Python SDK — FastAPI integration
paylera_fastapi.paylera_relay is a fastapi.APIRouter factory.
Mounted under a prefix (typically /api/paylera), it exposes the
ten relay routes plus GET /csrf-token. The merchant’s
API token stays on the backend; the browser holds only a session cookie + an
anti-CSRF token.
Install
pip install paylera paylera-fastapiMount the router
import osfrom fastapi import Depends, FastAPI, Requestfrom paylera_fastapi import PayleraCustomerIdentity, paylera_relay
from .auth import require_auth # your existing session middleware
app = FastAPI()
def identify(request: Request) -> PayleraCustomerIdentity: user = request.state.user return PayleraCustomerIdentity( customer_id=getattr(user, "paylera_customer_id", None), email=user.email, name=user.name, )
app.include_router( paylera_relay( api_token=os.environ["PAYLERA_API_TOKEN"], identify=identify, auto_create_customer=True, csrf=True, ), prefix="/api/paylera", dependencies=[Depends(require_auth)],)What that gets you:
| Method | Path | Notes |
|---|---|---|
GET | /csrf-token | Mints the double-submit cookie + token. |
POST | /attach | One-shot signup. |
POST | /check | Entitlement gate. |
POST | /track | Usage event. |
GET | /entitlements | Bulk feature read. |
GET | /me | Resolved customer. |
GET | /plans | Plan catalog (anonymous-friendly). |
GET | /invoices | Invoice history. |
POST | /billing-portal | Mint a hosted-portal session. |
POST | /subscriptions/{id}/upgrade | Change plan. |
POST | /subscriptions/{id}/cancel | Cancel. |
CSRF
The router ships double-submit-cookie CSRF by default:
- Header:
Paylera-CSRF-Token - Cookie:
paylera_csrf(notHttpOnly— your JS needs to read it) - Token lifetime: 24h
- Safe methods (
GET/HEAD/OPTIONS) are exempt
The browser fetches GET /api/paylera/csrf-token once on mount, then sends
the returned token in the header for every state-changing call. If header
and cookie disagree the router returns 403 with
type: "paylera.csrf_mismatch".
Override individual settings:
from paylera_fastapi import PayleraCsrfOptions
paylera_relay( ..., csrf=PayleraCsrfOptions( cookie_path="/api/paylera", cookie_secure=True, cookie_samesite="strict", token_lifetime_seconds=3600, ),)csrf=False opts out entirely. Don’t ship without CSRF — wire your
framework’s own middleware if you opt out.
Identifying the user
identify is called once per request with the FastAPI Request. It
must return a PayleraCustomerIdentity (a dataclass with customer_id,
email, name, currency, metadata).
from dataclasses import dataclassfrom paylera_fastapi import PayleraCustomerIdentity
@dataclassclass SessionUser: paylera_customer_id: str | None email: str name: str
def identify(request: Request) -> PayleraCustomerIdentity: user: SessionUser = request.state.user # populated by your auth middleware return PayleraCustomerIdentity( customer_id=user.paylera_customer_id, email=user.email, name=user.name, currency="USD", metadata={"source": "fastapi"}, )When customer_id is None and auto_create_customer=True, the router
creates a Paylera customer using the email + name + currency, then caches
the resulting id on request.state.paylera_customer for downstream
handlers. Auto-create is idempotent on email.
Per-route auth
dependencies=[Depends(require_auth)] is mounted on include_router, so
every relay route gets your auth dependency. The router’s /plans route
is anonymous-friendly inside the relay (it doesn’t require a resolved
customer), so if you want it public, omit it from the protected mount and
re-mount the same router twice, or skip Depends(require_auth) and gate
inside identify() instead.
Idempotency
The relay forwards inbound Idempotency-Key verbatim. If the browser
omits one for /track, the relay promotes the body’s dedup_key (if any);
otherwise it mints a fresh UUID v7. State-changing routes always carry an
idempotency key by the time they reach the API.
OpenTelemetry
Pass tracer= (an opentelemetry.trace.Tracer) or let the relay pick up
the global provider. Spans are named Paylera.Relay.<Op> on the
Paylera.Relay tracer, with paylera.* attribute keys. Zero overhead when
OpenTelemetry isn’t installed.
Receiving webhooks
paylera_fastapi.paylera_webhooks(...) is a separate factory — see
webhooks.
Full example
A runnable FastAPI service is at
examples/python/fastapi-app/
in the monorepo. It mounts the relay at /api/paylera and a webhook
receiver at /webhooks/paylera with handlers for invoice.paid and
subscription.canceled. Mock auth (one X-Demo-User header) keeps the
example self-contained.