@t402/core 2.0.0

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.
Files changed (66) hide show
  1. package/README.md +293 -0
  2. package/dist/cjs/client/index.d.ts +103 -0
  3. package/dist/cjs/client/index.js +510 -0
  4. package/dist/cjs/client/index.js.map +1 -0
  5. package/dist/cjs/facilitator/index.d.ts +192 -0
  6. package/dist/cjs/facilitator/index.js +398 -0
  7. package/dist/cjs/facilitator/index.js.map +1 -0
  8. package/dist/cjs/http/index.d.ts +52 -0
  9. package/dist/cjs/http/index.js +763 -0
  10. package/dist/cjs/http/index.js.map +1 -0
  11. package/dist/cjs/index.d.ts +3 -0
  12. package/dist/cjs/index.js +31 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/mechanisms-CmrqNl1M.d.ts +238 -0
  15. package/dist/cjs/mechanisms-DsJn3ZiM.d.ts +238 -0
  16. package/dist/cjs/server/index.d.ts +2 -0
  17. package/dist/cjs/server/index.js +1241 -0
  18. package/dist/cjs/server/index.js.map +1 -0
  19. package/dist/cjs/t402HTTPClient-m6cjzTek.d.ts +243 -0
  20. package/dist/cjs/t402HTTPResourceServer-B-xmYMwj.d.ts +719 -0
  21. package/dist/cjs/t402HTTPResourceServer-Bcfxp2UO.d.ts +719 -0
  22. package/dist/cjs/types/index.d.ts +1 -0
  23. package/dist/cjs/types/index.js +19 -0
  24. package/dist/cjs/types/index.js.map +1 -0
  25. package/dist/cjs/types/v1/index.d.ts +1 -0
  26. package/dist/cjs/types/v1/index.js +19 -0
  27. package/dist/cjs/types/v1/index.js.map +1 -0
  28. package/dist/cjs/utils/index.d.ts +48 -0
  29. package/dist/cjs/utils/index.js +108 -0
  30. package/dist/cjs/utils/index.js.map +1 -0
  31. package/dist/esm/chunk-3IUBYRYG.mjs +78 -0
  32. package/dist/esm/chunk-3IUBYRYG.mjs.map +1 -0
  33. package/dist/esm/chunk-3VTYR43U.mjs +7 -0
  34. package/dist/esm/chunk-3VTYR43U.mjs.map +1 -0
  35. package/dist/esm/chunk-BJTO5JO5.mjs +11 -0
  36. package/dist/esm/chunk-BJTO5JO5.mjs.map +1 -0
  37. package/dist/esm/chunk-D5DYKKCZ.mjs +722 -0
  38. package/dist/esm/chunk-D5DYKKCZ.mjs.map +1 -0
  39. package/dist/esm/client/index.d.mts +103 -0
  40. package/dist/esm/client/index.mjs +356 -0
  41. package/dist/esm/client/index.mjs.map +1 -0
  42. package/dist/esm/facilitator/index.d.mts +192 -0
  43. package/dist/esm/facilitator/index.mjs +373 -0
  44. package/dist/esm/facilitator/index.mjs.map +1 -0
  45. package/dist/esm/http/index.d.mts +52 -0
  46. package/dist/esm/http/index.mjs +28 -0
  47. package/dist/esm/http/index.mjs.map +1 -0
  48. package/dist/esm/index.d.mts +3 -0
  49. package/dist/esm/index.mjs +8 -0
  50. package/dist/esm/index.mjs.map +1 -0
  51. package/dist/esm/mechanisms-CmrqNl1M.d.mts +238 -0
  52. package/dist/esm/server/index.d.mts +2 -0
  53. package/dist/esm/server/index.mjs +562 -0
  54. package/dist/esm/server/index.mjs.map +1 -0
  55. package/dist/esm/t402HTTPClient-C285YGCp.d.mts +243 -0
  56. package/dist/esm/t402HTTPResourceServer-k_l3d8ua.d.mts +719 -0
  57. package/dist/esm/types/index.d.mts +1 -0
  58. package/dist/esm/types/index.mjs +1 -0
  59. package/dist/esm/types/index.mjs.map +1 -0
  60. package/dist/esm/types/v1/index.d.mts +1 -0
  61. package/dist/esm/types/v1/index.mjs +1 -0
  62. package/dist/esm/types/v1/index.mjs.map +1 -0
  63. package/dist/esm/utils/index.d.mts +48 -0
  64. package/dist/esm/utils/index.mjs +20 -0
  65. package/dist/esm/utils/index.mjs.map +1 -0
  66. package/package.json +129 -0
@@ -0,0 +1,722 @@
1
+ import {
2
+ t402Version
3
+ } from "./chunk-3VTYR43U.mjs";
4
+ import {
5
+ Base64EncodedRegex,
6
+ safeBase64Decode,
7
+ safeBase64Encode
8
+ } from "./chunk-3IUBYRYG.mjs";
9
+ import {
10
+ __require
11
+ } from "./chunk-BJTO5JO5.mjs";
12
+
13
+ // src/http/t402HTTPResourceServer.ts
14
+ var RouteConfigurationError = class extends Error {
15
+ /**
16
+ * Creates a new RouteConfigurationError with the given validation errors.
17
+ *
18
+ * @param errors - The validation errors that caused this exception.
19
+ */
20
+ constructor(errors) {
21
+ const message = `t402 Route Configuration Errors:
22
+ ${errors.map((e) => ` - ${e.message}`).join("\n")}`;
23
+ super(message);
24
+ this.name = "RouteConfigurationError";
25
+ this.errors = errors;
26
+ }
27
+ };
28
+ var t402HTTPResourceServer = class {
29
+ /**
30
+ * Creates a new t402HTTPResourceServer instance.
31
+ *
32
+ * @param ResourceServer - The core t402ResourceServer instance to use
33
+ * @param routes - Route configuration for payment-protected endpoints
34
+ */
35
+ constructor(ResourceServer, routes) {
36
+ this.compiledRoutes = [];
37
+ this.ResourceServer = ResourceServer;
38
+ this.routesConfig = routes;
39
+ const normalizedRoutes = typeof routes === "object" && !("accepts" in routes) ? routes : { "*": routes };
40
+ for (const [pattern, config] of Object.entries(normalizedRoutes)) {
41
+ const parsed = this.parseRoutePattern(pattern);
42
+ this.compiledRoutes.push({
43
+ verb: parsed.verb,
44
+ regex: parsed.regex,
45
+ config
46
+ });
47
+ }
48
+ }
49
+ /**
50
+ * Initialize the HTTP resource server.
51
+ *
52
+ * This method initializes the underlying resource server (fetching facilitator support)
53
+ * and then validates that all route payment configurations have corresponding
54
+ * registered schemes and facilitator support.
55
+ *
56
+ * @throws RouteConfigurationError if any route's payment options don't have
57
+ * corresponding registered schemes or facilitator support
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const httpServer = new t402HTTPResourceServer(server, routes);
62
+ * await httpServer.initialize();
63
+ * ```
64
+ */
65
+ async initialize() {
66
+ await this.ResourceServer.initialize();
67
+ const errors = this.validateRouteConfiguration();
68
+ if (errors.length > 0) {
69
+ throw new RouteConfigurationError(errors);
70
+ }
71
+ }
72
+ /**
73
+ * Register a custom paywall provider for generating HTML
74
+ *
75
+ * @param provider - PaywallProvider instance
76
+ * @returns This service instance for chaining
77
+ */
78
+ registerPaywallProvider(provider) {
79
+ this.paywallProvider = provider;
80
+ return this;
81
+ }
82
+ /**
83
+ * Process HTTP request and return response instructions
84
+ * This is the main entry point for framework middleware
85
+ *
86
+ * @param context - HTTP request context
87
+ * @param paywallConfig - Optional paywall configuration
88
+ * @returns Process result indicating next action for middleware
89
+ */
90
+ async processHTTPRequest(context, paywallConfig) {
91
+ const { adapter, path, method } = context;
92
+ const routeConfig = this.getRouteConfig(path, method);
93
+ if (!routeConfig) {
94
+ return { type: "no-payment-required" };
95
+ }
96
+ const paymentOptions = this.normalizePaymentOptions(routeConfig);
97
+ const paymentPayload = this.extractPayment(adapter);
98
+ const resourceInfo = {
99
+ url: routeConfig.resource || context.adapter.getUrl(),
100
+ description: routeConfig.description || "",
101
+ mimeType: routeConfig.mimeType || ""
102
+ };
103
+ const requirements = await this.ResourceServer.buildPaymentRequirementsFromOptions(
104
+ paymentOptions,
105
+ context
106
+ );
107
+ let extensions = routeConfig.extensions;
108
+ if (extensions) {
109
+ extensions = this.ResourceServer.enrichExtensions(extensions, context);
110
+ }
111
+ const paymentRequired = this.ResourceServer.createPaymentRequiredResponse(
112
+ requirements,
113
+ resourceInfo,
114
+ !paymentPayload ? "Payment required" : void 0,
115
+ extensions
116
+ );
117
+ if (!paymentPayload) {
118
+ const unpaidBody = routeConfig.unpaidResponseBody ? await routeConfig.unpaidResponseBody(context) : void 0;
119
+ return {
120
+ type: "payment-error",
121
+ response: this.createHTTPResponse(
122
+ paymentRequired,
123
+ this.isWebBrowser(adapter),
124
+ paywallConfig,
125
+ routeConfig.customPaywallHtml,
126
+ unpaidBody
127
+ )
128
+ };
129
+ }
130
+ try {
131
+ const matchingRequirements = this.ResourceServer.findMatchingRequirements(
132
+ paymentRequired.accepts,
133
+ paymentPayload
134
+ );
135
+ if (!matchingRequirements) {
136
+ const errorResponse = this.ResourceServer.createPaymentRequiredResponse(
137
+ requirements,
138
+ resourceInfo,
139
+ "No matching payment requirements",
140
+ routeConfig.extensions
141
+ );
142
+ return {
143
+ type: "payment-error",
144
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
145
+ };
146
+ }
147
+ const verifyResult = await this.ResourceServer.verifyPayment(
148
+ paymentPayload,
149
+ matchingRequirements
150
+ );
151
+ if (!verifyResult.isValid) {
152
+ const errorResponse = this.ResourceServer.createPaymentRequiredResponse(
153
+ requirements,
154
+ resourceInfo,
155
+ verifyResult.invalidReason,
156
+ routeConfig.extensions
157
+ );
158
+ return {
159
+ type: "payment-error",
160
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
161
+ };
162
+ }
163
+ return {
164
+ type: "payment-verified",
165
+ paymentPayload,
166
+ paymentRequirements: matchingRequirements
167
+ };
168
+ } catch (error) {
169
+ const errorResponse = this.ResourceServer.createPaymentRequiredResponse(
170
+ requirements,
171
+ resourceInfo,
172
+ error instanceof Error ? error.message : "Payment verification failed",
173
+ routeConfig.extensions
174
+ );
175
+ return {
176
+ type: "payment-error",
177
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
178
+ };
179
+ }
180
+ }
181
+ /**
182
+ * Process settlement after successful response
183
+ *
184
+ * @param paymentPayload - The verified payment payload
185
+ * @param requirements - The matching payment requirements
186
+ * @returns ProcessSettleResultResponse - SettleResponse with headers if success or errorReason if failure
187
+ */
188
+ async processSettlement(paymentPayload, requirements) {
189
+ try {
190
+ const settleResponse = await this.ResourceServer.settlePayment(paymentPayload, requirements);
191
+ if (!settleResponse.success) {
192
+ return {
193
+ ...settleResponse,
194
+ success: false,
195
+ errorReason: settleResponse.errorReason || "Settlement failed"
196
+ };
197
+ }
198
+ return {
199
+ ...settleResponse,
200
+ success: true,
201
+ headers: this.createSettlementHeaders(settleResponse, requirements),
202
+ requirements
203
+ };
204
+ } catch (error) {
205
+ throw new Error(error instanceof Error ? error.message : "Settlement failed");
206
+ }
207
+ }
208
+ /**
209
+ * Check if a request requires payment based on route configuration
210
+ *
211
+ * @param context - HTTP request context
212
+ * @returns True if the route requires payment, false otherwise
213
+ */
214
+ requiresPayment(context) {
215
+ const routeConfig = this.getRouteConfig(context.path, context.method);
216
+ return routeConfig !== void 0;
217
+ }
218
+ /**
219
+ * Normalizes a RouteConfig's accepts field into an array of PaymentOptions
220
+ * Handles both single PaymentOption and array formats
221
+ *
222
+ * @param routeConfig - Route configuration
223
+ * @returns Array of payment options
224
+ */
225
+ normalizePaymentOptions(routeConfig) {
226
+ return Array.isArray(routeConfig.accepts) ? routeConfig.accepts : [routeConfig.accepts];
227
+ }
228
+ /**
229
+ * Validates that all payment options in routes have corresponding registered schemes
230
+ * and facilitator support.
231
+ *
232
+ * @returns Array of validation errors (empty if all routes are valid)
233
+ */
234
+ validateRouteConfiguration() {
235
+ const errors = [];
236
+ const normalizedRoutes = typeof this.routesConfig === "object" && !("accepts" in this.routesConfig) ? Object.entries(this.routesConfig) : [["*", this.routesConfig]];
237
+ for (const [pattern, config] of normalizedRoutes) {
238
+ const paymentOptions = this.normalizePaymentOptions(config);
239
+ for (const option of paymentOptions) {
240
+ if (!this.ResourceServer.hasRegisteredScheme(option.network, option.scheme)) {
241
+ errors.push({
242
+ routePattern: pattern,
243
+ scheme: option.scheme,
244
+ network: option.network,
245
+ reason: "missing_scheme",
246
+ message: `Route "${pattern}": No scheme implementation registered for "${option.scheme}" on network "${option.network}"`
247
+ });
248
+ continue;
249
+ }
250
+ const supportedKind = this.ResourceServer.getSupportedKind(
251
+ t402Version,
252
+ option.network,
253
+ option.scheme
254
+ );
255
+ if (!supportedKind) {
256
+ errors.push({
257
+ routePattern: pattern,
258
+ scheme: option.scheme,
259
+ network: option.network,
260
+ reason: "missing_facilitator",
261
+ message: `Route "${pattern}": Facilitator does not support scheme "${option.scheme}" on network "${option.network}"`
262
+ });
263
+ }
264
+ }
265
+ }
266
+ return errors;
267
+ }
268
+ /**
269
+ * Get route configuration for a request
270
+ *
271
+ * @param path - Request path
272
+ * @param method - HTTP method
273
+ * @returns Route configuration or undefined if no match
274
+ */
275
+ getRouteConfig(path, method) {
276
+ const normalizedPath = this.normalizePath(path);
277
+ const upperMethod = method.toUpperCase();
278
+ const matchingRoute = this.compiledRoutes.find(
279
+ (route) => route.regex.test(normalizedPath) && (route.verb === "*" || route.verb === upperMethod)
280
+ );
281
+ return matchingRoute?.config;
282
+ }
283
+ /**
284
+ * Extract payment from HTTP headers (handles v1 and v2)
285
+ *
286
+ * @param adapter - HTTP adapter
287
+ * @returns Decoded payment payload or null
288
+ */
289
+ extractPayment(adapter) {
290
+ const header = adapter.getHeader("payment-signature") || adapter.getHeader("PAYMENT-SIGNATURE");
291
+ if (header) {
292
+ try {
293
+ return decodePaymentSignatureHeader(header);
294
+ } catch (error) {
295
+ console.warn("Failed to decode PAYMENT-SIGNATURE header:", error);
296
+ }
297
+ }
298
+ return null;
299
+ }
300
+ /**
301
+ * Check if request is from a web browser
302
+ *
303
+ * @param adapter - HTTP adapter
304
+ * @returns True if request appears to be from a browser
305
+ */
306
+ isWebBrowser(adapter) {
307
+ const accept = adapter.getAcceptHeader();
308
+ const userAgent = adapter.getUserAgent();
309
+ return accept.includes("text/html") && userAgent.includes("Mozilla");
310
+ }
311
+ /**
312
+ * Create HTTP response instructions from payment required
313
+ *
314
+ * @param paymentRequired - Payment requirements
315
+ * @param isWebBrowser - Whether request is from browser
316
+ * @param paywallConfig - Paywall configuration
317
+ * @param customHtml - Custom HTML template
318
+ * @param unpaidResponse - Optional custom response (content type and body) for unpaid API requests
319
+ * @returns Response instructions
320
+ */
321
+ createHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) {
322
+ if (isWebBrowser) {
323
+ const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml);
324
+ return {
325
+ status: 402,
326
+ headers: { "Content-Type": "text/html" },
327
+ body: html,
328
+ isHtml: true
329
+ };
330
+ }
331
+ const response = this.createHTTPPaymentRequiredResponse(paymentRequired);
332
+ const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json";
333
+ const body = unpaidResponse ? unpaidResponse.body : {};
334
+ return {
335
+ status: 402,
336
+ headers: {
337
+ "Content-Type": contentType,
338
+ ...response.headers
339
+ },
340
+ body
341
+ };
342
+ }
343
+ /**
344
+ * Create HTTP payment required response (v1 puts in body, v2 puts in header)
345
+ *
346
+ * @param paymentRequired - Payment required object
347
+ * @returns Headers and body for the HTTP response
348
+ */
349
+ createHTTPPaymentRequiredResponse(paymentRequired) {
350
+ return {
351
+ headers: {
352
+ "PAYMENT-REQUIRED": encodePaymentRequiredHeader(paymentRequired)
353
+ }
354
+ };
355
+ }
356
+ /**
357
+ * Create settlement response headers
358
+ *
359
+ * @param settleResponse - Settlement response
360
+ * @param requirements - Payment requirements that were settled
361
+ * @returns Headers to add to response
362
+ */
363
+ createSettlementHeaders(settleResponse, requirements) {
364
+ const encoded = encodePaymentResponseHeader({
365
+ ...settleResponse,
366
+ requirements
367
+ });
368
+ return { "PAYMENT-RESPONSE": encoded };
369
+ }
370
+ /**
371
+ * Parse route pattern into verb and regex
372
+ *
373
+ * @param pattern - Route pattern like "GET /api/*" or "/api/[id]"
374
+ * @returns Parsed pattern with verb and regex
375
+ */
376
+ parseRoutePattern(pattern) {
377
+ const [verb, path] = pattern.includes(" ") ? pattern.split(/\s+/) : ["*", pattern];
378
+ const regex = new RegExp(
379
+ `^${path.replace(/[$()+.?^{|}]/g, "\\$&").replace(/\*/g, ".*?").replace(/\[([^\]]+)\]/g, "[^/]+").replace(/\//g, "\\/")}$`,
380
+ "i"
381
+ );
382
+ return { verb: verb.toUpperCase(), regex };
383
+ }
384
+ /**
385
+ * Normalize path for matching
386
+ *
387
+ * @param path - Raw path from request
388
+ * @returns Normalized path
389
+ */
390
+ normalizePath(path) {
391
+ try {
392
+ const pathWithoutQuery = path.split(/[?#]/)[0];
393
+ const decodedPath = decodeURIComponent(pathWithoutQuery);
394
+ return decodedPath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/(.+?)\/+$/, "$1");
395
+ } catch {
396
+ return path;
397
+ }
398
+ }
399
+ /**
400
+ * Generate paywall HTML for browser requests
401
+ *
402
+ * @param paymentRequired - Payment required response
403
+ * @param paywallConfig - Optional paywall configuration
404
+ * @param customHtml - Optional custom HTML template
405
+ * @returns HTML string
406
+ */
407
+ generatePaywallHTML(paymentRequired, paywallConfig, customHtml) {
408
+ if (customHtml) {
409
+ return customHtml;
410
+ }
411
+ if (this.paywallProvider) {
412
+ return this.paywallProvider.generateHtml(paymentRequired, paywallConfig);
413
+ }
414
+ try {
415
+ const paywall = __require("@t402/paywall");
416
+ const displayAmount2 = this.getDisplayAmount(paymentRequired);
417
+ const resource2 = paymentRequired.resource;
418
+ return paywall.getPaywallHtml({
419
+ amount: displayAmount2,
420
+ paymentRequired,
421
+ currentUrl: resource2?.url || paywallConfig?.currentUrl || "",
422
+ testnet: paywallConfig?.testnet ?? true,
423
+ appName: paywallConfig?.appName,
424
+ appLogo: paywallConfig?.appLogo,
425
+ sessionTokenEndpoint: paywallConfig?.sessionTokenEndpoint
426
+ });
427
+ } catch {
428
+ }
429
+ const resource = paymentRequired.resource;
430
+ const displayAmount = this.getDisplayAmount(paymentRequired);
431
+ return `
432
+ <!DOCTYPE html>
433
+ <html>
434
+ <head>
435
+ <title>Payment Required</title>
436
+ <meta charset="UTF-8">
437
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
438
+ </head>
439
+ <body>
440
+ <div style="max-width: 600px; margin: 50px auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
441
+ ${paywallConfig?.appLogo ? `<img src="${paywallConfig.appLogo}" alt="${paywallConfig.appName || "App"}" style="max-width: 200px; margin-bottom: 20px;">` : ""}
442
+ <h1>Payment Required</h1>
443
+ ${resource ? `<p><strong>Resource:</strong> ${resource.description || resource.url}</p>` : ""}
444
+ <p><strong>Amount:</strong> $${displayAmount.toFixed(2)} USDC</p>
445
+ <div id="payment-widget"
446
+ data-requirements='${JSON.stringify(paymentRequired)}'
447
+ data-app-name="${paywallConfig?.appName || ""}"
448
+ data-testnet="${paywallConfig?.testnet || false}">
449
+ <!-- Install @t402/paywall for full wallet integration -->
450
+ <p style="margin-top: 2rem; padding: 1rem; background: #fef3c7; border-radius: 0.5rem;">
451
+ <strong>Note:</strong> Install <code>@t402/paywall</code> for full wallet connection and payment UI.
452
+ </p>
453
+ </div>
454
+ </div>
455
+ </body>
456
+ </html>
457
+ `;
458
+ }
459
+ /**
460
+ * Extract display amount from payment requirements.
461
+ *
462
+ * @param paymentRequired - The payment required object
463
+ * @returns The display amount in decimal format
464
+ */
465
+ getDisplayAmount(paymentRequired) {
466
+ const accepts = paymentRequired.accepts;
467
+ if (accepts && accepts.length > 0) {
468
+ const firstReq = accepts[0];
469
+ if ("amount" in firstReq) {
470
+ return parseFloat(firstReq.amount) / 1e6;
471
+ }
472
+ }
473
+ return 0;
474
+ }
475
+ };
476
+
477
+ // src/http/httpFacilitatorClient.ts
478
+ var DEFAULT_FACILITATOR_URL = "https://t402.org/facilitator";
479
+ var HTTPFacilitatorClient = class {
480
+ /**
481
+ * Creates a new HTTPFacilitatorClient instance.
482
+ *
483
+ * @param config - Configuration options for the facilitator client
484
+ */
485
+ constructor(config) {
486
+ this.url = config?.url || DEFAULT_FACILITATOR_URL;
487
+ this._createAuthHeaders = config?.createAuthHeaders;
488
+ }
489
+ /**
490
+ * Verify a payment with the facilitator
491
+ *
492
+ * @param paymentPayload - The payment to verify
493
+ * @param paymentRequirements - The requirements to verify against
494
+ * @returns Verification response
495
+ */
496
+ async verify(paymentPayload, paymentRequirements) {
497
+ let headers = {
498
+ "Content-Type": "application/json"
499
+ };
500
+ if (this._createAuthHeaders) {
501
+ const authHeaders = await this.createAuthHeaders("verify");
502
+ headers = { ...headers, ...authHeaders.headers };
503
+ }
504
+ const response = await fetch(`${this.url}/verify`, {
505
+ method: "POST",
506
+ headers,
507
+ body: JSON.stringify({
508
+ t402Version: paymentPayload.t402Version,
509
+ paymentPayload: this.toJsonSafe(paymentPayload),
510
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
511
+ })
512
+ });
513
+ if (!response.ok) {
514
+ const errorText = await response.text().catch(() => response.statusText);
515
+ throw new Error(`Facilitator verify failed (${response.status}): ${errorText}`);
516
+ }
517
+ return await response.json();
518
+ }
519
+ /**
520
+ * Settle a payment with the facilitator
521
+ *
522
+ * @param paymentPayload - The payment to settle
523
+ * @param paymentRequirements - The requirements for settlement
524
+ * @returns Settlement response
525
+ */
526
+ async settle(paymentPayload, paymentRequirements) {
527
+ let headers = {
528
+ "Content-Type": "application/json"
529
+ };
530
+ if (this._createAuthHeaders) {
531
+ const authHeaders = await this.createAuthHeaders("settle");
532
+ headers = { ...headers, ...authHeaders.headers };
533
+ }
534
+ const response = await fetch(`${this.url}/settle`, {
535
+ method: "POST",
536
+ headers,
537
+ body: JSON.stringify({
538
+ t402Version: paymentPayload.t402Version,
539
+ paymentPayload: this.toJsonSafe(paymentPayload),
540
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
541
+ })
542
+ });
543
+ if (!response.ok) {
544
+ const errorText = await response.text().catch(() => response.statusText);
545
+ throw new Error(`Facilitator settle failed (${response.status}): ${errorText}`);
546
+ }
547
+ return await response.json();
548
+ }
549
+ /**
550
+ * Get supported payment kinds and extensions from the facilitator
551
+ *
552
+ * @returns Supported payment kinds and extensions
553
+ */
554
+ async getSupported() {
555
+ let headers = {
556
+ "Content-Type": "application/json"
557
+ };
558
+ if (this._createAuthHeaders) {
559
+ const authHeaders = await this.createAuthHeaders("supported");
560
+ headers = { ...headers, ...authHeaders.headers };
561
+ }
562
+ const response = await fetch(`${this.url}/supported`, {
563
+ method: "GET",
564
+ headers
565
+ });
566
+ if (!response.ok) {
567
+ const errorText = await response.text().catch(() => response.statusText);
568
+ throw new Error(`Facilitator getSupported failed (${response.status}): ${errorText}`);
569
+ }
570
+ return await response.json();
571
+ }
572
+ /**
573
+ * Creates authentication headers for a specific path.
574
+ *
575
+ * @param path - The path to create authentication headers for (e.g., "verify", "settle", "supported")
576
+ * @returns An object containing the authentication headers for the specified path
577
+ */
578
+ async createAuthHeaders(path) {
579
+ if (this._createAuthHeaders) {
580
+ const authHeaders = await this._createAuthHeaders();
581
+ return {
582
+ headers: authHeaders[path] ?? {}
583
+ };
584
+ }
585
+ return {
586
+ headers: {}
587
+ };
588
+ }
589
+ /**
590
+ * Helper to convert objects to JSON-safe format.
591
+ * Handles BigInt and other non-JSON types.
592
+ *
593
+ * @param obj - The object to convert
594
+ * @returns The JSON-safe representation of the object
595
+ */
596
+ toJsonSafe(obj) {
597
+ return JSON.parse(
598
+ JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() : value)
599
+ );
600
+ }
601
+ };
602
+
603
+ // src/http/t402HTTPClient.ts
604
+ var t402HTTPClient = class {
605
+ /**
606
+ * Creates a new t402HTTPClient instance.
607
+ *
608
+ * @param client - The underlying t402Client for payment logic
609
+ */
610
+ constructor(client) {
611
+ this.client = client;
612
+ }
613
+ /**
614
+ * Encodes a payment payload into appropriate HTTP headers based on version.
615
+ *
616
+ * @param paymentPayload - The payment payload to encode
617
+ * @returns HTTP headers containing the encoded payment signature
618
+ */
619
+ encodePaymentSignatureHeader(paymentPayload) {
620
+ switch (paymentPayload.t402Version) {
621
+ case 2:
622
+ return {
623
+ "PAYMENT-SIGNATURE": encodePaymentSignatureHeader(paymentPayload)
624
+ };
625
+ case 1:
626
+ return {
627
+ "X-PAYMENT": encodePaymentSignatureHeader(paymentPayload)
628
+ };
629
+ default:
630
+ throw new Error(
631
+ `Unsupported t402 version: ${paymentPayload.t402Version}`
632
+ );
633
+ }
634
+ }
635
+ /**
636
+ * Extracts payment required information from HTTP response.
637
+ *
638
+ * @param getHeader - Function to retrieve header value by name (case-insensitive)
639
+ * @param body - Optional response body for v1 compatibility
640
+ * @returns The payment required object
641
+ */
642
+ getPaymentRequiredResponse(getHeader, body) {
643
+ const paymentRequired = getHeader("PAYMENT-REQUIRED");
644
+ if (paymentRequired) {
645
+ return decodePaymentRequiredHeader(paymentRequired);
646
+ }
647
+ if (body && body instanceof Object && "t402Version" in body && body.t402Version === 1) {
648
+ return body;
649
+ }
650
+ throw new Error("Invalid payment required response");
651
+ }
652
+ /**
653
+ * Extracts payment settlement response from HTTP headers.
654
+ *
655
+ * @param getHeader - Function to retrieve header value by name (case-insensitive)
656
+ * @returns The settlement response object
657
+ */
658
+ getPaymentSettleResponse(getHeader) {
659
+ const paymentResponse = getHeader("PAYMENT-RESPONSE");
660
+ if (paymentResponse) {
661
+ return decodePaymentResponseHeader(paymentResponse);
662
+ }
663
+ const xPaymentResponse = getHeader("X-PAYMENT-RESPONSE");
664
+ if (xPaymentResponse) {
665
+ return decodePaymentResponseHeader(xPaymentResponse);
666
+ }
667
+ throw new Error("Payment response header not found");
668
+ }
669
+ /**
670
+ * Creates a payment payload for the given payment requirements.
671
+ * Delegates to the underlying t402Client.
672
+ *
673
+ * @param paymentRequired - The payment required response from the server
674
+ * @returns Promise resolving to the payment payload
675
+ */
676
+ async createPaymentPayload(paymentRequired) {
677
+ return this.client.createPaymentPayload(paymentRequired);
678
+ }
679
+ };
680
+
681
+ // src/http/index.ts
682
+ function encodePaymentSignatureHeader(paymentPayload) {
683
+ return safeBase64Encode(JSON.stringify(paymentPayload));
684
+ }
685
+ function decodePaymentSignatureHeader(paymentSignatureHeader) {
686
+ if (!Base64EncodedRegex.test(paymentSignatureHeader)) {
687
+ throw new Error("Invalid payment signature header");
688
+ }
689
+ return JSON.parse(safeBase64Decode(paymentSignatureHeader));
690
+ }
691
+ function encodePaymentRequiredHeader(paymentRequired) {
692
+ return safeBase64Encode(JSON.stringify(paymentRequired));
693
+ }
694
+ function decodePaymentRequiredHeader(paymentRequiredHeader) {
695
+ if (!Base64EncodedRegex.test(paymentRequiredHeader)) {
696
+ throw new Error("Invalid payment required header");
697
+ }
698
+ return JSON.parse(safeBase64Decode(paymentRequiredHeader));
699
+ }
700
+ function encodePaymentResponseHeader(paymentResponse) {
701
+ return safeBase64Encode(JSON.stringify(paymentResponse));
702
+ }
703
+ function decodePaymentResponseHeader(paymentResponseHeader) {
704
+ if (!Base64EncodedRegex.test(paymentResponseHeader)) {
705
+ throw new Error("Invalid payment response header");
706
+ }
707
+ return JSON.parse(safeBase64Decode(paymentResponseHeader));
708
+ }
709
+
710
+ export {
711
+ RouteConfigurationError,
712
+ t402HTTPResourceServer,
713
+ HTTPFacilitatorClient,
714
+ encodePaymentSignatureHeader,
715
+ decodePaymentSignatureHeader,
716
+ encodePaymentRequiredHeader,
717
+ decodePaymentRequiredHeader,
718
+ encodePaymentResponseHeader,
719
+ decodePaymentResponseHeader,
720
+ t402HTTPClient
721
+ };
722
+ //# sourceMappingURL=chunk-D5DYKKCZ.mjs.map