@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,870 @@
|
|
|
1
|
+
# x402 Paywall Examples
|
|
2
|
+
|
|
3
|
+
## AVM-Only Paywall
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
7
|
+
|
|
8
|
+
const paywall = createPaywall()
|
|
9
|
+
.withNetwork(avmPaywall)
|
|
10
|
+
.withConfig({
|
|
11
|
+
appName: "My DApp",
|
|
12
|
+
appLogo: "https://example.com/logo.png",
|
|
13
|
+
testnet: true,
|
|
14
|
+
})
|
|
15
|
+
.build();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## EVM-Only Paywall
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { createPaywall, evmPaywall } from "@x402-avm/paywall";
|
|
24
|
+
|
|
25
|
+
const paywall = createPaywall()
|
|
26
|
+
.withNetwork(evmPaywall)
|
|
27
|
+
.withConfig({
|
|
28
|
+
appName: "EVM DApp",
|
|
29
|
+
testnet: true,
|
|
30
|
+
})
|
|
31
|
+
.build();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Multi-Network Paywall (AVM + EVM)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { createPaywall, avmPaywall, evmPaywall } from "@x402-avm/paywall";
|
|
40
|
+
|
|
41
|
+
const paywall = createPaywall()
|
|
42
|
+
.withNetwork(avmPaywall)
|
|
43
|
+
.withNetwork(evmPaywall)
|
|
44
|
+
.withConfig({
|
|
45
|
+
appName: "Multi-Chain Premium",
|
|
46
|
+
testnet: true,
|
|
47
|
+
})
|
|
48
|
+
.build();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Universal Paywall (AVM + EVM + SVM)
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createPaywall, avmPaywall, evmPaywall, svmPaywall } from "@x402-avm/paywall";
|
|
57
|
+
|
|
58
|
+
const paywall = createPaywall()
|
|
59
|
+
.withNetwork(avmPaywall)
|
|
60
|
+
.withNetwork(evmPaywall)
|
|
61
|
+
.withNetwork(svmPaywall)
|
|
62
|
+
.withConfig({
|
|
63
|
+
appName: "Universal Paywall",
|
|
64
|
+
testnet: true,
|
|
65
|
+
})
|
|
66
|
+
.build();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Express.js Basic Integration
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import express from "express";
|
|
75
|
+
import {
|
|
76
|
+
paymentMiddleware,
|
|
77
|
+
x402ResourceServer,
|
|
78
|
+
} from "@x402-avm/express";
|
|
79
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
80
|
+
|
|
81
|
+
const app = express();
|
|
82
|
+
|
|
83
|
+
const routes = {
|
|
84
|
+
"/api/premium-content": {
|
|
85
|
+
accepts: {
|
|
86
|
+
scheme: "exact",
|
|
87
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
88
|
+
asset: "10458941",
|
|
89
|
+
payTo: "YOUR_ALGORAND_ADDRESS_HERE",
|
|
90
|
+
price: "$0.01",
|
|
91
|
+
maxTimeoutSeconds: 300,
|
|
92
|
+
},
|
|
93
|
+
description: "Access to premium content",
|
|
94
|
+
mimeType: "application/json",
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const paywall = createPaywall()
|
|
99
|
+
.withNetwork(avmPaywall)
|
|
100
|
+
.withConfig({
|
|
101
|
+
appName: "Premium API",
|
|
102
|
+
appLogo: "https://example.com/logo.png",
|
|
103
|
+
testnet: true,
|
|
104
|
+
})
|
|
105
|
+
.build();
|
|
106
|
+
|
|
107
|
+
const facilitatorUrl = process.env.FACILITATOR_URL || "https://facilitator.example.com";
|
|
108
|
+
const server = new x402ResourceServer({ url: facilitatorUrl });
|
|
109
|
+
|
|
110
|
+
app.use(paymentMiddleware(routes, server, { testnet: true }, paywall));
|
|
111
|
+
|
|
112
|
+
app.get("/api/premium-content", (req, res) => {
|
|
113
|
+
res.json({
|
|
114
|
+
title: "Premium Article",
|
|
115
|
+
content: "This is the paid content that was unlocked by your USDC payment.",
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
app.get("/api/free-content", (req, res) => {
|
|
121
|
+
res.json({ message: "This content is free!" });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
app.listen(3000, () => {
|
|
125
|
+
console.log("Server running on http://localhost:3000");
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Express.js with HTTP Resource Server and Hooks
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import {
|
|
135
|
+
paymentMiddlewareFromHTTPServer,
|
|
136
|
+
x402ResourceServer,
|
|
137
|
+
x402HTTPResourceServer,
|
|
138
|
+
} from "@x402-avm/express";
|
|
139
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
140
|
+
|
|
141
|
+
const resourceServer = new x402ResourceServer({ url: facilitatorUrl });
|
|
142
|
+
|
|
143
|
+
const httpServer = new x402HTTPResourceServer(resourceServer, routes)
|
|
144
|
+
.onProtectedRequest(async (context) => {
|
|
145
|
+
console.log(`[x402] Protected request: ${context.path}`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const paywall = createPaywall()
|
|
149
|
+
.withNetwork(avmPaywall)
|
|
150
|
+
.withConfig({ appName: "My App", testnet: true })
|
|
151
|
+
.build();
|
|
152
|
+
|
|
153
|
+
app.use(paymentMiddlewareFromHTTPServer(httpServer, { testnet: true }, paywall));
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Express.js Multiple Protected Routes
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const routes = {
|
|
162
|
+
"/api/weather": {
|
|
163
|
+
accepts: {
|
|
164
|
+
scheme: "exact",
|
|
165
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
166
|
+
asset: "10458941",
|
|
167
|
+
payTo: "YOUR_ADDRESS",
|
|
168
|
+
price: "$0.001",
|
|
169
|
+
maxTimeoutSeconds: 60,
|
|
170
|
+
},
|
|
171
|
+
description: "Real-time weather data",
|
|
172
|
+
mimeType: "application/json",
|
|
173
|
+
},
|
|
174
|
+
"/api/analytics": {
|
|
175
|
+
accepts: {
|
|
176
|
+
scheme: "exact",
|
|
177
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
178
|
+
asset: "10458941",
|
|
179
|
+
payTo: "YOUR_ADDRESS",
|
|
180
|
+
price: "$0.05",
|
|
181
|
+
maxTimeoutSeconds: 300,
|
|
182
|
+
},
|
|
183
|
+
description: "Detailed analytics report",
|
|
184
|
+
mimeType: "application/json",
|
|
185
|
+
},
|
|
186
|
+
"/api/ai-summary": {
|
|
187
|
+
accepts: {
|
|
188
|
+
scheme: "exact",
|
|
189
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
190
|
+
asset: "10458941",
|
|
191
|
+
payTo: "YOUR_ADDRESS",
|
|
192
|
+
price: "$0.10",
|
|
193
|
+
maxTimeoutSeconds: 600,
|
|
194
|
+
},
|
|
195
|
+
description: "AI-powered content summary",
|
|
196
|
+
mimeType: "application/json",
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Hono Basic Integration
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { Hono } from "hono";
|
|
207
|
+
import { paymentMiddleware, x402ResourceServer } from "@x402-avm/hono";
|
|
208
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
209
|
+
|
|
210
|
+
const app = new Hono();
|
|
211
|
+
|
|
212
|
+
const routes = {
|
|
213
|
+
"/api/premium": {
|
|
214
|
+
accepts: {
|
|
215
|
+
scheme: "exact",
|
|
216
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
217
|
+
asset: "10458941",
|
|
218
|
+
payTo: "YOUR_ALGORAND_ADDRESS",
|
|
219
|
+
price: "$0.01",
|
|
220
|
+
maxTimeoutSeconds: 300,
|
|
221
|
+
},
|
|
222
|
+
description: "Premium content access",
|
|
223
|
+
mimeType: "application/json",
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const paywall = createPaywall()
|
|
228
|
+
.withNetwork(avmPaywall)
|
|
229
|
+
.withConfig({ appName: "Hono API", testnet: true })
|
|
230
|
+
.build();
|
|
231
|
+
|
|
232
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
233
|
+
|
|
234
|
+
app.use("*", paymentMiddleware(routes, server, { testnet: true }, paywall));
|
|
235
|
+
|
|
236
|
+
app.get("/api/premium", (c) => {
|
|
237
|
+
return c.json({
|
|
238
|
+
content: "This is premium content, paid with USDC on Algorand!",
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
app.get("/", (c) => c.text("Welcome to the Hono API"));
|
|
243
|
+
|
|
244
|
+
export default app;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Hono with Cloudflare Workers
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { Hono } from "hono";
|
|
253
|
+
import { paymentMiddlewareFromConfig, x402ResourceServer } from "@x402-avm/hono";
|
|
254
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
255
|
+
|
|
256
|
+
type Env = {
|
|
257
|
+
FACILITATOR_URL: string;
|
|
258
|
+
PAYTO_ADDRESS: string;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const app = new Hono<{ Bindings: Env }>();
|
|
262
|
+
|
|
263
|
+
app.use("*", async (c, next) => {
|
|
264
|
+
const routes = {
|
|
265
|
+
"/api/data": {
|
|
266
|
+
accepts: {
|
|
267
|
+
scheme: "exact",
|
|
268
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
269
|
+
asset: "10458941",
|
|
270
|
+
payTo: c.env.PAYTO_ADDRESS,
|
|
271
|
+
price: "$0.01",
|
|
272
|
+
maxTimeoutSeconds: 300,
|
|
273
|
+
},
|
|
274
|
+
description: "Paid API data",
|
|
275
|
+
mimeType: "application/json",
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const paywall = createPaywall()
|
|
280
|
+
.withNetwork(avmPaywall)
|
|
281
|
+
.withConfig({ appName: "Worker API", testnet: true })
|
|
282
|
+
.build();
|
|
283
|
+
|
|
284
|
+
const server = new x402ResourceServer({ url: c.env.FACILITATOR_URL });
|
|
285
|
+
|
|
286
|
+
const middleware = paymentMiddleware(routes, server, { testnet: true }, paywall);
|
|
287
|
+
return middleware(c, next);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
app.get("/api/data", (c) => {
|
|
291
|
+
return c.json({ data: "Premium data from Cloudflare Worker" });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
export default app;
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Next.js Middleware Approach
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// middleware.ts
|
|
303
|
+
import { paymentProxy, x402ResourceServer } from "@x402-avm/next";
|
|
304
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
305
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
306
|
+
|
|
307
|
+
const routes = {
|
|
308
|
+
"/api/premium": {
|
|
309
|
+
accepts: {
|
|
310
|
+
scheme: "exact",
|
|
311
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
312
|
+
asset: "10458941",
|
|
313
|
+
payTo: process.env.PAYTO_ADDRESS!,
|
|
314
|
+
price: "$0.01",
|
|
315
|
+
maxTimeoutSeconds: 300,
|
|
316
|
+
},
|
|
317
|
+
description: "Premium API endpoint",
|
|
318
|
+
mimeType: "application/json",
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const paywall = createPaywall()
|
|
323
|
+
.withNetwork(avmPaywall)
|
|
324
|
+
.withConfig({
|
|
325
|
+
appName: "Next.js App",
|
|
326
|
+
appLogo: "/logo.png",
|
|
327
|
+
testnet: true,
|
|
328
|
+
})
|
|
329
|
+
.build();
|
|
330
|
+
|
|
331
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
332
|
+
|
|
333
|
+
const proxy = paymentProxy(routes, server, { testnet: true }, paywall);
|
|
334
|
+
|
|
335
|
+
export async function middleware(request: NextRequest) {
|
|
336
|
+
if (request.nextUrl.pathname.startsWith("/api/premium")) {
|
|
337
|
+
return proxy(request);
|
|
338
|
+
}
|
|
339
|
+
return NextResponse.next();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export const config = {
|
|
343
|
+
matcher: ["/api/premium/:path*"],
|
|
344
|
+
};
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Next.js Route Handler (withX402)
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// app/api/premium/route.ts
|
|
353
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
354
|
+
import { withX402, x402ResourceServer } from "@x402-avm/next";
|
|
355
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
356
|
+
|
|
357
|
+
const server = new x402ResourceServer({
|
|
358
|
+
url: process.env.FACILITATOR_URL!,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const paywall = createPaywall()
|
|
362
|
+
.withNetwork(avmPaywall)
|
|
363
|
+
.withConfig({ appName: "Next.js App", testnet: true })
|
|
364
|
+
.build();
|
|
365
|
+
|
|
366
|
+
const routeConfig = {
|
|
367
|
+
accepts: {
|
|
368
|
+
scheme: "exact",
|
|
369
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
370
|
+
asset: "10458941",
|
|
371
|
+
payTo: process.env.PAYTO_ADDRESS!,
|
|
372
|
+
price: "$0.01",
|
|
373
|
+
maxTimeoutSeconds: 300,
|
|
374
|
+
},
|
|
375
|
+
description: "Premium content",
|
|
376
|
+
mimeType: "application/json",
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
async function handler(request: NextRequest) {
|
|
380
|
+
return NextResponse.json({
|
|
381
|
+
content: "Premium content unlocked with Algorand USDC payment!",
|
|
382
|
+
timestamp: new Date().toISOString(),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export const GET = withX402(handler, routeConfig, server, { testnet: true }, paywall);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Multi-Network Server Routes
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
const routes = {
|
|
395
|
+
"/api/premium": {
|
|
396
|
+
accepts: [
|
|
397
|
+
{
|
|
398
|
+
scheme: "exact",
|
|
399
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
400
|
+
asset: "10458941",
|
|
401
|
+
payTo: "ALGO_ADDRESS_HERE",
|
|
402
|
+
price: "$0.01",
|
|
403
|
+
maxTimeoutSeconds: 300,
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
scheme: "exact",
|
|
407
|
+
network: "eip155:84532",
|
|
408
|
+
asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
409
|
+
payTo: "0xEVM_ADDRESS_HERE",
|
|
410
|
+
price: "$0.01",
|
|
411
|
+
maxTimeoutSeconds: 30,
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
description: "Premium content - pay with USDC on Algorand or Base",
|
|
415
|
+
mimeType: "application/json",
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Custom Paywall Handler
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
import type { PaywallNetworkHandler, PaymentRequirements, PaymentRequired, PaywallConfig } from "@x402-avm/paywall";
|
|
426
|
+
|
|
427
|
+
const customAvmPaywall: PaywallNetworkHandler = {
|
|
428
|
+
supports(requirement: PaymentRequirements): boolean {
|
|
429
|
+
return requirement.network.startsWith("algorand:");
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
generateHtml(
|
|
433
|
+
requirement: PaymentRequirements,
|
|
434
|
+
paymentRequired: PaymentRequired,
|
|
435
|
+
config: PaywallConfig,
|
|
436
|
+
): string {
|
|
437
|
+
const amount = requirement.amount
|
|
438
|
+
? parseFloat(requirement.amount) / 1_000_000
|
|
439
|
+
: 0;
|
|
440
|
+
|
|
441
|
+
return `
|
|
442
|
+
<!DOCTYPE html>
|
|
443
|
+
<html>
|
|
444
|
+
<head>
|
|
445
|
+
<title>${config.appName || "Payment Required"}</title>
|
|
446
|
+
<meta charset="utf-8" />
|
|
447
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
448
|
+
<style>
|
|
449
|
+
body {
|
|
450
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
451
|
+
max-width: 600px;
|
|
452
|
+
margin: 40px auto;
|
|
453
|
+
padding: 20px;
|
|
454
|
+
text-align: center;
|
|
455
|
+
}
|
|
456
|
+
.amount { font-size: 2em; font-weight: bold; color: #1a1a2e; }
|
|
457
|
+
.pay-btn {
|
|
458
|
+
background: #6c63ff;
|
|
459
|
+
color: white;
|
|
460
|
+
border: none;
|
|
461
|
+
padding: 16px 48px;
|
|
462
|
+
font-size: 1.1em;
|
|
463
|
+
border-radius: 12px;
|
|
464
|
+
cursor: pointer;
|
|
465
|
+
margin-top: 24px;
|
|
466
|
+
}
|
|
467
|
+
</style>
|
|
468
|
+
<script>
|
|
469
|
+
window.x402 = {
|
|
470
|
+
amount: ${amount},
|
|
471
|
+
paymentRequired: ${JSON.stringify(paymentRequired)},
|
|
472
|
+
testnet: ${config.testnet ?? true},
|
|
473
|
+
currentUrl: "${paymentRequired.resource?.url || config.currentUrl || ""}",
|
|
474
|
+
config: { chainConfig: {} },
|
|
475
|
+
appName: "${config.appName || ""}",
|
|
476
|
+
appLogo: "${config.appLogo || ""}",
|
|
477
|
+
};
|
|
478
|
+
</script>
|
|
479
|
+
</head>
|
|
480
|
+
<body>
|
|
481
|
+
${config.appLogo ? `<img src="${config.appLogo}" style="width:64px;height:64px;border-radius:12px" />` : ""}
|
|
482
|
+
<h1>Payment Required</h1>
|
|
483
|
+
<p>${paymentRequired.resource?.description || "Access to premium content"}</p>
|
|
484
|
+
<div class="amount">$${amount.toFixed(2)} USDC</div>
|
|
485
|
+
<p>on Algorand ${config.testnet ? "Testnet" : "Mainnet"}</p>
|
|
486
|
+
<button class="pay-btn" id="payBtn">Connect Wallet & Pay</button>
|
|
487
|
+
</body>
|
|
488
|
+
</html>
|
|
489
|
+
`;
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const paywall = createPaywall()
|
|
494
|
+
.withNetwork(customAvmPaywall)
|
|
495
|
+
.withConfig({ appName: "Custom Paywall" })
|
|
496
|
+
.build();
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Wallet Configuration
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
import { getWallets } from "@wallet-standard/app";
|
|
505
|
+
|
|
506
|
+
const walletsApi = getWallets();
|
|
507
|
+
const algorandWallets = walletsApi.get().filter((wallet) => {
|
|
508
|
+
return "algorand:signTransaction" in wallet.features;
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Custom Wallet Integration (Outside Paywall)
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { useWallet } from "@txnlab/use-wallet-react";
|
|
518
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
519
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
520
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
521
|
+
|
|
522
|
+
function CustomPaymentUI() {
|
|
523
|
+
const { activeAccount, signTransactions } = useWallet();
|
|
524
|
+
|
|
525
|
+
async function payAndFetch(url: string) {
|
|
526
|
+
if (!activeAccount) throw new Error("Connect wallet first");
|
|
527
|
+
|
|
528
|
+
const signer: ClientAvmSigner = {
|
|
529
|
+
address: activeAccount.address,
|
|
530
|
+
signTransactions: async (txns, indexes) => signTransactions(txns, indexes),
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const client = new x402Client();
|
|
534
|
+
registerExactAvmScheme(client, { signer });
|
|
535
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
536
|
+
|
|
537
|
+
const response = await fetchWithPay(url);
|
|
538
|
+
return response;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Paywall Config Override at Runtime
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
app.use(paymentMiddleware(
|
|
549
|
+
routes,
|
|
550
|
+
server,
|
|
551
|
+
{
|
|
552
|
+
testnet: process.env.NODE_ENV !== "production",
|
|
553
|
+
appName: process.env.APP_NAME || "My App",
|
|
554
|
+
},
|
|
555
|
+
paywall,
|
|
556
|
+
));
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## Complete Express.js Server with AVM Paywall
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
import express from "express";
|
|
565
|
+
import cors from "cors";
|
|
566
|
+
import {
|
|
567
|
+
paymentMiddleware,
|
|
568
|
+
x402ResourceServer,
|
|
569
|
+
} from "@x402-avm/express";
|
|
570
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
571
|
+
|
|
572
|
+
const app = express();
|
|
573
|
+
app.use(cors());
|
|
574
|
+
app.use(express.json());
|
|
575
|
+
|
|
576
|
+
const FACILITATOR_URL = process.env.FACILITATOR_URL || "https://facilitator.example.com";
|
|
577
|
+
const PAYTO_ADDRESS = process.env.PAYTO_ADDRESS || "YOUR_ALGORAND_ADDRESS";
|
|
578
|
+
const PORT = parseInt(process.env.PORT || "3000");
|
|
579
|
+
const IS_TESTNET = process.env.NODE_ENV !== "production";
|
|
580
|
+
|
|
581
|
+
const ALGORAND_TESTNET_NETWORK = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=";
|
|
582
|
+
const ALGORAND_MAINNET_NETWORK = "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=";
|
|
583
|
+
const USDC_TESTNET_ASA = "10458941";
|
|
584
|
+
const USDC_MAINNET_ASA = "31566704";
|
|
585
|
+
|
|
586
|
+
const network = IS_TESTNET ? ALGORAND_TESTNET_NETWORK : ALGORAND_MAINNET_NETWORK;
|
|
587
|
+
const usdcAsset = IS_TESTNET ? USDC_TESTNET_ASA : USDC_MAINNET_ASA;
|
|
588
|
+
|
|
589
|
+
const routes = {
|
|
590
|
+
"/api/weather": {
|
|
591
|
+
accepts: {
|
|
592
|
+
scheme: "exact",
|
|
593
|
+
network,
|
|
594
|
+
asset: usdcAsset,
|
|
595
|
+
payTo: PAYTO_ADDRESS,
|
|
596
|
+
price: "$0.001",
|
|
597
|
+
maxTimeoutSeconds: 60,
|
|
598
|
+
},
|
|
599
|
+
description: "Real-time weather data for any city",
|
|
600
|
+
mimeType: "application/json",
|
|
601
|
+
},
|
|
602
|
+
"/api/article/:id": {
|
|
603
|
+
accepts: {
|
|
604
|
+
scheme: "exact",
|
|
605
|
+
network,
|
|
606
|
+
asset: usdcAsset,
|
|
607
|
+
payTo: PAYTO_ADDRESS,
|
|
608
|
+
price: "$0.05",
|
|
609
|
+
maxTimeoutSeconds: 300,
|
|
610
|
+
},
|
|
611
|
+
description: "Full premium article access",
|
|
612
|
+
mimeType: "application/json",
|
|
613
|
+
},
|
|
614
|
+
"/api/report": {
|
|
615
|
+
accepts: {
|
|
616
|
+
scheme: "exact",
|
|
617
|
+
network,
|
|
618
|
+
asset: usdcAsset,
|
|
619
|
+
payTo: PAYTO_ADDRESS,
|
|
620
|
+
price: "$1.00",
|
|
621
|
+
maxTimeoutSeconds: 600,
|
|
622
|
+
},
|
|
623
|
+
description: "Comprehensive analytics report",
|
|
624
|
+
mimeType: "application/json",
|
|
625
|
+
},
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
const paywall = createPaywall()
|
|
629
|
+
.withNetwork(avmPaywall)
|
|
630
|
+
.withConfig({
|
|
631
|
+
appName: "Premium Data API",
|
|
632
|
+
appLogo: "https://example.com/api-logo.png",
|
|
633
|
+
testnet: IS_TESTNET,
|
|
634
|
+
})
|
|
635
|
+
.build();
|
|
636
|
+
|
|
637
|
+
const server = new x402ResourceServer({ url: FACILITATOR_URL });
|
|
638
|
+
app.use(paymentMiddleware(routes, server, { testnet: IS_TESTNET }, paywall));
|
|
639
|
+
|
|
640
|
+
app.get("/", (req, res) => {
|
|
641
|
+
res.json({
|
|
642
|
+
name: "Premium Data API",
|
|
643
|
+
endpoints: {
|
|
644
|
+
"/api/weather?city=London": "$0.001 USDC - Weather data",
|
|
645
|
+
"/api/article/:id": "$0.05 USDC - Premium articles",
|
|
646
|
+
"/api/report": "$1.00 USDC - Full analytics report",
|
|
647
|
+
},
|
|
648
|
+
network: IS_TESTNET ? "Algorand Testnet" : "Algorand Mainnet",
|
|
649
|
+
payTo: PAYTO_ADDRESS,
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
app.get("/api/weather", (req, res) => {
|
|
654
|
+
const city = req.query.city || "London";
|
|
655
|
+
res.json({
|
|
656
|
+
city,
|
|
657
|
+
temperature: Math.round(15 + Math.random() * 20),
|
|
658
|
+
humidity: Math.round(40 + Math.random() * 40),
|
|
659
|
+
conditions: ["Sunny", "Cloudy", "Rainy", "Windy"][Math.floor(Math.random() * 4)],
|
|
660
|
+
timestamp: new Date().toISOString(),
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
app.get("/api/article/:id", (req, res) => {
|
|
665
|
+
res.json({
|
|
666
|
+
id: req.params.id,
|
|
667
|
+
title: "Premium Article: Advanced Algorand Development",
|
|
668
|
+
content: "This is the full premium article content...",
|
|
669
|
+
author: "x402-avm Team",
|
|
670
|
+
publishedAt: new Date().toISOString(),
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
app.get("/api/report", (req, res) => {
|
|
675
|
+
res.json({
|
|
676
|
+
title: "Comprehensive Analytics Report",
|
|
677
|
+
generatedAt: new Date().toISOString(),
|
|
678
|
+
metrics: {
|
|
679
|
+
totalUsers: 15420,
|
|
680
|
+
activeUsers: 8932,
|
|
681
|
+
revenue: "$42,150",
|
|
682
|
+
growth: "+15.3%",
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
app.listen(PORT, () => {
|
|
688
|
+
console.log(`Server running on http://localhost:${PORT}`);
|
|
689
|
+
console.log(`Network: ${IS_TESTNET ? "Algorand Testnet" : "Algorand Mainnet"}`);
|
|
690
|
+
console.log(`Facilitator: ${FACILITATOR_URL}`);
|
|
691
|
+
});
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
## Complete Hono Multi-Network API
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
import { Hono } from "hono";
|
|
700
|
+
import { cors } from "hono/cors";
|
|
701
|
+
import { paymentMiddleware, x402ResourceServer } from "@x402-avm/hono";
|
|
702
|
+
import { createPaywall, avmPaywall, evmPaywall } from "@x402-avm/paywall";
|
|
703
|
+
|
|
704
|
+
const app = new Hono();
|
|
705
|
+
app.use("*", cors());
|
|
706
|
+
|
|
707
|
+
const routes = {
|
|
708
|
+
"/api/data": {
|
|
709
|
+
accepts: [
|
|
710
|
+
{
|
|
711
|
+
scheme: "exact",
|
|
712
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
713
|
+
asset: "10458941",
|
|
714
|
+
payTo: process.env.ALGO_PAYTO!,
|
|
715
|
+
price: "$0.01",
|
|
716
|
+
maxTimeoutSeconds: 300,
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
scheme: "exact",
|
|
720
|
+
network: "eip155:84532",
|
|
721
|
+
asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
722
|
+
payTo: process.env.EVM_PAYTO!,
|
|
723
|
+
price: "$0.01",
|
|
724
|
+
maxTimeoutSeconds: 30,
|
|
725
|
+
},
|
|
726
|
+
],
|
|
727
|
+
description: "Premium data - pay with USDC on Algorand or Base",
|
|
728
|
+
mimeType: "application/json",
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
const paywall = createPaywall()
|
|
733
|
+
.withNetwork(avmPaywall)
|
|
734
|
+
.withNetwork(evmPaywall)
|
|
735
|
+
.withConfig({
|
|
736
|
+
appName: "Multi-Chain API",
|
|
737
|
+
testnet: true,
|
|
738
|
+
})
|
|
739
|
+
.build();
|
|
740
|
+
|
|
741
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
742
|
+
|
|
743
|
+
app.use("*", paymentMiddleware(routes, server, { testnet: true }, paywall));
|
|
744
|
+
|
|
745
|
+
app.get("/", (c) => {
|
|
746
|
+
return c.json({
|
|
747
|
+
name: "Multi-Chain Premium API",
|
|
748
|
+
endpoints: { "/api/data": "$0.01 USDC (Algorand or Base)" },
|
|
749
|
+
});
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
app.get("/api/data", (c) => {
|
|
753
|
+
return c.json({
|
|
754
|
+
message: "This content was unlocked with USDC payment!",
|
|
755
|
+
timestamp: new Date().toISOString(),
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
export default {
|
|
760
|
+
port: 3000,
|
|
761
|
+
fetch: app.fetch,
|
|
762
|
+
};
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
## Complete Next.js App with withX402
|
|
768
|
+
|
|
769
|
+
### app/api/premium/route.ts
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
773
|
+
import { withX402, x402ResourceServer } from "@x402-avm/next";
|
|
774
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
775
|
+
|
|
776
|
+
const server = new x402ResourceServer({
|
|
777
|
+
url: process.env.FACILITATOR_URL!,
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
const paywall = createPaywall()
|
|
781
|
+
.withNetwork(avmPaywall)
|
|
782
|
+
.withConfig({
|
|
783
|
+
appName: "Next.js Premium",
|
|
784
|
+
appLogo: "/logo.svg",
|
|
785
|
+
testnet: process.env.NODE_ENV !== "production",
|
|
786
|
+
})
|
|
787
|
+
.build();
|
|
788
|
+
|
|
789
|
+
const routeConfig = {
|
|
790
|
+
accepts: {
|
|
791
|
+
scheme: "exact",
|
|
792
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
793
|
+
asset: "10458941",
|
|
794
|
+
payTo: process.env.PAYTO_ADDRESS!,
|
|
795
|
+
price: "$0.01",
|
|
796
|
+
maxTimeoutSeconds: 300,
|
|
797
|
+
},
|
|
798
|
+
description: "Premium AI-generated content",
|
|
799
|
+
mimeType: "application/json",
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
async function handler(request: NextRequest) {
|
|
803
|
+
const searchParams = request.nextUrl.searchParams;
|
|
804
|
+
const topic = searchParams.get("topic") || "Algorand";
|
|
805
|
+
|
|
806
|
+
return NextResponse.json({
|
|
807
|
+
topic,
|
|
808
|
+
content: `Premium AI analysis of "${topic}"...`,
|
|
809
|
+
generatedAt: new Date().toISOString(),
|
|
810
|
+
model: "premium-v2",
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export const GET = withX402(
|
|
815
|
+
handler,
|
|
816
|
+
routeConfig,
|
|
817
|
+
server,
|
|
818
|
+
{ testnet: process.env.NODE_ENV !== "production" },
|
|
819
|
+
paywall,
|
|
820
|
+
);
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### app/page.tsx
|
|
824
|
+
|
|
825
|
+
```tsx
|
|
826
|
+
"use client";
|
|
827
|
+
|
|
828
|
+
import { useState } from "react";
|
|
829
|
+
|
|
830
|
+
export default function Home() {
|
|
831
|
+
const [result, setResult] = useState<string>("");
|
|
832
|
+
const [loading, setLoading] = useState(false);
|
|
833
|
+
|
|
834
|
+
async function fetchPremium() {
|
|
835
|
+
setLoading(true);
|
|
836
|
+
try {
|
|
837
|
+
const res = await fetch("/api/premium?topic=DeFi");
|
|
838
|
+
if (res.ok) {
|
|
839
|
+
const data = await res.json();
|
|
840
|
+
setResult(JSON.stringify(data, null, 2));
|
|
841
|
+
} else if (res.status === 402) {
|
|
842
|
+
setResult("Payment required -- navigate to /api/premium in browser to see paywall");
|
|
843
|
+
}
|
|
844
|
+
} catch (err) {
|
|
845
|
+
setResult(`Error: ${err}`);
|
|
846
|
+
} finally {
|
|
847
|
+
setLoading(false);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return (
|
|
852
|
+
<main style={{ padding: "40px", maxWidth: "600px", margin: "auto" }}>
|
|
853
|
+
<h1>x402-avm Next.js Demo</h1>
|
|
854
|
+
<p>
|
|
855
|
+
Visit{" "}
|
|
856
|
+
<a href="/api/premium?topic=Algorand">/api/premium</a>{" "}
|
|
857
|
+
in your browser to see the paywall.
|
|
858
|
+
</p>
|
|
859
|
+
<button onClick={fetchPremium} disabled={loading}>
|
|
860
|
+
{loading ? "Loading..." : "Fetch Premium (programmatic)"}
|
|
861
|
+
</button>
|
|
862
|
+
{result && (
|
|
863
|
+
<pre style={{ background: "#f0f0f0", padding: "16px", marginTop: "16px" }}>
|
|
864
|
+
{result}
|
|
865
|
+
</pre>
|
|
866
|
+
)}
|
|
867
|
+
</main>
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
```
|