@wtflabs/x402-server 0.0.1-beta.8 → 0.0.1-beta.9
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/README.md +449 -212
- package/dist/cjs/index.d.ts +1313 -0
- package/dist/cjs/index.js +599 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +1313 -0
- package/dist/esm/index.mjs +558 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +43 -15
- package/dist/index.d.mts +0 -217
- package/dist/index.d.ts +0 -217
- package/dist/index.js +0 -640
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -610
- package/dist/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,313 +1,550 @@
|
|
|
1
1
|
# @wtflabs/x402-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Server SDK for the x402 Payment Protocol. Handles payment verification and settlement with automatic token detection.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✅ **Simple API** - Just 2 required parameters to get started
|
|
8
|
+
✅ **Automatic Token Detection** - Built on `@wtflabs/x402-detector`
|
|
9
|
+
✅ **Payment Processing** - Verify and settle payments via `@wtflabs/x402-facilitator`
|
|
10
|
+
✅ **Dynamic Requirements** - Create payment requirements on-the-fly
|
|
11
|
+
✅ **Performance Optimized** - Built-in caching, non-blocking initialization
|
|
12
|
+
✅ **Framework Middlewares** - Ready-to-use Express and Hono middlewares
|
|
13
|
+
✅ **Zod Validation** - Runtime type safety for all data
|
|
14
|
+
✅ **Decoupled Design** - Facilitator and Server are independent
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
6
17
|
|
|
7
18
|
```bash
|
|
8
|
-
npm install @wtflabs/x402-server
|
|
9
|
-
# 或
|
|
10
|
-
pnpm add @wtflabs/x402-server @wtflabs/x402-facilitator @wtflabs/x402-schema viem
|
|
19
|
+
npm install @wtflabs/x402-server viem
|
|
11
20
|
```
|
|
12
21
|
|
|
13
|
-
##
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Option 1: Using Middleware (Easiest)
|
|
14
25
|
|
|
15
26
|
```typescript
|
|
16
|
-
import
|
|
27
|
+
import express from "express";
|
|
28
|
+
import { createExpressMiddleware, X402Server } from "@wtflabs/x402-server";
|
|
17
29
|
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
18
|
-
import { X402PaymentSchema } from "@wtflabs/x402-schema";
|
|
19
|
-
import { createPublicClient, http } from "viem";
|
|
20
|
-
import { bscTestnet } from "viem/chains";
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
const app = express();
|
|
32
|
+
|
|
33
|
+
// 1. Create facilitator
|
|
23
34
|
const facilitator = new Facilitator({
|
|
24
|
-
recipientAddress: "
|
|
25
|
-
relayer: "0x5678...", // 可选,内置 WTF Facilitator
|
|
26
|
-
waitUntil: "confirmed", // 可选
|
|
35
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
27
36
|
});
|
|
28
37
|
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
paymentType: "permit", // optional
|
|
41
|
-
outputSchema: {
|
|
42
|
-
input: {
|
|
43
|
-
type: "http",
|
|
44
|
-
method: "POST",
|
|
45
|
-
discoverable: true,
|
|
46
|
-
bodyFields: {},
|
|
47
|
-
},
|
|
48
|
-
output: {
|
|
49
|
-
message: "string",
|
|
50
|
-
authorizationType: "string",
|
|
51
|
-
payer: "string",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
38
|
+
// 2. Create server
|
|
39
|
+
const server = new X402Server({
|
|
40
|
+
client: createPublicClient({ chain: bscTestnet, transport: http() }),
|
|
41
|
+
facilitator,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 3. Create middleware
|
|
45
|
+
const paymentMiddleware = createExpressMiddleware({
|
|
46
|
+
server,
|
|
47
|
+
getToken: () => "0x25d066c4C68C8A6332DfDB4230263608305Ca991", // USDC
|
|
48
|
+
getAmount: () => "1000",
|
|
54
49
|
});
|
|
55
50
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
// 4. Use middleware
|
|
52
|
+
app.post("/api/resource", paymentMiddleware, (req, res) => {
|
|
53
|
+
const { payer, txHash } = req.x402!;
|
|
54
|
+
res.json({ data: "resource", payer, txHash });
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Option 2: Manual Processing
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
62
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
63
|
+
import { createPublicClient, http } from "viem";
|
|
64
|
+
import { bscTestnet } from "viem/chains";
|
|
65
|
+
|
|
66
|
+
// 1. Create facilitator
|
|
67
|
+
const facilitator = new Facilitator({
|
|
68
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
69
|
+
waitUntil: "confirmed",
|
|
60
70
|
});
|
|
61
71
|
|
|
62
|
-
//
|
|
72
|
+
// 2. Create server instance
|
|
63
73
|
const server = new X402Server({
|
|
74
|
+
client: createPublicClient({
|
|
75
|
+
chain: bscTestnet,
|
|
76
|
+
transport: http(),
|
|
77
|
+
}),
|
|
64
78
|
facilitator,
|
|
65
|
-
schema,
|
|
66
|
-
client,
|
|
67
79
|
});
|
|
68
80
|
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
// 3. Optional: Pre-warm cache (non-blocking)
|
|
82
|
+
server.initialize([
|
|
83
|
+
"0x25d066c4C68C8A6332DfDB4230263608305Ca991", // USDC
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
// 4. Handle payment in your route
|
|
87
|
+
app.post("/api/resource", async (req, res) => {
|
|
88
|
+
// Create payment requirements
|
|
89
|
+
const requirements = await server.createRequirements({
|
|
90
|
+
token: "0x25d066c4C68C8A6332DfDB4230263608305Ca991",
|
|
91
|
+
amount: "1000", // wei
|
|
92
|
+
});
|
|
74
93
|
|
|
75
|
-
//
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
94
|
+
// Process payment (parse → verify → settle)
|
|
95
|
+
const result = await server.process(
|
|
96
|
+
req.headers["x-payment"],
|
|
97
|
+
requirements
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
return res.status(402).json(result.response);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Payment successful!
|
|
105
|
+
res.json({
|
|
106
|
+
message: "Access granted",
|
|
107
|
+
payer: result.data.payer,
|
|
108
|
+
txHash: result.data.txHash,
|
|
109
|
+
data: "your protected resource",
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
```
|
|
80
113
|
|
|
81
|
-
|
|
82
|
-
const paymentVerifyResult = await server.verifyPayment(
|
|
83
|
-
paymentPayload,
|
|
84
|
-
paymentRequirements
|
|
85
|
-
);
|
|
114
|
+
## API Reference
|
|
86
115
|
|
|
87
|
-
|
|
88
|
-
console.log("支付验证成功!支付者:", paymentVerifyResult.payer);
|
|
89
|
-
}
|
|
116
|
+
### Constructor
|
|
90
117
|
|
|
91
|
-
|
|
92
|
-
const
|
|
118
|
+
```typescript
|
|
119
|
+
const server = new X402Server(config: X402ServerConfig)
|
|
120
|
+
```
|
|
93
121
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
122
|
+
**Required:**
|
|
123
|
+
- `client: PublicClient` - Viem PublicClient
|
|
124
|
+
- `facilitator: Facilitator` - Facilitator instance
|
|
125
|
+
|
|
126
|
+
**Optional:**
|
|
127
|
+
- `network?: string` - Network name (auto-detected from client)
|
|
128
|
+
|
|
129
|
+
**Example:**
|
|
130
|
+
```typescript
|
|
131
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
132
|
+
|
|
133
|
+
// 1. Create facilitator first
|
|
134
|
+
const facilitator = new Facilitator({
|
|
135
|
+
recipientAddress: "0x...",
|
|
136
|
+
relayer: "0x...", // optional
|
|
137
|
+
waitUntil: "confirmed", // optional
|
|
138
|
+
baseUrl: "https://facilitator.example.com", // optional
|
|
139
|
+
apiKey: "your-api-key", // optional
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// 2. Pass to server
|
|
143
|
+
const server = new X402Server({
|
|
144
|
+
client: viemClient,
|
|
145
|
+
facilitator,
|
|
146
|
+
network: "bsc-testnet", // optional
|
|
147
|
+
});
|
|
97
148
|
```
|
|
98
149
|
|
|
99
|
-
|
|
150
|
+
### Methods
|
|
100
151
|
|
|
101
|
-
|
|
152
|
+
#### `initialize(tokens: string[]): Promise<InitResult>`
|
|
102
153
|
|
|
103
|
-
|
|
154
|
+
Pre-warm the token detection cache. Non-blocking, can run in background.
|
|
104
155
|
|
|
105
156
|
```typescript
|
|
106
|
-
|
|
157
|
+
// Wait for initialization
|
|
158
|
+
await server.initialize([tokenAddress]);
|
|
159
|
+
|
|
160
|
+
// Or run in background
|
|
161
|
+
server.initialize([tokenAddress]).then(result => {
|
|
162
|
+
if (result.success) console.log("✅ Cache ready");
|
|
163
|
+
});
|
|
107
164
|
```
|
|
108
165
|
|
|
109
|
-
|
|
166
|
+
#### `createRequirements(config): Promise<PaymentRequirements>`
|
|
110
167
|
|
|
111
|
-
|
|
112
|
-
- `schema`: X402PaymentSchema 实例
|
|
113
|
-
- `client`: Viem PublicClient 实例
|
|
168
|
+
Create payment requirements. Supports dynamic amounts and auto-detection.
|
|
114
169
|
|
|
115
|
-
|
|
170
|
+
```typescript
|
|
171
|
+
const requirements = await server.createRequirements({
|
|
172
|
+
// Required
|
|
173
|
+
token: "0x...",
|
|
174
|
+
amount: "1000", // wei
|
|
175
|
+
|
|
176
|
+
// Optional - override global config
|
|
177
|
+
recipient?: "0x...",
|
|
178
|
+
network?: "bsc-testnet",
|
|
179
|
+
paymentType?: "permit" | "eip3009" | "permit2" | "auto",
|
|
180
|
+
relayer?: "0x...",
|
|
181
|
+
|
|
182
|
+
// Optional - resource description
|
|
183
|
+
resource?: "https://api.example.com/data",
|
|
184
|
+
description?: "Premium API access",
|
|
185
|
+
mimeType?: "application/json",
|
|
186
|
+
timeout?: 300,
|
|
187
|
+
|
|
188
|
+
// Optional - performance
|
|
189
|
+
autoDetect?: true, // false for fast mode
|
|
190
|
+
});
|
|
191
|
+
```
|
|
116
192
|
|
|
117
|
-
|
|
193
|
+
#### `process(paymentHeader, requirements): Promise<ProcessResult>`
|
|
118
194
|
|
|
119
|
-
|
|
195
|
+
Complete payment processing (parse → verify → settle).
|
|
120
196
|
|
|
121
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
const result = await server.process(
|
|
199
|
+
request.headers["x-payment"],
|
|
200
|
+
requirements
|
|
201
|
+
);
|
|
122
202
|
|
|
123
|
-
|
|
203
|
+
if (result.success) {
|
|
204
|
+
console.log("Payer:", result.data.payer);
|
|
205
|
+
console.log("TxHash:", result.data.txHash);
|
|
206
|
+
} else {
|
|
207
|
+
console.log("Error:", result.response.error);
|
|
208
|
+
// Return 402 with result.response
|
|
209
|
+
}
|
|
210
|
+
```
|
|
124
211
|
|
|
125
|
-
|
|
212
|
+
#### Advanced: Step-by-Step Processing
|
|
126
213
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
214
|
+
```typescript
|
|
215
|
+
// 1. Parse payment header
|
|
216
|
+
const parsed = server.parse(paymentHeader, requirements);
|
|
217
|
+
if (!parsed.success) {
|
|
218
|
+
return res.status(402).json(parsed.response402);
|
|
219
|
+
}
|
|
132
220
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
221
|
+
// 2. Verify payment
|
|
222
|
+
const verified = await server.verify(parsed.data);
|
|
223
|
+
if (!verified.success) {
|
|
224
|
+
return res.status(402).json(
|
|
225
|
+
server.get402Response(requirements, verified.error)
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
console.log("Payer:", verified.payer);
|
|
229
|
+
|
|
230
|
+
// 3. Settle payment (optional - you can skip this for verify-only mode)
|
|
231
|
+
const settled = await server.settle(parsed.data);
|
|
232
|
+
if (!settled.success) {
|
|
233
|
+
return res.status(402).json(
|
|
234
|
+
server.get402Response(requirements, settled.error)
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
console.log("TxHash:", settled.txHash);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Utility Methods
|
|
137
241
|
|
|
138
|
-
|
|
242
|
+
```typescript
|
|
243
|
+
// Generate 402 response
|
|
244
|
+
const response402 = server.get402Response(requirements, error?);
|
|
139
245
|
|
|
140
|
-
|
|
246
|
+
// Clear token cache
|
|
247
|
+
await server.clearCache(tokenAddress?); // specific or all
|
|
141
248
|
|
|
142
|
-
|
|
249
|
+
// Get cache stats
|
|
250
|
+
const stats = server.getCacheStats();
|
|
251
|
+
console.log(stats.size, stats.keys);
|
|
252
|
+
```
|
|
143
253
|
|
|
144
|
-
|
|
145
|
-
- `paymentPayload`: 支付负载
|
|
146
|
-
- `paymentRequirements`: 支付要求
|
|
147
|
-
- **返回:** `Promise<{ success: boolean; payer?: string; error?: string }>`
|
|
254
|
+
## Usage Examples
|
|
148
255
|
|
|
149
|
-
|
|
256
|
+
### Example 1: Fixed Amount
|
|
150
257
|
|
|
151
|
-
|
|
258
|
+
```typescript
|
|
259
|
+
const server = new X402Server({ client, recipient });
|
|
152
260
|
|
|
153
|
-
-
|
|
154
|
-
|
|
155
|
-
- `paymentRequirements`: 支付要求
|
|
156
|
-
- **返回:** `Promise<SettleResult>`
|
|
261
|
+
// Pre-warm cache (optional)
|
|
262
|
+
await server.initialize(["0xUSDC"]);
|
|
157
263
|
|
|
158
|
-
|
|
264
|
+
// Fixed requirements
|
|
265
|
+
const requirements = await server.createRequirements({
|
|
266
|
+
token: "0xUSDC",
|
|
267
|
+
amount: "1000",
|
|
268
|
+
description: "Access to premium API",
|
|
269
|
+
});
|
|
159
270
|
|
|
160
|
-
|
|
271
|
+
app.post("/premium-api", async (req, res) => {
|
|
272
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
273
|
+
|
|
274
|
+
if (!result.success) {
|
|
275
|
+
return res.status(402).json(result.response);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
res.json({ data: "premium content" });
|
|
279
|
+
});
|
|
280
|
+
```
|
|
161
281
|
|
|
162
|
-
|
|
282
|
+
### Example 2: Dynamic Pricing
|
|
163
283
|
|
|
164
|
-
|
|
284
|
+
```typescript
|
|
285
|
+
app.post("/api/compute", async (req, res) => {
|
|
286
|
+
const { complexity } = req.body;
|
|
287
|
+
|
|
288
|
+
// Calculate price based on complexity
|
|
289
|
+
const price = calculatePrice(complexity);
|
|
290
|
+
|
|
291
|
+
// Dynamic requirements
|
|
292
|
+
const requirements = await server.createRequirements({
|
|
293
|
+
token: "0xUSDC",
|
|
294
|
+
amount: price,
|
|
295
|
+
description: `Compute task (${complexity})`,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
299
|
+
|
|
300
|
+
if (!result.success) {
|
|
301
|
+
return res.status(402).json(result.response);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Execute computation
|
|
305
|
+
const computeResult = await performComputation(complexity);
|
|
306
|
+
res.json({ result: computeResult, paid: price });
|
|
307
|
+
});
|
|
308
|
+
```
|
|
165
309
|
|
|
166
|
-
|
|
310
|
+
### Example 3: Multiple Tokens
|
|
167
311
|
|
|
168
|
-
|
|
312
|
+
```typescript
|
|
313
|
+
const server = new X402Server({ client, recipient });
|
|
314
|
+
|
|
315
|
+
// Pre-warm multiple tokens
|
|
316
|
+
await server.initialize(["0xUSDC", "0xDAI", "0xUSDT"]);
|
|
317
|
+
|
|
318
|
+
app.get("/premium-api", async (req, res) => {
|
|
319
|
+
// Return multiple payment options
|
|
320
|
+
const accepts = await Promise.all([
|
|
321
|
+
server.createRequirements({ token: "0xUSDC", amount: "1000" }),
|
|
322
|
+
server.createRequirements({ token: "0xDAI", amount: "1000" }),
|
|
323
|
+
server.createRequirements({ token: "0xUSDT", amount: "1000" }),
|
|
324
|
+
]);
|
|
325
|
+
|
|
326
|
+
res.status(402).json({
|
|
327
|
+
x402Version: 1,
|
|
328
|
+
accepts,
|
|
329
|
+
});
|
|
330
|
+
});
|
|
169
331
|
|
|
170
|
-
|
|
332
|
+
app.post("/premium-api", async (req, res) => {
|
|
333
|
+
// User pays with their chosen token
|
|
334
|
+
const parsed = server.parse(req.headers["x-payment"], accepts[0]);
|
|
335
|
+
if (!parsed.success) {
|
|
336
|
+
return res.status(402).json(parsed.response402);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Detect which token was used
|
|
340
|
+
const tokenUsed = parsed.data.payload.payload.authorization.token;
|
|
341
|
+
|
|
342
|
+
// Create matching requirements
|
|
343
|
+
const requirements = await server.createRequirements({
|
|
344
|
+
token: tokenUsed,
|
|
345
|
+
amount: "1000",
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
349
|
+
// ...
|
|
350
|
+
});
|
|
351
|
+
```
|
|
171
352
|
|
|
172
|
-
|
|
353
|
+
### Example 4: Fast Mode (Skip Detection)
|
|
173
354
|
|
|
174
|
-
|
|
355
|
+
```typescript
|
|
356
|
+
// For maximum performance, skip auto-detection
|
|
357
|
+
const requirements = await server.createRequirements({
|
|
358
|
+
token: "0xUSDC",
|
|
359
|
+
amount: "1000",
|
|
360
|
+
paymentType: "permit", // Manually specify
|
|
361
|
+
autoDetect: false, // Skip detection (<1ms)
|
|
362
|
+
});
|
|
363
|
+
```
|
|
175
364
|
|
|
176
|
-
|
|
365
|
+
## Framework Integration
|
|
177
366
|
|
|
178
|
-
|
|
367
|
+
### Express
|
|
179
368
|
|
|
180
|
-
|
|
369
|
+
```typescript
|
|
370
|
+
import express from "express";
|
|
371
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
181
372
|
|
|
182
|
-
|
|
373
|
+
const app = express();
|
|
374
|
+
const server = new X402Server({ client, recipient });
|
|
183
375
|
|
|
184
|
-
|
|
376
|
+
app.post("/api/resource", async (req, res) => {
|
|
377
|
+
const requirements = await server.createRequirements({
|
|
378
|
+
token: "0xUSDC",
|
|
379
|
+
amount: "1000",
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
383
|
+
|
|
384
|
+
if (!result.success) {
|
|
385
|
+
return res.status(402).json(result.response);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
res.json({ data: "resource" });
|
|
389
|
+
});
|
|
390
|
+
```
|
|
185
391
|
|
|
186
|
-
|
|
392
|
+
### Hono
|
|
187
393
|
|
|
188
394
|
```typescript
|
|
189
|
-
import {
|
|
190
|
-
import {
|
|
191
|
-
import { bscTestnet } from "viem/chains";
|
|
395
|
+
import { Hono } from "hono";
|
|
396
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
192
397
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
398
|
+
const app = new Hono();
|
|
399
|
+
const server = new X402Server({ client, recipient });
|
|
400
|
+
|
|
401
|
+
app.post("/api/resource", async (c) => {
|
|
402
|
+
const requirements = await server.createRequirements({
|
|
403
|
+
token: "0xUSDC",
|
|
404
|
+
amount: "1000",
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const result = await server.process(c.req.header("x-payment"), requirements);
|
|
408
|
+
|
|
409
|
+
if (!result.success) {
|
|
410
|
+
return c.json(result.response, 402);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return c.json({ data: "resource" });
|
|
196
414
|
});
|
|
415
|
+
```
|
|
197
416
|
|
|
198
|
-
|
|
199
|
-
"0x25d066c4C68C8A6332DfDB4230263608305Ca991",
|
|
200
|
-
client
|
|
201
|
-
);
|
|
417
|
+
### Next.js API Route
|
|
202
418
|
|
|
203
|
-
|
|
204
|
-
|
|
419
|
+
```typescript
|
|
420
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
421
|
+
import { NextRequest } from "next/server";
|
|
205
422
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
423
|
+
const server = new X402Server({ client, recipient });
|
|
424
|
+
|
|
425
|
+
export async function POST(req: NextRequest) {
|
|
426
|
+
const requirements = await server.createRequirements({
|
|
427
|
+
token: "0xUSDC",
|
|
428
|
+
amount: "1000",
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const result = await server.process(
|
|
432
|
+
req.headers.get("x-payment") || undefined,
|
|
433
|
+
requirements
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
if (!result.success) {
|
|
437
|
+
return Response.json(result.response, { status: 402 });
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return Response.json({ data: "resource" });
|
|
441
|
+
}
|
|
212
442
|
```
|
|
213
443
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
444
|
+
## Performance
|
|
445
|
+
|
|
446
|
+
| Operation | First Call | Cached Call |
|
|
447
|
+
|-----------|-----------|-------------|
|
|
448
|
+
| `createRequirements(autoDetect: true)` | 2-5s | <1ms |
|
|
449
|
+
| `createRequirements(autoDetect: false)` | <1ms | <1ms |
|
|
450
|
+
| `process()` | 2-5s + network | <1ms + network |
|
|
218
451
|
|
|
219
|
-
|
|
452
|
+
**Tips:**
|
|
453
|
+
- Use `initialize()` on startup to pre-warm cache
|
|
454
|
+
- Set `autoDetect: false` for maximum speed (requires manual `paymentType`)
|
|
455
|
+
- Cache persists for the lifetime of the server instance
|
|
220
456
|
|
|
221
|
-
|
|
457
|
+
## Error Handling
|
|
222
458
|
|
|
223
459
|
```typescript
|
|
224
|
-
|
|
460
|
+
const result = await server.process(paymentHeader, requirements);
|
|
225
461
|
|
|
226
|
-
|
|
227
|
-
|
|
462
|
+
if (!result.success) {
|
|
463
|
+
// 402 response with error details
|
|
464
|
+
console.log(result.response.error);
|
|
465
|
+
return res.status(402).json(result.response);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Success!
|
|
469
|
+
console.log(result.data.payer);
|
|
470
|
+
console.log(result.data.txHash);
|
|
228
471
|
```
|
|
229
472
|
|
|
230
|
-
|
|
231
|
-
- `capabilities`: 从 `detectTokenPaymentMethods` 返回的检测结果
|
|
232
|
-
- **返回:** `PaymentMethod | null`
|
|
473
|
+
## TypeScript Support
|
|
233
474
|
|
|
234
|
-
|
|
475
|
+
Full TypeScript support with comprehensive type definitions:
|
|
235
476
|
|
|
236
477
|
```typescript
|
|
237
|
-
type
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
hasPermit: boolean;
|
|
245
|
-
hasPermit2Approval: boolean;
|
|
246
|
-
};
|
|
247
|
-
}
|
|
478
|
+
import type {
|
|
479
|
+
X402ServerConfig,
|
|
480
|
+
CreateRequirementsConfig,
|
|
481
|
+
PaymentRequirements,
|
|
482
|
+
ProcessResult,
|
|
483
|
+
InitResult,
|
|
484
|
+
} from "@wtflabs/x402-server";
|
|
248
485
|
```
|
|
249
486
|
|
|
250
|
-
##
|
|
487
|
+
## Middlewares
|
|
251
488
|
|
|
252
|
-
|
|
253
|
-
import { X402Server } from "@wtflabs/x402-server";
|
|
254
|
-
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
255
|
-
import { X402PaymentSchema } from "@wtflabs/x402-schema";
|
|
256
|
-
import { createPublicClient, http } from "viem";
|
|
257
|
-
import { bscTestnet } from "viem/chains";
|
|
489
|
+
### Express Middleware
|
|
258
490
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
491
|
+
```typescript
|
|
492
|
+
import { createExpressMiddleware } from "@wtflabs/x402-server";
|
|
493
|
+
|
|
494
|
+
const middleware = createExpressMiddleware({
|
|
495
|
+
server,
|
|
496
|
+
getToken: (req) => req.body.token,
|
|
497
|
+
getAmount: (req) => calculatePrice(req.body),
|
|
498
|
+
onPaymentSuccess: async (req, payer, txHash) => {
|
|
499
|
+
console.log(`Payment from ${payer}: ${txHash}`);
|
|
500
|
+
},
|
|
501
|
+
});
|
|
265
502
|
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
resource: "http://localhost:3000/protected-resource",
|
|
272
|
-
description: "Access to protected resource",
|
|
273
|
-
mimeType: "application/json",
|
|
274
|
-
payTo: "0x1234567890123456789012345678901234567890",
|
|
275
|
-
maxTimeoutSeconds: 3600,
|
|
276
|
-
asset: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
|
|
277
|
-
paymentType: "permit",
|
|
278
|
-
});
|
|
503
|
+
app.post("/api", middleware, (req, res) => {
|
|
504
|
+
const { payer, txHash } = req.x402!;
|
|
505
|
+
res.json({ data: "resource", payer, txHash });
|
|
506
|
+
});
|
|
507
|
+
```
|
|
279
508
|
|
|
280
|
-
|
|
281
|
-
const client = createPublicClient({
|
|
282
|
-
chain: bscTestnet,
|
|
283
|
-
transport: http(),
|
|
284
|
-
});
|
|
509
|
+
### Hono Middleware
|
|
285
510
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
511
|
+
```typescript
|
|
512
|
+
import { createHonoMiddleware } from "@wtflabs/x402-server";
|
|
513
|
+
|
|
514
|
+
const middleware = createHonoMiddleware({
|
|
515
|
+
server,
|
|
516
|
+
getToken: async (c) => (await c.req.json()).token,
|
|
517
|
+
getAmount: async (c) => calculatePrice(await c.req.json()),
|
|
518
|
+
onPaymentSuccess: async (c, payer, txHash) => {
|
|
519
|
+
console.log(`Payment from ${payer}: ${txHash}`);
|
|
520
|
+
},
|
|
521
|
+
});
|
|
292
522
|
|
|
293
|
-
|
|
294
|
-
|
|
523
|
+
app.post("/api", middleware, (c) => {
|
|
524
|
+
const x402 = c.get("x402")!;
|
|
525
|
+
return c.json({ data: "resource", payer: x402.payer });
|
|
526
|
+
});
|
|
527
|
+
```
|
|
295
528
|
|
|
296
|
-
|
|
297
|
-
const verifyResult = await server.verify();
|
|
298
|
-
console.log("验证结果:", verifyResult);
|
|
529
|
+
**See [MIDDLEWARES.md](./MIDDLEWARES.md) for detailed guide.**
|
|
299
530
|
|
|
300
|
-
|
|
301
|
-
// const settleResult = await server.settle(
|
|
302
|
-
// paymentPayload,
|
|
303
|
-
// paymentRequirements
|
|
304
|
-
// );
|
|
305
|
-
}
|
|
531
|
+
## Documentation
|
|
306
532
|
|
|
307
|
-
|
|
308
|
-
|
|
533
|
+
- **[README.md](./README.md)** - This file
|
|
534
|
+
- **[QUICK-START.md](./QUICK-START.md)** - 5-minute quick start guide
|
|
535
|
+
- **[USAGE.md](./USAGE.md)** - Detailed usage documentation
|
|
536
|
+
- **[MIDDLEWARES.md](./MIDDLEWARES.md)** - Express and Hono middleware guide
|
|
537
|
+
- **[ZOD-VALIDATION.md](./ZOD-VALIDATION.md)** - Zod validation explained
|
|
538
|
+
- **[ARCHITECTURE.md](./ARCHITECTURE.md)** - Architecture and design
|
|
539
|
+
- **[examples/](./examples/)** - Complete examples
|
|
309
540
|
|
|
310
541
|
## License
|
|
311
542
|
|
|
312
543
|
Apache-2.0
|
|
313
544
|
|
|
545
|
+
## Related Packages
|
|
546
|
+
|
|
547
|
+
- `@wtflabs/x402-detector` - Token detection (used internally)
|
|
548
|
+
- `@wtflabs/x402-facilitator` - Payment processing (used internally)
|
|
549
|
+
- `@wtflabs/x402-fetch` - Client SDK
|
|
550
|
+
- `@wtflabs/x402` - Core protocol types
|