fastapi_mpp
MPP-based payment gating for FastAPI. Add a single decorator to any route and it handles the full 402 → pay → verify flow automatically.
Quick start
from fastapi import FastAPI
from fastapi_mpp import ChargeableFactory, PaymentMethod
app = FastAPI()
factory = ChargeableFactory.create(
PaymentMethod.tempo(
currency="0x20C000...", # USDC token address
recipient="0xYourWallet",
),
realm="my-api", # shown in WWW-Authenticate
secret_key="...", # or set MPP_SECRET_KEY env var
)
@app.get("/premium")
@factory.Chargeable(amount="0.50", description="Premium data")
async def premium():
return {"data": "paid content"}
Without a valid payment credential the route returns:
HTTP/1.1 402 Payment Required
WWW-Authenticate: MPP realm="my-api", ...
{"error": "Payment Required", "challenge": {...}}
With a valid credential the route runs and responds:
HTTP/1.1 200 OK
Payment-Receipt: MPP receipt=...
{"data": "paid content"}
Factory config reference
ChargeableFactory.create(*methods, realm=None, secret_key=None)
| Parameter | Type | Description |
|---|---|---|
*methods |
positional | Exactly one PaymentMethod.*() result. Multi-method support is planned. |
realm |
str | None |
Server realm for WWW-Authenticate. Falls back to MPP_REALM env var. |
secret_key |
str | None |
HMAC secret for challenge signing. Falls back to MPP_SECRET_KEY env var. |
PaymentMethod.tempo() config reference
| Parameter | Type | Default | Description |
|---|---|---|---|
currency |
str | None |
None |
Default currency token address (e.g. USDC). |
recipient |
str | None |
None |
Default recipient wallet address. |
decimals |
int |
6 |
Token decimal places. |
rpc_url |
str | None |
None |
Override the Tempo RPC endpoint. |
chain_id |
int | None |
None |
4217 = mainnet, 42431 = testnet (Moderato). |
fee_payer_key |
str | None |
None |
Hex private key (0x-prefixed) for gas-sponsored transactions. |
root_account |
str | None |
None |
Root account address for access key signing. |
client_id |
str | None |
None |
Optional client identity for attribution memos. |
enable_store |
bool |
False |
Enable in-memory replay protection (prevents credential reuse). |
Decorator config reference
@factory.Chargeable(amount, *, ...)
| Parameter | Type | Default | Description |
|---|---|---|---|
amount |
str |
required | Human-readable charge amount, e.g. "0.50". |
currency |
str | None |
None |
Override the method-level default currency for this route. |
recipient |
str | None |
None |
Override the method-level default recipient for this route. |
description |
str | None |
None |
Human-readable payment description shown to the payer. |
expires |
str | None |
None |
ISO 8601 challenge expiry. Defaults to now + 5 minutes. |
attach_receipt |
bool |
True |
Set Payment-Receipt header on successful payment. |
on_payment |
async (credential, receipt) -> None |
None |
Async callback fired after each successful payment. |
Optional middleware
MppMiddleware attaches the Mpp instance to request.state.mpp for advanced use cases:
from fastapi_mpp import MppMiddleware
app.add_middleware(MppMiddleware, factory=factory)
@app.get("/advanced")
async def route(request: Request):
mpp = request.state.mpp
result = await mpp.charge(
authorization=request.headers.get("Authorization"),
amount="1.00",
)
...
Testing with npx mppx
mppx is the MPP CLI test client. Run your FastAPI app locally, then:
# Install once
npm install -g mppx
# Hit a gated endpoint — mppx handles the 402 → wallet → credential flow
npx mppx http://localhost:8000/premium
# Specify a wallet private key
npx mppx --key 0xYourPrivateKey http://localhost:8000/premium
# Testnet (Moderato)
npx mppx --network moderato http://localhost:8000/premium
Start the example app:
export MPP_SECRET_KEY=dev-secret
export MPP_REALM=example-api
export TEMPO_CURRENCY=0x20c0000000000000000000000000000000000000 # Tempo PATH/USD (testnet)
export TEMPO_RECIPIENT=0xYourWalletAddress
uv run fastapi dev examples/basic.py
Running tests
uv sync --group dev
uv run pytest
Run a single test file:
uv run pytest tests/test_402_flow.py -v
Metadata
Release files for fastmpp 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastmpp-0.2.1.tar.gz | 14.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastmpp-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.1 kB
Release files / fastmpp-0.2.1.tar.gz
| Download URL | fastmpp-0.2.1.tar.gz |
|---|---|
| Size | 14.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2ff8c44ec98301627b6095c65424a4f7b40294d387874a8bc7e758675a7699fc
|
|
BLAKE2b-256 checksum How to use checksums |
daf12bcd4fef52229e3188a70f56892da72d868f305c40be86832947198ed2d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / fastmpp-0.2.1-py3-none-any.whl
| Download URL | fastmpp-0.2.1-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2fca518f95c64a72bc3e7b76471940880ad895000e6c2a0bfbe9a16b8b2c60d3
|
|
BLAKE2b-256 checksum How to use checksums |
51136e71ee15e50439cabb09d829afc5058d6b52652a531adec4445a49b80d0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|