x402z-server 0.1.2 → 0.1.3

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,8 @@ 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)
80
81
 
81
82
  ## Examples
82
83
 
@@ -101,9 +102,9 @@ const server = await createX402zServer({
101
102
  ],
102
103
  description: "Exact USDC payment demo",
103
104
  mimeType: "text/plain",
105
+ onPaid: async () => ({ body: "paid content" }),
104
106
  },
105
107
  },
106
- onPaid: async () => ({ body: "paid content" }),
107
108
  });
108
109
 
109
110
  server.listen(8090);
package/dist/index.d.mts CHANGED
@@ -60,11 +60,6 @@ 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>;
@@ -73,6 +68,27 @@ type X402zRouteHandler = (args: {
73
68
  headers?: Record<string, string>;
74
69
  body: string;
75
70
  }>;
71
+ type X402zRequestHandler = (args: {
72
+ method: string;
73
+ path: string;
74
+ url: string;
75
+ headers: Record<string, string>;
76
+ }) => Promise<{
77
+ status?: number;
78
+ headers?: Record<string, string>;
79
+ body: string;
80
+ } | void> | {
81
+ status?: number;
82
+ headers?: Record<string, string>;
83
+ body: string;
84
+ } | void;
85
+ type X402zRouteOptions = NoScheme & {
86
+ accepts: X402zRouteAccept[];
87
+ description: string;
88
+ mimeType: string;
89
+ onRequest?: X402zRequestHandler;
90
+ onPaid: X402zRouteHandler;
91
+ };
76
92
  type X402zBaseServerOptions = {
77
93
  facilitatorUrl: string;
78
94
  debug?: boolean;
@@ -101,7 +117,6 @@ type X402zConfidentialServerConfig = {
101
117
  } & NoScheme;
102
118
  type X402zServerOptions = X402zBaseServerOptions & {
103
119
  routes: Record<string, X402zRouteOptions>;
104
- onPaid: X402zRouteHandler;
105
120
  confidential?: X402zConfidentialServerConfig;
106
121
  } & NoScheme;
107
122
  declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
package/dist/index.d.ts CHANGED
@@ -60,11 +60,6 @@ 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>;
@@ -73,6 +68,27 @@ type X402zRouteHandler = (args: {
73
68
  headers?: Record<string, string>;
74
69
  body: string;
75
70
  }>;
71
+ type X402zRequestHandler = (args: {
72
+ method: string;
73
+ path: string;
74
+ url: string;
75
+ headers: Record<string, string>;
76
+ }) => Promise<{
77
+ status?: number;
78
+ headers?: Record<string, string>;
79
+ body: string;
80
+ } | void> | {
81
+ status?: number;
82
+ headers?: Record<string, string>;
83
+ body: string;
84
+ } | void;
85
+ type X402zRouteOptions = NoScheme & {
86
+ accepts: X402zRouteAccept[];
87
+ description: string;
88
+ mimeType: string;
89
+ onRequest?: X402zRequestHandler;
90
+ onPaid: X402zRouteHandler;
91
+ };
76
92
  type X402zBaseServerOptions = {
77
93
  facilitatorUrl: string;
78
94
  debug?: boolean;
@@ -101,7 +117,6 @@ type X402zConfidentialServerConfig = {
101
117
  } & NoScheme;
102
118
  type X402zServerOptions = X402zBaseServerOptions & {
103
119
  routes: Record<string, X402zRouteOptions>;
104
- onPaid: X402zRouteHandler;
105
120
  confidential?: X402zConfidentialServerConfig;
106
121
  } & NoScheme;
107
122
  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,7 +499,15 @@ 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 payload = await routeConfig.onPaid({
474
511
  paymentRequirements: result.paymentRequirements,
475
512
  settleHeaders
476
513
  });
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,7 +463,15 @@ 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 payload = await routeConfig.onPaid({
438
475
  paymentRequirements: result.paymentRequirements,
439
476
  settleHeaders
440
477
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402z-server",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",