@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
package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-reference.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# x402 Paywall Reference
|
|
2
|
+
|
|
3
|
+
Detailed API reference for `@x402-avm/paywall` and framework middleware packages.
|
|
4
|
+
|
|
5
|
+
## Package: @x402-avm/paywall
|
|
6
|
+
|
|
7
|
+
### Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @x402-avm/paywall @x402-avm/avm @x402-avm/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Exports from @x402-avm/paywall
|
|
14
|
+
|
|
15
|
+
| Export | Type | Description |
|
|
16
|
+
|--------|------|-------------|
|
|
17
|
+
| `createPaywall` | Function | Creates a new `PaywallBuilder` instance |
|
|
18
|
+
| `PaywallBuilder` | Class | Builder for configuring and building paywall providers |
|
|
19
|
+
| `avmPaywall` | Object | AVM network handler (algorand:*) |
|
|
20
|
+
| `evmPaywall` | Object | EVM network handler (eip155:*) |
|
|
21
|
+
| `svmPaywall` | Object | SVM network handler (solana:*) |
|
|
22
|
+
| `PaywallProvider` | Type | Interface for paywall HTML generation |
|
|
23
|
+
| `PaywallConfig` | Type | Configuration options for paywall branding |
|
|
24
|
+
| `PaywallNetworkHandler` | Type | Interface for network-specific handlers |
|
|
25
|
+
| `PaymentRequired` | Type | 402 response structure |
|
|
26
|
+
| `PaymentRequirements` | Type | Individual payment requirement |
|
|
27
|
+
|
|
28
|
+
### Exports from @x402-avm/paywall/avm
|
|
29
|
+
|
|
30
|
+
| Export | Type | Description |
|
|
31
|
+
|--------|------|-------------|
|
|
32
|
+
| `avmPaywall` | Object | AVM paywall handler (same as main export) |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## PaywallBuilder
|
|
37
|
+
|
|
38
|
+
### createPaywall()
|
|
39
|
+
|
|
40
|
+
Factory function that creates a new `PaywallBuilder` instance.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
function createPaywall(): PaywallBuilder;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### PaywallBuilder Methods
|
|
47
|
+
|
|
48
|
+
| Method | Signature | Description |
|
|
49
|
+
|--------|-----------|-------------|
|
|
50
|
+
| `.withNetwork(handler)` | `(handler: PaywallNetworkHandler) => PaywallBuilder` | Register a network-specific paywall handler. Order matters -- first match wins. |
|
|
51
|
+
| `.withConfig(config)` | `(config: PaywallConfig) => PaywallBuilder` | Set default paywall configuration. |
|
|
52
|
+
| `.build()` | `() => PaywallProvider` | Build and return a `PaywallProvider`. |
|
|
53
|
+
|
|
54
|
+
### PaywallProvider
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface PaywallProvider {
|
|
58
|
+
/**
|
|
59
|
+
* Generate an HTML paywall page for the given payment requirements.
|
|
60
|
+
* @param paymentRequired - The full 402 response from the server
|
|
61
|
+
* @param config - Optional runtime config override
|
|
62
|
+
* @returns Full HTML page as a string
|
|
63
|
+
*/
|
|
64
|
+
generateHtml(paymentRequired: PaymentRequired, config?: PaywallConfig): string;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### PaywallConfig
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
interface PaywallConfig {
|
|
72
|
+
/** App name shown in wallet connection modal title */
|
|
73
|
+
appName?: string;
|
|
74
|
+
/** App logo URL shown in the paywall header */
|
|
75
|
+
appLogo?: string;
|
|
76
|
+
/** URL of the content being accessed (used for retry after payment) */
|
|
77
|
+
currentUrl?: string;
|
|
78
|
+
/** Whether to use testnet (default: true) */
|
|
79
|
+
testnet?: boolean;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## PaywallNetworkHandler
|
|
86
|
+
|
|
87
|
+
Interface for creating custom network-specific paywall handlers.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
interface PaywallNetworkHandler {
|
|
91
|
+
/**
|
|
92
|
+
* Check if this handler supports the given payment requirement.
|
|
93
|
+
* @param requirement - A single payment requirement from the 402 response
|
|
94
|
+
* @returns true if this handler can generate HTML for this requirement
|
|
95
|
+
*/
|
|
96
|
+
supports(requirement: PaymentRequirements): boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Generate the full HTML paywall page.
|
|
100
|
+
* @param requirement - The matched payment requirement
|
|
101
|
+
* @param paymentRequired - The full PaymentRequired response
|
|
102
|
+
* @param config - Paywall configuration
|
|
103
|
+
* @returns Full HTML page as a string
|
|
104
|
+
*/
|
|
105
|
+
generateHtml(
|
|
106
|
+
requirement: PaymentRequirements,
|
|
107
|
+
paymentRequired: PaymentRequired,
|
|
108
|
+
config: PaywallConfig,
|
|
109
|
+
): string;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Built-in Handlers
|
|
114
|
+
|
|
115
|
+
| Handler | Supports | Networks |
|
|
116
|
+
|---------|----------|----------|
|
|
117
|
+
| `avmPaywall` | `network.startsWith("algorand:")` | Algorand Testnet, Algorand Mainnet |
|
|
118
|
+
| `evmPaywall` | `network.startsWith("eip155:")` | Base, Ethereum, etc. |
|
|
119
|
+
| `svmPaywall` | `network.startsWith("solana:")` | Solana |
|
|
120
|
+
|
|
121
|
+
### Handler Selection
|
|
122
|
+
|
|
123
|
+
When `PaywallProvider.generateHtml()` is called, it iterates through registered handlers in order. The first handler whose `supports()` returns true for any of the `accepts` requirements is used to generate the HTML.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## avmPaywall Handler
|
|
128
|
+
|
|
129
|
+
The `avmPaywall` handler generates a full React-based HTML page with:
|
|
130
|
+
|
|
131
|
+
1. **window.x402 global** -- Embeds payment configuration for the client-side app
|
|
132
|
+
2. **Wallet discovery** -- Uses `@wallet-standard/app` to find Algorand wallets
|
|
133
|
+
3. **Wallet selection UI** -- Dropdown with icons for detected wallets
|
|
134
|
+
4. **Balance checking** -- Reads USDC ASA balance for the connected account
|
|
135
|
+
5. **Transaction signing** -- Uses `algorand:signTransaction` wallet-standard feature
|
|
136
|
+
6. **Payment submission** -- Retries original request with `PAYMENT-SIGNATURE` header
|
|
137
|
+
7. **Automatic redirect** -- Redirects to paid content after successful payment
|
|
138
|
+
|
|
139
|
+
### Supported Wallets
|
|
140
|
+
|
|
141
|
+
| Wallet | Type | Feature |
|
|
142
|
+
|--------|------|---------|
|
|
143
|
+
| Pera Wallet | Mobile + WalletConnect | `algorand:signTransaction` |
|
|
144
|
+
| Defly Wallet | Mobile + WalletConnect | `algorand:signTransaction` |
|
|
145
|
+
| Lute Wallet | Browser Extension | `algorand:signTransaction` |
|
|
146
|
+
|
|
147
|
+
### window.x402 Configuration Object
|
|
148
|
+
|
|
149
|
+
The generated HTML page injects this configuration:
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
window.x402 = {
|
|
153
|
+
amount: 0.01, // Amount in USDC (human-readable)
|
|
154
|
+
paymentRequired: {...}, // Full PaymentRequired response
|
|
155
|
+
testnet: true, // Network mode
|
|
156
|
+
currentUrl: "...", // URL to retry after payment
|
|
157
|
+
config: { chainConfig: {} },
|
|
158
|
+
appName: "My App",
|
|
159
|
+
appLogo: "https://...",
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Framework Middleware
|
|
166
|
+
|
|
167
|
+
### @x402-avm/express
|
|
168
|
+
|
|
169
|
+
| Function | Signature | Description |
|
|
170
|
+
|----------|-----------|-------------|
|
|
171
|
+
| `paymentMiddleware` | `(routes, server, config?, paywall?)` | Express middleware |
|
|
172
|
+
| `paymentMiddlewareFromHTTPServer` | `(httpServer, config?, paywall?)` | Express middleware from HTTP server |
|
|
173
|
+
| `paymentMiddlewareFromConfig` | `(routes, facilitator?, schemes?, config?, paywall?)` | Express middleware from config |
|
|
174
|
+
| `x402ResourceServer` | Class | Resource server for facilitator communication |
|
|
175
|
+
| `x402HTTPResourceServer` | Class | HTTP-aware resource server with route config |
|
|
176
|
+
|
|
177
|
+
### @x402-avm/hono
|
|
178
|
+
|
|
179
|
+
| Function | Signature | Description |
|
|
180
|
+
|----------|-----------|-------------|
|
|
181
|
+
| `paymentMiddleware` | `(routes, server, config?, paywall?)` | Hono middleware |
|
|
182
|
+
| `paymentMiddlewareFromHTTPServer` | `(httpServer, config?, paywall?)` | Hono middleware from HTTP server |
|
|
183
|
+
| `paymentMiddlewareFromConfig` | `(routes, facilitator?, schemes?, config?, paywall?)` | Hono middleware from config |
|
|
184
|
+
| `x402ResourceServer` | Class | Resource server for facilitator communication |
|
|
185
|
+
|
|
186
|
+
### @x402-avm/next
|
|
187
|
+
|
|
188
|
+
| Function | Signature | Description |
|
|
189
|
+
|----------|-----------|-------------|
|
|
190
|
+
| `paymentProxy` | `(routes, server, config?, paywall?)` | Next.js middleware proxy |
|
|
191
|
+
| `withX402` | `(handler, routeConfig, server, config?, paywall?)` | Next.js route wrapper |
|
|
192
|
+
| `paymentProxyFromHTTPServer` | `(httpServer, config?, paywall?)` | Next.js proxy from HTTP server |
|
|
193
|
+
| `paymentProxyFromConfig` | `(routes, facilitator?, schemes?, config?, paywall?)` | Next.js proxy from config |
|
|
194
|
+
| `x402ResourceServer` | Class | Resource server for facilitator communication |
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Route Configuration
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
interface RouteConfig {
|
|
202
|
+
[path: string]: {
|
|
203
|
+
/** Payment requirements -- single object or array for multi-network */
|
|
204
|
+
accepts: PaymentAccepts | PaymentAccepts[];
|
|
205
|
+
/** Human-readable description of the resource */
|
|
206
|
+
description: string;
|
|
207
|
+
/** MIME type of the response */
|
|
208
|
+
mimeType: string;
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
interface PaymentAccepts {
|
|
213
|
+
/** Payment scheme (currently "exact") */
|
|
214
|
+
scheme: string;
|
|
215
|
+
/** CAIP-2 network identifier */
|
|
216
|
+
network: string;
|
|
217
|
+
/** Asset identifier (ASA ID for Algorand, contract address for EVM) */
|
|
218
|
+
asset: string;
|
|
219
|
+
/** Receiver address */
|
|
220
|
+
payTo: string;
|
|
221
|
+
/** Price string (e.g., "$0.01") */
|
|
222
|
+
price: string;
|
|
223
|
+
/** Maximum timeout in seconds for payment validity */
|
|
224
|
+
maxTimeoutSeconds: number;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Bundle Sizes
|
|
231
|
+
|
|
232
|
+
The paywall system is designed for tree-shaking. Import only the network handlers you need:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// AVM only -- smallest bundle
|
|
236
|
+
import { avmPaywall } from "@x402-avm/paywall/avm";
|
|
237
|
+
|
|
238
|
+
// AVM + EVM
|
|
239
|
+
import { avmPaywall, evmPaywall } from "@x402-avm/paywall";
|
|
240
|
+
|
|
241
|
+
// All networks
|
|
242
|
+
import { avmPaywall, evmPaywall, svmPaywall } from "@x402-avm/paywall";
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## USDC Asset IDs
|
|
248
|
+
|
|
249
|
+
| Network | ASA ID | CAIP-2 |
|
|
250
|
+
|---------|--------|--------|
|
|
251
|
+
| Algorand Testnet | `10458941` | `algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=` |
|
|
252
|
+
| Algorand Mainnet | `31566704` | `algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=` |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Testing
|
|
257
|
+
|
|
258
|
+
### Testing the Paywall HTML Generation
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
262
|
+
|
|
263
|
+
const paywall = createPaywall()
|
|
264
|
+
.withNetwork(avmPaywall)
|
|
265
|
+
.withConfig({ appName: "Test App", testnet: true })
|
|
266
|
+
.build();
|
|
267
|
+
|
|
268
|
+
const html = paywall.generateHtml({
|
|
269
|
+
x402Version: 2,
|
|
270
|
+
accepts: [{
|
|
271
|
+
scheme: "exact",
|
|
272
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
273
|
+
asset: "10458941",
|
|
274
|
+
payTo: "TEST_ADDRESS",
|
|
275
|
+
amount: "10000",
|
|
276
|
+
maxTimeoutSeconds: 300,
|
|
277
|
+
}],
|
|
278
|
+
resource: {
|
|
279
|
+
url: "https://test.example.com/premium",
|
|
280
|
+
description: "Test content",
|
|
281
|
+
mimeType: "application/json",
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Verify the HTML contains expected elements
|
|
286
|
+
console.assert(html.includes("window.x402"));
|
|
287
|
+
console.assert(html.includes("Test App"));
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Testing Middleware Integration
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Start server
|
|
294
|
+
npm start
|
|
295
|
+
|
|
296
|
+
# Test JSON 402 response (API client)
|
|
297
|
+
curl -H "Accept: application/json" http://localhost:3000/api/premium
|
|
298
|
+
|
|
299
|
+
# Test HTML paywall (browser-like)
|
|
300
|
+
curl -H "Accept: text/html" http://localhost:3000/api/premium
|
|
301
|
+
|
|
302
|
+
# Test with payment header (after obtaining one)
|
|
303
|
+
curl -H "PAYMENT-SIGNATURE: <base64-payload>" http://localhost:3000/api/premium
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Important Notes
|
|
309
|
+
|
|
310
|
+
- The paywall HTML page is a self-contained React application served inline. No additional static files or CDN dependencies are required.
|
|
311
|
+
- `withNetwork()` order determines handler priority. Register your preferred network first.
|
|
312
|
+
- The `testnet` flag in `PaywallConfig` affects which Algod endpoint the paywall page connects to (AlgoNode testnet vs mainnet).
|
|
313
|
+
- Route paths support Express-style patterns (`:param`, `*` wildcards).
|
|
314
|
+
- The `price` field in route config accepts human-readable strings like `"$0.01"` which are converted to microunits internally.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## External Resources
|
|
319
|
+
|
|
320
|
+
- [x402-avm Examples Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
321
|
+
- [x402-avm Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|
|
322
|
+
- [@wallet-standard/app](https://github.com/wallet-standard/wallet-standard)
|
|
323
|
+
- [@txnlab/use-wallet](https://txnlab.gitbook.io/use-wallet)
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Creating Paywall UIs for x402-Protected Endpoints
|
|
2
|
+
|
|
3
|
+
Build server-side middleware that serves payment HTML pages to browsers and JSON 402 responses to API clients, with automatic wallet integration for Algorand (Pera, Defly, Lute).
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before using this skill, ensure:
|
|
8
|
+
|
|
9
|
+
1. **A backend framework** (Express.js, Hono, or Next.js)
|
|
10
|
+
2. **A facilitator service** running and accessible via URL
|
|
11
|
+
3. **An Algorand address** to receive payments (payTo address)
|
|
12
|
+
|
|
13
|
+
## Core Workflow: Server-Side Paywall Architecture
|
|
14
|
+
|
|
15
|
+
The paywall system has two sides. The server middleware detects missing payments and serves either a JSON 402 or an HTML paywall page. The HTML page handles wallet connection, signing, and retry:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Browser Request
|
|
19
|
+
|
|
|
20
|
+
v
|
|
21
|
+
[Server Middleware]
|
|
22
|
+
|
|
|
23
|
+
+-- Has PAYMENT-SIGNATURE header?
|
|
24
|
+
| |
|
|
25
|
+
| +-- Yes: Verify payment, settle transaction, return content
|
|
26
|
+
| +-- No: Is this a browser request (Accept: text/html)?
|
|
27
|
+
| |
|
|
28
|
+
| +-- Yes: Return paywall HTML page (402)
|
|
29
|
+
| +-- No: Return JSON 402 response
|
|
30
|
+
|
|
|
31
|
+
v
|
|
32
|
+
[Paywall HTML Page]
|
|
33
|
+
|
|
|
34
|
+
+-- Reads window.x402 config
|
|
35
|
+
+-- Shows wallet connection UI (Pera/Defly/Lute)
|
|
36
|
+
+-- User connects wallet and approves payment
|
|
37
|
+
+-- Retries request with PAYMENT-SIGNATURE header
|
|
38
|
+
+-- Redirects to paid content
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## How to Proceed
|
|
42
|
+
|
|
43
|
+
### Step 1: Install Dependencies
|
|
44
|
+
|
|
45
|
+
Server-side packages:
|
|
46
|
+
```bash
|
|
47
|
+
npm install @x402-avm/paywall @x402-avm/avm @x402-avm/core
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Plus your framework middleware:
|
|
51
|
+
```bash
|
|
52
|
+
# Express.js
|
|
53
|
+
npm install @x402-avm/express
|
|
54
|
+
|
|
55
|
+
# Hono
|
|
56
|
+
npm install @x402-avm/hono
|
|
57
|
+
|
|
58
|
+
# Next.js
|
|
59
|
+
npm install @x402-avm/next
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 2: Create the Paywall with PaywallBuilder
|
|
63
|
+
|
|
64
|
+
The `PaywallBuilder` creates a `PaywallProvider` that generates HTML paywall pages. Register network handlers in priority order -- first match wins:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { createPaywall, avmPaywall } from "@x402-avm/paywall";
|
|
68
|
+
|
|
69
|
+
const paywall = createPaywall()
|
|
70
|
+
.withNetwork(avmPaywall) // Supports algorand:* networks
|
|
71
|
+
.withConfig({
|
|
72
|
+
appName: "My Premium API",
|
|
73
|
+
appLogo: "https://example.com/logo.png",
|
|
74
|
+
testnet: true,
|
|
75
|
+
})
|
|
76
|
+
.build();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For multi-network support, register multiple handlers:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { createPaywall, avmPaywall, evmPaywall, svmPaywall } from "@x402-avm/paywall";
|
|
83
|
+
|
|
84
|
+
const paywall = createPaywall()
|
|
85
|
+
.withNetwork(avmPaywall) // algorand:*
|
|
86
|
+
.withNetwork(evmPaywall) // eip155:*
|
|
87
|
+
.withNetwork(svmPaywall) // solana:*
|
|
88
|
+
.withConfig({ appName: "Universal Paywall", testnet: true })
|
|
89
|
+
.build();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Step 3: Define Protected Routes
|
|
93
|
+
|
|
94
|
+
Routes specify what payment is required for each endpoint:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const routes = {
|
|
98
|
+
"/api/premium-content": {
|
|
99
|
+
accepts: {
|
|
100
|
+
scheme: "exact",
|
|
101
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
102
|
+
asset: "10458941", // USDC ASA ID on testnet
|
|
103
|
+
payTo: "YOUR_ALGORAND_ADDRESS_HERE",
|
|
104
|
+
price: "$0.01", // 0.01 USDC
|
|
105
|
+
maxTimeoutSeconds: 300,
|
|
106
|
+
},
|
|
107
|
+
description: "Access to premium content",
|
|
108
|
+
mimeType: "application/json",
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Step 4: Apply Middleware (Express.js)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import express from "express";
|
|
117
|
+
import { paymentMiddleware, x402ResourceServer } from "@x402-avm/express";
|
|
118
|
+
|
|
119
|
+
const app = express();
|
|
120
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
121
|
+
|
|
122
|
+
app.use(paymentMiddleware(routes, server, { testnet: true }, paywall));
|
|
123
|
+
|
|
124
|
+
app.get("/api/premium-content", (req, res) => {
|
|
125
|
+
res.json({ content: "This is the paid content." });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
app.listen(3000);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Step 4 (Alt): Apply Middleware (Hono)
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { Hono } from "hono";
|
|
135
|
+
import { paymentMiddleware, x402ResourceServer } from "@x402-avm/hono";
|
|
136
|
+
|
|
137
|
+
const app = new Hono();
|
|
138
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
139
|
+
|
|
140
|
+
app.use("*", paymentMiddleware(routes, server, { testnet: true }, paywall));
|
|
141
|
+
|
|
142
|
+
app.get("/api/premium-content", (c) => {
|
|
143
|
+
return c.json({ content: "Premium content unlocked!" });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
export default app;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Step 4 (Alt): Apply Middleware (Next.js)
|
|
150
|
+
|
|
151
|
+
**Middleware approach:**
|
|
152
|
+
```typescript
|
|
153
|
+
// middleware.ts
|
|
154
|
+
import { paymentProxy, x402ResourceServer } from "@x402-avm/next";
|
|
155
|
+
|
|
156
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
157
|
+
const proxy = paymentProxy(routes, server, { testnet: true }, paywall);
|
|
158
|
+
|
|
159
|
+
export async function middleware(request: NextRequest) {
|
|
160
|
+
if (request.nextUrl.pathname.startsWith("/api/premium")) {
|
|
161
|
+
return proxy(request);
|
|
162
|
+
}
|
|
163
|
+
return NextResponse.next();
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Route handler approach (withX402):**
|
|
168
|
+
```typescript
|
|
169
|
+
// app/api/premium/route.ts
|
|
170
|
+
import { withX402, x402ResourceServer } from "@x402-avm/next";
|
|
171
|
+
|
|
172
|
+
const server = new x402ResourceServer({ url: process.env.FACILITATOR_URL! });
|
|
173
|
+
|
|
174
|
+
async function handler(request: NextRequest) {
|
|
175
|
+
return NextResponse.json({ content: "Premium content!" });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const GET = withX402(handler, routeConfig, server, { testnet: true }, paywall);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Important Rules / Guidelines
|
|
182
|
+
|
|
183
|
+
1. **Handler order matters** -- `withNetwork()` calls determine priority. The first handler whose `supports()` returns true for a payment requirement is used
|
|
184
|
+
2. **avmPaywall supports `algorand:*`** -- Any network starting with `algorand:` is matched
|
|
185
|
+
3. **Testnet vs Mainnet** -- Set `testnet: true/false` in both `PaywallConfig` and middleware config
|
|
186
|
+
4. **USDC ASA IDs** -- Testnet: `10458941`, Mainnet: `31566704`
|
|
187
|
+
5. **Facilitator URL is required** -- The middleware needs a running facilitator to verify and settle payments
|
|
188
|
+
6. **Tree-shaking** -- Import only the network handlers you need. `avmPaywall` can be imported from `@x402-avm/paywall` or `@x402-avm/paywall/avm`
|
|
189
|
+
7. **Multiple routes** -- Define multiple entries in the routes object, each with its own price, description, and asset
|
|
190
|
+
|
|
191
|
+
## Wallet Integration
|
|
192
|
+
|
|
193
|
+
The AVM paywall HTML page uses `@wallet-standard/app` to discover Algorand wallets:
|
|
194
|
+
|
|
195
|
+
| Wallet | Type | Feature |
|
|
196
|
+
|--------|------|---------|
|
|
197
|
+
| Pera Wallet | Mobile + WalletConnect | `algorand:signTransaction` |
|
|
198
|
+
| Defly Wallet | Mobile + WalletConnect | `algorand:signTransaction` |
|
|
199
|
+
| Lute Wallet | Browser Extension | `algorand:signTransaction` |
|
|
200
|
+
|
|
201
|
+
The paywall page automatically:
|
|
202
|
+
- Discovers available wallets via wallet-standard
|
|
203
|
+
- Shows a wallet selection UI
|
|
204
|
+
- Checks USDC balance for the connected account
|
|
205
|
+
- Handles transaction signing
|
|
206
|
+
- Retries the original request with the payment header
|
|
207
|
+
- Redirects to the paid content on success
|
|
208
|
+
|
|
209
|
+
## Multi-Network Paywalls
|
|
210
|
+
|
|
211
|
+
Accept payments on multiple chains by specifying an array in `accepts`:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const routes = {
|
|
215
|
+
"/api/premium": {
|
|
216
|
+
accepts: [
|
|
217
|
+
{
|
|
218
|
+
scheme: "exact",
|
|
219
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
220
|
+
asset: "10458941",
|
|
221
|
+
payTo: "ALGO_ADDRESS_HERE",
|
|
222
|
+
price: "$0.01",
|
|
223
|
+
maxTimeoutSeconds: 300,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
scheme: "exact",
|
|
227
|
+
network: "eip155:84532",
|
|
228
|
+
asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
229
|
+
payTo: "0xEVM_ADDRESS_HERE",
|
|
230
|
+
price: "$0.01",
|
|
231
|
+
maxTimeoutSeconds: 30,
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
description: "Premium content - pay with USDC on Algorand or Base",
|
|
235
|
+
mimeType: "application/json",
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Custom Paywall Handler
|
|
241
|
+
|
|
242
|
+
If the built-in paywall does not meet your needs, implement a custom `PaywallNetworkHandler`:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import type { PaywallNetworkHandler } from "@x402-avm/paywall";
|
|
246
|
+
|
|
247
|
+
const customHandler: PaywallNetworkHandler = {
|
|
248
|
+
supports(requirement) {
|
|
249
|
+
return requirement.network.startsWith("algorand:");
|
|
250
|
+
},
|
|
251
|
+
generateHtml(requirement, paymentRequired, config) {
|
|
252
|
+
return `<!DOCTYPE html>
|
|
253
|
+
<html><body>
|
|
254
|
+
<h1>Pay ${requirement.amount} to access</h1>
|
|
255
|
+
<script>window.x402 = ${JSON.stringify({ paymentRequired, ...config })};</script>
|
|
256
|
+
</body></html>`;
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const paywall = createPaywall()
|
|
261
|
+
.withNetwork(customHandler)
|
|
262
|
+
.build();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Common Errors / Troubleshooting
|
|
266
|
+
|
|
267
|
+
| Error | Cause | Solution |
|
|
268
|
+
|-------|-------|----------|
|
|
269
|
+
| Paywall not shown in browser | Middleware not applied or route not matched | Check route patterns match request paths |
|
|
270
|
+
| JSON 402 returned to browser | Browser not sending `Accept: text/html` | Ensure direct browser navigation, not programmatic fetch |
|
|
271
|
+
| Wallet not detected | Wallet extension not installed | Install Pera, Defly, or Lute wallet |
|
|
272
|
+
| "Insufficient balance" | Account has no USDC | Fund the wallet with USDC on the correct network |
|
|
273
|
+
| Facilitator unreachable | Wrong URL or service down | Verify `FACILITATOR_URL` environment variable |
|
|
274
|
+
| Payment not settling | Facilitator signer not funded | Ensure the facilitator address has ALGO for fees |
|
|
275
|
+
|
|
276
|
+
## References / Further Reading
|
|
277
|
+
|
|
278
|
+
- [create-typescript-x402-paywall-reference.md](./create-typescript-x402-paywall-reference.md) - Detailed API reference
|
|
279
|
+
- [create-typescript-x402-paywall-examples.md](./create-typescript-x402-paywall-examples.md) - Complete code examples
|
|
280
|
+
- [x402-avm Examples Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
281
|
+
- [x402-avm Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|