@bankofai/x402-core 2.6.0-beta.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 (74) hide show
  1. package/README.md +293 -0
  2. package/dist/cjs/assetRegistry-CL0zA4s0.d.ts +831 -0
  3. package/dist/cjs/assetRegistry-CRVM0KEs.d.ts +831 -0
  4. package/dist/cjs/client/index.d.ts +329 -0
  5. package/dist/cjs/client/index.js +712 -0
  6. package/dist/cjs/client/index.js.map +1 -0
  7. package/dist/cjs/facilitator/index.d.ts +206 -0
  8. package/dist/cjs/facilitator/index.js +625 -0
  9. package/dist/cjs/facilitator/index.js.map +1 -0
  10. package/dist/cjs/http/index.d.ts +51 -0
  11. package/dist/cjs/http/index.js +1178 -0
  12. package/dist/cjs/http/index.js.map +1 -0
  13. package/dist/cjs/index.d.ts +13 -0
  14. package/dist/cjs/index.js +250 -0
  15. package/dist/cjs/index.js.map +1 -0
  16. package/dist/cjs/mechanisms-q7I6xfUE.d.ts +726 -0
  17. package/dist/cjs/schemas/index.d.ts +825 -0
  18. package/dist/cjs/schemas/index.js +212 -0
  19. package/dist/cjs/schemas/index.js.map +1 -0
  20. package/dist/cjs/server/index.d.ts +2 -0
  21. package/dist/cjs/server/index.js +1782 -0
  22. package/dist/cjs/server/index.js.map +1 -0
  23. package/dist/cjs/types/index.d.ts +1 -0
  24. package/dist/cjs/types/index.js +72 -0
  25. package/dist/cjs/types/index.js.map +1 -0
  26. package/dist/cjs/types/v1/index.d.ts +1 -0
  27. package/dist/cjs/types/v1/index.js +19 -0
  28. package/dist/cjs/types/v1/index.js.map +1 -0
  29. package/dist/cjs/utils/index.d.ts +48 -0
  30. package/dist/cjs/utils/index.js +116 -0
  31. package/dist/cjs/utils/index.js.map +1 -0
  32. package/dist/cjs/x402HTTPResourceServer-BFVo1_74.d.ts +433 -0
  33. package/dist/cjs/x402HTTPResourceServer-DaU2yFzy.d.ts +434 -0
  34. package/dist/cjs/x402HTTPResourceServer-DswI2hZQ.d.ts +434 -0
  35. package/dist/esm/assetRegistry-CRVM0KEs.d.mts +831 -0
  36. package/dist/esm/chunk-BJTO5JO5.mjs +11 -0
  37. package/dist/esm/chunk-BJTO5JO5.mjs.map +1 -0
  38. package/dist/esm/chunk-DACUCTGT.mjs +891 -0
  39. package/dist/esm/chunk-DACUCTGT.mjs.map +1 -0
  40. package/dist/esm/chunk-DFUINDLZ.mjs +221 -0
  41. package/dist/esm/chunk-DFUINDLZ.mjs.map +1 -0
  42. package/dist/esm/chunk-HRQUGJ3Y.mjs +45 -0
  43. package/dist/esm/chunk-HRQUGJ3Y.mjs.map +1 -0
  44. package/dist/esm/chunk-TDLQZ6MP.mjs +86 -0
  45. package/dist/esm/chunk-TDLQZ6MP.mjs.map +1 -0
  46. package/dist/esm/client/index.d.mts +329 -0
  47. package/dist/esm/client/index.mjs +330 -0
  48. package/dist/esm/client/index.mjs.map +1 -0
  49. package/dist/esm/facilitator/index.d.mts +206 -0
  50. package/dist/esm/facilitator/index.mjs +398 -0
  51. package/dist/esm/facilitator/index.mjs.map +1 -0
  52. package/dist/esm/http/index.d.mts +51 -0
  53. package/dist/esm/http/index.mjs +29 -0
  54. package/dist/esm/http/index.mjs.map +1 -0
  55. package/dist/esm/index.d.mts +13 -0
  56. package/dist/esm/index.mjs +14 -0
  57. package/dist/esm/index.mjs.map +1 -0
  58. package/dist/esm/schemas/index.d.mts +825 -0
  59. package/dist/esm/schemas/index.mjs +158 -0
  60. package/dist/esm/schemas/index.mjs.map +1 -0
  61. package/dist/esm/server/index.d.mts +2 -0
  62. package/dist/esm/server/index.mjs +712 -0
  63. package/dist/esm/server/index.mjs.map +1 -0
  64. package/dist/esm/types/index.d.mts +1 -0
  65. package/dist/esm/types/index.mjs +10 -0
  66. package/dist/esm/types/index.mjs.map +1 -0
  67. package/dist/esm/types/v1/index.d.mts +1 -0
  68. package/dist/esm/types/v1/index.mjs +1 -0
  69. package/dist/esm/types/v1/index.mjs.map +1 -0
  70. package/dist/esm/utils/index.d.mts +48 -0
  71. package/dist/esm/utils/index.mjs +20 -0
  72. package/dist/esm/utils/index.mjs.map +1 -0
  73. package/dist/esm/x402HTTPResourceServer-B6uf_UDm.d.mts +434 -0
  74. package/package.json +139 -0
@@ -0,0 +1,1782 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server/index.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ HTTPFacilitatorClient: () => HTTPFacilitatorClient,
24
+ RouteConfigurationError: () => RouteConfigurationError,
25
+ x402HTTPResourceServer: () => x402HTTPResourceServer,
26
+ x402ResourceServer: () => x402ResourceServer
27
+ });
28
+ module.exports = __toCommonJS(server_exports);
29
+
30
+ // src/types/facilitator.ts
31
+ var VerifyError = class extends Error {
32
+ /**
33
+ * Creates a VerifyError from a failed verification response.
34
+ *
35
+ * @param statusCode - HTTP status code from the facilitator
36
+ * @param response - The verify response containing error details
37
+ */
38
+ constructor(statusCode, response) {
39
+ const reason = response.invalidReason || "unknown reason";
40
+ const message = response.invalidMessage;
41
+ super(message ? `${reason}: ${message}` : reason);
42
+ this.name = "VerifyError";
43
+ this.statusCode = statusCode;
44
+ this.invalidReason = response.invalidReason;
45
+ this.invalidMessage = response.invalidMessage;
46
+ this.payer = response.payer;
47
+ }
48
+ };
49
+ var SettleError = class extends Error {
50
+ /**
51
+ * Creates a SettleError from a failed settlement response.
52
+ *
53
+ * @param statusCode - HTTP status code from the facilitator
54
+ * @param response - The settle response containing error details
55
+ */
56
+ constructor(statusCode, response) {
57
+ const reason = response.errorReason || "unknown reason";
58
+ const message = response.errorMessage;
59
+ super(message ? `${reason}: ${message}` : reason);
60
+ this.name = "SettleError";
61
+ this.statusCode = statusCode;
62
+ this.errorReason = response.errorReason;
63
+ this.errorMessage = response.errorMessage;
64
+ this.payer = response.payer;
65
+ this.transaction = response.transaction;
66
+ this.network = response.network;
67
+ }
68
+ };
69
+
70
+ // src/utils/index.ts
71
+ var findSchemesByNetwork = (map, network) => {
72
+ let implementationsByScheme = map.get(network);
73
+ if (!implementationsByScheme) {
74
+ for (const [registeredNetworkPattern, implementations] of map.entries()) {
75
+ const pattern = registeredNetworkPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
76
+ const regex = new RegExp(`^${pattern}$`);
77
+ if (regex.test(network)) {
78
+ implementationsByScheme = implementations;
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ return implementationsByScheme;
84
+ };
85
+ var findByNetworkAndScheme = (map, scheme, network) => {
86
+ return findSchemesByNetwork(map, network)?.get(scheme);
87
+ };
88
+ var Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;
89
+ function safeBase64Encode(data) {
90
+ if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") {
91
+ const bytes = new TextEncoder().encode(data);
92
+ const binaryString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
93
+ return globalThis.btoa(binaryString);
94
+ }
95
+ return Buffer.from(data, "utf8").toString("base64");
96
+ }
97
+ function safeBase64Decode(data) {
98
+ if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") {
99
+ const binaryString = globalThis.atob(data);
100
+ const bytes = new Uint8Array(binaryString.length);
101
+ for (let i = 0; i < binaryString.length; i++) {
102
+ bytes[i] = binaryString.charCodeAt(i);
103
+ }
104
+ const decoder = new TextDecoder("utf-8");
105
+ return decoder.decode(bytes);
106
+ }
107
+ return Buffer.from(data, "base64").toString("utf-8");
108
+ }
109
+ function deepEqual(obj1, obj2) {
110
+ const normalize = (obj) => {
111
+ if (obj === null || obj === void 0) return JSON.stringify(obj);
112
+ if (typeof obj !== "object") return JSON.stringify(obj);
113
+ if (Array.isArray(obj)) {
114
+ return JSON.stringify(
115
+ obj.map(
116
+ (item) => typeof item === "object" && item !== null ? JSON.parse(normalize(item)) : item
117
+ )
118
+ );
119
+ }
120
+ const sorted = {};
121
+ Object.keys(obj).sort().forEach((key) => {
122
+ const value = obj[key];
123
+ sorted[key] = typeof value === "object" && value !== null ? JSON.parse(normalize(value)) : value;
124
+ });
125
+ return JSON.stringify(sorted);
126
+ };
127
+ try {
128
+ return normalize(obj1) === normalize(obj2);
129
+ } catch {
130
+ return JSON.stringify(obj1) === JSON.stringify(obj2);
131
+ }
132
+ }
133
+
134
+ // src/http/httpFacilitatorClient.ts
135
+ var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
136
+ var GET_SUPPORTED_RETRIES = 3;
137
+ var GET_SUPPORTED_RETRY_DELAY_MS = 1e3;
138
+ var HTTPFacilitatorClient = class {
139
+ /**
140
+ * Creates a new HTTPFacilitatorClient instance.
141
+ *
142
+ * @param config - Configuration options for the facilitator client
143
+ */
144
+ constructor(config) {
145
+ this.url = config?.url || DEFAULT_FACILITATOR_URL;
146
+ this._createAuthHeaders = config?.createAuthHeaders;
147
+ }
148
+ /**
149
+ * Verify a payment with the facilitator
150
+ *
151
+ * @param paymentPayload - The payment to verify
152
+ * @param paymentRequirements - The requirements to verify against
153
+ * @returns Verification response
154
+ */
155
+ async verify(paymentPayload, paymentRequirements) {
156
+ let headers = {
157
+ "Content-Type": "application/json"
158
+ };
159
+ if (this._createAuthHeaders) {
160
+ const authHeaders = await this.createAuthHeaders("verify");
161
+ headers = { ...headers, ...authHeaders.headers };
162
+ }
163
+ const response = await fetch(`${this.url}/verify`, {
164
+ method: "POST",
165
+ headers,
166
+ body: JSON.stringify({
167
+ x402Version: paymentPayload.x402Version,
168
+ paymentPayload: this.toJsonSafe(paymentPayload),
169
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
170
+ })
171
+ });
172
+ const data = await response.json();
173
+ if (typeof data === "object" && data !== null && "isValid" in data) {
174
+ const verifyResponse = data;
175
+ if (!response.ok) {
176
+ throw new VerifyError(response.status, verifyResponse);
177
+ }
178
+ return verifyResponse;
179
+ }
180
+ throw new Error(`Facilitator verify failed (${response.status}): ${JSON.stringify(data)}`);
181
+ }
182
+ /**
183
+ * Settle a payment with the facilitator
184
+ *
185
+ * @param paymentPayload - The payment to settle
186
+ * @param paymentRequirements - The requirements for settlement
187
+ * @returns Settlement response
188
+ */
189
+ async settle(paymentPayload, paymentRequirements) {
190
+ let headers = {
191
+ "Content-Type": "application/json"
192
+ };
193
+ if (this._createAuthHeaders) {
194
+ const authHeaders = await this.createAuthHeaders("settle");
195
+ headers = { ...headers, ...authHeaders.headers };
196
+ }
197
+ const response = await fetch(`${this.url}/settle`, {
198
+ method: "POST",
199
+ headers,
200
+ body: JSON.stringify({
201
+ x402Version: paymentPayload.x402Version,
202
+ paymentPayload: this.toJsonSafe(paymentPayload),
203
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
204
+ })
205
+ });
206
+ const data = await response.json();
207
+ if (typeof data === "object" && data !== null && "success" in data) {
208
+ const settleResponse = data;
209
+ if (!response.ok) {
210
+ throw new SettleError(response.status, settleResponse);
211
+ }
212
+ return settleResponse;
213
+ }
214
+ throw new Error(`Facilitator settle failed (${response.status}): ${JSON.stringify(data)}`);
215
+ }
216
+ /**
217
+ * Get supported payment kinds and extensions from the facilitator.
218
+ * Retries with exponential backoff on 429 rate limit errors.
219
+ *
220
+ * @returns Supported payment kinds and extensions
221
+ */
222
+ async getSupported() {
223
+ let headers = {
224
+ "Content-Type": "application/json"
225
+ };
226
+ if (this._createAuthHeaders) {
227
+ const authHeaders = await this.createAuthHeaders("supported");
228
+ headers = { ...headers, ...authHeaders.headers };
229
+ }
230
+ let lastError = null;
231
+ for (let attempt = 0; attempt < GET_SUPPORTED_RETRIES; attempt++) {
232
+ const response = await fetch(`${this.url}/supported`, {
233
+ method: "GET",
234
+ headers
235
+ });
236
+ if (response.ok) {
237
+ return await response.json();
238
+ }
239
+ const errorText = await response.text().catch(() => response.statusText);
240
+ lastError = new Error(`Facilitator getSupported failed (${response.status}): ${errorText}`);
241
+ if (response.status === 429 && attempt < GET_SUPPORTED_RETRIES - 1) {
242
+ const delay = GET_SUPPORTED_RETRY_DELAY_MS * Math.pow(2, attempt);
243
+ await new Promise((resolve) => setTimeout(resolve, delay));
244
+ continue;
245
+ }
246
+ throw lastError;
247
+ }
248
+ throw lastError ?? new Error("Facilitator getSupported failed after retries");
249
+ }
250
+ /**
251
+ * Creates authentication headers for a specific path.
252
+ *
253
+ * @param path - The path to create authentication headers for (e.g., "verify", "settle", "supported")
254
+ * @returns An object containing the authentication headers for the specified path
255
+ */
256
+ async createAuthHeaders(path) {
257
+ if (this._createAuthHeaders) {
258
+ const authHeaders = await this._createAuthHeaders();
259
+ return {
260
+ headers: authHeaders[path] ?? {}
261
+ };
262
+ }
263
+ return {
264
+ headers: {}
265
+ };
266
+ }
267
+ /**
268
+ * Helper to convert objects to JSON-safe format.
269
+ * Handles BigInt and other non-JSON types.
270
+ *
271
+ * @param obj - The object to convert
272
+ * @returns The JSON-safe representation of the object
273
+ */
274
+ toJsonSafe(obj) {
275
+ return JSON.parse(
276
+ JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() : value)
277
+ );
278
+ }
279
+ };
280
+
281
+ // src/registry/assetRegistry.ts
282
+ var AssetRegistry = class {
283
+ /** Creates a new AssetRegistry pre-populated with built-in token data. */
284
+ constructor() {
285
+ this.assets = /* @__PURE__ */ new Map();
286
+ this.defaults = /* @__PURE__ */ new Map();
287
+ this.registerBuiltins();
288
+ }
289
+ /**
290
+ * Register a single asset for a network.
291
+ *
292
+ * @param network - The network identifier (e.g. "eip155:1")
293
+ * @param symbol - The token symbol (e.g. "USDT")
294
+ * @param info - Asset metadata
295
+ */
296
+ register(network, symbol, info) {
297
+ if (!this.assets.has(network)) {
298
+ this.assets.set(network, /* @__PURE__ */ new Map());
299
+ }
300
+ this.assets.get(network).set(symbol, info);
301
+ }
302
+ /**
303
+ * Batch-register multiple assets for a network.
304
+ *
305
+ * @param network - The network identifier
306
+ * @param assets - Map of symbol to AssetInfo
307
+ */
308
+ registerAll(network, assets) {
309
+ for (const [symbol, info] of Object.entries(assets)) {
310
+ this.register(network, symbol, info);
311
+ }
312
+ }
313
+ /**
314
+ * Set the default asset symbol for a network.
315
+ *
316
+ * @param network - The network identifier
317
+ * @param symbol - The symbol to set as default
318
+ */
319
+ setDefault(network, symbol) {
320
+ if (!this.has(network, symbol)) {
321
+ throw new Error(
322
+ `Cannot set default: asset "${symbol}" is not registered on network "${network}"`
323
+ );
324
+ }
325
+ this.defaults.set(network, symbol);
326
+ }
327
+ /**
328
+ * Resolve a symbol to its AssetInfo on a network.
329
+ *
330
+ * @param network - The network identifier
331
+ * @param symbol - The token symbol
332
+ * @returns The resolved AssetInfo
333
+ * @throws If the asset is not registered
334
+ */
335
+ resolve(network, symbol) {
336
+ const networkAssets = this.assets.get(network);
337
+ if (!networkAssets || !networkAssets.has(symbol)) {
338
+ throw new Error(
339
+ `Asset "${symbol}" is not registered on network "${network}". Available: ${networkAssets ? Array.from(networkAssets.keys()).join(", ") : "none"}`
340
+ );
341
+ }
342
+ return networkAssets.get(symbol);
343
+ }
344
+ /**
345
+ * Get the default asset for a network.
346
+ *
347
+ * @param network - The network identifier
348
+ * @returns The default symbol and its AssetInfo
349
+ * @throws If no default is configured
350
+ */
351
+ getDefault(network) {
352
+ const symbol = this.defaults.get(network);
353
+ if (!symbol) {
354
+ throw new Error(`No default asset configured for network "${network}"`);
355
+ }
356
+ return { symbol, info: this.resolve(network, symbol) };
357
+ }
358
+ /**
359
+ * List all registered symbols for a network.
360
+ *
361
+ * @param network - The network identifier
362
+ * @returns Array of registered symbols
363
+ */
364
+ getSymbols(network) {
365
+ const networkAssets = this.assets.get(network);
366
+ return networkAssets ? Array.from(networkAssets.keys()) : [];
367
+ }
368
+ /**
369
+ * Check if an asset is registered on a network.
370
+ *
371
+ * @param network - The network identifier
372
+ * @param symbol - The token symbol
373
+ * @returns True if the asset is registered
374
+ */
375
+ has(network, symbol) {
376
+ return this.assets.get(network)?.has(symbol) ?? false;
377
+ }
378
+ /**
379
+ * Register built-in known assets.
380
+ * Data sourced from mechanism defaults and built-in registry.
381
+ */
382
+ registerBuiltins() {
383
+ this.registerAll("eip155:1", {
384
+ USDC: {
385
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
386
+ decimals: 6,
387
+ name: "USD Coin",
388
+ version: "2"
389
+ },
390
+ USDT: {
391
+ address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
392
+ decimals: 6,
393
+ name: "Tether USD",
394
+ version: "1",
395
+ assetTransferMethod: "permit2"
396
+ }
397
+ });
398
+ this.defaults.set("eip155:1", "USDC");
399
+ this.registerAll("eip155:56", {
400
+ USDC: {
401
+ address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
402
+ decimals: 18,
403
+ name: "USD Coin",
404
+ version: "1",
405
+ assetTransferMethod: "permit2"
406
+ },
407
+ USDT: {
408
+ address: "0x55d398326f99059fF775485246999027B3197955",
409
+ decimals: 18,
410
+ name: "Tether USD",
411
+ version: "1",
412
+ assetTransferMethod: "permit2"
413
+ }
414
+ });
415
+ this.defaults.set("eip155:56", "USDC");
416
+ this.registerAll("eip155:97", {
417
+ USDT: {
418
+ address: "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd",
419
+ decimals: 18,
420
+ name: "Tether USD",
421
+ version: "1",
422
+ assetTransferMethod: "permit2"
423
+ },
424
+ USDC: {
425
+ address: "0x64544969ed7EBf5f083679233325356EbE738930",
426
+ decimals: 18,
427
+ name: "USD Coin",
428
+ version: "1",
429
+ assetTransferMethod: "permit2"
430
+ }
431
+ });
432
+ this.defaults.set("eip155:97", "USDT");
433
+ this.registerAll("tron:mainnet", {
434
+ USDT: {
435
+ address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
436
+ decimals: 6,
437
+ name: "Tether USD",
438
+ version: "1",
439
+ assetTransferMethod: "permit2"
440
+ },
441
+ USDD: {
442
+ address: "TXDk8mbtRbXeYuMNS83CfKPaYYT8XWv9Hz",
443
+ decimals: 18,
444
+ name: "Decentralized USD",
445
+ version: "1",
446
+ supportsEip2612: true,
447
+ assetTransferMethod: "permit2"
448
+ }
449
+ });
450
+ this.defaults.set("tron:mainnet", "USDT");
451
+ this.register("tron:shasta", "USDT", {
452
+ address: "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs",
453
+ decimals: 6,
454
+ name: "Tether USD",
455
+ version: "1",
456
+ assetTransferMethod: "permit2"
457
+ });
458
+ this.defaults.set("tron:shasta", "USDT");
459
+ this.registerAll("tron:nile", {
460
+ USDT: {
461
+ address: "TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf",
462
+ decimals: 6,
463
+ name: "Tether USD",
464
+ version: "1",
465
+ assetTransferMethod: "permit2"
466
+ },
467
+ USDD: {
468
+ address: "TGjgvdTWWrybVLaVeFqSyVqJQWjxqRYbaK",
469
+ decimals: 18,
470
+ name: "Decentralized USD",
471
+ version: "1",
472
+ supportsEip2612: true,
473
+ assetTransferMethod: "permit2"
474
+ }
475
+ });
476
+ this.defaults.set("tron:nile", "USDT");
477
+ }
478
+ };
479
+ function convertMoney(price, decimals) {
480
+ const numericAmount = typeof price === "string" ? parseFloat(price.replace(/^\$/, "").trim()) : price;
481
+ if (isNaN(numericAmount)) {
482
+ throw new Error(`Invalid money format: ${price}`);
483
+ }
484
+ const [intPart, decPart = ""] = String(numericAmount).split(".");
485
+ const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
486
+ return (intPart + paddedDec).replace(/^0+/, "") || "0";
487
+ }
488
+
489
+ // src/registry/index.ts
490
+ var globalAssetRegistry = new AssetRegistry();
491
+
492
+ // src/index.ts
493
+ var x402Version = 2;
494
+
495
+ // src/server/x402ResourceServer.ts
496
+ var x402ResourceServer = class {
497
+ /**
498
+ * Creates a new x402ResourceServer instance.
499
+ *
500
+ * @param facilitatorClients - Optional facilitator client(s) for payment processing
501
+ * @param assetRegistry - Optional asset registry for symbol-based asset resolution
502
+ */
503
+ constructor(facilitatorClients, assetRegistry) {
504
+ this.registeredServerSchemes = /* @__PURE__ */ new Map();
505
+ this.supportedResponsesMap = /* @__PURE__ */ new Map();
506
+ this.facilitatorClientsMap = /* @__PURE__ */ new Map();
507
+ this.registeredExtensions = /* @__PURE__ */ new Map();
508
+ this.beforeVerifyHooks = [];
509
+ this.afterVerifyHooks = [];
510
+ this.onVerifyFailureHooks = [];
511
+ this.beforeSettleHooks = [];
512
+ this.afterSettleHooks = [];
513
+ this.onSettleFailureHooks = [];
514
+ this.assetRegistry = assetRegistry ?? globalAssetRegistry;
515
+ if (!facilitatorClients) {
516
+ this.facilitatorClients = [new HTTPFacilitatorClient()];
517
+ } else if (Array.isArray(facilitatorClients)) {
518
+ this.facilitatorClients = facilitatorClients.length > 0 ? facilitatorClients : [new HTTPFacilitatorClient()];
519
+ } else {
520
+ this.facilitatorClients = [facilitatorClients];
521
+ }
522
+ }
523
+ /**
524
+ * Register a scheme/network server implementation.
525
+ *
526
+ * @param network - The network identifier
527
+ * @param server - The scheme/network server implementation
528
+ * @returns The x402ResourceServer instance for chaining
529
+ */
530
+ register(network, server) {
531
+ if (!this.registeredServerSchemes.has(network)) {
532
+ this.registeredServerSchemes.set(network, /* @__PURE__ */ new Map());
533
+ }
534
+ const serverByScheme = this.registeredServerSchemes.get(network);
535
+ if (!serverByScheme.has(server.scheme)) {
536
+ serverByScheme.set(server.scheme, server);
537
+ }
538
+ return this;
539
+ }
540
+ /**
541
+ * Check if a scheme is registered for a given network.
542
+ *
543
+ * @param network - The network identifier
544
+ * @param scheme - The payment scheme name
545
+ * @returns True if the scheme is registered for the network, false otherwise
546
+ */
547
+ hasRegisteredScheme(network, scheme) {
548
+ return !!findByNetworkAndScheme(this.registeredServerSchemes, scheme, network);
549
+ }
550
+ /**
551
+ * Registers a resource service extension that can enrich extension declarations.
552
+ *
553
+ * @param extension - The extension to register
554
+ * @returns The x402ResourceServer instance for chaining
555
+ */
556
+ registerExtension(extension) {
557
+ this.registeredExtensions.set(extension.key, extension);
558
+ return this;
559
+ }
560
+ /**
561
+ * Check if an extension is registered.
562
+ *
563
+ * @param key - The extension key
564
+ * @returns True if the extension is registered
565
+ */
566
+ hasExtension(key) {
567
+ return this.registeredExtensions.has(key);
568
+ }
569
+ /**
570
+ * Get all registered extensions.
571
+ *
572
+ * @returns Array of registered extensions
573
+ */
574
+ getExtensions() {
575
+ return Array.from(this.registeredExtensions.values());
576
+ }
577
+ /**
578
+ * Enriches declared extensions using registered extension hooks.
579
+ *
580
+ * @param declaredExtensions - Extensions declared on the route
581
+ * @param transportContext - Transport-specific context (HTTP, A2A, MCP, etc.)
582
+ * @returns Enriched extensions map
583
+ */
584
+ enrichExtensions(declaredExtensions, transportContext) {
585
+ const enriched = {};
586
+ for (const [key, declaration] of Object.entries(declaredExtensions)) {
587
+ const extension = this.registeredExtensions.get(key);
588
+ if (extension?.enrichDeclaration) {
589
+ enriched[key] = extension.enrichDeclaration(declaration, transportContext);
590
+ } else {
591
+ enriched[key] = declaration;
592
+ }
593
+ }
594
+ return enriched;
595
+ }
596
+ /**
597
+ * Register a hook to execute before payment verification.
598
+ * Can abort verification by returning { abort: true, reason: string }
599
+ *
600
+ * @param hook - The hook function to register
601
+ * @returns The x402ResourceServer instance for chaining
602
+ */
603
+ onBeforeVerify(hook) {
604
+ this.beforeVerifyHooks.push(hook);
605
+ return this;
606
+ }
607
+ /**
608
+ * Register a hook to execute after successful payment verification.
609
+ *
610
+ * @param hook - The hook function to register
611
+ * @returns The x402ResourceServer instance for chaining
612
+ */
613
+ onAfterVerify(hook) {
614
+ this.afterVerifyHooks.push(hook);
615
+ return this;
616
+ }
617
+ /**
618
+ * Register a hook to execute when payment verification fails.
619
+ * Can recover from failure by returning { recovered: true, result: VerifyResponse }
620
+ *
621
+ * @param hook - The hook function to register
622
+ * @returns The x402ResourceServer instance for chaining
623
+ */
624
+ onVerifyFailure(hook) {
625
+ this.onVerifyFailureHooks.push(hook);
626
+ return this;
627
+ }
628
+ /**
629
+ * Register a hook to execute before payment settlement.
630
+ * Can abort settlement by returning { abort: true, reason: string }
631
+ *
632
+ * @param hook - The hook function to register
633
+ * @returns The x402ResourceServer instance for chaining
634
+ */
635
+ onBeforeSettle(hook) {
636
+ this.beforeSettleHooks.push(hook);
637
+ return this;
638
+ }
639
+ /**
640
+ * Register a hook to execute after successful payment settlement.
641
+ *
642
+ * @param hook - The hook function to register
643
+ * @returns The x402ResourceServer instance for chaining
644
+ */
645
+ onAfterSettle(hook) {
646
+ this.afterSettleHooks.push(hook);
647
+ return this;
648
+ }
649
+ /**
650
+ * Register a hook to execute when payment settlement fails.
651
+ * Can recover from failure by returning { recovered: true, result: SettleResponse }
652
+ *
653
+ * @param hook - The hook function to register
654
+ * @returns The x402ResourceServer instance for chaining
655
+ */
656
+ onSettleFailure(hook) {
657
+ this.onSettleFailureHooks.push(hook);
658
+ return this;
659
+ }
660
+ /**
661
+ * Initialize by fetching supported kinds from all facilitators
662
+ * Creates mappings for supported responses and facilitator clients
663
+ * Earlier facilitators in the array get precedence
664
+ */
665
+ async initialize() {
666
+ this.supportedResponsesMap.clear();
667
+ this.facilitatorClientsMap.clear();
668
+ for (const facilitatorClient of this.facilitatorClients) {
669
+ try {
670
+ const supported = await facilitatorClient.getSupported();
671
+ for (const kind of supported.kinds) {
672
+ const x402Version2 = kind.x402Version;
673
+ if (!this.supportedResponsesMap.has(x402Version2)) {
674
+ this.supportedResponsesMap.set(x402Version2, /* @__PURE__ */ new Map());
675
+ }
676
+ const responseVersionMap = this.supportedResponsesMap.get(x402Version2);
677
+ if (!this.facilitatorClientsMap.has(x402Version2)) {
678
+ this.facilitatorClientsMap.set(x402Version2, /* @__PURE__ */ new Map());
679
+ }
680
+ const clientVersionMap = this.facilitatorClientsMap.get(x402Version2);
681
+ if (!responseVersionMap.has(kind.network)) {
682
+ responseVersionMap.set(kind.network, /* @__PURE__ */ new Map());
683
+ }
684
+ const responseNetworkMap = responseVersionMap.get(kind.network);
685
+ if (!clientVersionMap.has(kind.network)) {
686
+ clientVersionMap.set(kind.network, /* @__PURE__ */ new Map());
687
+ }
688
+ const clientNetworkMap = clientVersionMap.get(kind.network);
689
+ if (!responseNetworkMap.has(kind.scheme)) {
690
+ responseNetworkMap.set(kind.scheme, supported);
691
+ clientNetworkMap.set(kind.scheme, facilitatorClient);
692
+ }
693
+ }
694
+ } catch (error) {
695
+ console.warn(`Failed to fetch supported kinds from facilitator: ${error}`);
696
+ }
697
+ }
698
+ if (this.supportedResponsesMap.size === 0) {
699
+ throw new Error(
700
+ "Failed to initialize: no supported payment kinds loaded from any facilitator."
701
+ );
702
+ }
703
+ }
704
+ /**
705
+ * Get supported kind for a specific version, network, and scheme
706
+ *
707
+ * @param x402Version - The x402 version
708
+ * @param network - The network identifier
709
+ * @param scheme - The payment scheme
710
+ * @returns The supported kind or undefined if not found
711
+ */
712
+ getSupportedKind(x402Version2, network, scheme) {
713
+ const versionMap = this.supportedResponsesMap.get(x402Version2);
714
+ if (!versionMap) return void 0;
715
+ const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
716
+ if (!supportedResponse) return void 0;
717
+ return supportedResponse.kinds.find(
718
+ (kind) => kind.x402Version === x402Version2 && kind.network === network && kind.scheme === scheme
719
+ );
720
+ }
721
+ /**
722
+ * Get facilitator extensions for a specific version, network, and scheme
723
+ *
724
+ * @param x402Version - The x402 version
725
+ * @param network - The network identifier
726
+ * @param scheme - The payment scheme
727
+ * @returns The facilitator extensions or empty array if not found
728
+ */
729
+ getFacilitatorExtensions(x402Version2, network, scheme) {
730
+ const versionMap = this.supportedResponsesMap.get(x402Version2);
731
+ if (!versionMap) return [];
732
+ const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
733
+ return supportedResponse?.extensions || [];
734
+ }
735
+ /**
736
+ * Build payment requirements for a protected resource
737
+ *
738
+ * @param resourceConfig - Configuration for the protected resource
739
+ * @returns Array of payment requirements
740
+ */
741
+ async buildPaymentRequirements(resourceConfig) {
742
+ const requirements = [];
743
+ const scheme = resourceConfig.scheme;
744
+ const SchemeNetworkServer = findByNetworkAndScheme(
745
+ this.registeredServerSchemes,
746
+ scheme,
747
+ resourceConfig.network
748
+ );
749
+ if (!SchemeNetworkServer) {
750
+ console.warn(
751
+ `No server implementation registered for scheme: ${scheme}, network: ${resourceConfig.network}`
752
+ );
753
+ return requirements;
754
+ }
755
+ const supportedKind = this.getSupportedKind(
756
+ x402Version,
757
+ resourceConfig.network,
758
+ SchemeNetworkServer.scheme
759
+ );
760
+ if (!supportedKind) {
761
+ throw new Error(
762
+ `Facilitator does not support ${SchemeNetworkServer.scheme} on ${resourceConfig.network}. Make sure to call initialize() to fetch supported kinds from facilitators.`
763
+ );
764
+ }
765
+ const facilitatorExtensions = this.getFacilitatorExtensions(
766
+ x402Version,
767
+ resourceConfig.network,
768
+ SchemeNetworkServer.scheme
769
+ );
770
+ if (resourceConfig.assets?.length && (typeof resourceConfig.price === "string" || typeof resourceConfig.price === "number")) {
771
+ const results = [];
772
+ for (const symbol of resourceConfig.assets) {
773
+ const assetInfo = this.assetRegistry.resolve(resourceConfig.network, symbol);
774
+ const amount = convertMoney(resourceConfig.price, assetInfo.decimals);
775
+ const { address, decimals, name, version, assetTransferMethod, supportsEip2612, ...rest } = assetInfo;
776
+ const includeEip712Domain = !assetTransferMethod || supportsEip2612;
777
+ const extra = {
778
+ ...includeEip712Domain && name && { name },
779
+ ...includeEip712Domain && version && { version },
780
+ ...assetTransferMethod && { assetTransferMethod },
781
+ ...rest,
782
+ ...resourceConfig.extra
783
+ };
784
+ const baseReq = {
785
+ scheme: SchemeNetworkServer.scheme,
786
+ network: resourceConfig.network,
787
+ amount,
788
+ asset: address,
789
+ payTo: resourceConfig.payTo,
790
+ maxTimeoutSeconds: resourceConfig.maxTimeoutSeconds || 300,
791
+ extra
792
+ };
793
+ const enhanced = await SchemeNetworkServer.enhancePaymentRequirements(
794
+ baseReq,
795
+ { ...supportedKind, x402Version },
796
+ facilitatorExtensions
797
+ );
798
+ results.push(enhanced);
799
+ }
800
+ return results;
801
+ }
802
+ const parsedPrice = await SchemeNetworkServer.parsePrice(
803
+ resourceConfig.price,
804
+ resourceConfig.network
805
+ );
806
+ const baseRequirements = {
807
+ scheme: SchemeNetworkServer.scheme,
808
+ network: resourceConfig.network,
809
+ amount: parsedPrice.amount,
810
+ asset: parsedPrice.asset,
811
+ payTo: resourceConfig.payTo,
812
+ maxTimeoutSeconds: resourceConfig.maxTimeoutSeconds || 300,
813
+ // Default 5 minutes
814
+ extra: {
815
+ ...parsedPrice.extra,
816
+ ...resourceConfig.extra
817
+ // Merge user-provided extra
818
+ }
819
+ };
820
+ const requirement = await SchemeNetworkServer.enhancePaymentRequirements(
821
+ baseRequirements,
822
+ {
823
+ ...supportedKind,
824
+ x402Version
825
+ },
826
+ facilitatorExtensions
827
+ );
828
+ requirements.push(requirement);
829
+ return requirements;
830
+ }
831
+ /**
832
+ * Build payment requirements from multiple payment options
833
+ * This method handles resolving dynamic payTo/price functions and builds requirements for each option
834
+ *
835
+ * @param paymentOptions - Array of payment options to convert
836
+ * @param context - HTTP request context for resolving dynamic functions
837
+ * @returns Array of payment requirements (one per option)
838
+ */
839
+ async buildPaymentRequirementsFromOptions(paymentOptions, context) {
840
+ const allRequirements = [];
841
+ for (const option of paymentOptions) {
842
+ const resolvedPayTo = typeof option.payTo === "function" ? await option.payTo(context) : option.payTo;
843
+ const resolvedPrice = typeof option.price === "function" ? await option.price(context) : option.price;
844
+ const resourceConfig = {
845
+ scheme: option.scheme,
846
+ payTo: resolvedPayTo,
847
+ price: resolvedPrice,
848
+ network: option.network,
849
+ maxTimeoutSeconds: option.maxTimeoutSeconds,
850
+ extra: option.extra,
851
+ assets: option.assets
852
+ };
853
+ const requirements = await this.buildPaymentRequirements(resourceConfig);
854
+ allRequirements.push(...requirements);
855
+ }
856
+ return allRequirements;
857
+ }
858
+ /**
859
+ * Create a payment required response
860
+ *
861
+ * @param requirements - Payment requirements
862
+ * @param resourceInfo - Resource information
863
+ * @param error - Error message
864
+ * @param extensions - Optional declared extensions (for per-key enrichment)
865
+ * @param transportContext - Optional transport-specific context (e.g., HTTP request, MCP tool context)
866
+ * @returns Payment required response object
867
+ */
868
+ async createPaymentRequiredResponse(requirements, resourceInfo, error, extensions, transportContext) {
869
+ let response = {
870
+ x402Version: 2,
871
+ error,
872
+ resource: resourceInfo,
873
+ accepts: requirements
874
+ };
875
+ if (extensions && Object.keys(extensions).length > 0) {
876
+ response.extensions = extensions;
877
+ }
878
+ if (extensions) {
879
+ for (const [key, declaration] of Object.entries(extensions)) {
880
+ const extension = this.registeredExtensions.get(key);
881
+ if (extension?.enrichPaymentRequiredResponse) {
882
+ try {
883
+ const context = {
884
+ requirements,
885
+ resourceInfo,
886
+ error,
887
+ paymentRequiredResponse: response,
888
+ transportContext
889
+ };
890
+ const extensionData = await extension.enrichPaymentRequiredResponse(
891
+ declaration,
892
+ context
893
+ );
894
+ if (extensionData !== void 0) {
895
+ if (!response.extensions) {
896
+ response.extensions = {};
897
+ }
898
+ response.extensions[key] = extensionData;
899
+ }
900
+ } catch (error2) {
901
+ console.error(
902
+ `Error in enrichPaymentRequiredResponse hook for extension ${key}:`,
903
+ error2
904
+ );
905
+ }
906
+ }
907
+ }
908
+ }
909
+ return response;
910
+ }
911
+ /**
912
+ * Verify a payment against requirements
913
+ *
914
+ * @param paymentPayload - The payment payload to verify
915
+ * @param requirements - The payment requirements
916
+ * @returns Verification response
917
+ */
918
+ async verifyPayment(paymentPayload, requirements) {
919
+ const context = {
920
+ paymentPayload,
921
+ requirements
922
+ };
923
+ for (const hook of this.beforeVerifyHooks) {
924
+ try {
925
+ const result = await hook(context);
926
+ if (result && "abort" in result && result.abort) {
927
+ return {
928
+ isValid: false,
929
+ invalidReason: result.reason,
930
+ invalidMessage: result.message
931
+ };
932
+ }
933
+ } catch (error) {
934
+ throw new VerifyError(400, {
935
+ isValid: false,
936
+ invalidReason: "before_verify_hook_error",
937
+ invalidMessage: error instanceof Error ? error.message : ""
938
+ });
939
+ }
940
+ }
941
+ try {
942
+ const facilitatorClient = this.getFacilitatorClient(
943
+ paymentPayload.x402Version,
944
+ requirements.network,
945
+ requirements.scheme
946
+ );
947
+ let verifyResult;
948
+ if (!facilitatorClient) {
949
+ let lastError;
950
+ for (const client of this.facilitatorClients) {
951
+ try {
952
+ verifyResult = await client.verify(paymentPayload, requirements);
953
+ break;
954
+ } catch (error) {
955
+ lastError = error;
956
+ }
957
+ }
958
+ if (!verifyResult) {
959
+ throw lastError || new Error(
960
+ `No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
961
+ );
962
+ }
963
+ } else {
964
+ verifyResult = await facilitatorClient.verify(paymentPayload, requirements);
965
+ }
966
+ const resultContext = {
967
+ ...context,
968
+ result: verifyResult
969
+ };
970
+ for (const hook of this.afterVerifyHooks) {
971
+ await hook(resultContext);
972
+ }
973
+ return verifyResult;
974
+ } catch (error) {
975
+ const failureContext = {
976
+ ...context,
977
+ error
978
+ };
979
+ for (const hook of this.onVerifyFailureHooks) {
980
+ const result = await hook(failureContext);
981
+ if (result && "recovered" in result && result.recovered) {
982
+ return result.result;
983
+ }
984
+ }
985
+ throw error;
986
+ }
987
+ }
988
+ /**
989
+ * Settle a verified payment
990
+ *
991
+ * @param paymentPayload - The payment payload to settle
992
+ * @param requirements - The payment requirements
993
+ * @param declaredExtensions - Optional declared extensions (for per-key enrichment)
994
+ * @param transportContext - Optional transport-specific context (e.g., HTTP request/response, MCP tool context)
995
+ * @returns Settlement response
996
+ */
997
+ async settlePayment(paymentPayload, requirements, declaredExtensions, transportContext) {
998
+ const context = {
999
+ paymentPayload,
1000
+ requirements
1001
+ };
1002
+ for (const hook of this.beforeSettleHooks) {
1003
+ try {
1004
+ const result = await hook(context);
1005
+ if (result && "abort" in result && result.abort) {
1006
+ throw new SettleError(400, {
1007
+ success: false,
1008
+ errorReason: result.reason,
1009
+ errorMessage: result.message,
1010
+ transaction: "",
1011
+ network: requirements.network
1012
+ });
1013
+ }
1014
+ } catch (error) {
1015
+ if (error instanceof SettleError) {
1016
+ throw error;
1017
+ }
1018
+ throw new SettleError(400, {
1019
+ success: false,
1020
+ errorReason: "before_settle_hook_error",
1021
+ errorMessage: error instanceof Error ? error.message : "",
1022
+ transaction: "",
1023
+ network: requirements.network
1024
+ });
1025
+ }
1026
+ }
1027
+ try {
1028
+ const facilitatorClient = this.getFacilitatorClient(
1029
+ paymentPayload.x402Version,
1030
+ requirements.network,
1031
+ requirements.scheme
1032
+ );
1033
+ let settleResult;
1034
+ if (!facilitatorClient) {
1035
+ let lastError;
1036
+ for (const client of this.facilitatorClients) {
1037
+ try {
1038
+ settleResult = await client.settle(paymentPayload, requirements);
1039
+ break;
1040
+ } catch (error) {
1041
+ lastError = error;
1042
+ }
1043
+ }
1044
+ if (!settleResult) {
1045
+ throw lastError || new Error(
1046
+ `No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
1047
+ );
1048
+ }
1049
+ } else {
1050
+ settleResult = await facilitatorClient.settle(paymentPayload, requirements);
1051
+ }
1052
+ const resultContext = {
1053
+ ...context,
1054
+ result: settleResult,
1055
+ transportContext
1056
+ };
1057
+ for (const hook of this.afterSettleHooks) {
1058
+ await hook(resultContext);
1059
+ }
1060
+ if (declaredExtensions) {
1061
+ for (const [key, declaration] of Object.entries(declaredExtensions)) {
1062
+ const extension = this.registeredExtensions.get(key);
1063
+ if (extension?.enrichSettlementResponse) {
1064
+ try {
1065
+ const extensionData = await extension.enrichSettlementResponse(
1066
+ declaration,
1067
+ resultContext
1068
+ );
1069
+ if (extensionData !== void 0) {
1070
+ if (!settleResult.extensions) {
1071
+ settleResult.extensions = {};
1072
+ }
1073
+ settleResult.extensions[key] = extensionData;
1074
+ }
1075
+ } catch (error) {
1076
+ console.error(`Error in enrichSettlementResponse hook for extension ${key}:`, error);
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+ return settleResult;
1082
+ } catch (error) {
1083
+ const failureContext = {
1084
+ ...context,
1085
+ error
1086
+ };
1087
+ for (const hook of this.onSettleFailureHooks) {
1088
+ const result = await hook(failureContext);
1089
+ if (result && "recovered" in result && result.recovered) {
1090
+ return result.result;
1091
+ }
1092
+ }
1093
+ throw error;
1094
+ }
1095
+ }
1096
+ /**
1097
+ * Find matching payment requirements for a payment
1098
+ *
1099
+ * @param availableRequirements - Array of available payment requirements
1100
+ * @param paymentPayload - The payment payload
1101
+ * @returns Matching payment requirements or undefined
1102
+ */
1103
+ findMatchingRequirements(availableRequirements, paymentPayload) {
1104
+ switch (paymentPayload.x402Version) {
1105
+ case 2:
1106
+ return availableRequirements.find(
1107
+ (paymentRequirements) => deepEqual(paymentRequirements, paymentPayload.accepted)
1108
+ );
1109
+ case 1:
1110
+ return availableRequirements.find(
1111
+ (req) => req.scheme === paymentPayload.accepted.scheme && req.network === paymentPayload.accepted.network
1112
+ );
1113
+ default:
1114
+ throw new Error(
1115
+ `Unsupported x402 version: ${paymentPayload.x402Version}`
1116
+ );
1117
+ }
1118
+ }
1119
+ /**
1120
+ * Process a payment request
1121
+ *
1122
+ * @param paymentPayload - Optional payment payload if provided
1123
+ * @param resourceConfig - Configuration for the protected resource
1124
+ * @param resourceInfo - Information about the resource being accessed
1125
+ * @param extensions - Optional extensions to include in the response
1126
+ * @returns Processing result
1127
+ */
1128
+ async processPaymentRequest(paymentPayload, resourceConfig, resourceInfo, extensions) {
1129
+ const requirements = await this.buildPaymentRequirements(resourceConfig);
1130
+ if (!paymentPayload) {
1131
+ return {
1132
+ success: false,
1133
+ requiresPayment: await this.createPaymentRequiredResponse(
1134
+ requirements,
1135
+ resourceInfo,
1136
+ "Payment required",
1137
+ extensions
1138
+ )
1139
+ };
1140
+ }
1141
+ const matchingRequirements = this.findMatchingRequirements(requirements, paymentPayload);
1142
+ if (!matchingRequirements) {
1143
+ return {
1144
+ success: false,
1145
+ requiresPayment: await this.createPaymentRequiredResponse(
1146
+ requirements,
1147
+ resourceInfo,
1148
+ "No matching payment requirements found",
1149
+ extensions
1150
+ )
1151
+ };
1152
+ }
1153
+ const verificationResult = await this.verifyPayment(paymentPayload, matchingRequirements);
1154
+ if (!verificationResult.isValid) {
1155
+ return {
1156
+ success: false,
1157
+ error: verificationResult.invalidReason,
1158
+ verificationResult
1159
+ };
1160
+ }
1161
+ return {
1162
+ success: true,
1163
+ verificationResult
1164
+ };
1165
+ }
1166
+ /**
1167
+ * Get facilitator client for a specific version, network, and scheme
1168
+ *
1169
+ * @param x402Version - The x402 version
1170
+ * @param network - The network identifier
1171
+ * @param scheme - The payment scheme
1172
+ * @returns The facilitator client or undefined if not found
1173
+ */
1174
+ getFacilitatorClient(x402Version2, network, scheme) {
1175
+ const versionMap = this.facilitatorClientsMap.get(x402Version2);
1176
+ if (!versionMap) return void 0;
1177
+ return findByNetworkAndScheme(versionMap, scheme, network);
1178
+ }
1179
+ };
1180
+
1181
+ // src/http/index.ts
1182
+ function decodePaymentSignatureHeader(paymentSignatureHeader) {
1183
+ if (!Base64EncodedRegex.test(paymentSignatureHeader)) {
1184
+ throw new Error("Invalid payment signature header");
1185
+ }
1186
+ return JSON.parse(safeBase64Decode(paymentSignatureHeader));
1187
+ }
1188
+ function encodePaymentRequiredHeader(paymentRequired) {
1189
+ return safeBase64Encode(JSON.stringify(paymentRequired));
1190
+ }
1191
+ function encodePaymentResponseHeader(paymentResponse) {
1192
+ return safeBase64Encode(JSON.stringify(paymentResponse));
1193
+ }
1194
+
1195
+ // src/http/x402HTTPResourceServer.ts
1196
+ var RouteConfigurationError = class extends Error {
1197
+ /**
1198
+ * Creates a new RouteConfigurationError with the given validation errors.
1199
+ *
1200
+ * @param errors - The validation errors that caused this exception.
1201
+ */
1202
+ constructor(errors) {
1203
+ const message = `x402 Route Configuration Errors:
1204
+ ${errors.map((e) => ` - ${e.message}`).join("\n")}`;
1205
+ super(message);
1206
+ this.name = "RouteConfigurationError";
1207
+ this.errors = errors;
1208
+ }
1209
+ };
1210
+ var x402HTTPResourceServer = class {
1211
+ /**
1212
+ * Creates a new x402HTTPResourceServer instance.
1213
+ *
1214
+ * @param ResourceServer - The core x402ResourceServer instance to use
1215
+ * @param routes - Route configuration for payment-protected endpoints
1216
+ */
1217
+ constructor(ResourceServer, routes) {
1218
+ this.compiledRoutes = [];
1219
+ this.protectedRequestHooks = [];
1220
+ this.ResourceServer = ResourceServer;
1221
+ this.routesConfig = routes;
1222
+ const normalizedRoutes = typeof routes === "object" && !("accepts" in routes) ? routes : { "*": routes };
1223
+ for (const [pattern, config] of Object.entries(normalizedRoutes)) {
1224
+ const parsed = this.parseRoutePattern(pattern);
1225
+ this.compiledRoutes.push({
1226
+ verb: parsed.verb,
1227
+ regex: parsed.regex,
1228
+ config
1229
+ });
1230
+ }
1231
+ }
1232
+ /**
1233
+ * Get the underlying x402ResourceServer instance.
1234
+ *
1235
+ * @returns The underlying x402ResourceServer instance
1236
+ */
1237
+ get server() {
1238
+ return this.ResourceServer;
1239
+ }
1240
+ /**
1241
+ * Get the routes configuration.
1242
+ *
1243
+ * @returns The routes configuration
1244
+ */
1245
+ get routes() {
1246
+ return this.routesConfig;
1247
+ }
1248
+ /**
1249
+ * Initialize the HTTP resource server.
1250
+ *
1251
+ * This method initializes the underlying resource server (fetching facilitator support)
1252
+ * and then validates that all route payment configurations have corresponding
1253
+ * registered schemes and facilitator support.
1254
+ *
1255
+ * @throws RouteConfigurationError if any route's payment options don't have
1256
+ * corresponding registered schemes or facilitator support
1257
+ *
1258
+ * @example
1259
+ * ```typescript
1260
+ * const httpServer = new x402HTTPResourceServer(server, routes);
1261
+ * await httpServer.initialize();
1262
+ * ```
1263
+ */
1264
+ async initialize() {
1265
+ await this.ResourceServer.initialize();
1266
+ const errors = this.validateRouteConfiguration();
1267
+ if (errors.length > 0) {
1268
+ throw new RouteConfigurationError(errors);
1269
+ }
1270
+ }
1271
+ /**
1272
+ * Register a custom paywall provider for generating HTML
1273
+ *
1274
+ * @param provider - PaywallProvider instance
1275
+ * @returns This service instance for chaining
1276
+ */
1277
+ registerPaywallProvider(provider) {
1278
+ this.paywallProvider = provider;
1279
+ return this;
1280
+ }
1281
+ /**
1282
+ * Register a hook that runs on every request to a protected route, before payment processing.
1283
+ * Hooks are executed in order of registration. The first hook to return a non-void result wins.
1284
+ *
1285
+ * @param hook - The request hook function
1286
+ * @returns The x402HTTPResourceServer instance for chaining
1287
+ */
1288
+ onProtectedRequest(hook) {
1289
+ this.protectedRequestHooks.push(hook);
1290
+ return this;
1291
+ }
1292
+ /**
1293
+ * Process HTTP request and return response instructions
1294
+ * This is the main entry point for framework middleware
1295
+ *
1296
+ * @param context - HTTP request context
1297
+ * @param paywallConfig - Optional paywall configuration
1298
+ * @returns Process result indicating next action for middleware
1299
+ */
1300
+ async processHTTPRequest(context, paywallConfig) {
1301
+ const { adapter, path, method } = context;
1302
+ const routeConfig = this.getRouteConfig(path, method);
1303
+ if (!routeConfig) {
1304
+ return { type: "no-payment-required" };
1305
+ }
1306
+ for (const hook of this.protectedRequestHooks) {
1307
+ const result = await hook(context, routeConfig);
1308
+ if (result && "grantAccess" in result) {
1309
+ return { type: "no-payment-required" };
1310
+ }
1311
+ if (result && "abort" in result) {
1312
+ return {
1313
+ type: "payment-error",
1314
+ response: {
1315
+ status: 403,
1316
+ headers: { "Content-Type": "application/json" },
1317
+ body: { error: result.reason }
1318
+ }
1319
+ };
1320
+ }
1321
+ }
1322
+ const paymentOptions = this.normalizePaymentOptions(routeConfig);
1323
+ const paymentPayload = this.extractPayment(adapter);
1324
+ const resourceInfo = {
1325
+ url: routeConfig.resource || context.adapter.getUrl(),
1326
+ description: routeConfig.description || "",
1327
+ mimeType: routeConfig.mimeType || ""
1328
+ };
1329
+ let requirements = await this.ResourceServer.buildPaymentRequirementsFromOptions(
1330
+ paymentOptions,
1331
+ context
1332
+ );
1333
+ let extensions = routeConfig.extensions;
1334
+ if (extensions) {
1335
+ extensions = this.ResourceServer.enrichExtensions(extensions, context);
1336
+ }
1337
+ const transportContext = { request: context };
1338
+ const paymentRequired = await this.ResourceServer.createPaymentRequiredResponse(
1339
+ requirements,
1340
+ resourceInfo,
1341
+ !paymentPayload ? "Payment required" : void 0,
1342
+ extensions,
1343
+ transportContext
1344
+ );
1345
+ if (!paymentPayload) {
1346
+ const unpaidBody = routeConfig.unpaidResponseBody ? await routeConfig.unpaidResponseBody(context) : void 0;
1347
+ return {
1348
+ type: "payment-error",
1349
+ response: this.createHTTPResponse(
1350
+ paymentRequired,
1351
+ this.isWebBrowser(adapter),
1352
+ paywallConfig,
1353
+ routeConfig.customPaywallHtml,
1354
+ unpaidBody
1355
+ )
1356
+ };
1357
+ }
1358
+ try {
1359
+ const matchingRequirements = this.ResourceServer.findMatchingRequirements(
1360
+ paymentRequired.accepts,
1361
+ paymentPayload
1362
+ );
1363
+ if (!matchingRequirements) {
1364
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
1365
+ requirements,
1366
+ resourceInfo,
1367
+ "No matching payment requirements",
1368
+ routeConfig.extensions,
1369
+ transportContext
1370
+ );
1371
+ return {
1372
+ type: "payment-error",
1373
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
1374
+ };
1375
+ }
1376
+ const verifyResult = await this.ResourceServer.verifyPayment(
1377
+ paymentPayload,
1378
+ matchingRequirements
1379
+ );
1380
+ if (!verifyResult.isValid) {
1381
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
1382
+ requirements,
1383
+ resourceInfo,
1384
+ verifyResult.invalidReason,
1385
+ routeConfig.extensions,
1386
+ transportContext
1387
+ );
1388
+ return {
1389
+ type: "payment-error",
1390
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
1391
+ };
1392
+ }
1393
+ return {
1394
+ type: "payment-verified",
1395
+ paymentPayload,
1396
+ paymentRequirements: matchingRequirements,
1397
+ declaredExtensions: routeConfig.extensions
1398
+ };
1399
+ } catch (error) {
1400
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
1401
+ requirements,
1402
+ resourceInfo,
1403
+ error instanceof Error ? error.message : "Payment verification failed",
1404
+ routeConfig.extensions,
1405
+ transportContext
1406
+ );
1407
+ return {
1408
+ type: "payment-error",
1409
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
1410
+ };
1411
+ }
1412
+ }
1413
+ /**
1414
+ * Process settlement after successful response
1415
+ *
1416
+ * @param paymentPayload - The verified payment payload
1417
+ * @param requirements - The matching payment requirements
1418
+ * @param declaredExtensions - Optional declared extensions (for per-key enrichment)
1419
+ * @param transportContext - Optional HTTP transport context
1420
+ * @returns ProcessSettleResultResponse - SettleResponse with headers if success or errorReason if failure
1421
+ */
1422
+ async processSettlement(paymentPayload, requirements, declaredExtensions, transportContext) {
1423
+ try {
1424
+ const settleResponse = await this.ResourceServer.settlePayment(
1425
+ paymentPayload,
1426
+ requirements,
1427
+ declaredExtensions,
1428
+ transportContext
1429
+ );
1430
+ if (!settleResponse.success) {
1431
+ const failure = {
1432
+ ...settleResponse,
1433
+ success: false,
1434
+ errorReason: settleResponse.errorReason || "Settlement failed",
1435
+ errorMessage: settleResponse.errorMessage || settleResponse.errorReason || "Settlement failed",
1436
+ headers: this.createSettlementHeaders(settleResponse)
1437
+ };
1438
+ const response = await this.buildSettlementFailureResponse(failure, transportContext);
1439
+ return { ...failure, response };
1440
+ }
1441
+ return {
1442
+ ...settleResponse,
1443
+ success: true,
1444
+ headers: this.createSettlementHeaders(settleResponse),
1445
+ requirements
1446
+ };
1447
+ } catch (error) {
1448
+ if (error instanceof SettleError) {
1449
+ const errorReason2 = error.errorReason || error.message;
1450
+ const settleResponse2 = {
1451
+ success: false,
1452
+ errorReason: errorReason2,
1453
+ errorMessage: error.errorMessage || errorReason2,
1454
+ payer: error.payer,
1455
+ network: error.network,
1456
+ transaction: error.transaction
1457
+ };
1458
+ const failure2 = {
1459
+ ...settleResponse2,
1460
+ success: false,
1461
+ errorReason: errorReason2,
1462
+ headers: this.createSettlementHeaders(settleResponse2)
1463
+ };
1464
+ const response2 = await this.buildSettlementFailureResponse(failure2, transportContext);
1465
+ return { ...failure2, response: response2 };
1466
+ }
1467
+ const errorReason = error instanceof Error ? error.message : "Settlement failed";
1468
+ const settleResponse = {
1469
+ success: false,
1470
+ errorReason,
1471
+ errorMessage: errorReason,
1472
+ network: requirements.network,
1473
+ transaction: ""
1474
+ };
1475
+ const failure = {
1476
+ ...settleResponse,
1477
+ success: false,
1478
+ errorReason,
1479
+ headers: this.createSettlementHeaders(settleResponse)
1480
+ };
1481
+ const response = await this.buildSettlementFailureResponse(failure, transportContext);
1482
+ return { ...failure, response };
1483
+ }
1484
+ }
1485
+ /**
1486
+ * Check if a request requires payment based on route configuration
1487
+ *
1488
+ * @param context - HTTP request context
1489
+ * @returns True if the route requires payment, false otherwise
1490
+ */
1491
+ requiresPayment(context) {
1492
+ const routeConfig = this.getRouteConfig(context.path, context.method);
1493
+ return routeConfig !== void 0;
1494
+ }
1495
+ /**
1496
+ * Build HTTPResponseInstructions for settlement failure.
1497
+ * Uses settlementFailedResponseBody hook if configured, otherwise defaults to empty body.
1498
+ *
1499
+ * @param failure - Settlement failure result with headers
1500
+ * @param transportContext - Optional HTTP transport context for the request
1501
+ * @returns HTTP response instructions for the 402 settlement failure response
1502
+ */
1503
+ async buildSettlementFailureResponse(failure, transportContext) {
1504
+ const settlementHeaders = failure.headers;
1505
+ const routeConfig = transportContext ? this.getRouteConfig(transportContext.request.path, transportContext.request.method) : void 0;
1506
+ const customBody = routeConfig?.settlementFailedResponseBody ? await routeConfig.settlementFailedResponseBody(transportContext.request, failure) : void 0;
1507
+ const contentType = customBody ? customBody.contentType : "application/json";
1508
+ const body = customBody ? customBody.body : {};
1509
+ return {
1510
+ status: 402,
1511
+ headers: {
1512
+ "Content-Type": contentType,
1513
+ ...settlementHeaders
1514
+ },
1515
+ body,
1516
+ isHtml: contentType.includes("text/html")
1517
+ };
1518
+ }
1519
+ /**
1520
+ * Normalizes a RouteConfig's accepts field into an array of PaymentOptions
1521
+ * Handles both single PaymentOption and array formats
1522
+ *
1523
+ * @param routeConfig - Route configuration
1524
+ * @returns Array of payment options
1525
+ */
1526
+ normalizePaymentOptions(routeConfig) {
1527
+ return Array.isArray(routeConfig.accepts) ? routeConfig.accepts : [routeConfig.accepts];
1528
+ }
1529
+ /**
1530
+ * Validates that all payment options in routes have corresponding registered schemes
1531
+ * and facilitator support.
1532
+ *
1533
+ * @returns Array of validation errors (empty if all routes are valid)
1534
+ */
1535
+ validateRouteConfiguration() {
1536
+ const errors = [];
1537
+ const normalizedRoutes = typeof this.routesConfig === "object" && !("accepts" in this.routesConfig) ? Object.entries(this.routesConfig) : [["*", this.routesConfig]];
1538
+ for (const [pattern, config] of normalizedRoutes) {
1539
+ const paymentOptions = this.normalizePaymentOptions(config);
1540
+ for (const option of paymentOptions) {
1541
+ if (!this.ResourceServer.hasRegisteredScheme(option.network, option.scheme)) {
1542
+ errors.push({
1543
+ routePattern: pattern,
1544
+ scheme: option.scheme,
1545
+ network: option.network,
1546
+ reason: "missing_scheme",
1547
+ message: `Route "${pattern}": No scheme implementation registered for "${option.scheme}" on network "${option.network}"`
1548
+ });
1549
+ continue;
1550
+ }
1551
+ const supportedKind = this.ResourceServer.getSupportedKind(
1552
+ x402Version,
1553
+ option.network,
1554
+ option.scheme
1555
+ );
1556
+ if (!supportedKind) {
1557
+ errors.push({
1558
+ routePattern: pattern,
1559
+ scheme: option.scheme,
1560
+ network: option.network,
1561
+ reason: "missing_facilitator",
1562
+ message: `Route "${pattern}": Facilitator does not support scheme "${option.scheme}" on network "${option.network}"`
1563
+ });
1564
+ }
1565
+ }
1566
+ }
1567
+ return errors;
1568
+ }
1569
+ /**
1570
+ * Get route configuration for a request
1571
+ *
1572
+ * @param path - Request path
1573
+ * @param method - HTTP method
1574
+ * @returns Route configuration or undefined if no match
1575
+ */
1576
+ getRouteConfig(path, method) {
1577
+ const normalizedPath = this.normalizePath(path);
1578
+ const upperMethod = method.toUpperCase();
1579
+ const matchingRoute = this.compiledRoutes.find(
1580
+ (route) => route.regex.test(normalizedPath) && (route.verb === "*" || route.verb === upperMethod)
1581
+ );
1582
+ return matchingRoute?.config;
1583
+ }
1584
+ /**
1585
+ * Extract payment from HTTP headers (handles v1 and v2)
1586
+ *
1587
+ * @param adapter - HTTP adapter
1588
+ * @returns Decoded payment payload or null
1589
+ */
1590
+ extractPayment(adapter) {
1591
+ const header = adapter.getHeader("payment-signature") || adapter.getHeader("PAYMENT-SIGNATURE");
1592
+ if (header) {
1593
+ try {
1594
+ return decodePaymentSignatureHeader(header);
1595
+ } catch (error) {
1596
+ console.warn("Failed to decode PAYMENT-SIGNATURE header:", error);
1597
+ }
1598
+ }
1599
+ return null;
1600
+ }
1601
+ /**
1602
+ * Check if request is from a web browser
1603
+ *
1604
+ * @param adapter - HTTP adapter
1605
+ * @returns True if request appears to be from a browser
1606
+ */
1607
+ isWebBrowser(adapter) {
1608
+ const accept = adapter.getAcceptHeader();
1609
+ const userAgent = adapter.getUserAgent();
1610
+ return accept.includes("text/html") && userAgent.includes("Mozilla");
1611
+ }
1612
+ /**
1613
+ * Create HTTP response instructions from payment required
1614
+ *
1615
+ * @param paymentRequired - Payment requirements
1616
+ * @param isWebBrowser - Whether request is from browser
1617
+ * @param paywallConfig - Paywall configuration
1618
+ * @param customHtml - Custom HTML template
1619
+ * @param unpaidResponse - Optional custom response (content type and body) for unpaid API requests
1620
+ * @returns Response instructions
1621
+ */
1622
+ createHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) {
1623
+ const status = paymentRequired.error === "permit2_allowance_required" ? 412 : 402;
1624
+ if (isWebBrowser) {
1625
+ const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml);
1626
+ return {
1627
+ status,
1628
+ headers: { "Content-Type": "text/html" },
1629
+ body: html,
1630
+ isHtml: true
1631
+ };
1632
+ }
1633
+ const response = this.createHTTPPaymentRequiredResponse(paymentRequired);
1634
+ const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json";
1635
+ const body = unpaidResponse ? unpaidResponse.body : {};
1636
+ return {
1637
+ status,
1638
+ headers: {
1639
+ "Content-Type": contentType,
1640
+ ...response.headers
1641
+ },
1642
+ body
1643
+ };
1644
+ }
1645
+ /**
1646
+ * Create HTTP payment required response (v1 puts in body, v2 puts in header)
1647
+ *
1648
+ * @param paymentRequired - Payment required object
1649
+ * @returns Headers and body for the HTTP response
1650
+ */
1651
+ createHTTPPaymentRequiredResponse(paymentRequired) {
1652
+ return {
1653
+ headers: {
1654
+ "PAYMENT-REQUIRED": encodePaymentRequiredHeader(paymentRequired)
1655
+ }
1656
+ };
1657
+ }
1658
+ /**
1659
+ * Create settlement response headers
1660
+ *
1661
+ * @param settleResponse - Settlement response
1662
+ * @returns Headers to add to response
1663
+ */
1664
+ createSettlementHeaders(settleResponse) {
1665
+ const encoded = encodePaymentResponseHeader(settleResponse);
1666
+ return { "PAYMENT-RESPONSE": encoded };
1667
+ }
1668
+ /**
1669
+ * Parse route pattern into verb and regex
1670
+ *
1671
+ * @param pattern - Route pattern like "GET /api/*" or "/api/[id]"
1672
+ * @returns Parsed pattern with verb and regex
1673
+ */
1674
+ parseRoutePattern(pattern) {
1675
+ const [verb, path] = pattern.includes(" ") ? pattern.split(/\s+/) : ["*", pattern];
1676
+ const regex = new RegExp(
1677
+ `^${path.replace(/[$()+.?^{|}]/g, "\\$&").replace(/\*/g, ".*?").replace(/\[([^\]]+)\]/g, "[^/]+").replace(/\//g, "\\/")}$`,
1678
+ "i"
1679
+ );
1680
+ return { verb: verb.toUpperCase(), regex };
1681
+ }
1682
+ /**
1683
+ * Normalize path for matching
1684
+ *
1685
+ * @param path - Raw path from request
1686
+ * @returns Normalized path
1687
+ */
1688
+ normalizePath(path) {
1689
+ const pathWithoutQuery = path.split(/[?#]/)[0];
1690
+ let decodedOrRawPath;
1691
+ try {
1692
+ decodedOrRawPath = decodeURIComponent(pathWithoutQuery);
1693
+ } catch {
1694
+ decodedOrRawPath = pathWithoutQuery;
1695
+ }
1696
+ return decodedOrRawPath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/(.+?)\/+$/, "$1");
1697
+ }
1698
+ /**
1699
+ * Generate paywall HTML for browser requests
1700
+ *
1701
+ * @param paymentRequired - Payment required response
1702
+ * @param paywallConfig - Optional paywall configuration
1703
+ * @param customHtml - Optional custom HTML template
1704
+ * @returns HTML string
1705
+ */
1706
+ generatePaywallHTML(paymentRequired, paywallConfig, customHtml) {
1707
+ if (customHtml) {
1708
+ return customHtml;
1709
+ }
1710
+ if (this.paywallProvider) {
1711
+ return this.paywallProvider.generateHtml(paymentRequired, paywallConfig);
1712
+ }
1713
+ try {
1714
+ const paywall = require("@bankofai/x402-paywall");
1715
+ const displayAmount2 = this.getDisplayAmount(paymentRequired);
1716
+ const resource2 = paymentRequired.resource;
1717
+ return paywall.getPaywallHtml({
1718
+ amount: displayAmount2,
1719
+ paymentRequired,
1720
+ currentUrl: resource2?.url || paywallConfig?.currentUrl || "",
1721
+ testnet: paywallConfig?.testnet ?? true,
1722
+ appName: paywallConfig?.appName,
1723
+ appLogo: paywallConfig?.appLogo,
1724
+ sessionTokenEndpoint: paywallConfig?.sessionTokenEndpoint
1725
+ });
1726
+ } catch {
1727
+ }
1728
+ const resource = paymentRequired.resource;
1729
+ const displayAmount = this.getDisplayAmount(paymentRequired);
1730
+ return `
1731
+ <!DOCTYPE html>
1732
+ <html>
1733
+ <head>
1734
+ <title>Payment Required</title>
1735
+ <meta charset="UTF-8">
1736
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1737
+ </head>
1738
+ <body>
1739
+ <div style="max-width: 600px; margin: 50px auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
1740
+ ${paywallConfig?.appLogo ? `<img src="${paywallConfig.appLogo}" alt="${paywallConfig.appName || "App"}" style="max-width: 200px; margin-bottom: 20px;">` : ""}
1741
+ <h1>Payment Required</h1>
1742
+ ${resource ? `<p><strong>Resource:</strong> ${resource.description || resource.url}</p>` : ""}
1743
+ <p><strong>Amount:</strong> $${displayAmount.toFixed(2)} USDC</p>
1744
+ <div id="payment-widget"
1745
+ data-requirements='${JSON.stringify(paymentRequired)}'
1746
+ data-app-name="${paywallConfig?.appName || ""}"
1747
+ data-testnet="${paywallConfig?.testnet || false}">
1748
+ <!-- Install @bankofai/x402-paywall for full wallet integration -->
1749
+ <p style="margin-top: 2rem; padding: 1rem; background: #fef3c7; border-radius: 0.5rem;">
1750
+ <strong>Note:</strong> Install <code>@bankofai/x402-paywall</code> for full wallet connection and payment UI.
1751
+ </p>
1752
+ </div>
1753
+ </div>
1754
+ </body>
1755
+ </html>
1756
+ `;
1757
+ }
1758
+ /**
1759
+ * Extract display amount from payment requirements.
1760
+ *
1761
+ * @param paymentRequired - The payment required object
1762
+ * @returns The display amount in decimal format
1763
+ */
1764
+ getDisplayAmount(paymentRequired) {
1765
+ const accepts = paymentRequired.accepts;
1766
+ if (accepts && accepts.length > 0) {
1767
+ const firstReq = accepts[0];
1768
+ if ("amount" in firstReq) {
1769
+ return parseFloat(firstReq.amount) / 1e6;
1770
+ }
1771
+ }
1772
+ return 0;
1773
+ }
1774
+ };
1775
+ // Annotate the CommonJS export names for ESM import in node:
1776
+ 0 && (module.exports = {
1777
+ HTTPFacilitatorClient,
1778
+ RouteConfigurationError,
1779
+ x402HTTPResourceServer,
1780
+ x402ResourceServer
1781
+ });
1782
+ //# sourceMappingURL=index.js.map