fastapi-mpp 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_mpp-0.2.0/.gitignore +24 -0
- fastapi_mpp-0.2.0/LICENSE +21 -0
- fastapi_mpp-0.2.0/PKG-INFO +230 -0
- fastapi_mpp-0.2.0/README.md +187 -0
- fastapi_mpp-0.2.0/examples/session_app.py +26 -0
- fastapi_mpp-0.2.0/examples/simple_app.py +51 -0
- fastapi_mpp-0.2.0/pyproject.toml +98 -0
- fastapi_mpp-0.2.0/src/mpp_fastapi/__init__.py +17 -0
- fastapi_mpp-0.2.0/src/mpp_fastapi/core.py +756 -0
- fastapi_mpp-0.2.0/src/mpp_fastapi/decorators.py +29 -0
- fastapi_mpp-0.2.0/src/mpp_fastapi/dependencies.py +129 -0
- fastapi_mpp-0.2.0/src/mpp_fastapi/exceptions.py +33 -0
- fastapi_mpp-0.2.0/src/mpp_fastapi/types.py +173 -0
- fastapi_mpp-0.2.0/tests/test_core.py +101 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Python cache and bytecode
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
|
|
6
|
+
# Build artifacts
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
|
|
11
|
+
# Environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
.env
|
|
15
|
+
|
|
16
|
+
# Tooling
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# OS files
|
|
24
|
+
.DS_Store
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fastapi-mpp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-mpp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Add Machine Payments Protocol (MPP) support to FastAPI endpoints in a few lines.
|
|
5
|
+
Project-URL: Homepage, https://github.com/your-org/fastapi-mpp
|
|
6
|
+
Project-URL: Repository, https://github.com/your-org/fastapi-mpp
|
|
7
|
+
Project-URL: Issues, https://github.com/your-org/fastapi-mpp/issues
|
|
8
|
+
Author: fastapi-mpp contributors
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agents,fastapi,machine-payments-protocol,mpp,payments
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: fastapi>=0.115.0
|
|
24
|
+
Requires-Dist: httpx>=0.28.0
|
|
25
|
+
Requires-Dist: pydantic>=2.7.0
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: pympp>=0.4.1; extra == 'all'
|
|
28
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == 'all'
|
|
29
|
+
Requires-Dist: stripe>=11.0.0; extra == 'all'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: uvicorn[standard]>=0.35.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: dotenv
|
|
37
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == 'dotenv'
|
|
38
|
+
Provides-Extra: stripe
|
|
39
|
+
Requires-Dist: stripe>=11.0.0; extra == 'stripe'
|
|
40
|
+
Provides-Extra: tempo
|
|
41
|
+
Requires-Dist: pympp>=0.4.1; extra == 'tempo'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# fastapi-mpp
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/fastapi-mpp/)
|
|
47
|
+
[](https://pypi.org/project/fastapi-mpp/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
[](https://github.com/your-org/fastapi-mpp)
|
|
50
|
+
|
|
51
|
+
Machine Payments Protocol middleware for FastAPI.
|
|
52
|
+
|
|
53
|
+
Version `v0.2` hardens receipt validation, replay protection, session binding, and
|
|
54
|
+
HTTP authentication semantics. This project is still beta.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install fastapi-mpp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For production validation, install Tempo support so default cryptographic validation
|
|
63
|
+
is available:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install "fastapi-mpp[tempo]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Optional extras:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install "fastapi-mpp[dotenv]"
|
|
73
|
+
pip install "fastapi-mpp[stripe]"
|
|
74
|
+
pip install "fastapi-mpp[all]"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### Server setup
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from fastapi import FastAPI, Request
|
|
83
|
+
from mpp_fastapi.core import MPP
|
|
84
|
+
|
|
85
|
+
app = FastAPI()
|
|
86
|
+
|
|
87
|
+
# Production mode (default): requires receipt_validator or fastapi-mpp[tempo].
|
|
88
|
+
mpp = MPP()
|
|
89
|
+
|
|
90
|
+
@app.get("/premium")
|
|
91
|
+
@mpp.charge(amount="0.05", currency="USD", description="Premium data")
|
|
92
|
+
async def premium(request: Request):
|
|
93
|
+
return {"data": "paid content"}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### HTTP flow (v0.2 hardened)
|
|
97
|
+
|
|
98
|
+
1. Client calls endpoint without credential.
|
|
99
|
+
2. Server responds `402 Payment Required` with:
|
|
100
|
+
- `WWW-Authenticate: Payment challenge="<base64url(JSON)>", realm="MyAPI", expires="..."`
|
|
101
|
+
- challenge body containing `challenge_id`, `intent`, `amount`, `currency`, `expires_at`, `hints`
|
|
102
|
+
3. Wallet pays and retries with:
|
|
103
|
+
- `Authorization: Payment credential="<base64url(receipt-json)>"`
|
|
104
|
+
4. Server validates receipt (fail-closed in production), applies replay checks, and returns success with:
|
|
105
|
+
- `Payment-Receipt: <base64url(receipt-json)>`
|
|
106
|
+
- optional session headers when session mode is enabled.
|
|
107
|
+
|
|
108
|
+
Legacy compatibility can be kept with `allow_legacy_headers=True`:
|
|
109
|
+
- `X-MPP-Receipt`
|
|
110
|
+
- `X-MPP-Session-Id`
|
|
111
|
+
|
|
112
|
+
### Session budgets
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from fastapi import FastAPI, Request
|
|
116
|
+
from mpp_fastapi.core import MPP
|
|
117
|
+
from mpp_fastapi.types import MPPChargeOptions
|
|
118
|
+
|
|
119
|
+
app = FastAPI()
|
|
120
|
+
mpp = MPP()
|
|
121
|
+
|
|
122
|
+
session_options = MPPChargeOptions(
|
|
123
|
+
amount="0.01",
|
|
124
|
+
currency="USD",
|
|
125
|
+
description="Session-metered call",
|
|
126
|
+
session=True,
|
|
127
|
+
max_amount="0.50",
|
|
128
|
+
require_idempotency_key=True,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
@app.post("/agent/infer")
|
|
132
|
+
@mpp.charge(options=session_options)
|
|
133
|
+
async def infer(request: Request):
|
|
134
|
+
return {"result": "paid inference"}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Sessions are HMAC-signed opaque tokens bound to:
|
|
138
|
+
- route scope
|
|
139
|
+
- optional payer source
|
|
140
|
+
- currency/provider
|
|
141
|
+
- issued-at and expiry (default 15 minutes)
|
|
142
|
+
- max budget tracked in store
|
|
143
|
+
|
|
144
|
+
## Local Run
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv venv
|
|
148
|
+
source .venv/bin/activate
|
|
149
|
+
uv pip install -e ".[dev]"
|
|
150
|
+
uvicorn examples.simple_app:app --reload
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Then test:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
curl -i http://127.0.0.1:8000/free
|
|
157
|
+
curl -i http://127.0.0.1:8000/premium
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Expected behavior demo:
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
GET /free -> 200 OK
|
|
164
|
+
GET /premium (without Authorization) -> 402 Payment Required
|
|
165
|
+
GET /premium (with Authorization: Payment credential="...") -> 200 OK
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Security
|
|
169
|
+
|
|
170
|
+
Read [SECURITY.md](SECURITY.md) before production usage.
|
|
171
|
+
|
|
172
|
+
- Beta warning: use with caution.
|
|
173
|
+
- In-memory replay/session/rate-limit stores are suitable for single-process deployments only.
|
|
174
|
+
- Production mode is fail-closed when receipt validation is not configured.
|
|
175
|
+
- HTTPS is enforced in production mode.
|
|
176
|
+
|
|
177
|
+
## Headers
|
|
178
|
+
|
|
179
|
+
Incoming:
|
|
180
|
+
- `Authorization: Payment credential="..."` (preferred)
|
|
181
|
+
- `Payment-Receipt` (supported)
|
|
182
|
+
- `Payment-Session` (session spends)
|
|
183
|
+
- `X-MPP-Receipt` and `X-MPP-Session-Id` in legacy mode
|
|
184
|
+
- `Idempotency-Key` for safer retries
|
|
185
|
+
|
|
186
|
+
Response on 402:
|
|
187
|
+
- `WWW-Authenticate: Payment challenge="...", realm="...", expires="..."`
|
|
188
|
+
- JSON challenge payload
|
|
189
|
+
|
|
190
|
+
Response on success:
|
|
191
|
+
- `Payment-Receipt`
|
|
192
|
+
- `Payment-Session` when session authorization is established
|
|
193
|
+
|
|
194
|
+
## Design Notes
|
|
195
|
+
|
|
196
|
+
- In-memory stores are intentionally simple for `v0.2`; Redis-backed stores are planned.
|
|
197
|
+
- Header size limit is enforced (`8KB`) for authorization and receipt headers.
|
|
198
|
+
- A basic in-memory challenge rate limiter is enabled (default `10` challenges/IP/minute).
|
|
199
|
+
|
|
200
|
+
## Roadmap
|
|
201
|
+
|
|
202
|
+
- Redis-backed replay/session/rate-limit stores
|
|
203
|
+
- Full conformance with evolving HTTP Payment auth draft semantics
|
|
204
|
+
- Advanced rate limiting and abuse controls
|
|
205
|
+
- Payment provider adapters and richer telemetry
|
|
206
|
+
|
|
207
|
+
## Contributing
|
|
208
|
+
|
|
209
|
+
1. Fork the repository.
|
|
210
|
+
2. Create a feature branch.
|
|
211
|
+
3. Add tests for behavior changes.
|
|
212
|
+
4. Run:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
uv pip install -e ".[dev]"
|
|
216
|
+
pytest
|
|
217
|
+
ruff check .
|
|
218
|
+
mypy src
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
5. Open a PR with clear before/after behavior.
|
|
222
|
+
|
|
223
|
+
## Credits
|
|
224
|
+
|
|
225
|
+
Inspired by the MPP ecosystem work and early protocol specs from Tempo and Stripe collaborators.
|
|
226
|
+
Please refer to official protocol repos/specs for normative behavior and updates.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# fastapi-mpp
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/fastapi-mpp/)
|
|
4
|
+
[](https://pypi.org/project/fastapi-mpp/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/your-org/fastapi-mpp)
|
|
7
|
+
|
|
8
|
+
Machine Payments Protocol middleware for FastAPI.
|
|
9
|
+
|
|
10
|
+
Version `v0.2` hardens receipt validation, replay protection, session binding, and
|
|
11
|
+
HTTP authentication semantics. This project is still beta.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install fastapi-mpp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For production validation, install Tempo support so default cryptographic validation
|
|
20
|
+
is available:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install "fastapi-mpp[tempo]"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Optional extras:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install "fastapi-mpp[dotenv]"
|
|
30
|
+
pip install "fastapi-mpp[stripe]"
|
|
31
|
+
pip install "fastapi-mpp[all]"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Server setup
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from fastapi import FastAPI, Request
|
|
40
|
+
from mpp_fastapi.core import MPP
|
|
41
|
+
|
|
42
|
+
app = FastAPI()
|
|
43
|
+
|
|
44
|
+
# Production mode (default): requires receipt_validator or fastapi-mpp[tempo].
|
|
45
|
+
mpp = MPP()
|
|
46
|
+
|
|
47
|
+
@app.get("/premium")
|
|
48
|
+
@mpp.charge(amount="0.05", currency="USD", description="Premium data")
|
|
49
|
+
async def premium(request: Request):
|
|
50
|
+
return {"data": "paid content"}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### HTTP flow (v0.2 hardened)
|
|
54
|
+
|
|
55
|
+
1. Client calls endpoint without credential.
|
|
56
|
+
2. Server responds `402 Payment Required` with:
|
|
57
|
+
- `WWW-Authenticate: Payment challenge="<base64url(JSON)>", realm="MyAPI", expires="..."`
|
|
58
|
+
- challenge body containing `challenge_id`, `intent`, `amount`, `currency`, `expires_at`, `hints`
|
|
59
|
+
3. Wallet pays and retries with:
|
|
60
|
+
- `Authorization: Payment credential="<base64url(receipt-json)>"`
|
|
61
|
+
4. Server validates receipt (fail-closed in production), applies replay checks, and returns success with:
|
|
62
|
+
- `Payment-Receipt: <base64url(receipt-json)>`
|
|
63
|
+
- optional session headers when session mode is enabled.
|
|
64
|
+
|
|
65
|
+
Legacy compatibility can be kept with `allow_legacy_headers=True`:
|
|
66
|
+
- `X-MPP-Receipt`
|
|
67
|
+
- `X-MPP-Session-Id`
|
|
68
|
+
|
|
69
|
+
### Session budgets
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from fastapi import FastAPI, Request
|
|
73
|
+
from mpp_fastapi.core import MPP
|
|
74
|
+
from mpp_fastapi.types import MPPChargeOptions
|
|
75
|
+
|
|
76
|
+
app = FastAPI()
|
|
77
|
+
mpp = MPP()
|
|
78
|
+
|
|
79
|
+
session_options = MPPChargeOptions(
|
|
80
|
+
amount="0.01",
|
|
81
|
+
currency="USD",
|
|
82
|
+
description="Session-metered call",
|
|
83
|
+
session=True,
|
|
84
|
+
max_amount="0.50",
|
|
85
|
+
require_idempotency_key=True,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@app.post("/agent/infer")
|
|
89
|
+
@mpp.charge(options=session_options)
|
|
90
|
+
async def infer(request: Request):
|
|
91
|
+
return {"result": "paid inference"}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Sessions are HMAC-signed opaque tokens bound to:
|
|
95
|
+
- route scope
|
|
96
|
+
- optional payer source
|
|
97
|
+
- currency/provider
|
|
98
|
+
- issued-at and expiry (default 15 minutes)
|
|
99
|
+
- max budget tracked in store
|
|
100
|
+
|
|
101
|
+
## Local Run
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
uv venv
|
|
105
|
+
source .venv/bin/activate
|
|
106
|
+
uv pip install -e ".[dev]"
|
|
107
|
+
uvicorn examples.simple_app:app --reload
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then test:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
curl -i http://127.0.0.1:8000/free
|
|
114
|
+
curl -i http://127.0.0.1:8000/premium
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Expected behavior demo:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
GET /free -> 200 OK
|
|
121
|
+
GET /premium (without Authorization) -> 402 Payment Required
|
|
122
|
+
GET /premium (with Authorization: Payment credential="...") -> 200 OK
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Security
|
|
126
|
+
|
|
127
|
+
Read [SECURITY.md](SECURITY.md) before production usage.
|
|
128
|
+
|
|
129
|
+
- Beta warning: use with caution.
|
|
130
|
+
- In-memory replay/session/rate-limit stores are suitable for single-process deployments only.
|
|
131
|
+
- Production mode is fail-closed when receipt validation is not configured.
|
|
132
|
+
- HTTPS is enforced in production mode.
|
|
133
|
+
|
|
134
|
+
## Headers
|
|
135
|
+
|
|
136
|
+
Incoming:
|
|
137
|
+
- `Authorization: Payment credential="..."` (preferred)
|
|
138
|
+
- `Payment-Receipt` (supported)
|
|
139
|
+
- `Payment-Session` (session spends)
|
|
140
|
+
- `X-MPP-Receipt` and `X-MPP-Session-Id` in legacy mode
|
|
141
|
+
- `Idempotency-Key` for safer retries
|
|
142
|
+
|
|
143
|
+
Response on 402:
|
|
144
|
+
- `WWW-Authenticate: Payment challenge="...", realm="...", expires="..."`
|
|
145
|
+
- JSON challenge payload
|
|
146
|
+
|
|
147
|
+
Response on success:
|
|
148
|
+
- `Payment-Receipt`
|
|
149
|
+
- `Payment-Session` when session authorization is established
|
|
150
|
+
|
|
151
|
+
## Design Notes
|
|
152
|
+
|
|
153
|
+
- In-memory stores are intentionally simple for `v0.2`; Redis-backed stores are planned.
|
|
154
|
+
- Header size limit is enforced (`8KB`) for authorization and receipt headers.
|
|
155
|
+
- A basic in-memory challenge rate limiter is enabled (default `10` challenges/IP/minute).
|
|
156
|
+
|
|
157
|
+
## Roadmap
|
|
158
|
+
|
|
159
|
+
- Redis-backed replay/session/rate-limit stores
|
|
160
|
+
- Full conformance with evolving HTTP Payment auth draft semantics
|
|
161
|
+
- Advanced rate limiting and abuse controls
|
|
162
|
+
- Payment provider adapters and richer telemetry
|
|
163
|
+
|
|
164
|
+
## Contributing
|
|
165
|
+
|
|
166
|
+
1. Fork the repository.
|
|
167
|
+
2. Create a feature branch.
|
|
168
|
+
3. Add tests for behavior changes.
|
|
169
|
+
4. Run:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
uv pip install -e ".[dev]"
|
|
173
|
+
pytest
|
|
174
|
+
ruff check .
|
|
175
|
+
mypy src
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
5. Open a PR with clear before/after behavior.
|
|
179
|
+
|
|
180
|
+
## Credits
|
|
181
|
+
|
|
182
|
+
Inspired by the MPP ecosystem work and early protocol specs from Tempo and Stripe collaborators.
|
|
183
|
+
Please refer to official protocol repos/specs for normative behavior and updates.
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Advanced example focusing on MPP sessions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from fastapi import FastAPI, Request
|
|
6
|
+
|
|
7
|
+
from mpp_fastapi.core import MPP
|
|
8
|
+
from mpp_fastapi.types import MPPChargeOptions
|
|
9
|
+
|
|
10
|
+
app = FastAPI(title="MPP session demo")
|
|
11
|
+
mpp = MPP(debug_mode=True, weak_debug_validation=True)
|
|
12
|
+
|
|
13
|
+
session_options = MPPChargeOptions(
|
|
14
|
+
amount="0.02",
|
|
15
|
+
currency="USD",
|
|
16
|
+
description="Tokenized model inference call",
|
|
17
|
+
session=True,
|
|
18
|
+
max_amount="1.00",
|
|
19
|
+
require_idempotency_key=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.post("/session/infer")
|
|
24
|
+
@mpp.charge(options=session_options)
|
|
25
|
+
async def infer(request: Request) -> dict[str, str]:
|
|
26
|
+
return {"result": "Inference result after MPP authorization"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Simple FastAPI app showing free, fixed and session MPP endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from fastapi import FastAPI, Request
|
|
8
|
+
|
|
9
|
+
from mpp_fastapi.core import MPP
|
|
10
|
+
from mpp_fastapi.dependencies import WalletConfig
|
|
11
|
+
from mpp_fastapi.types import MPPChargeOptions
|
|
12
|
+
|
|
13
|
+
wallet_config = WalletConfig(
|
|
14
|
+
auto_retry_enabled=os.getenv("MPP_AUTO_RETRY", "true").lower() == "true",
|
|
15
|
+
tempo_wallet_url=os.getenv("TEMPO_WALLET_URL"),
|
|
16
|
+
tempo_api_key=os.getenv("TEMPO_API_KEY"),
|
|
17
|
+
stripe_shared_payment_token=os.getenv("STRIPE_SHARED_PAYMENT_TOKEN"),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
mpp = MPP(wallet_config=wallet_config, debug_mode=True, weak_debug_validation=True)
|
|
21
|
+
app = FastAPI(title="fastapi-mpp demo")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@app.get("/free")
|
|
25
|
+
async def free_endpoint() -> dict[str, str]:
|
|
26
|
+
return {"message": "This endpoint is free."}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.get("/premium")
|
|
30
|
+
@mpp.charge(amount="0.05", currency="USD", description="Access to premium data")
|
|
31
|
+
async def premium_endpoint(request: Request) -> dict[str, str]:
|
|
32
|
+
return {"data": "Premium content unlocked via MPP receipt."}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.get("/session")
|
|
36
|
+
@mpp.charge(
|
|
37
|
+
options=MPPChargeOptions(
|
|
38
|
+
amount="0.01",
|
|
39
|
+
currency="USD",
|
|
40
|
+
description="Session-metered endpoint",
|
|
41
|
+
session=True,
|
|
42
|
+
max_amount="0.25",
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
async def session_endpoint(request: Request) -> dict[str, str]:
|
|
46
|
+
return {"data": "Charged from session budget."}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.get("/health")
|
|
50
|
+
async def health() -> dict[str, str]:
|
|
51
|
+
return {"status": "ok"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fastapi-mpp"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Add Machine Payments Protocol (MPP) support to FastAPI endpoints in a few lines."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "fastapi-mpp contributors" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"fastapi",
|
|
17
|
+
"mpp",
|
|
18
|
+
"machine-payments-protocol",
|
|
19
|
+
"ai-agents",
|
|
20
|
+
"payments"
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 3 - Alpha",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Framework :: FastAPI",
|
|
31
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
32
|
+
"Topic :: Software Development :: Libraries :: Python Modules"
|
|
33
|
+
]
|
|
34
|
+
dependencies = [
|
|
35
|
+
"fastapi>=0.115.0",
|
|
36
|
+
"pydantic>=2.7.0",
|
|
37
|
+
"httpx>=0.28.0"
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
dotenv = [
|
|
42
|
+
"python-dotenv>=1.0.0"
|
|
43
|
+
]
|
|
44
|
+
tempo = [
|
|
45
|
+
"pympp>=0.4.1"
|
|
46
|
+
]
|
|
47
|
+
stripe = [
|
|
48
|
+
"stripe>=11.0.0"
|
|
49
|
+
]
|
|
50
|
+
dev = [
|
|
51
|
+
"pytest>=8.0.0",
|
|
52
|
+
"pytest-asyncio>=0.24.0",
|
|
53
|
+
"ruff>=0.8.0",
|
|
54
|
+
"mypy>=1.10.0",
|
|
55
|
+
"uvicorn[standard]>=0.35.0"
|
|
56
|
+
]
|
|
57
|
+
all = [
|
|
58
|
+
"python-dotenv>=1.0.0",
|
|
59
|
+
"pympp>=0.4.1",
|
|
60
|
+
"stripe>=11.0.0"
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[project.urls]
|
|
64
|
+
Homepage = "https://github.com/your-org/fastapi-mpp"
|
|
65
|
+
Repository = "https://github.com/your-org/fastapi-mpp"
|
|
66
|
+
Issues = "https://github.com/your-org/fastapi-mpp/issues"
|
|
67
|
+
|
|
68
|
+
[tool.hatch.build.targets.wheel]
|
|
69
|
+
packages = ["src/mpp_fastapi"]
|
|
70
|
+
|
|
71
|
+
[tool.hatch.build.targets.sdist]
|
|
72
|
+
include = [
|
|
73
|
+
"src/mpp_fastapi",
|
|
74
|
+
"examples",
|
|
75
|
+
"tests",
|
|
76
|
+
"README.md",
|
|
77
|
+
"LICENSE",
|
|
78
|
+
"pyproject.toml"
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.pytest.ini_options]
|
|
82
|
+
pythonpath = ["src"]
|
|
83
|
+
testpaths = ["tests"]
|
|
84
|
+
|
|
85
|
+
[tool.ruff]
|
|
86
|
+
line-length = 100
|
|
87
|
+
target-version = "py310"
|
|
88
|
+
|
|
89
|
+
[tool.ruff.lint]
|
|
90
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
91
|
+
ignore = ["E501"]
|
|
92
|
+
|
|
93
|
+
[tool.mypy]
|
|
94
|
+
python_version = "3.10"
|
|
95
|
+
strict = true
|
|
96
|
+
warn_unused_ignores = true
|
|
97
|
+
warn_redundant_casts = true
|
|
98
|
+
warn_return_any = true
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""fastapi-mpp public package exports."""
|
|
2
|
+
|
|
3
|
+
from .core import MPP
|
|
4
|
+
from .exceptions import InvalidReceiptError, MPPError, PaymentRequiredError
|
|
5
|
+
from .types import MPPChargeOptions, MPPReceipt, MPPSession
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"MPP",
|
|
9
|
+
"MPPChargeOptions",
|
|
10
|
+
"MPPReceipt",
|
|
11
|
+
"MPPSession",
|
|
12
|
+
"MPPError",
|
|
13
|
+
"PaymentRequiredError",
|
|
14
|
+
"InvalidReceiptError",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
__version__ = "0.2.0"
|