@goplausible/openclaw-algorand-plugin 0.5.0
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.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/index.ts +361 -0
- package/lib/mcp-servers.ts +14 -0
- package/lib/x402-fetch.ts +213 -0
- package/memory/algorand-plugin.md +82 -0
- package/openclaw.plugin.json +30 -0
- package/package.json +41 -0
- package/setup.ts +80 -0
- package/skills/algorand-development/SKILL.md +90 -0
- package/skills/algorand-development/references/build-smart-contracts-reference.md +79 -0
- package/skills/algorand-development/references/build-smart-contracts.md +52 -0
- package/skills/algorand-development/references/create-project-reference.md +86 -0
- package/skills/algorand-development/references/create-project.md +89 -0
- package/skills/algorand-development/references/implement-arc-standards-arc32-arc56.md +396 -0
- package/skills/algorand-development/references/implement-arc-standards-arc4.md +265 -0
- package/skills/algorand-development/references/implement-arc-standards.md +92 -0
- package/skills/algorand-development/references/search-algorand-examples-reference.md +119 -0
- package/skills/algorand-development/references/search-algorand-examples.md +89 -0
- package/skills/algorand-development/references/troubleshoot-errors-contract.md +373 -0
- package/skills/algorand-development/references/troubleshoot-errors-transaction.md +599 -0
- package/skills/algorand-development/references/troubleshoot-errors.md +105 -0
- package/skills/algorand-development/references/use-algokit-cli-reference.md +228 -0
- package/skills/algorand-development/references/use-algokit-cli.md +64 -0
- package/skills/algorand-interaction/SKILL.md +223 -0
- package/skills/algorand-interaction/references/algorand-mcp.md +743 -0
- package/skills/algorand-interaction/references/examples-algorand-mcp.md +647 -0
- package/skills/algorand-python/SKILL.md +95 -0
- package/skills/algorand-python/references/build-smart-contracts-decorators.md +413 -0
- package/skills/algorand-python/references/build-smart-contracts-reference.md +55 -0
- package/skills/algorand-python/references/build-smart-contracts-storage.md +452 -0
- package/skills/algorand-python/references/build-smart-contracts-transactions.md +445 -0
- package/skills/algorand-python/references/build-smart-contracts-types.md +438 -0
- package/skills/algorand-python/references/build-smart-contracts.md +82 -0
- package/skills/algorand-python/references/create-project-reference.md +55 -0
- package/skills/algorand-python/references/create-project.md +75 -0
- package/skills/algorand-python/references/implement-arc-standards-arc32-arc56.md +101 -0
- package/skills/algorand-python/references/implement-arc-standards-arc4.md +154 -0
- package/skills/algorand-python/references/implement-arc-standards.md +39 -0
- package/skills/algorand-python/references/troubleshoot-errors-contract.md +355 -0
- package/skills/algorand-python/references/troubleshoot-errors-transaction.md +430 -0
- package/skills/algorand-python/references/troubleshoot-errors.md +46 -0
- package/skills/algorand-python/references/use-algokit-utils-reference.md +350 -0
- package/skills/algorand-python/references/use-algokit-utils.md +76 -0
- package/skills/algorand-typescript/SKILL.md +131 -0
- package/skills/algorand-typescript/references/algorand-ts-migration-from-beta.md +448 -0
- package/skills/algorand-typescript/references/algorand-ts-migration-from-tealscript.md +487 -0
- package/skills/algorand-typescript/references/algorand-ts-migration.md +102 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-methods-and-abi.md +134 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-reference.md +58 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-storage.md +154 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-transactions.md +187 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-types-and-values.md +150 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax.md +84 -0
- package/skills/algorand-typescript/references/build-smart-contracts-reference.md +52 -0
- package/skills/algorand-typescript/references/build-smart-contracts.md +74 -0
- package/skills/algorand-typescript/references/call-smart-contracts-reference.md +237 -0
- package/skills/algorand-typescript/references/call-smart-contracts.md +183 -0
- package/skills/algorand-typescript/references/create-project-reference.md +53 -0
- package/skills/algorand-typescript/references/create-project.md +86 -0
- package/skills/algorand-typescript/references/deploy-react-frontend-examples.md +527 -0
- package/skills/algorand-typescript/references/deploy-react-frontend-reference.md +412 -0
- package/skills/algorand-typescript/references/deploy-react-frontend.md +239 -0
- package/skills/algorand-typescript/references/implement-arc-standards-arc32-arc56.md +73 -0
- package/skills/algorand-typescript/references/implement-arc-standards-arc4.md +126 -0
- package/skills/algorand-typescript/references/implement-arc-standards.md +44 -0
- package/skills/algorand-typescript/references/test-smart-contracts-examples.md +245 -0
- package/skills/algorand-typescript/references/test-smart-contracts-unit-tests.md +147 -0
- package/skills/algorand-typescript/references/test-smart-contracts.md +127 -0
- package/skills/algorand-typescript/references/troubleshoot-errors-contract.md +296 -0
- package/skills/algorand-typescript/references/troubleshoot-errors-transaction.md +438 -0
- package/skills/algorand-typescript/references/troubleshoot-errors.md +56 -0
- package/skills/algorand-typescript/references/use-algokit-utils-reference.md +342 -0
- package/skills/algorand-typescript/references/use-algokit-utils.md +74 -0
- package/skills/algorand-x402-python/SKILL.md +113 -0
- package/skills/algorand-x402-python/references/create-python-x402-client-examples.md +469 -0
- package/skills/algorand-x402-python/references/create-python-x402-client-reference.md +313 -0
- package/skills/algorand-x402-python/references/create-python-x402-client.md +207 -0
- package/skills/algorand-x402-python/references/create-python-x402-facilitator-examples.md +924 -0
- package/skills/algorand-x402-python/references/create-python-x402-facilitator-reference.md +629 -0
- package/skills/algorand-x402-python/references/create-python-x402-facilitator.md +408 -0
- package/skills/algorand-x402-python/references/create-python-x402-server-examples.md +703 -0
- package/skills/algorand-x402-python/references/create-python-x402-server-reference.md +303 -0
- package/skills/algorand-x402-python/references/create-python-x402-server.md +221 -0
- package/skills/algorand-x402-python/references/explain-algorand-x402-python-examples.md +605 -0
- package/skills/algorand-x402-python/references/explain-algorand-x402-python-reference.md +315 -0
- package/skills/algorand-x402-python/references/explain-algorand-x402-python.md +167 -0
- package/skills/algorand-x402-python/references/use-python-x402-core-avm-examples.md +554 -0
- package/skills/algorand-x402-python/references/use-python-x402-core-avm-reference.md +278 -0
- package/skills/algorand-x402-python/references/use-python-x402-core-avm.md +166 -0
- package/skills/algorand-x402-typescript/SKILL.md +129 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-client-examples.md +879 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-client-reference.md +371 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-client.md +236 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator-examples.md +875 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator-reference.md +461 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator.md +270 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs-examples.md +1181 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs-reference.md +360 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs.md +251 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-examples.md +870 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-reference.md +323 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall.md +281 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-server-examples.md +1135 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-server-reference.md +382 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-server.md +216 -0
- package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript-examples.md +616 -0
- package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript-reference.md +323 -0
- package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript.md +232 -0
- package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm-examples.md +1417 -0
- package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm-reference.md +504 -0
- package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm.md +158 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# x402 Python Server Middleware Reference
|
|
2
|
+
|
|
3
|
+
Detailed API reference for FastAPI and Flask payment middleware in the `x402-avm` Python package.
|
|
4
|
+
|
|
5
|
+
## FastAPI Middleware API
|
|
6
|
+
|
|
7
|
+
### `payment_middleware(routes, server, ...)`
|
|
8
|
+
|
|
9
|
+
Creates an async middleware callable for FastAPI.
|
|
10
|
+
|
|
11
|
+
| Parameter | Type | Default | Description |
|
|
12
|
+
|-----------|------|---------|-------------|
|
|
13
|
+
| `routes` | `RoutesConfig` | required | Route configuration for protected endpoints |
|
|
14
|
+
| `server` | `x402ResourceServer` | required | Pre-configured async resource server |
|
|
15
|
+
| `paywall_config` | `PaywallConfig \| None` | `None` | Optional paywall UI configuration |
|
|
16
|
+
| `paywall_provider` | `PaywallProvider \| None` | `None` | Optional custom paywall provider |
|
|
17
|
+
| `sync_facilitator_on_start` | `bool` | `True` | Fetch facilitator support on first request |
|
|
18
|
+
|
|
19
|
+
**Returns**: Async middleware callable `(Request, call_next) -> Response`
|
|
20
|
+
|
|
21
|
+
**Usage**:
|
|
22
|
+
```python
|
|
23
|
+
x402_mw = payment_middleware(routes=routes, server=server)
|
|
24
|
+
|
|
25
|
+
@app.middleware("http")
|
|
26
|
+
async def x402_middleware(request: Request, call_next):
|
|
27
|
+
return await x402_mw(request, call_next)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### `payment_middleware_from_config(routes, facilitator_client, ...)`
|
|
31
|
+
|
|
32
|
+
Creates middleware with an internally-created resource server.
|
|
33
|
+
|
|
34
|
+
| Parameter | Type | Default | Description |
|
|
35
|
+
|-----------|------|---------|-------------|
|
|
36
|
+
| `routes` | `RoutesConfig` | required | Route configuration |
|
|
37
|
+
| `facilitator_client` | `Any` | `None` | Facilitator client for payment processing |
|
|
38
|
+
| `schemes` | `list[dict] \| None` | `None` | Scheme registrations (`{"network": ..., "server": ...}`) |
|
|
39
|
+
| `paywall_config` | `PaywallConfig \| None` | `None` | Optional paywall config |
|
|
40
|
+
| `paywall_provider` | `PaywallProvider \| None` | `None` | Optional custom paywall |
|
|
41
|
+
| `sync_facilitator_on_start` | `bool` | `True` | Lazy initialization flag |
|
|
42
|
+
|
|
43
|
+
**Returns**: Async middleware callable `(Request, call_next) -> Response`
|
|
44
|
+
|
|
45
|
+
### `PaymentMiddlewareASGI`
|
|
46
|
+
|
|
47
|
+
Starlette `BaseHTTPMiddleware` subclass. Accepts same parameters as `payment_middleware` via `app.add_middleware()`.
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Description |
|
|
50
|
+
|-----------|------|-------------|
|
|
51
|
+
| `app` | `ASGIApp` | Passed automatically by Starlette |
|
|
52
|
+
| `routes` | `RoutesConfig` | Route configuration |
|
|
53
|
+
| `server` | `x402ResourceServer` | Pre-configured server instance |
|
|
54
|
+
| `paywall_config` | `PaywallConfig \| None` | Optional paywall UI config |
|
|
55
|
+
| `paywall_provider` | `PaywallProvider \| None` | Optional custom paywall |
|
|
56
|
+
|
|
57
|
+
**Usage**:
|
|
58
|
+
```python
|
|
59
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `FastAPIAdapter`
|
|
63
|
+
|
|
64
|
+
Implements the `HTTPAdapter` protocol for FastAPI/Starlette requests. Used internally.
|
|
65
|
+
|
|
66
|
+
| Method | Returns | Description |
|
|
67
|
+
|--------|---------|-------------|
|
|
68
|
+
| `get_header(name)` | `str \| None` | Case-insensitive header lookup |
|
|
69
|
+
| `get_method()` | `str` | HTTP method (GET, POST, etc.) |
|
|
70
|
+
| `get_path()` | `str` | Request path |
|
|
71
|
+
| `get_url()` | `str` | Full request URL |
|
|
72
|
+
| `get_accept_header()` | `str` | Accept header value |
|
|
73
|
+
| `get_user_agent()` | `str` | User-Agent header value |
|
|
74
|
+
| `get_query_params()` | `dict` | All query parameters |
|
|
75
|
+
| `get_query_param(name)` | `str \| None` | Single query parameter |
|
|
76
|
+
| `get_body()` | `None` | Returns None (body requires async access) |
|
|
77
|
+
|
|
78
|
+
## Flask Middleware API
|
|
79
|
+
|
|
80
|
+
### `PaymentMiddleware(app, routes, server, ...)`
|
|
81
|
+
|
|
82
|
+
The main WSGI middleware class. Replaces `app.wsgi_app` on initialization.
|
|
83
|
+
|
|
84
|
+
| Parameter | Type | Default | Description |
|
|
85
|
+
|-----------|------|---------|-------------|
|
|
86
|
+
| `app` | `Flask` | required | Flask application instance |
|
|
87
|
+
| `routes` | `RoutesConfig` | required | Route configuration for protected endpoints |
|
|
88
|
+
| `server` | `x402ResourceServerSync` | required | Pre-configured **sync** resource server |
|
|
89
|
+
| `paywall_config` | `PaywallConfig \| None` | `None` | Optional paywall UI configuration |
|
|
90
|
+
| `paywall_provider` | `PaywallProvider \| None` | `None` | Optional custom paywall provider |
|
|
91
|
+
| `sync_facilitator_on_start` | `bool` | `True` | Fetch facilitator support on first request |
|
|
92
|
+
|
|
93
|
+
**Side effect**: Replaces `app.wsgi_app` with the payment-checking WSGI middleware.
|
|
94
|
+
|
|
95
|
+
### `payment_middleware(app, routes, server, ...)`
|
|
96
|
+
|
|
97
|
+
Factory function. Accepts same parameters as `PaymentMiddleware`, returns `PaymentMiddleware` instance.
|
|
98
|
+
|
|
99
|
+
| Parameter | Type | Default | Description |
|
|
100
|
+
|-----------|------|---------|-------------|
|
|
101
|
+
| `app` | `Flask` | required | Flask application instance |
|
|
102
|
+
| `routes` | `RoutesConfig` | required | Route configuration |
|
|
103
|
+
| `server` | `x402ResourceServerSync` | required | Pre-configured **sync** resource server |
|
|
104
|
+
| `paywall_config` | `PaywallConfig \| None` | `None` | Optional paywall config |
|
|
105
|
+
| `paywall_provider` | `PaywallProvider \| None` | `None` | Optional custom paywall |
|
|
106
|
+
| `sync_facilitator_on_start` | `bool` | `True` | Lazy initialization flag |
|
|
107
|
+
|
|
108
|
+
**Returns**: `PaymentMiddleware` instance.
|
|
109
|
+
|
|
110
|
+
### `payment_middleware_from_config(app, routes, ...)`
|
|
111
|
+
|
|
112
|
+
Convenience function that creates the `x402ResourceServer` internally.
|
|
113
|
+
|
|
114
|
+
| Parameter | Type | Default | Description |
|
|
115
|
+
|-----------|------|---------|-------------|
|
|
116
|
+
| `app` | `Flask` | required | Flask application instance |
|
|
117
|
+
| `routes` | `RoutesConfig` | required | Route configuration |
|
|
118
|
+
| `facilitator_client` | `Any` | `None` | Facilitator client for payment processing |
|
|
119
|
+
| `schemes` | `list[dict] \| None` | `None` | Scheme registrations (`{"network": ..., "server": ...}`) |
|
|
120
|
+
| `paywall_config` | `PaywallConfig \| None` | `None` | Optional paywall config |
|
|
121
|
+
| `paywall_provider` | `PaywallProvider \| None` | `None` | Optional custom paywall |
|
|
122
|
+
| `sync_facilitator_on_start` | `bool` | `True` | Lazy initialization flag |
|
|
123
|
+
|
|
124
|
+
**Returns**: `PaymentMiddleware` instance.
|
|
125
|
+
|
|
126
|
+
### `FlaskAdapter`
|
|
127
|
+
|
|
128
|
+
Implements the `HTTPAdapter` protocol for Flask requests. Used internally.
|
|
129
|
+
|
|
130
|
+
| Method | Returns | Description |
|
|
131
|
+
|--------|---------|-------------|
|
|
132
|
+
| `get_header(name)` | `str \| None` | Case-insensitive header lookup |
|
|
133
|
+
| `get_method()` | `str` | HTTP method (GET, POST, etc.) |
|
|
134
|
+
| `get_path()` | `str` | Request path |
|
|
135
|
+
| `get_url()` | `str` | Full request URL |
|
|
136
|
+
| `get_accept_header()` | `str` | Accept header value |
|
|
137
|
+
| `get_user_agent()` | `str` | User-Agent header value |
|
|
138
|
+
| `get_query_params()` | `dict` | All query parameters |
|
|
139
|
+
| `get_query_param(name)` | `str \| None` | Single query parameter |
|
|
140
|
+
| `get_body()` | `Any \| None` | Parsed JSON body (via `request.get_json(silent=True)`) |
|
|
141
|
+
|
|
142
|
+
### `ResponseWrapper`
|
|
143
|
+
|
|
144
|
+
Internal class that captures and buffers the WSGI response for settlement processing.
|
|
145
|
+
|
|
146
|
+
| Method/Property | Description |
|
|
147
|
+
|-----------------|-------------|
|
|
148
|
+
| `status` | Captured HTTP status string |
|
|
149
|
+
| `status_code` | Parsed integer status code |
|
|
150
|
+
| `headers` | List of (name, value) header tuples |
|
|
151
|
+
| `add_header(name, value)` | Add a header to the response |
|
|
152
|
+
| `send_response(body_chunks)` | Send the buffered response to the client |
|
|
153
|
+
|
|
154
|
+
## RouteConfig
|
|
155
|
+
|
|
156
|
+
Dataclass for route configuration.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from x402.http.types import RouteConfig
|
|
160
|
+
|
|
161
|
+
RouteConfig(
|
|
162
|
+
accepts=PaymentOption(...), # Required: single payment option
|
|
163
|
+
# or: accepts=[PaymentOption(...), PaymentOption(...)], # Multiple options
|
|
164
|
+
resource="https://api.example.com/api/weather", # Optional: resource URL
|
|
165
|
+
description="Weather data API", # Optional: description
|
|
166
|
+
mime_type="application/json", # Optional: response MIME type
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Route Key Format
|
|
171
|
+
|
|
172
|
+
Route keys follow the pattern `"METHOD /path"`:
|
|
173
|
+
|
|
174
|
+
| Pattern | Matches |
|
|
175
|
+
|---------|---------|
|
|
176
|
+
| `"GET /api/weather"` | Exactly `/api/weather` |
|
|
177
|
+
| `"GET /api/premium/*"` | `/api/premium/anything`, `/api/premium/a/b` |
|
|
178
|
+
| `"POST /api/generate"` | Exactly `POST /api/generate` |
|
|
179
|
+
| `"GET /api/data/*"` | Any GET under `/api/data/` |
|
|
180
|
+
|
|
181
|
+
## PaymentOption
|
|
182
|
+
|
|
183
|
+
Dataclass for payment options.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from x402.http import PaymentOption
|
|
187
|
+
|
|
188
|
+
PaymentOption(
|
|
189
|
+
scheme="exact", # Payment scheme name
|
|
190
|
+
network="algorand:SGO...", # CAIP-2 network identifier
|
|
191
|
+
pay_to="ALGO_ADDRESS", # Recipient address
|
|
192
|
+
price="$0.01", # Auto-converted to AssetAmount
|
|
193
|
+
# or: price=AssetAmount(amount="10000", asset="10458941"),
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## AssetAmount
|
|
198
|
+
|
|
199
|
+
Explicit asset amount specification.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from x402.schemas import AssetAmount
|
|
203
|
+
|
|
204
|
+
AssetAmount(
|
|
205
|
+
amount="10000", # Atomic units (string)
|
|
206
|
+
asset="10458941", # Asset identifier (ASA ID as string)
|
|
207
|
+
extra={"name": "USDC", "decimals": 6}, # Optional metadata
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Sync vs Async Comparison
|
|
212
|
+
|
|
213
|
+
| Component | Flask (Sync) | FastAPI (Async) |
|
|
214
|
+
|-----------|-------------|-----------------|
|
|
215
|
+
| Resource server | `x402ResourceServerSync` | `x402ResourceServer` |
|
|
216
|
+
| Facilitator client | `HTTPFacilitatorClientSync` | `HTTPFacilitatorClient` |
|
|
217
|
+
| Middleware class | `PaymentMiddleware` (WSGI) | `PaymentMiddlewareASGI` (ASGI) |
|
|
218
|
+
| Payment info storage | `flask.g.payment_payload` | `request.state.payment_payload` |
|
|
219
|
+
| HTTP server wrapper | `x402HTTPResourceServerSync` | `x402HTTPResourceServer` |
|
|
220
|
+
| Middleware invocation | `PaymentMiddleware(app, ...)` | `app.add_middleware(PaymentMiddlewareASGI, ...)` |
|
|
221
|
+
| Settlement | Synchronous (blocking) | `await` async settlement |
|
|
222
|
+
|
|
223
|
+
## Middleware Flow
|
|
224
|
+
|
|
225
|
+
### FastAPI (ASGI)
|
|
226
|
+
|
|
227
|
+
1. Client sends request with `Payment-Signature` header
|
|
228
|
+
2. Middleware checks if the route requires payment via `requires_payment(context)`
|
|
229
|
+
3. On first protected request, facilitator support is synchronized (lazy init)
|
|
230
|
+
4. `process_http_request(context)` verifies the payment:
|
|
231
|
+
- `"no-payment-required"` -- route not protected, passes through
|
|
232
|
+
- `"payment-error"` -- returns 402 response (JSON or HTML paywall)
|
|
233
|
+
- `"payment-verified"` -- stores on `request.state`, calls route handler
|
|
234
|
+
5. If route handler returns a successful (2xx) response, settlement is processed
|
|
235
|
+
6. Settlement headers are added to the response
|
|
236
|
+
|
|
237
|
+
### Flask (WSGI)
|
|
238
|
+
|
|
239
|
+
1. Client sends request with `Payment-Signature` header
|
|
240
|
+
2. The WSGI middleware intercepts the request within a Flask request context
|
|
241
|
+
3. Middleware checks if the route requires payment via `requires_payment(context)`
|
|
242
|
+
4. On first protected request, facilitator support is synchronized (lazy init)
|
|
243
|
+
5. `process_http_request(context)` verifies the payment **synchronously**:
|
|
244
|
+
- `"no-payment-required"` -- passes through to `original_wsgi`
|
|
245
|
+
- `"payment-error"` -- returns 402 response
|
|
246
|
+
- `"payment-verified"` -- stores on `flask.g`, calls original WSGI app
|
|
247
|
+
6. Response is captured by `ResponseWrapper`, settlement is processed synchronously
|
|
248
|
+
7. Settlement headers are added to the buffered response
|
|
249
|
+
|
|
250
|
+
## Environment Variables
|
|
251
|
+
|
|
252
|
+
| Variable | Description | Default |
|
|
253
|
+
|----------|-------------|---------|
|
|
254
|
+
| `AVM_ADDRESS` | Algorand address to receive payments | Required |
|
|
255
|
+
| `FACILITATOR_URL` | URL of the x402 facilitator service | `https://x402.org/facilitator` |
|
|
256
|
+
|
|
257
|
+
## Algorand-Specific Constants
|
|
258
|
+
|
|
259
|
+
| Constant | Value | Import |
|
|
260
|
+
|----------|-------|--------|
|
|
261
|
+
| `ALGORAND_TESTNET_CAIP2` | `"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="` | `x402.mechanisms.avm` |
|
|
262
|
+
| `ALGORAND_MAINNET_CAIP2` | `"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="` | `x402.mechanisms.avm` |
|
|
263
|
+
| `USDC_TESTNET_ASA_ID` | `10458941` | `x402.mechanisms.avm` |
|
|
264
|
+
| `USDC_MAINNET_ASA_ID` | `31566704` | `x402.mechanisms.avm` |
|
|
265
|
+
|
|
266
|
+
## CAIP-2 Network Identifiers
|
|
267
|
+
|
|
268
|
+
| Network | CAIP-2 Identifier |
|
|
269
|
+
|---------|-------------------|
|
|
270
|
+
| Algorand Mainnet | `algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=` |
|
|
271
|
+
| Algorand Testnet | `algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=` |
|
|
272
|
+
| Base Sepolia (EVM) | `eip155:84532` |
|
|
273
|
+
| Solana Devnet (SVM) | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` |
|
|
274
|
+
|
|
275
|
+
## Error Handling
|
|
276
|
+
|
|
277
|
+
### Payment Errors (402 Responses)
|
|
278
|
+
|
|
279
|
+
When a client sends a request without a valid payment, the middleware returns a 402 response containing `PaymentRequirements` the client needs to fulfill.
|
|
280
|
+
|
|
281
|
+
### Settlement Failures
|
|
282
|
+
|
|
283
|
+
If the route handler succeeds but settlement fails:
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"error": "Settlement failed",
|
|
288
|
+
"details": "reason for failure"
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Unprotected Routes
|
|
293
|
+
|
|
294
|
+
Routes not listed in the `routes` configuration are served normally without any payment check.
|
|
295
|
+
|
|
296
|
+
## External Resources
|
|
297
|
+
|
|
298
|
+
- [x402-avm on PyPI](https://pypi.org/project/x402-avm/)
|
|
299
|
+
- [x402-avm Examples Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
300
|
+
- [x402 Algorand Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|
|
301
|
+
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
|
|
302
|
+
- [Flask Documentation](https://flask.palletsprojects.com/)
|
|
303
|
+
- [Coinbase x402 Specification](https://github.com/coinbase/x402)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Creating x402 Payment-Protected Servers in Python
|
|
2
|
+
|
|
3
|
+
Build FastAPI (async) or Flask (sync) servers that protect API endpoints behind Algorand USDC payments using x402 middleware.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before using this skill, ensure:
|
|
8
|
+
|
|
9
|
+
1. **Python 3.10+** is installed
|
|
10
|
+
2. **An Algorand address** to receive payments (the `payTo` address)
|
|
11
|
+
3. **A facilitator URL** -- use `https://x402.org/facilitator` or run your own
|
|
12
|
+
4. **Understanding of FastAPI or Flask** basics
|
|
13
|
+
|
|
14
|
+
## Core Workflow: Middleware-Based Payment Protection
|
|
15
|
+
|
|
16
|
+
The middleware intercepts requests to protected routes, checks for payment headers, verifies payments through the facilitator, and settles on success.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Client Request
|
|
20
|
+
|
|
|
21
|
+
v
|
|
22
|
+
x402 Middleware (checks route config)
|
|
23
|
+
|
|
|
24
|
+
+-- Not protected -> Pass through to handler
|
|
25
|
+
|
|
|
26
|
+
+-- Protected, no payment -> Return 402 with PaymentRequirements
|
|
27
|
+
|
|
|
28
|
+
+-- Protected, has payment -> Verify via Facilitator
|
|
29
|
+
|
|
|
30
|
+
+-- Invalid -> Return 402
|
|
31
|
+
|
|
|
32
|
+
+-- Valid -> Call handler, then settle payment
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## How to Proceed
|
|
36
|
+
|
|
37
|
+
### Step 1: Install Dependencies
|
|
38
|
+
|
|
39
|
+
For FastAPI (async):
|
|
40
|
+
```bash
|
|
41
|
+
pip install "x402-avm[fastapi,avm]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For Flask (sync):
|
|
45
|
+
```bash
|
|
46
|
+
pip install "x402-avm[flask,avm]"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 2: Choose Your Framework Pattern
|
|
50
|
+
|
|
51
|
+
**FastAPI** uses async components:
|
|
52
|
+
- `x402ResourceServer` (async)
|
|
53
|
+
- `HTTPFacilitatorClient` (async)
|
|
54
|
+
- `PaymentMiddlewareASGI` or `payment_middleware`
|
|
55
|
+
|
|
56
|
+
**Flask** uses sync components:
|
|
57
|
+
- `x402ResourceServerSync` (sync)
|
|
58
|
+
- `HTTPFacilitatorClientSync` (sync)
|
|
59
|
+
- `PaymentMiddleware` or `payment_middleware`
|
|
60
|
+
|
|
61
|
+
### Step 3: Set Up the Resource Server
|
|
62
|
+
|
|
63
|
+
Create a facilitator client, resource server, and register the AVM scheme:
|
|
64
|
+
|
|
65
|
+
**FastAPI:**
|
|
66
|
+
```python
|
|
67
|
+
from x402.server import x402ResourceServer
|
|
68
|
+
from x402.http import HTTPFacilitatorClient, FacilitatorConfig
|
|
69
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
70
|
+
|
|
71
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
72
|
+
server = x402ResourceServer(facilitator)
|
|
73
|
+
server.register("algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", ExactAvmServerScheme())
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Flask:**
|
|
77
|
+
```python
|
|
78
|
+
from x402.server import x402ResourceServerSync
|
|
79
|
+
from x402.http import HTTPFacilitatorClientSync, FacilitatorConfig
|
|
80
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
81
|
+
|
|
82
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
83
|
+
server = x402ResourceServerSync(facilitator)
|
|
84
|
+
server.register("algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", ExactAvmServerScheme())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 4: Define Route Configurations
|
|
88
|
+
|
|
89
|
+
Routes map HTTP method + path patterns to payment requirements:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from x402.http import PaymentOption
|
|
93
|
+
from x402.http.types import RouteConfig
|
|
94
|
+
|
|
95
|
+
routes = {
|
|
96
|
+
"GET /api/weather": RouteConfig(
|
|
97
|
+
accepts=PaymentOption(
|
|
98
|
+
scheme="exact",
|
|
99
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
100
|
+
pay_to="YOUR_ALGORAND_ADDRESS",
|
|
101
|
+
price="$0.01",
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Step 5: Apply Middleware
|
|
108
|
+
|
|
109
|
+
**FastAPI -- Option A (ASGI class, recommended):**
|
|
110
|
+
```python
|
|
111
|
+
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
|
|
112
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**FastAPI -- Option B (function-based):**
|
|
116
|
+
```python
|
|
117
|
+
from x402.http.middleware.fastapi import payment_middleware
|
|
118
|
+
x402_mw = payment_middleware(routes=routes, server=server)
|
|
119
|
+
|
|
120
|
+
@app.middleware("http")
|
|
121
|
+
async def x402_middleware(request, call_next):
|
|
122
|
+
return await x402_mw(request, call_next)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Flask:**
|
|
126
|
+
```python
|
|
127
|
+
from x402.http.middleware.flask import PaymentMiddleware
|
|
128
|
+
PaymentMiddleware(app, routes, server)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Step 6: Define Route Handlers
|
|
132
|
+
|
|
133
|
+
Routes listed in the configuration require payment. Unlisted routes pass through freely.
|
|
134
|
+
|
|
135
|
+
**FastAPI:**
|
|
136
|
+
```python
|
|
137
|
+
@app.get("/api/weather")
|
|
138
|
+
async def get_weather():
|
|
139
|
+
return {"temperature": 72, "unit": "F"}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Flask:**
|
|
143
|
+
```python
|
|
144
|
+
@app.route("/api/weather")
|
|
145
|
+
def get_weather():
|
|
146
|
+
return {"temperature": 72, "unit": "F"}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Important Rules / Guidelines
|
|
150
|
+
|
|
151
|
+
1. **Match async/sync variants** -- FastAPI uses `x402ResourceServer` + `HTTPFacilitatorClient`, Flask uses `x402ResourceServerSync` + `HTTPFacilitatorClientSync`
|
|
152
|
+
2. **Route format** -- Keys must be `"METHOD /path"` (e.g., `"GET /api/weather"`, `"POST /api/generate/*"`)
|
|
153
|
+
3. **Wildcard paths** -- Use `/*` suffix to match all sub-paths (e.g., `"GET /api/premium/*"`)
|
|
154
|
+
4. **Unlisted routes pass through** -- Only routes in the config require payment
|
|
155
|
+
5. **Register scheme before middleware** -- Call `server.register(...)` before adding middleware
|
|
156
|
+
6. **Price format** -- Use `"$0.01"` for auto-conversion or `AssetAmount(amount="10000", asset="10458941")` for explicit control
|
|
157
|
+
7. **CAIP-2 network IDs** -- Use full CAIP-2 identifiers like `"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="`
|
|
158
|
+
|
|
159
|
+
## Pricing Options
|
|
160
|
+
|
|
161
|
+
### Simple String Price
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
PaymentOption(
|
|
165
|
+
scheme="exact",
|
|
166
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
167
|
+
pay_to="YOUR_ADDRESS",
|
|
168
|
+
price="$0.01", # Auto-converts to 10000 microUSDC
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Explicit AssetAmount
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from x402.schemas import AssetAmount
|
|
176
|
+
|
|
177
|
+
PaymentOption(
|
|
178
|
+
scheme="exact",
|
|
179
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
180
|
+
pay_to="YOUR_ADDRESS",
|
|
181
|
+
price=AssetAmount(
|
|
182
|
+
amount="50000", # 50000 microUSDC = $0.05
|
|
183
|
+
asset="10458941", # USDC ASA ID on testnet
|
|
184
|
+
extra={"name": "USDC", "decimals": 6},
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Multi-Network (AVM + EVM + SVM)
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
routes = {
|
|
193
|
+
"GET /api/data/*": RouteConfig(
|
|
194
|
+
accepts=[
|
|
195
|
+
PaymentOption(scheme="exact", pay_to=AVM_ADDRESS, price="$0.01",
|
|
196
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="),
|
|
197
|
+
PaymentOption(scheme="exact", pay_to=EVM_ADDRESS, price="$0.01",
|
|
198
|
+
network="eip155:84532"),
|
|
199
|
+
PaymentOption(scheme="exact", pay_to=SVM_ADDRESS, price="$0.01",
|
|
200
|
+
network="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"),
|
|
201
|
+
],
|
|
202
|
+
),
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Common Errors / Troubleshooting
|
|
207
|
+
|
|
208
|
+
| Error | Cause | Solution |
|
|
209
|
+
|-------|-------|----------|
|
|
210
|
+
| `TypeError: async` errors in Flask | Using async variants with Flask | Use `x402ResourceServerSync` and `HTTPFacilitatorClientSync` |
|
|
211
|
+
| 402 returned to all requests | Middleware applied but facilitator unreachable | Check `FACILITATOR_URL` and network connectivity |
|
|
212
|
+
| Route not protected | Path pattern mismatch | Verify route key format matches: `"GET /exact/path"` or `"GET /prefix/*"` |
|
|
213
|
+
| Settlement fails | Facilitator cannot reach Algorand network | Check facilitator logs and algod endpoint |
|
|
214
|
+
| `ImportError` on middleware | Missing extras | `pip install "x402-avm[fastapi,avm]"` or `"x402-avm[flask,avm]"` |
|
|
215
|
+
|
|
216
|
+
## References / Further Reading
|
|
217
|
+
|
|
218
|
+
- [create-python-x402-server-reference.md](./create-python-x402-server-reference.md) - Detailed API reference for middleware
|
|
219
|
+
- [create-python-x402-server-examples.md](./create-python-x402-server-examples.md) - Complete code examples for FastAPI and Flask
|
|
220
|
+
- [x402-avm Examples Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
221
|
+
- [x402 Algorand Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|