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 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
- - `onPaid`: handler for successful payment (returns response body + optional headers)
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
- const payload = await config.onPaid({
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
- const payload = await config.onPaid({
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 = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402z-server",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",