Add Machine Payments Protocol (MPP) support to FastAPI endpoints in a few lines.
Project description
fastapi-mpp
Machine Payments Protocol middleware for FastAPI.
Version v0.3 hardens receipt validation, replay protection, session binding, and
adds pluggable storage backends with Redis support for production deployments.
This project is still beta.
Installation
pip install fastapi-mpp
For production validation, install Tempo support so default cryptographic validation is available:
pip install "fastapi-mpp[tempo]"
Optional extras:
pip install "fastapi-mpp[dotenv]"
pip install "fastapi-mpp[redis]"
pip install "fastapi-mpp[stripe]"
pip install "fastapi-mpp[all]"
System Prerequisites
RedisStoreis designed for Redis6.2+whereGETDELis available for atomic one-time challenge consumption.- For older Redis versions, the library falls back to an atomic Lua script, but
Redis
6.2+is strongly recommended for production compatibility and operations.
Usage
Server setup
import os
from fastapi import FastAPI, Request
from mpp_fastapi.core import MPP
from mpp_fastapi.stores import RedisStore
app = FastAPI()
# Production mode (default): requires receipt_validator or fastapi-mpp[tempo].
mpp = MPP(
store=RedisStore(
redis_url=os.getenv("MPP_REDIS_URL", "redis://localhost:6379/0"),
)
)
@app.get("/premium")
@mpp.charge(amount="0.05", currency="USD", description="Premium data")
async def premium(request: Request):
return {"data": "paid content"}
HTTP flow (v0.3 hardened)
- Client calls endpoint without credential.
- Server responds
402 Payment Requiredwith:WWW-Authenticate: Payment challenge="<base64url(JSON)>", realm="MyAPI", expires="..."- challenge body containing
challenge_id,intent,amount,currency,expires_at,hints
- Wallet pays and retries with:
Authorization: Payment credential="<base64url(receipt-json)>"
- Server validates receipt (fail-closed in production), applies replay checks, and returns success with:
Payment-Receipt: <base64url(receipt-json)>- optional session headers when session mode is enabled.
Legacy compatibility can be kept with allow_legacy_headers=True:
X-MPP-ReceiptX-MPP-Session-Id
Session budgets
from fastapi import FastAPI, Request
from mpp_fastapi.core import MPP
from mpp_fastapi.types import MPPChargeOptions
app = FastAPI()
mpp = MPP()
session_options = MPPChargeOptions(
amount="0.01",
currency="USD",
description="Session-metered call",
session=True,
max_amount="0.50",
require_idempotency_key=True,
)
@app.post("/agent/infer")
@mpp.charge(options=session_options)
async def infer(request: Request):
return {"result": "paid inference"}
Sessions are HMAC-signed opaque tokens bound to:
- route scope
- optional payer source
- currency/provider
- issued-at and expiry (default 15 minutes)
- max budget tracked in store
Local Run
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
uvicorn examples.simple_app:app --reload
Then test:
curl -i http://127.0.0.1:8000/free
curl -i http://127.0.0.1:8000/premium
Expected behavior demo:
GET /free -> 200 OK
GET /premium (without Authorization) -> 402 Payment Required
GET /premium (with Authorization: Payment credential="...") -> 200 OK
Security
Read SECURITY.md before production usage.
- Beta warning: use with caution.
- In-memory replay/session/rate-limit stores are suitable for single-process deployments only.
- Production mode is fail-closed when receipt validation is not configured.
- HTTPS is enforced in production mode.
Headers
Incoming:
Authorization: Payment credential="..."(preferred)Payment-Receipt(supported)Payment-Session(session spends)X-MPP-ReceiptandX-MPP-Session-Idin legacy modeIdempotency-Keyfor safer retries
Response on 402:
WWW-Authenticate: Payment challenge="...", realm="...", expires="..."- JSON challenge payload
Response on success:
Payment-ReceiptPayment-Sessionwhen session authorization is established
Design Notes
- Storage is abstracted via
BaseStore; defaultInMemoryStoreis single-process only. RedisStoreis available for multi-worker production deployments.- Header size limit is enforced (
8KB) for authorization and receipt headers. - A basic in-memory challenge rate limiter is enabled (default
10challenges/IP/minute).
Roadmap
- Redis-backed replay/session/rate-limit stores
- Full conformance with evolving HTTP Payment auth draft semantics
- Advanced rate limiting and abuse controls
- Payment provider adapters and richer telemetry
Contributing
- Fork the repository.
- Create a feature branch.
- Add tests for behavior changes.
- Run:
uv pip install -e ".[dev]"
pytest
ruff check .
mypy src
- Open a PR with clear before/after behavior.
Credits
Inspired by the MPP ecosystem work and early protocol specs from Tempo and Stripe collaborators. Please refer to official protocol repos/specs for normative behavior and updates.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_mpp-0.3.0.tar.gz.
File metadata
- Download URL: fastapi_mpp-0.3.0.tar.gz
- Upload date:
- Size: 20.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7944602410afbdf5d66553d47a861925f3e218fbc4466c2cccec30c20ac06b25
|
|
| MD5 |
187e797a9e2a07acf23149b2bee6f2d3
|
|
| BLAKE2b-256 |
c63124722a8ff974564acbec10f02a53a66ad13b7569800647d7e3f295c5d1a4
|
Provenance
The following attestation bundles were made for fastapi_mpp-0.3.0.tar.gz:
Publisher:
publish.yml on SylvainCostes/fastapi-mpp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_mpp-0.3.0.tar.gz -
Subject digest:
7944602410afbdf5d66553d47a861925f3e218fbc4466c2cccec30c20ac06b25 - Sigstore transparency entry: 1135440921
- Sigstore integration time:
-
Permalink:
SylvainCostes/fastapi-mpp@2497cde760e9d2aa5231b2a2d67b4ad4be334dd7 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/SylvainCostes
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2497cde760e9d2aa5231b2a2d67b4ad4be334dd7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastapi_mpp-0.3.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_mpp-0.3.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fb69deb2ea955c62219e322119fd9195b57ddd1bb6e42c785d961287bf89dd8
|
|
| MD5 |
0ae2033177891a2d079bb9a2b8485af7
|
|
| BLAKE2b-256 |
05bb764083e89709ae384211c208bb392c7fca9a897765c3836c5b031238f4e1
|
Provenance
The following attestation bundles were made for fastapi_mpp-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on SylvainCostes/fastapi-mpp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_mpp-0.3.0-py3-none-any.whl -
Subject digest:
9fb69deb2ea955c62219e322119fd9195b57ddd1bb6e42c785d961287bf89dd8 - Sigstore transparency entry: 1135440962
- Sigstore integration time:
-
Permalink:
SylvainCostes/fastapi-mpp@2497cde760e9d2aa5231b2a2d67b4ad4be334dd7 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/SylvainCostes
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2497cde760e9d2aa5231b2a2d67b4ad4be334dd7 -
Trigger Event:
push
-
Statement type: