@wtflabs/x402-server 0.0.1-beta.10
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 +630 -0
- package/dist/cjs/index.d.ts +1750 -0
- package/dist/cjs/index.js +589 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +1750 -0
- package/dist/esm/index.mjs +552 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
# @wtflabs/x402-server
|
|
2
|
+
|
|
3
|
+
Server SDK for the x402 Payment Protocol. Handles payment verification and settlement with automatic token detection.
|
|
4
|
+
|
|
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
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @wtflabs/x402-server viem
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Option 1: Using Middleware (Easiest)
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import express from "express";
|
|
28
|
+
import { createExpressMiddleware, X402Server } from "@wtflabs/x402-server";
|
|
29
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
30
|
+
|
|
31
|
+
const app = express();
|
|
32
|
+
|
|
33
|
+
// 1. Create facilitator
|
|
34
|
+
const facilitator = new Facilitator({
|
|
35
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
36
|
+
});
|
|
37
|
+
|
|
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",
|
|
49
|
+
});
|
|
50
|
+
|
|
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",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 2. Create server instance
|
|
73
|
+
const server = new X402Server({
|
|
74
|
+
client: createPublicClient({
|
|
75
|
+
chain: bscTestnet,
|
|
76
|
+
transport: http(),
|
|
77
|
+
}),
|
|
78
|
+
facilitator,
|
|
79
|
+
});
|
|
80
|
+
|
|
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
|
+
asset: "0x25d066c4C68C8A6332DfDB4230263608305Ca991",
|
|
91
|
+
maxAmountRequired: "1000", // wei
|
|
92
|
+
});
|
|
93
|
+
|
|
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
|
+
```
|
|
113
|
+
|
|
114
|
+
## API Reference
|
|
115
|
+
|
|
116
|
+
### Constructor
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const server = new X402Server(config: X402ServerConfig)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Required:**
|
|
123
|
+
- `client: PublicClient` - Viem PublicClient instance
|
|
124
|
+
- `facilitator: Facilitator` - Facilitator instance (handles payment processing)
|
|
125
|
+
|
|
126
|
+
**Optional:**
|
|
127
|
+
- `network?: string` - Network name (auto-detected from client if not provided)
|
|
128
|
+
|
|
129
|
+
**Example:**
|
|
130
|
+
```typescript
|
|
131
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
132
|
+
import { createPublicClient, http } from "viem";
|
|
133
|
+
import { bscTestnet } from "viem/chains";
|
|
134
|
+
|
|
135
|
+
// 1. Create viem client
|
|
136
|
+
const client = createPublicClient({
|
|
137
|
+
chain: bscTestnet,
|
|
138
|
+
transport: http(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 2. Create facilitator first
|
|
142
|
+
const facilitator = new Facilitator({
|
|
143
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
144
|
+
waitUntil: "confirmed", // optional: "confirmed" | "finalized"
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// 3. Create server with client and facilitator
|
|
148
|
+
const server = new X402Server({
|
|
149
|
+
client,
|
|
150
|
+
facilitator,
|
|
151
|
+
network: "bsc-testnet", // optional
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Methods
|
|
156
|
+
|
|
157
|
+
#### `initialize(tokens: string[]): Promise<InitResult>`
|
|
158
|
+
|
|
159
|
+
Pre-warm the token detection cache. Non-blocking, can run in background.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Wait for initialization
|
|
163
|
+
await server.initialize([tokenAddress]);
|
|
164
|
+
|
|
165
|
+
// Or run in background
|
|
166
|
+
server.initialize([tokenAddress]).then(result => {
|
|
167
|
+
if (result.success) console.log("✅ Cache ready");
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `createRequirements(config): Promise<PaymentRequirements>`
|
|
172
|
+
|
|
173
|
+
Create payment requirements. Supports dynamic amounts and auto-detection.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const requirements = await server.createRequirements({
|
|
177
|
+
// Required
|
|
178
|
+
asset: "0x...", // Token contract address
|
|
179
|
+
maxAmountRequired: "1000", // Amount in wei (as string)
|
|
180
|
+
|
|
181
|
+
// Optional - network and scheme
|
|
182
|
+
network?: "bsc-testnet", // Network name (overrides server config)
|
|
183
|
+
scheme?: "exact", // Payment scheme (currently only "exact")
|
|
184
|
+
outputSchema?: {}, // Output schema (for validation)
|
|
185
|
+
|
|
186
|
+
// Optional - payment type
|
|
187
|
+
paymentType?: "permit" | "eip3009" | "permit2" | "auto",
|
|
188
|
+
|
|
189
|
+
// Optional - resource description
|
|
190
|
+
resource?: "https://api.example.com/data",
|
|
191
|
+
description?: "Premium API access",
|
|
192
|
+
mimeType?: "application/json",
|
|
193
|
+
maxTimeoutSeconds?: 300,
|
|
194
|
+
|
|
195
|
+
// Optional - extra metadata
|
|
196
|
+
extra?: {}, // Additional metadata
|
|
197
|
+
|
|
198
|
+
// Optional - performance
|
|
199
|
+
autoDetect?: true, // Set to false for fast mode (requires manual paymentType)
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### `process(paymentHeader, requirements): Promise<ProcessResult>`
|
|
204
|
+
|
|
205
|
+
Complete payment processing (parse → verify → settle).
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const result = await server.process(
|
|
209
|
+
request.headers["x-payment"],
|
|
210
|
+
requirements
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
if (result.success) {
|
|
214
|
+
console.log("Payer:", result.data.payer);
|
|
215
|
+
console.log("TxHash:", result.data.txHash);
|
|
216
|
+
} else {
|
|
217
|
+
console.log("Error:", result.response.error);
|
|
218
|
+
// Return 402 with result.response
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Advanced: Step-by-Step Processing
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// 1. Parse payment header
|
|
226
|
+
const parsed = server.parse(paymentHeader, requirements);
|
|
227
|
+
if (!parsed.success) {
|
|
228
|
+
return res.status(402).json(parsed.response402);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 2. Verify payment
|
|
232
|
+
const verified = await server.verify(parsed.data);
|
|
233
|
+
if (!verified.success) {
|
|
234
|
+
return res.status(402).json(
|
|
235
|
+
server.get402Response(requirements, verified.error)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
console.log("Payer:", verified.payer);
|
|
239
|
+
|
|
240
|
+
// 3. Settle payment (optional - you can skip this for verify-only mode)
|
|
241
|
+
const settled = await server.settle(parsed.data);
|
|
242
|
+
if (!settled.success) {
|
|
243
|
+
return res.status(402).json(
|
|
244
|
+
server.get402Response(requirements, settled.error)
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
console.log("TxHash:", settled.txHash);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Utility Methods
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Generate 402 response
|
|
254
|
+
const response402 = server.get402Response(requirements, error?);
|
|
255
|
+
|
|
256
|
+
// Clear token cache
|
|
257
|
+
await server.clearCache(tokenAddress?); // specific or all
|
|
258
|
+
|
|
259
|
+
// Get cache stats
|
|
260
|
+
const stats = server.getCacheStats();
|
|
261
|
+
console.log(stats.size, stats.keys);
|
|
262
|
+
|
|
263
|
+
// Get underlying instances (for advanced usage)
|
|
264
|
+
const facilitator = server.getFacilitator();
|
|
265
|
+
const detector = server.getDetector();
|
|
266
|
+
const client = server.getClient();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Usage Examples
|
|
270
|
+
|
|
271
|
+
### Example 1: Fixed Amount
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
275
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
276
|
+
|
|
277
|
+
// 1. Create facilitator
|
|
278
|
+
const facilitator = new Facilitator({
|
|
279
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// 2. Create server
|
|
283
|
+
const server = new X402Server({ client, facilitator });
|
|
284
|
+
|
|
285
|
+
// 3. Pre-warm cache (optional)
|
|
286
|
+
await server.initialize(["0xUSDC"]);
|
|
287
|
+
|
|
288
|
+
// 4. Fixed requirements
|
|
289
|
+
const requirements = await server.createRequirements({
|
|
290
|
+
asset: "0xUSDC",
|
|
291
|
+
maxAmountRequired: "1000",
|
|
292
|
+
description: "Access to premium API",
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
app.post("/premium-api", async (req, res) => {
|
|
296
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
297
|
+
|
|
298
|
+
if (!result.success) {
|
|
299
|
+
return res.status(402).json(result.response);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
res.json({ data: "premium content" });
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Example 2: Dynamic Pricing
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
app.post("/api/compute", async (req, res) => {
|
|
310
|
+
const { complexity } = req.body;
|
|
311
|
+
|
|
312
|
+
// Calculate price based on complexity
|
|
313
|
+
const price = calculatePrice(complexity);
|
|
314
|
+
|
|
315
|
+
// Dynamic requirements
|
|
316
|
+
const requirements = await server.createRequirements({
|
|
317
|
+
asset: "0xUSDC",
|
|
318
|
+
maxAmountRequired: price,
|
|
319
|
+
description: `Compute task (${complexity})`,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
323
|
+
|
|
324
|
+
if (!result.success) {
|
|
325
|
+
return res.status(402).json(result.response);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Execute computation
|
|
329
|
+
const computeResult = await performComputation(complexity);
|
|
330
|
+
res.json({ result: computeResult, paid: price });
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Example 3: Multiple Tokens
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
338
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
339
|
+
|
|
340
|
+
// 1. Create facilitator
|
|
341
|
+
const facilitator = new Facilitator({
|
|
342
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// 2. Create server
|
|
346
|
+
const server = new X402Server({ client, facilitator });
|
|
347
|
+
|
|
348
|
+
// 3. Pre-warm multiple tokens
|
|
349
|
+
await server.initialize(["0xUSDC", "0xDAI", "0xUSDT"]);
|
|
350
|
+
|
|
351
|
+
app.get("/premium-api", async (req, res) => {
|
|
352
|
+
// Return multiple payment options
|
|
353
|
+
const accepts = await Promise.all([
|
|
354
|
+
server.createRequirements({ asset: "0xUSDC", maxAmountRequired: "1000" }),
|
|
355
|
+
server.createRequirements({ asset: "0xDAI", maxAmountRequired: "1000" }),
|
|
356
|
+
server.createRequirements({ asset: "0xUSDT", maxAmountRequired: "1000" }),
|
|
357
|
+
]);
|
|
358
|
+
|
|
359
|
+
res.status(402).json({
|
|
360
|
+
x402Version: 1,
|
|
361
|
+
accepts,
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
app.post("/premium-api", async (req, res) => {
|
|
366
|
+
// User pays with their chosen token
|
|
367
|
+
const parsed = server.parse(req.headers["x-payment"], accepts[0]);
|
|
368
|
+
if (!parsed.success) {
|
|
369
|
+
return res.status(402).json(parsed.response402);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Detect which token was used
|
|
373
|
+
const tokenUsed = parsed.data.payload.payload.authorization.token;
|
|
374
|
+
|
|
375
|
+
// Create matching requirements
|
|
376
|
+
const requirements = await server.createRequirements({
|
|
377
|
+
asset: tokenUsed,
|
|
378
|
+
maxAmountRequired: "1000",
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
382
|
+
// ...
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Example 4: Fast Mode (Skip Detection)
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// For maximum performance, skip auto-detection
|
|
390
|
+
const requirements = await server.createRequirements({
|
|
391
|
+
asset: "0xUSDC",
|
|
392
|
+
maxAmountRequired: "1000",
|
|
393
|
+
paymentType: "permit", // Manually specify
|
|
394
|
+
autoDetect: false, // Skip detection (<1ms)
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Framework Integration
|
|
399
|
+
|
|
400
|
+
### Express
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
import express from "express";
|
|
404
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
405
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
406
|
+
import { createPublicClient, http } from "viem";
|
|
407
|
+
import { bscTestnet } from "viem/chains";
|
|
408
|
+
|
|
409
|
+
const app = express();
|
|
410
|
+
|
|
411
|
+
// Create client
|
|
412
|
+
const client = createPublicClient({
|
|
413
|
+
chain: bscTestnet,
|
|
414
|
+
transport: http(),
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Create facilitator
|
|
418
|
+
const facilitator = new Facilitator({
|
|
419
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Create server
|
|
423
|
+
const server = new X402Server({ client, facilitator });
|
|
424
|
+
|
|
425
|
+
app.post("/api/resource", async (req, res) => {
|
|
426
|
+
const requirements = await server.createRequirements({
|
|
427
|
+
asset: "0xUSDC",
|
|
428
|
+
maxAmountRequired: "1000",
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const result = await server.process(req.headers["x-payment"], requirements);
|
|
432
|
+
|
|
433
|
+
if (!result.success) {
|
|
434
|
+
return res.status(402).json(result.response);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
res.json({ data: "resource" });
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Hono
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
import { Hono } from "hono";
|
|
445
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
446
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
447
|
+
import { createPublicClient, http } from "viem";
|
|
448
|
+
import { bscTestnet } from "viem/chains";
|
|
449
|
+
|
|
450
|
+
const app = new Hono();
|
|
451
|
+
|
|
452
|
+
// Create client
|
|
453
|
+
const client = createPublicClient({
|
|
454
|
+
chain: bscTestnet,
|
|
455
|
+
transport: http(),
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Create facilitator
|
|
459
|
+
const facilitator = new Facilitator({
|
|
460
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Create server
|
|
464
|
+
const server = new X402Server({ client, facilitator });
|
|
465
|
+
|
|
466
|
+
app.post("/api/resource", async (c) => {
|
|
467
|
+
const requirements = await server.createRequirements({
|
|
468
|
+
asset: "0xUSDC",
|
|
469
|
+
maxAmountRequired: "1000",
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const result = await server.process(c.req.header("x-payment"), requirements);
|
|
473
|
+
|
|
474
|
+
if (!result.success) {
|
|
475
|
+
return c.json(result.response, 402);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return c.json({ data: "resource" });
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Next.js API Route
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
import { X402Server } from "@wtflabs/x402-server";
|
|
486
|
+
import { Facilitator } from "@wtflabs/x402-facilitator";
|
|
487
|
+
import { NextRequest } from "next/server";
|
|
488
|
+
import { createPublicClient, http } from "viem";
|
|
489
|
+
import { bscTestnet } from "viem/chains";
|
|
490
|
+
|
|
491
|
+
// Create client
|
|
492
|
+
const client = createPublicClient({
|
|
493
|
+
chain: bscTestnet,
|
|
494
|
+
transport: http(),
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// Create facilitator
|
|
498
|
+
const facilitator = new Facilitator({
|
|
499
|
+
recipientAddress: "0x5D06b8145D908DDb7ca116664Fcf113ddaA4d6F3",
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// Create server
|
|
503
|
+
const server = new X402Server({ client, facilitator });
|
|
504
|
+
|
|
505
|
+
export async function POST(req: NextRequest) {
|
|
506
|
+
const requirements = await server.createRequirements({
|
|
507
|
+
asset: "0xUSDC",
|
|
508
|
+
maxAmountRequired: "1000",
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const result = await server.process(
|
|
512
|
+
req.headers.get("x-payment") || undefined,
|
|
513
|
+
requirements
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
if (!result.success) {
|
|
517
|
+
return Response.json(result.response, { status: 402 });
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return Response.json({ data: "resource" });
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Performance
|
|
525
|
+
|
|
526
|
+
| Operation | First Call | Cached Call |
|
|
527
|
+
|-----------|-----------|-------------|
|
|
528
|
+
| `createRequirements(autoDetect: true)` | 2-5s | <1ms |
|
|
529
|
+
| `createRequirements(autoDetect: false)` | <1ms | <1ms |
|
|
530
|
+
| `process()` | 2-5s + network | <1ms + network |
|
|
531
|
+
|
|
532
|
+
**Tips:**
|
|
533
|
+
- Use `initialize()` on startup to pre-warm cache
|
|
534
|
+
- Set `autoDetect: false` for maximum speed (requires manual `paymentType`)
|
|
535
|
+
- Cache persists for the lifetime of the server instance
|
|
536
|
+
|
|
537
|
+
## Error Handling
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
const result = await server.process(paymentHeader, requirements);
|
|
541
|
+
|
|
542
|
+
if (!result.success) {
|
|
543
|
+
// 402 response with error details
|
|
544
|
+
console.log(result.response.error);
|
|
545
|
+
return res.status(402).json(result.response);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Success!
|
|
549
|
+
console.log(result.data.payer);
|
|
550
|
+
console.log(result.data.txHash);
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## TypeScript Support
|
|
554
|
+
|
|
555
|
+
Full TypeScript support with comprehensive type definitions:
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
import type {
|
|
559
|
+
X402ServerConfig,
|
|
560
|
+
CreateRequirementsConfig,
|
|
561
|
+
PaymentRequirements,
|
|
562
|
+
ProcessResult,
|
|
563
|
+
InitResult,
|
|
564
|
+
} from "@wtflabs/x402-server";
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
## Middlewares
|
|
568
|
+
|
|
569
|
+
### Express Middleware
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
import { createExpressMiddleware } from "@wtflabs/x402-server";
|
|
573
|
+
|
|
574
|
+
const middleware = createExpressMiddleware({
|
|
575
|
+
server,
|
|
576
|
+
getToken: (req) => req.body.token,
|
|
577
|
+
getAmount: (req) => calculatePrice(req.body),
|
|
578
|
+
onPaymentSuccess: async (req, payer, txHash) => {
|
|
579
|
+
console.log(`Payment from ${payer}: ${txHash}`);
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
app.post("/api", middleware, (req, res) => {
|
|
584
|
+
const { payer, txHash } = req.x402!;
|
|
585
|
+
res.json({ data: "resource", payer, txHash });
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Hono Middleware
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
import { createHonoMiddleware } from "@wtflabs/x402-server";
|
|
593
|
+
|
|
594
|
+
const middleware = createHonoMiddleware({
|
|
595
|
+
server,
|
|
596
|
+
getToken: async (c) => (await c.req.json()).token,
|
|
597
|
+
getAmount: async (c) => calculatePrice(await c.req.json()),
|
|
598
|
+
onPaymentSuccess: async (c, payer, txHash) => {
|
|
599
|
+
console.log(`Payment from ${payer}: ${txHash}`);
|
|
600
|
+
},
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
app.post("/api", middleware, (c) => {
|
|
604
|
+
const x402 = c.get("x402")!;
|
|
605
|
+
return c.json({ data: "resource", payer: x402.payer });
|
|
606
|
+
});
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**See [MIDDLEWARES.md](./MIDDLEWARES.md) for detailed guide.**
|
|
610
|
+
|
|
611
|
+
## Documentation
|
|
612
|
+
|
|
613
|
+
- **[README.md](./README.md)** - This file
|
|
614
|
+
- **[QUICK-START.md](./QUICK-START.md)** - 5-minute quick start guide
|
|
615
|
+
- **[USAGE.md](./USAGE.md)** - Detailed usage documentation
|
|
616
|
+
- **[MIDDLEWARES.md](./MIDDLEWARES.md)** - Express and Hono middleware guide
|
|
617
|
+
- **[ZOD-VALIDATION.md](./ZOD-VALIDATION.md)** - Zod validation explained
|
|
618
|
+
- **[ARCHITECTURE.md](./ARCHITECTURE.md)** - Architecture and design
|
|
619
|
+
- **[examples/](./examples/)** - Complete examples
|
|
620
|
+
|
|
621
|
+
## License
|
|
622
|
+
|
|
623
|
+
Apache-2.0
|
|
624
|
+
|
|
625
|
+
## Related Packages
|
|
626
|
+
|
|
627
|
+
- `@wtflabs/x402-detector` - Token detection (used internally)
|
|
628
|
+
- `@wtflabs/x402-facilitator` - Payment processing (used internally)
|
|
629
|
+
- `@wtflabs/x402-fetch` - Client SDK
|
|
630
|
+
- `@wtflabs/x402` - Core protocol types
|