@okxweb3/app-x402-core 0.1.2

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