MPP-based payment gating for FastAPI
Project description
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
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 fastmpp-0.1.0.tar.gz.
File metadata
- Download URL: fastmpp-0.1.0.tar.gz
- Upload date:
- Size: 14.5 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d8998b8102ac80084f132b5bcc9c0e240ee2a1f9cf2e755f796ea7828c22b17
|
|
| MD5 |
ef9cb50f13ae93cf1d6a3f3c6597b644
|
|
| BLAKE2b-256 |
3abec3a8aed454e96afe2ba037eaa53c8c05e65798421ce21a4e719f1bf9487c
|
File details
Details for the file fastmpp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastmpp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.7 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54435fa08e328b4ceaf007f9f53bd38d6041a81bcf5c3af40b6ddfb21e7ac0f2
|
|
| MD5 |
47bdf5c613ce1d11e97f27808444f22d
|
|
| BLAKE2b-256 |
4c1ea23cb39da4971ef6d5c48a704c8f5f9c34946bcaf34be9d1bd7789ce01b2
|