@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,703 @@
|
|
|
1
|
+
# x402 Python Server Middleware Examples
|
|
2
|
+
|
|
3
|
+
## FastAPI: Basic Middleware Setup (Function-Based)
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from fastapi import FastAPI, Request
|
|
7
|
+
from x402.server import x402ResourceServer
|
|
8
|
+
from x402.http import HTTPFacilitatorClient, FacilitatorConfig, PaymentOption
|
|
9
|
+
from x402.http.types import RouteConfig
|
|
10
|
+
from x402.http.middleware.fastapi import payment_middleware
|
|
11
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
12
|
+
|
|
13
|
+
app = FastAPI()
|
|
14
|
+
|
|
15
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
16
|
+
server = x402ResourceServer(facilitator)
|
|
17
|
+
server.register(
|
|
18
|
+
"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
19
|
+
ExactAvmServerScheme(),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
routes = {
|
|
23
|
+
"GET /api/data/*": RouteConfig(
|
|
24
|
+
accepts=PaymentOption(
|
|
25
|
+
scheme="exact",
|
|
26
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
27
|
+
pay_to="YOUR_ALGORAND_ADDRESS",
|
|
28
|
+
price="$0.01",
|
|
29
|
+
),
|
|
30
|
+
),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
x402_mw = payment_middleware(routes=routes, server=server)
|
|
34
|
+
|
|
35
|
+
@app.middleware("http")
|
|
36
|
+
async def x402_middleware(request: Request, call_next):
|
|
37
|
+
return await x402_mw(request, call_next)
|
|
38
|
+
|
|
39
|
+
@app.get("/api/data/weather")
|
|
40
|
+
async def get_weather():
|
|
41
|
+
return {"temperature": 72, "unit": "F", "condition": "sunny"}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## FastAPI: ASGI Middleware Class
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from fastapi import FastAPI
|
|
48
|
+
from x402.server import x402ResourceServer
|
|
49
|
+
from x402.http import HTTPFacilitatorClient, FacilitatorConfig, PaymentOption
|
|
50
|
+
from x402.http.types import RouteConfig
|
|
51
|
+
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
|
|
52
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
53
|
+
|
|
54
|
+
app = FastAPI()
|
|
55
|
+
|
|
56
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
57
|
+
server = x402ResourceServer(facilitator)
|
|
58
|
+
server.register(
|
|
59
|
+
"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
60
|
+
ExactAvmServerScheme(),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
routes = {
|
|
64
|
+
"GET /weather": RouteConfig(
|
|
65
|
+
accepts=PaymentOption(
|
|
66
|
+
scheme="exact",
|
|
67
|
+
pay_to="YOUR_ALGORAND_ADDRESS",
|
|
68
|
+
price="$0.01",
|
|
69
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
70
|
+
),
|
|
71
|
+
mime_type="application/json",
|
|
72
|
+
description="Weather report",
|
|
73
|
+
),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
77
|
+
|
|
78
|
+
@app.get("/weather")
|
|
79
|
+
async def get_weather():
|
|
80
|
+
return {"report": {"weather": "sunny", "temperature": 70}}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## FastAPI: Middleware from Config
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from x402.http import HTTPFacilitatorClient, FacilitatorConfig
|
|
87
|
+
from x402.http.middleware.fastapi import payment_middleware_from_config
|
|
88
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
89
|
+
|
|
90
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
91
|
+
|
|
92
|
+
routes = {
|
|
93
|
+
"GET /api/data/*": {
|
|
94
|
+
"accepts": {
|
|
95
|
+
"scheme": "exact",
|
|
96
|
+
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
97
|
+
"payTo": "YOUR_ALGORAND_ADDRESS",
|
|
98
|
+
"maxAmountRequired": "10000",
|
|
99
|
+
"asset": "10458941",
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
mw = payment_middleware_from_config(
|
|
105
|
+
routes=routes,
|
|
106
|
+
facilitator_client=facilitator,
|
|
107
|
+
schemes=[
|
|
108
|
+
{
|
|
109
|
+
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
110
|
+
"server": ExactAvmServerScheme(),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@app.middleware("http")
|
|
116
|
+
async def x402_middleware(request, call_next):
|
|
117
|
+
return await mw(request, call_next)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## FastAPI: Simple String Price
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
routes = {
|
|
124
|
+
"GET /api/weather": RouteConfig(
|
|
125
|
+
accepts=PaymentOption(
|
|
126
|
+
scheme="exact",
|
|
127
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
128
|
+
pay_to="RECEIVER_ALGORAND_ADDRESS",
|
|
129
|
+
price="$0.01",
|
|
130
|
+
),
|
|
131
|
+
),
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## FastAPI: Explicit AssetAmount
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from x402.schemas import AssetAmount
|
|
139
|
+
|
|
140
|
+
routes = {
|
|
141
|
+
"GET /api/premium/*": RouteConfig(
|
|
142
|
+
accepts=PaymentOption(
|
|
143
|
+
scheme="exact",
|
|
144
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
145
|
+
pay_to="RECEIVER_ALGORAND_ADDRESS",
|
|
146
|
+
price=AssetAmount(
|
|
147
|
+
amount="10000",
|
|
148
|
+
asset="10458941",
|
|
149
|
+
extra={"name": "USDC", "decimals": 6},
|
|
150
|
+
),
|
|
151
|
+
),
|
|
152
|
+
),
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## FastAPI: Accessing Payment Info in Route Handlers
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from fastapi import FastAPI, Request
|
|
160
|
+
|
|
161
|
+
@app.get("/api/paid-resource")
|
|
162
|
+
async def paid_resource(request: Request):
|
|
163
|
+
payment_payload = getattr(request.state, "payment_payload", None)
|
|
164
|
+
payment_requirements = getattr(request.state, "payment_requirements", None)
|
|
165
|
+
|
|
166
|
+
if payment_payload:
|
|
167
|
+
payer_address = payment_payload.payload.get("from", "unknown")
|
|
168
|
+
return {
|
|
169
|
+
"data": "premium content",
|
|
170
|
+
"paid_by": payer_address,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {"data": "premium content"}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## FastAPI: Multiple Routes with Different Prices
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from x402.http import PaymentOption
|
|
180
|
+
from x402.http.types import RouteConfig
|
|
181
|
+
from x402.schemas import AssetAmount
|
|
182
|
+
|
|
183
|
+
routes = {
|
|
184
|
+
"GET /api/status": RouteConfig(
|
|
185
|
+
accepts=PaymentOption(
|
|
186
|
+
scheme="exact", pay_to=AVM_ADDRESS,
|
|
187
|
+
price="$0.001", network=AVM_NETWORK,
|
|
188
|
+
),
|
|
189
|
+
),
|
|
190
|
+
"GET /api/weather/*": RouteConfig(
|
|
191
|
+
accepts=PaymentOption(
|
|
192
|
+
scheme="exact", pay_to=AVM_ADDRESS,
|
|
193
|
+
price="$0.01", network=AVM_NETWORK,
|
|
194
|
+
),
|
|
195
|
+
description="Weather data",
|
|
196
|
+
),
|
|
197
|
+
"GET /api/analytics/*": RouteConfig(
|
|
198
|
+
accepts=PaymentOption(
|
|
199
|
+
scheme="exact", pay_to=AVM_ADDRESS,
|
|
200
|
+
price=AssetAmount(
|
|
201
|
+
amount="100000",
|
|
202
|
+
asset=str(USDC_TESTNET_ASA_ID),
|
|
203
|
+
extra={"name": "USDC", "decimals": 6},
|
|
204
|
+
),
|
|
205
|
+
network=AVM_NETWORK,
|
|
206
|
+
),
|
|
207
|
+
description="Premium analytics",
|
|
208
|
+
),
|
|
209
|
+
"POST /api/generate": RouteConfig(
|
|
210
|
+
accepts=PaymentOption(
|
|
211
|
+
scheme="exact", pay_to=AVM_ADDRESS,
|
|
212
|
+
price="$0.05", network=AVM_NETWORK,
|
|
213
|
+
),
|
|
214
|
+
description="AI generation",
|
|
215
|
+
),
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## FastAPI: Multi-Network (AVM + EVM + SVM)
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
import os
|
|
223
|
+
from dotenv import load_dotenv
|
|
224
|
+
from fastapi import FastAPI
|
|
225
|
+
from x402.http import FacilitatorConfig, HTTPFacilitatorClient, PaymentOption
|
|
226
|
+
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
|
|
227
|
+
from x402.http.types import RouteConfig
|
|
228
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
229
|
+
from x402.mechanisms.evm.exact import ExactEvmServerScheme
|
|
230
|
+
from x402.mechanisms.svm.exact import ExactSvmServerScheme
|
|
231
|
+
from x402.schemas import Network
|
|
232
|
+
from x402.server import x402ResourceServer
|
|
233
|
+
|
|
234
|
+
load_dotenv()
|
|
235
|
+
|
|
236
|
+
EVM_ADDRESS = os.getenv("EVM_ADDRESS")
|
|
237
|
+
SVM_ADDRESS = os.getenv("SVM_ADDRESS")
|
|
238
|
+
AVM_ADDRESS = os.getenv("AVM_ADDRESS")
|
|
239
|
+
AVM_NETWORK: Network = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
|
|
240
|
+
EVM_NETWORK: Network = "eip155:84532"
|
|
241
|
+
SVM_NETWORK: Network = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
|
|
242
|
+
|
|
243
|
+
app = FastAPI()
|
|
244
|
+
|
|
245
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
246
|
+
server = x402ResourceServer(facilitator)
|
|
247
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
248
|
+
server.register(EVM_NETWORK, ExactEvmServerScheme())
|
|
249
|
+
server.register(SVM_NETWORK, ExactSvmServerScheme())
|
|
250
|
+
|
|
251
|
+
routes = {
|
|
252
|
+
"GET /weather": RouteConfig(
|
|
253
|
+
accepts=[
|
|
254
|
+
PaymentOption(scheme="exact", pay_to=AVM_ADDRESS, price="$0.01", network=AVM_NETWORK),
|
|
255
|
+
PaymentOption(scheme="exact", pay_to=EVM_ADDRESS, price="$0.01", network=EVM_NETWORK),
|
|
256
|
+
PaymentOption(scheme="exact", pay_to=SVM_ADDRESS, price="$0.01", network=SVM_NETWORK),
|
|
257
|
+
],
|
|
258
|
+
mime_type="application/json",
|
|
259
|
+
description="Weather report",
|
|
260
|
+
),
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
264
|
+
|
|
265
|
+
@app.get("/weather")
|
|
266
|
+
async def get_weather():
|
|
267
|
+
return {"report": {"weather": "sunny", "temperature": 70}}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## FastAPI: Complete Algorand Example
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
"""FastAPI resource server accepting USDC payments on Algorand Testnet via x402."""
|
|
274
|
+
|
|
275
|
+
import os
|
|
276
|
+
|
|
277
|
+
from dotenv import load_dotenv
|
|
278
|
+
from fastapi import FastAPI, Request
|
|
279
|
+
from pydantic import BaseModel
|
|
280
|
+
|
|
281
|
+
from x402.http import FacilitatorConfig, HTTPFacilitatorClient, PaymentOption
|
|
282
|
+
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
|
|
283
|
+
from x402.http.types import RouteConfig
|
|
284
|
+
from x402.mechanisms.avm import USDC_TESTNET_ASA_ID
|
|
285
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
286
|
+
from x402.schemas import AssetAmount, Network
|
|
287
|
+
from x402.server import x402ResourceServer
|
|
288
|
+
|
|
289
|
+
load_dotenv()
|
|
290
|
+
|
|
291
|
+
AVM_ADDRESS = os.getenv("AVM_ADDRESS")
|
|
292
|
+
if not AVM_ADDRESS:
|
|
293
|
+
raise ValueError("AVM_ADDRESS environment variable is required")
|
|
294
|
+
|
|
295
|
+
AVM_NETWORK: Network = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
|
|
296
|
+
FACILITATOR_URL = os.getenv("FACILITATOR_URL", "https://x402.org/facilitator")
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class WeatherReport(BaseModel):
|
|
300
|
+
weather: str
|
|
301
|
+
temperature: int
|
|
302
|
+
unit: str
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class WeatherResponse(BaseModel):
|
|
306
|
+
report: WeatherReport
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
app = FastAPI(title="x402 AVM Resource Server")
|
|
310
|
+
|
|
311
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url=FACILITATOR_URL))
|
|
312
|
+
server = x402ResourceServer(facilitator)
|
|
313
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
314
|
+
|
|
315
|
+
routes = {
|
|
316
|
+
"GET /weather": RouteConfig(
|
|
317
|
+
accepts=PaymentOption(
|
|
318
|
+
scheme="exact",
|
|
319
|
+
pay_to=AVM_ADDRESS,
|
|
320
|
+
price="$0.01",
|
|
321
|
+
network=AVM_NETWORK,
|
|
322
|
+
),
|
|
323
|
+
mime_type="application/json",
|
|
324
|
+
description="Weather report",
|
|
325
|
+
),
|
|
326
|
+
"GET /premium/*": RouteConfig(
|
|
327
|
+
accepts=PaymentOption(
|
|
328
|
+
scheme="exact",
|
|
329
|
+
pay_to=AVM_ADDRESS,
|
|
330
|
+
price=AssetAmount(
|
|
331
|
+
amount="50000",
|
|
332
|
+
asset=str(USDC_TESTNET_ASA_ID),
|
|
333
|
+
extra={"name": "USDC", "decimals": 6},
|
|
334
|
+
),
|
|
335
|
+
network=AVM_NETWORK,
|
|
336
|
+
),
|
|
337
|
+
mime_type="application/json",
|
|
338
|
+
description="Premium content",
|
|
339
|
+
),
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
@app.get("/health")
|
|
346
|
+
async def health_check():
|
|
347
|
+
return {"status": "ok"}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@app.get("/weather")
|
|
351
|
+
async def get_weather(request: Request):
|
|
352
|
+
return WeatherResponse(
|
|
353
|
+
report=WeatherReport(weather="sunny", temperature=72, unit="F")
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@app.get("/premium/content")
|
|
358
|
+
async def get_premium_content(request: Request):
|
|
359
|
+
return {"content": "Premium Algorand analytics data.", "tier": "gold"}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if __name__ == "__main__":
|
|
363
|
+
import uvicorn
|
|
364
|
+
uvicorn.run(app, host="0.0.0.0", port=4021)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Flask: Basic Middleware Setup (PaymentMiddleware Class)
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
from flask import Flask
|
|
373
|
+
from x402.server import x402ResourceServerSync
|
|
374
|
+
from x402.http import HTTPFacilitatorClientSync, FacilitatorConfig, PaymentOption
|
|
375
|
+
from x402.http.types import RouteConfig
|
|
376
|
+
from x402.http.middleware.flask import PaymentMiddleware
|
|
377
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
378
|
+
|
|
379
|
+
app = Flask(__name__)
|
|
380
|
+
|
|
381
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
382
|
+
server = x402ResourceServerSync(facilitator)
|
|
383
|
+
server.register(
|
|
384
|
+
"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
385
|
+
ExactAvmServerScheme(),
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
routes = {
|
|
389
|
+
"GET /api/data/*": RouteConfig(
|
|
390
|
+
accepts=PaymentOption(
|
|
391
|
+
scheme="exact",
|
|
392
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
393
|
+
pay_to="YOUR_ALGORAND_ADDRESS",
|
|
394
|
+
price="$0.01",
|
|
395
|
+
),
|
|
396
|
+
),
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
PaymentMiddleware(app, routes, server)
|
|
400
|
+
|
|
401
|
+
@app.route("/api/data/weather")
|
|
402
|
+
def get_weather():
|
|
403
|
+
return {"temperature": 72, "unit": "F", "condition": "sunny"}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Flask: Factory Function
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
from flask import Flask
|
|
410
|
+
from x402.server import x402ResourceServerSync
|
|
411
|
+
from x402.http import HTTPFacilitatorClientSync, FacilitatorConfig, PaymentOption
|
|
412
|
+
from x402.http.types import RouteConfig
|
|
413
|
+
from x402.http.middleware.flask import payment_middleware
|
|
414
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
415
|
+
|
|
416
|
+
app = Flask(__name__)
|
|
417
|
+
|
|
418
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
419
|
+
server = x402ResourceServerSync(facilitator)
|
|
420
|
+
server.register(
|
|
421
|
+
"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
422
|
+
ExactAvmServerScheme(),
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
routes = {
|
|
426
|
+
"GET /weather": RouteConfig(
|
|
427
|
+
accepts=PaymentOption(
|
|
428
|
+
scheme="exact",
|
|
429
|
+
pay_to="YOUR_ALGORAND_ADDRESS",
|
|
430
|
+
price="$0.01",
|
|
431
|
+
network="algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
432
|
+
),
|
|
433
|
+
mime_type="application/json",
|
|
434
|
+
description="Weather report",
|
|
435
|
+
),
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
middleware_instance = payment_middleware(app, routes=routes, server=server)
|
|
439
|
+
|
|
440
|
+
@app.route("/weather")
|
|
441
|
+
def get_weather():
|
|
442
|
+
return {"report": {"weather": "sunny", "temperature": 70}}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Flask: Middleware from Config
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
from flask import Flask
|
|
449
|
+
from x402.http import HTTPFacilitatorClientSync, FacilitatorConfig
|
|
450
|
+
from x402.http.middleware.flask import payment_middleware_from_config
|
|
451
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
452
|
+
|
|
453
|
+
app = Flask(__name__)
|
|
454
|
+
|
|
455
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
456
|
+
|
|
457
|
+
routes = {
|
|
458
|
+
"GET /api/data/*": {
|
|
459
|
+
"accepts": {
|
|
460
|
+
"scheme": "exact",
|
|
461
|
+
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
462
|
+
"payTo": "YOUR_ALGORAND_ADDRESS",
|
|
463
|
+
"maxAmountRequired": "10000",
|
|
464
|
+
"asset": "10458941",
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
payment_middleware_from_config(
|
|
470
|
+
app,
|
|
471
|
+
routes=routes,
|
|
472
|
+
facilitator_client=facilitator,
|
|
473
|
+
schemes=[
|
|
474
|
+
{
|
|
475
|
+
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
476
|
+
"server": ExactAvmServerScheme(),
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
@app.route("/api/data/weather")
|
|
482
|
+
def get_weather():
|
|
483
|
+
return {"temperature": 72}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Flask: Accessing Payment Info in Route Handlers
|
|
487
|
+
|
|
488
|
+
```python
|
|
489
|
+
from flask import g, jsonify
|
|
490
|
+
|
|
491
|
+
@app.route("/api/paid-resource")
|
|
492
|
+
def paid_resource():
|
|
493
|
+
payment_payload = getattr(g, "payment_payload", None)
|
|
494
|
+
payment_requirements = getattr(g, "payment_requirements", None)
|
|
495
|
+
|
|
496
|
+
if payment_payload:
|
|
497
|
+
return jsonify({
|
|
498
|
+
"data": "premium content",
|
|
499
|
+
"paid": True,
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
return jsonify({"data": "premium content"})
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Flask: Multi-Network (AVM + EVM + SVM)
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
import os
|
|
509
|
+
from dotenv import load_dotenv
|
|
510
|
+
from flask import Flask, jsonify
|
|
511
|
+
from x402.http import FacilitatorConfig, HTTPFacilitatorClientSync, PaymentOption
|
|
512
|
+
from x402.http.middleware.flask import payment_middleware
|
|
513
|
+
from x402.http.types import RouteConfig
|
|
514
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
515
|
+
from x402.mechanisms.evm.exact import ExactEvmServerScheme
|
|
516
|
+
from x402.mechanisms.svm.exact import ExactSvmServerScheme
|
|
517
|
+
from x402.schemas import Network
|
|
518
|
+
from x402.server import x402ResourceServerSync
|
|
519
|
+
|
|
520
|
+
load_dotenv()
|
|
521
|
+
|
|
522
|
+
EVM_ADDRESS = os.getenv("EVM_ADDRESS")
|
|
523
|
+
SVM_ADDRESS = os.getenv("SVM_ADDRESS")
|
|
524
|
+
AVM_ADDRESS = os.getenv("AVM_ADDRESS")
|
|
525
|
+
AVM_NETWORK: Network = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
|
|
526
|
+
EVM_NETWORK: Network = "eip155:84532"
|
|
527
|
+
SVM_NETWORK: Network = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
|
|
528
|
+
|
|
529
|
+
app = Flask(__name__)
|
|
530
|
+
|
|
531
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
532
|
+
server = x402ResourceServerSync(facilitator)
|
|
533
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
534
|
+
server.register(EVM_NETWORK, ExactEvmServerScheme())
|
|
535
|
+
server.register(SVM_NETWORK, ExactSvmServerScheme())
|
|
536
|
+
|
|
537
|
+
routes = {
|
|
538
|
+
"GET /weather": RouteConfig(
|
|
539
|
+
accepts=[
|
|
540
|
+
PaymentOption(scheme="exact", pay_to=AVM_ADDRESS, price="$0.01", network=AVM_NETWORK),
|
|
541
|
+
PaymentOption(scheme="exact", pay_to=EVM_ADDRESS, price="$0.01", network=EVM_NETWORK),
|
|
542
|
+
PaymentOption(scheme="exact", pay_to=SVM_ADDRESS, price="$0.01", network=SVM_NETWORK),
|
|
543
|
+
],
|
|
544
|
+
mime_type="application/json",
|
|
545
|
+
description="Weather report",
|
|
546
|
+
),
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
payment_middleware(app, routes=routes, server=server)
|
|
550
|
+
|
|
551
|
+
@app.route("/weather")
|
|
552
|
+
def get_weather():
|
|
553
|
+
return jsonify({"report": {"weather": "sunny", "temperature": 70}})
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
## Flask: Complete Algorand Example
|
|
557
|
+
|
|
558
|
+
```python
|
|
559
|
+
"""Flask resource server accepting USDC payments on Algorand Testnet via x402."""
|
|
560
|
+
|
|
561
|
+
import os
|
|
562
|
+
|
|
563
|
+
from dotenv import load_dotenv
|
|
564
|
+
from flask import Flask, g, jsonify
|
|
565
|
+
|
|
566
|
+
from x402.http import FacilitatorConfig, HTTPFacilitatorClientSync, PaymentOption
|
|
567
|
+
from x402.http.middleware.flask import payment_middleware
|
|
568
|
+
from x402.http.types import RouteConfig
|
|
569
|
+
from x402.mechanisms.avm import USDC_TESTNET_ASA_ID
|
|
570
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
571
|
+
from x402.schemas import AssetAmount, Network
|
|
572
|
+
from x402.server import x402ResourceServerSync
|
|
573
|
+
|
|
574
|
+
load_dotenv()
|
|
575
|
+
|
|
576
|
+
AVM_ADDRESS = os.getenv("AVM_ADDRESS")
|
|
577
|
+
if not AVM_ADDRESS:
|
|
578
|
+
raise ValueError("AVM_ADDRESS environment variable is required")
|
|
579
|
+
|
|
580
|
+
AVM_NETWORK: Network = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
|
|
581
|
+
FACILITATOR_URL = os.getenv("FACILITATOR_URL", "https://x402.org/facilitator")
|
|
582
|
+
|
|
583
|
+
app = Flask(__name__)
|
|
584
|
+
|
|
585
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url=FACILITATOR_URL))
|
|
586
|
+
server = x402ResourceServerSync(facilitator)
|
|
587
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
588
|
+
|
|
589
|
+
routes = {
|
|
590
|
+
"GET /weather": RouteConfig(
|
|
591
|
+
accepts=PaymentOption(
|
|
592
|
+
scheme="exact",
|
|
593
|
+
pay_to=AVM_ADDRESS,
|
|
594
|
+
price="$0.01",
|
|
595
|
+
network=AVM_NETWORK,
|
|
596
|
+
),
|
|
597
|
+
mime_type="application/json",
|
|
598
|
+
description="Weather report",
|
|
599
|
+
),
|
|
600
|
+
"GET /premium/*": RouteConfig(
|
|
601
|
+
accepts=PaymentOption(
|
|
602
|
+
scheme="exact",
|
|
603
|
+
pay_to=AVM_ADDRESS,
|
|
604
|
+
price=AssetAmount(
|
|
605
|
+
amount="50000",
|
|
606
|
+
asset=str(USDC_TESTNET_ASA_ID),
|
|
607
|
+
extra={"name": "USDC", "decimals": 6},
|
|
608
|
+
),
|
|
609
|
+
network=AVM_NETWORK,
|
|
610
|
+
),
|
|
611
|
+
mime_type="application/json",
|
|
612
|
+
description="Premium content",
|
|
613
|
+
),
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
payment_middleware(app, routes=routes, server=server)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
@app.route("/health")
|
|
620
|
+
def health_check():
|
|
621
|
+
return jsonify({"status": "ok"})
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
@app.route("/weather")
|
|
625
|
+
def get_weather():
|
|
626
|
+
return jsonify({"report": {"weather": "sunny", "temperature": 72, "unit": "F"}})
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
@app.route("/premium/content")
|
|
630
|
+
def get_premium_content():
|
|
631
|
+
payment = getattr(g, "payment_payload", None)
|
|
632
|
+
return jsonify({
|
|
633
|
+
"content": "Premium Algorand analytics data.",
|
|
634
|
+
"tier": "gold",
|
|
635
|
+
"paid": payment is not None,
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
if __name__ == "__main__":
|
|
640
|
+
app.run(host="0.0.0.0", port=4021, debug=False)
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
## Flask vs FastAPI: Sync/Async Comparison
|
|
644
|
+
|
|
645
|
+
```python
|
|
646
|
+
# Flask (sync)
|
|
647
|
+
from flask import Flask, g, jsonify
|
|
648
|
+
from x402.server import x402ResourceServerSync
|
|
649
|
+
from x402.http import HTTPFacilitatorClientSync, FacilitatorConfig
|
|
650
|
+
from x402.http.middleware.flask import payment_middleware
|
|
651
|
+
|
|
652
|
+
app = Flask(__name__)
|
|
653
|
+
facilitator = HTTPFacilitatorClientSync(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
654
|
+
server = x402ResourceServerSync(facilitator)
|
|
655
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
656
|
+
payment_middleware(app, routes=routes, server=server)
|
|
657
|
+
|
|
658
|
+
@app.route("/weather")
|
|
659
|
+
def get_weather():
|
|
660
|
+
payment = getattr(g, "payment_payload", None)
|
|
661
|
+
return jsonify({"weather": "sunny"})
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
```python
|
|
665
|
+
# FastAPI (async)
|
|
666
|
+
from fastapi import FastAPI, Request
|
|
667
|
+
from x402.server import x402ResourceServer
|
|
668
|
+
from x402.http import HTTPFacilitatorClient, FacilitatorConfig
|
|
669
|
+
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
|
|
670
|
+
|
|
671
|
+
app = FastAPI()
|
|
672
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
673
|
+
server = x402ResourceServer(facilitator)
|
|
674
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
675
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
676
|
+
|
|
677
|
+
@app.get("/weather")
|
|
678
|
+
async def get_weather(request: Request):
|
|
679
|
+
payment = getattr(request.state, "payment_payload", None)
|
|
680
|
+
return {"weather": "sunny"}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
## Custom Error Responses (Paywall Config)
|
|
684
|
+
|
|
685
|
+
```python
|
|
686
|
+
from x402.http import PaywallConfig
|
|
687
|
+
|
|
688
|
+
# FastAPI
|
|
689
|
+
app.add_middleware(
|
|
690
|
+
PaymentMiddlewareASGI,
|
|
691
|
+
routes=routes,
|
|
692
|
+
server=server,
|
|
693
|
+
paywall_config=PaywallConfig(...),
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
# Flask
|
|
697
|
+
PaymentMiddleware(
|
|
698
|
+
app,
|
|
699
|
+
routes,
|
|
700
|
+
server,
|
|
701
|
+
paywall_config=PaywallConfig(...),
|
|
702
|
+
)
|
|
703
|
+
```
|