x402z-server 0.1.2 → 0.1.4
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 +8 -5
- package/dist/index.d.mts +27 -6
- package/dist/index.d.ts +27 -6
- package/dist/index.js +50 -2
- package/dist/index.mjs +50 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,11 +58,11 @@ const server = await createX402zServer({
|
|
|
58
58
|
accepts: buildAcceptsFromConfigs(allowedPayments),
|
|
59
59
|
description: "Demo content",
|
|
60
60
|
mimeType: "text/plain",
|
|
61
|
+
onPaid: async () => ({
|
|
62
|
+
body: "demo content from server",
|
|
63
|
+
}),
|
|
61
64
|
},
|
|
62
65
|
},
|
|
63
|
-
onPaid: async () => ({
|
|
64
|
-
body: "demo content from server",
|
|
65
|
-
}),
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
server.listen(8080);
|
|
@@ -76,7 +76,10 @@ server.listen(8080);
|
|
|
76
76
|
- `confidential.signer`: signer used to decrypt transfer amounts
|
|
77
77
|
- `confidential.relayer`: FHEVM relayer instance used for decryption
|
|
78
78
|
- `routes`: map of `METHOD /path` to payment requirements
|
|
79
|
-
- `
|
|
79
|
+
- `routes["METHOD /path"].onRequest`: optional pre-payment hook; return a response to short-circuit
|
|
80
|
+
- `routes["METHOD /path"].onPaid`: handler for successful payment (returns response body + optional headers)
|
|
81
|
+
- `onPaid` receives `{ paymentRequirements, settleHeaders, request }`
|
|
82
|
+
- `request` includes `method`, `path`, `url`, and normalized `headers`
|
|
80
83
|
|
|
81
84
|
## Examples
|
|
82
85
|
|
|
@@ -101,9 +104,9 @@ const server = await createX402zServer({
|
|
|
101
104
|
],
|
|
102
105
|
description: "Exact USDC payment demo",
|
|
103
106
|
mimeType: "text/plain",
|
|
107
|
+
onPaid: async () => ({ body: "paid content" }),
|
|
104
108
|
},
|
|
105
109
|
},
|
|
106
|
-
onPaid: async () => ({ body: "paid content" }),
|
|
107
110
|
});
|
|
108
111
|
|
|
109
112
|
server.listen(8090);
|
package/dist/index.d.mts
CHANGED
|
@@ -60,19 +60,41 @@ type X402zRouteAccept = {
|
|
|
60
60
|
type NoScheme = {
|
|
61
61
|
scheme?: never;
|
|
62
62
|
};
|
|
63
|
-
type X402zRouteOptions = NoScheme & {
|
|
64
|
-
accepts: X402zRouteAccept[];
|
|
65
|
-
description: string;
|
|
66
|
-
mimeType: string;
|
|
67
|
-
};
|
|
68
63
|
type X402zRouteHandler = (args: {
|
|
69
64
|
paymentRequirements: PaymentRequirements;
|
|
70
65
|
settleHeaders: Record<string, string>;
|
|
66
|
+
request: {
|
|
67
|
+
method: string;
|
|
68
|
+
path: string;
|
|
69
|
+
url: string;
|
|
70
|
+
headers: Record<string, string>;
|
|
71
|
+
};
|
|
71
72
|
}) => Promise<{
|
|
72
73
|
status?: number;
|
|
73
74
|
headers?: Record<string, string>;
|
|
74
75
|
body: string;
|
|
75
76
|
}>;
|
|
77
|
+
type X402zRequestHandler = (args: {
|
|
78
|
+
method: string;
|
|
79
|
+
path: string;
|
|
80
|
+
url: string;
|
|
81
|
+
headers: Record<string, string>;
|
|
82
|
+
}) => Promise<{
|
|
83
|
+
status?: number;
|
|
84
|
+
headers?: Record<string, string>;
|
|
85
|
+
body: string;
|
|
86
|
+
} | void> | {
|
|
87
|
+
status?: number;
|
|
88
|
+
headers?: Record<string, string>;
|
|
89
|
+
body: string;
|
|
90
|
+
} | void;
|
|
91
|
+
type X402zRouteOptions = NoScheme & {
|
|
92
|
+
accepts: X402zRouteAccept[];
|
|
93
|
+
description: string;
|
|
94
|
+
mimeType: string;
|
|
95
|
+
onRequest?: X402zRequestHandler;
|
|
96
|
+
onPaid: X402zRouteHandler;
|
|
97
|
+
};
|
|
76
98
|
type X402zBaseServerOptions = {
|
|
77
99
|
facilitatorUrl: string;
|
|
78
100
|
debug?: boolean;
|
|
@@ -101,7 +123,6 @@ type X402zConfidentialServerConfig = {
|
|
|
101
123
|
} & NoScheme;
|
|
102
124
|
type X402zServerOptions = X402zBaseServerOptions & {
|
|
103
125
|
routes: Record<string, X402zRouteOptions>;
|
|
104
|
-
onPaid: X402zRouteHandler;
|
|
105
126
|
confidential?: X402zConfidentialServerConfig;
|
|
106
127
|
} & NoScheme;
|
|
107
128
|
declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -60,19 +60,41 @@ type X402zRouteAccept = {
|
|
|
60
60
|
type NoScheme = {
|
|
61
61
|
scheme?: never;
|
|
62
62
|
};
|
|
63
|
-
type X402zRouteOptions = NoScheme & {
|
|
64
|
-
accepts: X402zRouteAccept[];
|
|
65
|
-
description: string;
|
|
66
|
-
mimeType: string;
|
|
67
|
-
};
|
|
68
63
|
type X402zRouteHandler = (args: {
|
|
69
64
|
paymentRequirements: PaymentRequirements;
|
|
70
65
|
settleHeaders: Record<string, string>;
|
|
66
|
+
request: {
|
|
67
|
+
method: string;
|
|
68
|
+
path: string;
|
|
69
|
+
url: string;
|
|
70
|
+
headers: Record<string, string>;
|
|
71
|
+
};
|
|
71
72
|
}) => Promise<{
|
|
72
73
|
status?: number;
|
|
73
74
|
headers?: Record<string, string>;
|
|
74
75
|
body: string;
|
|
75
76
|
}>;
|
|
77
|
+
type X402zRequestHandler = (args: {
|
|
78
|
+
method: string;
|
|
79
|
+
path: string;
|
|
80
|
+
url: string;
|
|
81
|
+
headers: Record<string, string>;
|
|
82
|
+
}) => Promise<{
|
|
83
|
+
status?: number;
|
|
84
|
+
headers?: Record<string, string>;
|
|
85
|
+
body: string;
|
|
86
|
+
} | void> | {
|
|
87
|
+
status?: number;
|
|
88
|
+
headers?: Record<string, string>;
|
|
89
|
+
body: string;
|
|
90
|
+
} | void;
|
|
91
|
+
type X402zRouteOptions = NoScheme & {
|
|
92
|
+
accepts: X402zRouteAccept[];
|
|
93
|
+
description: string;
|
|
94
|
+
mimeType: string;
|
|
95
|
+
onRequest?: X402zRequestHandler;
|
|
96
|
+
onPaid: X402zRouteHandler;
|
|
97
|
+
};
|
|
76
98
|
type X402zBaseServerOptions = {
|
|
77
99
|
facilitatorUrl: string;
|
|
78
100
|
debug?: boolean;
|
|
@@ -101,7 +123,6 @@ type X402zConfidentialServerConfig = {
|
|
|
101
123
|
} & NoScheme;
|
|
102
124
|
type X402zServerOptions = X402zBaseServerOptions & {
|
|
103
125
|
routes: Record<string, X402zRouteOptions>;
|
|
104
|
-
onPaid: X402zRouteHandler;
|
|
105
126
|
confidential?: X402zConfidentialServerConfig;
|
|
106
127
|
} & NoScheme;
|
|
107
128
|
declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
package/dist/index.js
CHANGED
|
@@ -354,6 +354,35 @@ async function createX402zServer(config) {
|
|
|
354
354
|
const adapter = buildAdapter(req);
|
|
355
355
|
const method = adapter.getMethod();
|
|
356
356
|
const path = adapter.getPath();
|
|
357
|
+
const routeKey = `${method} ${path}`;
|
|
358
|
+
const routeConfig = config.routes[routeKey];
|
|
359
|
+
if (routeConfig?.onRequest) {
|
|
360
|
+
const headers = {};
|
|
361
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
362
|
+
if (value === void 0) continue;
|
|
363
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(",") : value;
|
|
364
|
+
}
|
|
365
|
+
const response = await routeConfig.onRequest({
|
|
366
|
+
method,
|
|
367
|
+
path,
|
|
368
|
+
url: adapter.getUrl(),
|
|
369
|
+
headers
|
|
370
|
+
});
|
|
371
|
+
if (response) {
|
|
372
|
+
const status = response.status ?? 200;
|
|
373
|
+
const responseHeaders = {
|
|
374
|
+
...response.headers ?? {}
|
|
375
|
+
};
|
|
376
|
+
if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
|
|
377
|
+
responseHeaders["Content-Type"] = "text/plain";
|
|
378
|
+
}
|
|
379
|
+
sendText(res, status, responseHeaders, response.body);
|
|
380
|
+
if (debugEnabled) {
|
|
381
|
+
console.debug(`[server] ${method} ${path} -> ${status} (onRequest)`);
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
357
386
|
const paymentHeader = adapter.getHeader("payment-signature") ?? adapter.getHeader("x402-payment") ?? adapter.getHeader("x-payment");
|
|
358
387
|
if (debugEnabled) {
|
|
359
388
|
console.debug(`[server] ${method} ${path}`);
|
|
@@ -470,9 +499,28 @@ async function createX402zServer(config) {
|
|
|
470
499
|
}
|
|
471
500
|
}
|
|
472
501
|
const settleHeaders = settle.headers ?? {};
|
|
473
|
-
|
|
502
|
+
if (!routeConfig) {
|
|
503
|
+
res.writeHead(500, { ...corsHeaders, "Content-Type": "application/json" });
|
|
504
|
+
res.end(JSON.stringify({ error: "route_config_missing" }));
|
|
505
|
+
if (debugEnabled) {
|
|
506
|
+
console.debug(`[server] ${method} ${path} -> 500 route_config_missing`);
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const headers = {};
|
|
511
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
512
|
+
if (value === void 0) continue;
|
|
513
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(",") : value;
|
|
514
|
+
}
|
|
515
|
+
const payload = await routeConfig.onPaid({
|
|
474
516
|
paymentRequirements: result.paymentRequirements,
|
|
475
|
-
settleHeaders
|
|
517
|
+
settleHeaders,
|
|
518
|
+
request: {
|
|
519
|
+
method,
|
|
520
|
+
path,
|
|
521
|
+
url: adapter.getUrl(),
|
|
522
|
+
headers
|
|
523
|
+
}
|
|
476
524
|
});
|
|
477
525
|
const status = payload.status ?? 200;
|
|
478
526
|
const responseHeaders = {
|
package/dist/index.mjs
CHANGED
|
@@ -318,6 +318,35 @@ async function createX402zServer(config) {
|
|
|
318
318
|
const adapter = buildAdapter(req);
|
|
319
319
|
const method = adapter.getMethod();
|
|
320
320
|
const path = adapter.getPath();
|
|
321
|
+
const routeKey = `${method} ${path}`;
|
|
322
|
+
const routeConfig = config.routes[routeKey];
|
|
323
|
+
if (routeConfig?.onRequest) {
|
|
324
|
+
const headers = {};
|
|
325
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
326
|
+
if (value === void 0) continue;
|
|
327
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(",") : value;
|
|
328
|
+
}
|
|
329
|
+
const response = await routeConfig.onRequest({
|
|
330
|
+
method,
|
|
331
|
+
path,
|
|
332
|
+
url: adapter.getUrl(),
|
|
333
|
+
headers
|
|
334
|
+
});
|
|
335
|
+
if (response) {
|
|
336
|
+
const status = response.status ?? 200;
|
|
337
|
+
const responseHeaders = {
|
|
338
|
+
...response.headers ?? {}
|
|
339
|
+
};
|
|
340
|
+
if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
|
|
341
|
+
responseHeaders["Content-Type"] = "text/plain";
|
|
342
|
+
}
|
|
343
|
+
sendText(res, status, responseHeaders, response.body);
|
|
344
|
+
if (debugEnabled) {
|
|
345
|
+
console.debug(`[server] ${method} ${path} -> ${status} (onRequest)`);
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
321
350
|
const paymentHeader = adapter.getHeader("payment-signature") ?? adapter.getHeader("x402-payment") ?? adapter.getHeader("x-payment");
|
|
322
351
|
if (debugEnabled) {
|
|
323
352
|
console.debug(`[server] ${method} ${path}`);
|
|
@@ -434,9 +463,28 @@ async function createX402zServer(config) {
|
|
|
434
463
|
}
|
|
435
464
|
}
|
|
436
465
|
const settleHeaders = settle.headers ?? {};
|
|
437
|
-
|
|
466
|
+
if (!routeConfig) {
|
|
467
|
+
res.writeHead(500, { ...corsHeaders, "Content-Type": "application/json" });
|
|
468
|
+
res.end(JSON.stringify({ error: "route_config_missing" }));
|
|
469
|
+
if (debugEnabled) {
|
|
470
|
+
console.debug(`[server] ${method} ${path} -> 500 route_config_missing`);
|
|
471
|
+
}
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const headers = {};
|
|
475
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
476
|
+
if (value === void 0) continue;
|
|
477
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(",") : value;
|
|
478
|
+
}
|
|
479
|
+
const payload = await routeConfig.onPaid({
|
|
438
480
|
paymentRequirements: result.paymentRequirements,
|
|
439
|
-
settleHeaders
|
|
481
|
+
settleHeaders,
|
|
482
|
+
request: {
|
|
483
|
+
method,
|
|
484
|
+
path,
|
|
485
|
+
url: adapter.getUrl(),
|
|
486
|
+
headers
|
|
487
|
+
}
|
|
440
488
|
});
|
|
441
489
|
const status = payload.status ?? 200;
|
|
442
490
|
const responseHeaders = {
|