Skip to content

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

Terminal window
pip install paylera paylera-fastapi

Mount the router

import os
from fastapi import Depends, FastAPI, Request
from 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:

MethodPathNotes
GET/csrf-tokenMints the double-submit cookie + token.
POST/attachOne-shot signup.
POST/checkEntitlement gate.
POST/trackUsage event.
GET/entitlementsBulk feature read.
GET/meResolved customer.
GET/plansPlan catalog (anonymous-friendly).
GET/invoicesInvoice history.
POST/billing-portalMint a hosted-portal session.
POST/subscriptions/{id}/upgradeChange plan.
POST/subscriptions/{id}/cancelCancel.

CSRF

The router ships double-submit-cookie CSRF by default:

  • Header: Paylera-CSRF-Token
  • Cookie: paylera_csrf (not HttpOnly — 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 dataclass
from paylera_fastapi import PayleraCustomerIdentity
@dataclass
class 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.