@bankofai/x402-core 1.0.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 (72) hide show
  1. package/README.md +294 -0
  2. package/dist/cjs/client/index.d.ts +149 -0
  3. package/dist/cjs/client/index.js +909 -0
  4. package/dist/cjs/client/index.js.map +1 -0
  5. package/dist/cjs/facilitator/index.d.ts +206 -0
  6. package/dist/cjs/facilitator/index.js +431 -0
  7. package/dist/cjs/facilitator/index.js.map +1 -0
  8. package/dist/cjs/http/index.d.ts +50 -0
  9. package/dist/cjs/http/index.js +1452 -0
  10. package/dist/cjs/http/index.js.map +1 -0
  11. package/dist/cjs/index.d.ts +3 -0
  12. package/dist/cjs/index.js +31 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/schemas/index.d.ts +891 -0
  15. package/dist/cjs/schemas/index.js +216 -0
  16. package/dist/cjs/schemas/index.js.map +1 -0
  17. package/dist/cjs/server/index.d.ts +79 -0
  18. package/dist/cjs/server/index.js +2568 -0
  19. package/dist/cjs/server/index.js.map +1 -0
  20. package/dist/cjs/types/index.d.ts +1 -0
  21. package/dist/cjs/types/index.js +97 -0
  22. package/dist/cjs/types/index.js.map +1 -0
  23. package/dist/cjs/types/v1/index.d.ts +1 -0
  24. package/dist/cjs/types/v1/index.js +19 -0
  25. package/dist/cjs/types/v1/index.js.map +1 -0
  26. package/dist/cjs/utils/index.d.ts +77 -0
  27. package/dist/cjs/utils/index.js +179 -0
  28. package/dist/cjs/utils/index.js.map +1 -0
  29. package/dist/cjs/x402Client-BgegfQgE.d.ts +1807 -0
  30. package/dist/cjs/x402Client-TQHctrG7.d.ts +1807 -0
  31. package/dist/esm/chunk-ABS7D6VX.mjs +145 -0
  32. package/dist/esm/chunk-ABS7D6VX.mjs.map +1 -0
  33. package/dist/esm/chunk-AGOUMC4P.mjs +68 -0
  34. package/dist/esm/chunk-AGOUMC4P.mjs.map +1 -0
  35. package/dist/esm/chunk-BJTO5JO5.mjs +11 -0
  36. package/dist/esm/chunk-BJTO5JO5.mjs.map +1 -0
  37. package/dist/esm/chunk-FPXAE3OS.mjs +161 -0
  38. package/dist/esm/chunk-FPXAE3OS.mjs.map +1 -0
  39. package/dist/esm/chunk-IL77TMJL.mjs +1275 -0
  40. package/dist/esm/chunk-IL77TMJL.mjs.map +1 -0
  41. package/dist/esm/chunk-VE37GDG2.mjs +7 -0
  42. package/dist/esm/chunk-VE37GDG2.mjs.map +1 -0
  43. package/dist/esm/client/index.d.mts +149 -0
  44. package/dist/esm/client/index.mjs +504 -0
  45. package/dist/esm/client/index.mjs.map +1 -0
  46. package/dist/esm/facilitator/index.d.mts +206 -0
  47. package/dist/esm/facilitator/index.mjs +399 -0
  48. package/dist/esm/facilitator/index.mjs.map +1 -0
  49. package/dist/esm/http/index.d.mts +50 -0
  50. package/dist/esm/http/index.mjs +35 -0
  51. package/dist/esm/http/index.mjs.map +1 -0
  52. package/dist/esm/index.d.mts +3 -0
  53. package/dist/esm/index.mjs +8 -0
  54. package/dist/esm/index.mjs.map +1 -0
  55. package/dist/esm/schemas/index.d.mts +891 -0
  56. package/dist/esm/schemas/index.mjs +70 -0
  57. package/dist/esm/schemas/index.mjs.map +1 -0
  58. package/dist/esm/server/index.d.mts +79 -0
  59. package/dist/esm/server/index.mjs +1318 -0
  60. package/dist/esm/server/index.mjs.map +1 -0
  61. package/dist/esm/types/index.d.mts +1 -0
  62. package/dist/esm/types/index.mjs +14 -0
  63. package/dist/esm/types/index.mjs.map +1 -0
  64. package/dist/esm/types/v1/index.d.mts +1 -0
  65. package/dist/esm/types/v1/index.mjs +1 -0
  66. package/dist/esm/types/v1/index.mjs.map +1 -0
  67. package/dist/esm/utils/index.d.mts +77 -0
  68. package/dist/esm/utils/index.mjs +28 -0
  69. package/dist/esm/utils/index.mjs.map +1 -0
  70. package/dist/esm/x402Client-BgegfQgE.d.mts +1807 -0
  71. package/dist/esm/x402Client-TQHctrG7.d.mts +1807 -0
  72. package/package.json +142 -0
@@ -0,0 +1,2568 @@
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
+ FacilitatorResponseError: () => FacilitatorResponseError,
24
+ HTTPFacilitatorClient: () => HTTPFacilitatorClient,
25
+ RouteConfigurationError: () => RouteConfigurationError,
26
+ SETTLEMENT_OVERRIDES_HEADER: () => SETTLEMENT_OVERRIDES_HEADER,
27
+ assertAcceptsAdditiveExtraAfterSchemeEnrich: () => assertAcceptsAdditiveExtraAfterSchemeEnrich,
28
+ assertAcceptsAllowlistedAfterExtensionEnrich: () => assertAcceptsAllowlistedAfterExtensionEnrich,
29
+ assertAdditivePayloadEnrichment: () => assertAdditivePayloadEnrichment,
30
+ assertAdditiveSettlementExtra: () => assertAdditiveSettlementExtra,
31
+ assertSettleResponseCoreUnchanged: () => assertSettleResponseCoreUnchanged,
32
+ checkIfBazaarNeeded: () => checkIfBazaarNeeded,
33
+ getFacilitatorResponseError: () => getFacilitatorResponseError,
34
+ isVacantStringField: () => isVacantStringField,
35
+ snapshotPaymentRequirementsList: () => snapshotPaymentRequirementsList,
36
+ snapshotSettleResponseCore: () => snapshotSettleResponseCore,
37
+ x402HTTPResourceServer: () => x402HTTPResourceServer,
38
+ x402ResourceServer: () => x402ResourceServer
39
+ });
40
+ module.exports = __toCommonJS(server_exports);
41
+
42
+ // src/types/facilitator.ts
43
+ var VerifyError = class extends Error {
44
+ /**
45
+ * Creates a VerifyError from a failed verification response.
46
+ *
47
+ * @param statusCode - HTTP status code from the facilitator
48
+ * @param response - The verify response containing failure details
49
+ */
50
+ constructor(statusCode, response) {
51
+ const reason = response.invalidReason || "unknown reason";
52
+ const message = response.invalidMessage;
53
+ super(message ? `${reason}: ${message}` : reason);
54
+ this.name = "VerifyError";
55
+ this.statusCode = statusCode;
56
+ this.invalidReason = response.invalidReason;
57
+ this.invalidMessage = response.invalidMessage;
58
+ this.payer = response.payer;
59
+ }
60
+ };
61
+ var SettleError = class extends Error {
62
+ /**
63
+ * Creates a SettleError from a failed settlement response.
64
+ *
65
+ * @param statusCode - HTTP status code from the facilitator
66
+ * @param response - The settle response containing error details
67
+ */
68
+ constructor(statusCode, response) {
69
+ const reason = response.errorReason || "unknown reason";
70
+ const message = response.errorMessage;
71
+ super(message ? `${reason}: ${message}` : reason);
72
+ this.name = "SettleError";
73
+ this.statusCode = statusCode;
74
+ this.errorReason = response.errorReason;
75
+ this.errorMessage = response.errorMessage;
76
+ this.payer = response.payer;
77
+ this.transaction = response.transaction;
78
+ this.network = response.network;
79
+ }
80
+ };
81
+ var FacilitatorResponseError = class extends Error {
82
+ /**
83
+ * Creates a FacilitatorResponseError for malformed facilitator responses.
84
+ *
85
+ * @param message - The boundary error message
86
+ */
87
+ constructor(message) {
88
+ super(message);
89
+ this.name = "FacilitatorResponseError";
90
+ }
91
+ };
92
+ function getFacilitatorResponseError(error) {
93
+ let current = error;
94
+ while (current instanceof Error) {
95
+ if (current instanceof FacilitatorResponseError) {
96
+ return current;
97
+ }
98
+ current = current.cause;
99
+ }
100
+ return null;
101
+ }
102
+
103
+ // src/utils/index.ts
104
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
105
+ var networkPatternToRegExp = (pattern) => {
106
+ const source = escapeRegExp(pattern).replace(/\\\*/g, ".*");
107
+ return new RegExp(`^${source}$`);
108
+ };
109
+ var networkMatchesPattern = (pattern, network) => {
110
+ return networkPatternToRegExp(pattern).test(network);
111
+ };
112
+ var findSchemesByNetwork = (map, network) => {
113
+ let implementationsByScheme = map.get(network);
114
+ if (!implementationsByScheme) {
115
+ for (const [registeredNetworkPattern, implementations] of map.entries()) {
116
+ if (networkMatchesPattern(registeredNetworkPattern, network)) {
117
+ implementationsByScheme = implementations;
118
+ break;
119
+ }
120
+ }
121
+ }
122
+ return implementationsByScheme;
123
+ };
124
+ var findByNetworkAndScheme = (map, scheme, network) => {
125
+ return findSchemesByNetwork(map, network)?.get(scheme);
126
+ };
127
+ var Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;
128
+ function safeBase64Encode(data) {
129
+ if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") {
130
+ const bytes = new TextEncoder().encode(data);
131
+ const binaryString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
132
+ return globalThis.btoa(binaryString);
133
+ }
134
+ return Buffer.from(data, "utf8").toString("base64");
135
+ }
136
+ function safeBase64Decode(data) {
137
+ if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") {
138
+ const binaryString = globalThis.atob(data);
139
+ const bytes = new Uint8Array(binaryString.length);
140
+ for (let i = 0; i < binaryString.length; i++) {
141
+ bytes[i] = binaryString.charCodeAt(i);
142
+ }
143
+ const decoder = new TextDecoder("utf-8");
144
+ return decoder.decode(bytes);
145
+ }
146
+ return Buffer.from(data, "base64").toString("utf-8");
147
+ }
148
+ function deepEqual(obj1, obj2) {
149
+ const normalize = (obj) => {
150
+ if (obj === null || obj === void 0) return JSON.stringify(obj);
151
+ if (typeof obj !== "object") return JSON.stringify(obj);
152
+ if (Array.isArray(obj)) {
153
+ return JSON.stringify(
154
+ obj.map(
155
+ (item) => typeof item === "object" && item !== null ? JSON.parse(normalize(item)) : item
156
+ )
157
+ );
158
+ }
159
+ const sorted = {};
160
+ Object.keys(obj).sort().forEach((key) => {
161
+ const value = obj[key];
162
+ sorted[key] = typeof value === "object" && value !== null ? JSON.parse(normalize(value)) : value;
163
+ });
164
+ return JSON.stringify(sorted);
165
+ };
166
+ try {
167
+ return normalize(obj1) === normalize(obj2);
168
+ } catch {
169
+ return JSON.stringify(obj1) === JSON.stringify(obj2);
170
+ }
171
+ }
172
+
173
+ // src/server/hookPolicy.ts
174
+ function isVacantStringField(value) {
175
+ return value.trim() === "";
176
+ }
177
+ function snapshotPaymentRequirementsList(requirements) {
178
+ return requirements.map((req) => ({
179
+ ...req,
180
+ extra: structuredClone(req.extra)
181
+ }));
182
+ }
183
+ function assertAcceptsAllowlistedAfterExtensionEnrich(baseline, current, extensionKey) {
184
+ if (baseline.length !== current.length) {
185
+ throw new Error(
186
+ `[x402] extension "${extensionKey}" violated accepts mutation policy: accepts length changed (${baseline.length} \u2192 ${current.length})`
187
+ );
188
+ }
189
+ for (let i = 0; i < baseline.length; i++) {
190
+ const b = baseline[i];
191
+ const c = current[i];
192
+ if (b.scheme !== c.scheme || b.network !== c.network) {
193
+ throw new Error(
194
+ `[x402] extension "${extensionKey}" violated accepts mutation policy: scheme/network are immutable (index ${i})`
195
+ );
196
+ }
197
+ if (b.maxTimeoutSeconds !== c.maxTimeoutSeconds) {
198
+ throw new Error(
199
+ `[x402] extension "${extensionKey}" violated accepts mutation policy: maxTimeoutSeconds is immutable (index ${i})`
200
+ );
201
+ }
202
+ for (const field of ["payTo", "amount", "asset"]) {
203
+ const bv = b[field];
204
+ const cv = c[field];
205
+ if (!isVacantStringField(bv) && cv !== bv) {
206
+ throw new Error(
207
+ `[x402] extension "${extensionKey}" violated accepts mutation policy: "${field}" may only be set when the resource left it vacant (""); non-vacant values are immutable (index ${i})`
208
+ );
209
+ }
210
+ }
211
+ for (const key of Object.keys(b.extra)) {
212
+ if (!Object.prototype.hasOwnProperty.call(c.extra, key)) {
213
+ throw new Error(
214
+ `[x402] extension "${extensionKey}" violated accepts mutation policy: extra["${key}"] was removed (index ${i})`
215
+ );
216
+ }
217
+ if (!deepEqual(c.extra[key], b.extra[key])) {
218
+ throw new Error(
219
+ `[x402] extension "${extensionKey}" violated accepts mutation policy: extra["${key}"] may not be changed (index ${i})`
220
+ );
221
+ }
222
+ }
223
+ }
224
+ }
225
+ function assertAcceptsAdditiveExtraAfterSchemeEnrich(baseline, current, scheme, network) {
226
+ if (baseline.length !== current.length) {
227
+ throw new Error(
228
+ `[x402] scheme "${scheme}" violated accepts mutation policy: accepts length changed (${baseline.length} \u2192 ${current.length})`
229
+ );
230
+ }
231
+ for (let i = 0; i < baseline.length; i++) {
232
+ const b = baseline[i];
233
+ const c = current[i];
234
+ const isMatchingAccept = b.scheme === scheme && b.network === network;
235
+ if (b.scheme !== c.scheme || b.network !== c.network) {
236
+ throw new Error(
237
+ `[x402] scheme "${scheme}" violated accepts mutation policy: scheme/network are immutable (index ${i})`
238
+ );
239
+ }
240
+ if (b.maxTimeoutSeconds !== c.maxTimeoutSeconds || b.payTo !== c.payTo || b.amount !== c.amount || b.asset !== c.asset) {
241
+ throw new Error(
242
+ `[x402] scheme "${scheme}" violated accepts mutation policy: payment terms are immutable (index ${i})`
243
+ );
244
+ }
245
+ for (const key of Object.keys(b.extra)) {
246
+ if (!Object.prototype.hasOwnProperty.call(c.extra, key)) {
247
+ throw new Error(
248
+ `[x402] scheme "${scheme}" violated accepts mutation policy: extra["${key}"] was removed (index ${i})`
249
+ );
250
+ }
251
+ if (!deepEqual(c.extra[key], b.extra[key])) {
252
+ throw new Error(
253
+ `[x402] scheme "${scheme}" violated accepts mutation policy: extra["${key}"] may not be changed (index ${i})`
254
+ );
255
+ }
256
+ }
257
+ if (!isMatchingAccept && Object.keys(c.extra).length !== Object.keys(b.extra).length) {
258
+ throw new Error(
259
+ `[x402] scheme "${scheme}" violated accepts mutation policy: only matching accepts may receive new extra fields (index ${i})`
260
+ );
261
+ }
262
+ }
263
+ }
264
+ function snapshotSettleResponseCore(result) {
265
+ return {
266
+ success: result.success,
267
+ transaction: result.transaction,
268
+ network: result.network,
269
+ amount: result.amount,
270
+ payer: result.payer,
271
+ errorReason: result.errorReason,
272
+ errorMessage: result.errorMessage
273
+ };
274
+ }
275
+ function assertSettleResponseCoreUnchanged(before, after, extensionKey) {
276
+ const keys = [
277
+ "success",
278
+ "transaction",
279
+ "network",
280
+ "amount",
281
+ "payer",
282
+ "errorReason",
283
+ "errorMessage"
284
+ ];
285
+ for (const k of keys) {
286
+ if (!deepEqual(after[k], before[k])) {
287
+ throw new Error(
288
+ `[x402] extension "${extensionKey}" violated settlement mutation policy: field "${String(k)}" is immutable after facilitator settle`
289
+ );
290
+ }
291
+ }
292
+ }
293
+ function assertAdditivePayloadEnrichment(payload, enrichment, callerLabel) {
294
+ for (const key of Object.keys(enrichment)) {
295
+ if (!Object.prototype.hasOwnProperty.call(payload, key)) continue;
296
+ throw new Error(
297
+ `[x402] ${callerLabel} violated settlement payload enrichment policy: "${key}" already exists on the client payload`
298
+ );
299
+ }
300
+ }
301
+ function isPlainRecord(value) {
302
+ return typeof value === "object" && value !== null && !Array.isArray(value);
303
+ }
304
+ function assertAdditiveSettlementExtra(extra, enrichment, callerLabel) {
305
+ assertAdditiveRecord(extra, enrichment, callerLabel, "extra");
306
+ }
307
+ function mergeAdditiveSettlementExtra(extra, enrichment) {
308
+ return mergeAdditiveRecord(extra, enrichment);
309
+ }
310
+ function assertAdditiveRecord(target, enrichment, callerLabel, path) {
311
+ for (const [key, enrichmentValue] of Object.entries(enrichment)) {
312
+ const nextPath = `${path}["${key}"]`;
313
+ if (!Object.prototype.hasOwnProperty.call(target, key)) continue;
314
+ const targetValue = target[key];
315
+ if (isPlainRecord(targetValue) && isPlainRecord(enrichmentValue)) {
316
+ assertAdditiveRecord(targetValue, enrichmentValue, callerLabel, nextPath);
317
+ continue;
318
+ }
319
+ throw new Error(
320
+ `[x402] ${callerLabel} violated settlement response enrichment policy: ${nextPath} already exists on the settlement result`
321
+ );
322
+ }
323
+ }
324
+ function mergeAdditiveRecord(target, enrichment) {
325
+ const merged = { ...target };
326
+ for (const [key, enrichmentValue] of Object.entries(enrichment)) {
327
+ const targetValue = merged[key];
328
+ if (isPlainRecord(targetValue) && isPlainRecord(enrichmentValue)) {
329
+ merged[key] = mergeAdditiveRecord(targetValue, enrichmentValue);
330
+ continue;
331
+ }
332
+ merged[key] = enrichmentValue;
333
+ }
334
+ return merged;
335
+ }
336
+
337
+ // src/schemas/index.ts
338
+ var import_zod = require("zod");
339
+ var import_zod2 = require("zod");
340
+ var NonEmptyString = import_zod.z.string().min(1);
341
+ var Any = import_zod.z.record(import_zod.z.unknown());
342
+ var OptionalAny = import_zod.z.record(import_zod.z.unknown()).optional().nullable();
343
+ var NetworkSchemaV1 = NonEmptyString;
344
+ var NetworkSchemaV2 = import_zod.z.string().min(3).refine((val) => val.includes(":"), {
345
+ message: "Network must be in CAIP-2 format (e.g., 'eip155:84532')"
346
+ });
347
+ var NetworkSchema = import_zod.z.union([NetworkSchemaV1, NetworkSchemaV2]);
348
+ var PRINTABLE_ASCII_REGEX = /^[\x20-\x7e]+$/;
349
+ var ResourceInfoSchema = import_zod.z.object({
350
+ url: NonEmptyString,
351
+ description: import_zod.z.string().optional(),
352
+ mimeType: import_zod.z.string().optional(),
353
+ serviceName: import_zod.z.string().min(1).max(32).regex(PRINTABLE_ASCII_REGEX).optional(),
354
+ tags: import_zod.z.array(import_zod.z.string().min(1).max(32).regex(PRINTABLE_ASCII_REGEX)).max(5).optional(),
355
+ iconUrl: import_zod.z.string().max(2048).optional()
356
+ });
357
+ var PaymentRequirementsV1Schema = import_zod.z.object({
358
+ scheme: NonEmptyString,
359
+ network: NetworkSchemaV1,
360
+ maxAmountRequired: NonEmptyString,
361
+ resource: NonEmptyString,
362
+ // URL string in V1
363
+ description: import_zod.z.string(),
364
+ mimeType: import_zod.z.string().optional(),
365
+ outputSchema: Any.optional().nullable(),
366
+ payTo: NonEmptyString,
367
+ maxTimeoutSeconds: import_zod.z.number().positive(),
368
+ asset: NonEmptyString,
369
+ extra: OptionalAny
370
+ });
371
+ var PaymentRequiredV1Schema = import_zod.z.object({
372
+ x402Version: import_zod.z.literal(1),
373
+ error: import_zod.z.string().optional(),
374
+ accepts: import_zod.z.array(PaymentRequirementsV1Schema).min(1)
375
+ });
376
+ var PaymentPayloadV1Schema = import_zod.z.object({
377
+ x402Version: import_zod.z.literal(1),
378
+ scheme: NonEmptyString,
379
+ network: NetworkSchemaV1,
380
+ payload: Any
381
+ });
382
+ var PaymentRequirementsV2Schema = import_zod.z.object({
383
+ scheme: NonEmptyString,
384
+ network: NetworkSchemaV2,
385
+ amount: NonEmptyString,
386
+ asset: NonEmptyString,
387
+ payTo: NonEmptyString,
388
+ maxTimeoutSeconds: import_zod.z.number().positive(),
389
+ extra: OptionalAny
390
+ });
391
+ var PaymentRequiredV2Schema = import_zod.z.object({
392
+ x402Version: import_zod.z.literal(2),
393
+ error: import_zod.z.string().optional(),
394
+ resource: ResourceInfoSchema,
395
+ accepts: import_zod.z.array(PaymentRequirementsV2Schema).min(1),
396
+ extensions: OptionalAny
397
+ });
398
+ var PaymentPayloadV2Schema = import_zod.z.object({
399
+ x402Version: import_zod.z.literal(2),
400
+ resource: ResourceInfoSchema.optional(),
401
+ accepted: PaymentRequirementsV2Schema,
402
+ payload: Any,
403
+ extensions: OptionalAny
404
+ });
405
+ var PaymentRequirementsSchema = import_zod.z.union([
406
+ PaymentRequirementsV1Schema,
407
+ PaymentRequirementsV2Schema
408
+ ]);
409
+ var PaymentRequiredSchema = import_zod.z.discriminatedUnion("x402Version", [
410
+ PaymentRequiredV1Schema,
411
+ PaymentRequiredV2Schema
412
+ ]);
413
+ var PaymentPayloadSchema = import_zod.z.discriminatedUnion("x402Version", [
414
+ PaymentPayloadV1Schema,
415
+ PaymentPayloadV2Schema
416
+ ]);
417
+
418
+ // src/http/httpFacilitatorClient.ts
419
+ var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
420
+ var GET_SUPPORTED_RETRIES = 3;
421
+ var GET_SUPPORTED_RETRY_DELAY_MS = 1e3;
422
+ var MAX_RETRY_DELAY_MS = 3e4;
423
+ function computeRetryDelay(retryAfter, attempt) {
424
+ let delay = null;
425
+ if (retryAfter !== null) {
426
+ const seconds = Number(retryAfter);
427
+ if (!isNaN(seconds)) {
428
+ delay = seconds * 1e3;
429
+ } else {
430
+ const retryDate = Date.parse(retryAfter);
431
+ if (!isNaN(retryDate)) {
432
+ delay = retryDate - Date.now();
433
+ }
434
+ }
435
+ }
436
+ if (delay === null || delay <= 0) {
437
+ delay = GET_SUPPORTED_RETRY_DELAY_MS * Math.pow(2, attempt);
438
+ }
439
+ return Math.min(delay, MAX_RETRY_DELAY_MS);
440
+ }
441
+ var verifyResponseSchema = import_zod2.z.object({
442
+ isValid: import_zod2.z.boolean(),
443
+ invalidReason: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
444
+ invalidMessage: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
445
+ payer: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
446
+ extensions: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).nullish().transform((v) => v ?? void 0),
447
+ extra: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).nullish().transform((v) => v ?? void 0)
448
+ });
449
+ var settleResponseSchema = import_zod2.z.object({
450
+ success: import_zod2.z.boolean(),
451
+ errorReason: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
452
+ errorMessage: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
453
+ payer: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
454
+ transaction: import_zod2.z.string(),
455
+ network: import_zod2.z.custom((value) => typeof value === "string"),
456
+ amount: import_zod2.z.string().nullish().transform((v) => v ?? void 0),
457
+ extensions: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).nullish().transform((v) => v ?? void 0),
458
+ extra: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).nullish().transform((v) => v ?? void 0)
459
+ });
460
+ var supportedKindSchema = import_zod2.z.object({
461
+ x402Version: import_zod2.z.number(),
462
+ scheme: import_zod2.z.string(),
463
+ network: import_zod2.z.custom(
464
+ (value) => typeof value === "string"
465
+ ),
466
+ extra: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).nullish().transform((v) => v ?? void 0)
467
+ });
468
+ var supportedResponseSchema = import_zod2.z.object({
469
+ kinds: import_zod2.z.array(supportedKindSchema),
470
+ extensions: import_zod2.z.array(import_zod2.z.string()).default([]),
471
+ signers: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string())).default({})
472
+ });
473
+ function responseExcerpt(text, limit = 200) {
474
+ const compact = text.trim().replace(/\s+/g, " ");
475
+ if (!compact) {
476
+ return "<empty response>";
477
+ }
478
+ if (compact.length <= limit) {
479
+ return compact;
480
+ }
481
+ return `${compact.slice(0, limit - 3)}...`;
482
+ }
483
+ var EXTENSION_RESPONSE_LOG_FIELD_ALLOWLIST = ["status", "rejectedReason", "reason", "code"];
484
+ function logExtensionResponsesHeader(response) {
485
+ const header = response.headers.get("EXTENSION-RESPONSES");
486
+ if (!header) return;
487
+ try {
488
+ const decoded = JSON.parse(safeBase64Decode(header));
489
+ if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return;
490
+ const sanitized = {};
491
+ for (const [extensionKey, payload] of Object.entries(decoded)) {
492
+ const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
493
+ const filtered = {};
494
+ for (const key of EXTENSION_RESPONSE_LOG_FIELD_ALLOWLIST) {
495
+ if (source[key] !== void 0) {
496
+ filtered[key] = source[key];
497
+ }
498
+ }
499
+ sanitized[extensionKey] = filtered;
500
+ }
501
+ console.log(`[x402] extension responses: ${JSON.stringify(sanitized)}`);
502
+ } catch {
503
+ }
504
+ }
505
+ async function parseSuccessResponse(response, schema, operation) {
506
+ const text = await response.text();
507
+ let data;
508
+ try {
509
+ data = JSON.parse(text);
510
+ } catch {
511
+ throw new FacilitatorResponseError(
512
+ `Facilitator ${operation} returned invalid JSON: ${responseExcerpt(text)}`
513
+ );
514
+ }
515
+ const parsed = schema.safeParse(data);
516
+ if (!parsed.success) {
517
+ throw new FacilitatorResponseError(
518
+ `Facilitator ${operation} returned invalid data: ${responseExcerpt(text)}`
519
+ );
520
+ }
521
+ return parsed.data;
522
+ }
523
+ var HTTPFacilitatorClient = class {
524
+ /**
525
+ * Creates a new HTTPFacilitatorClient instance.
526
+ *
527
+ * @param config - Configuration options for the facilitator client
528
+ */
529
+ constructor(config) {
530
+ this.url = (config?.url || DEFAULT_FACILITATOR_URL).replace(/\/+$/, "");
531
+ this._createAuthHeaders = config?.createAuthHeaders;
532
+ }
533
+ /**
534
+ * Verify a payment with the facilitator
535
+ *
536
+ * @param paymentPayload - The payment to verify
537
+ * @param paymentRequirements - The requirements to verify against
538
+ * @returns Verification response
539
+ */
540
+ async verify(paymentPayload, paymentRequirements) {
541
+ let headers = {
542
+ "Content-Type": "application/json"
543
+ };
544
+ if (this._createAuthHeaders) {
545
+ const authHeaders = await this.createAuthHeaders("verify");
546
+ headers = { ...headers, ...authHeaders.headers };
547
+ }
548
+ const response = await fetch(`${this.url}/verify`, {
549
+ method: "POST",
550
+ headers,
551
+ redirect: "follow",
552
+ body: JSON.stringify({
553
+ x402Version: paymentPayload.x402Version,
554
+ paymentPayload: this.toJsonSafe(paymentPayload),
555
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
556
+ })
557
+ });
558
+ if (!response.ok) {
559
+ const text = await response.text();
560
+ let data;
561
+ try {
562
+ data = JSON.parse(text);
563
+ } catch {
564
+ throw new Error(`Facilitator verify failed (${response.status}): ${responseExcerpt(text)}`);
565
+ }
566
+ if (typeof data === "object" && data !== null && "isValid" in data) {
567
+ throw new VerifyError(response.status, data);
568
+ }
569
+ throw new Error(
570
+ `Facilitator verify failed (${response.status}): ${responseExcerpt(JSON.stringify(data))}`
571
+ );
572
+ }
573
+ const verifyResult = await parseSuccessResponse(response, verifyResponseSchema, "verify");
574
+ logExtensionResponsesHeader(response);
575
+ return verifyResult;
576
+ }
577
+ /**
578
+ * Settle a payment with the facilitator
579
+ *
580
+ * @param paymentPayload - The payment to settle
581
+ * @param paymentRequirements - The requirements for settlement
582
+ * @returns Settlement response
583
+ */
584
+ async settle(paymentPayload, paymentRequirements) {
585
+ let headers = {
586
+ "Content-Type": "application/json"
587
+ };
588
+ if (this._createAuthHeaders) {
589
+ const authHeaders = await this.createAuthHeaders("settle");
590
+ headers = { ...headers, ...authHeaders.headers };
591
+ }
592
+ const response = await fetch(`${this.url}/settle`, {
593
+ method: "POST",
594
+ headers,
595
+ redirect: "follow",
596
+ body: JSON.stringify({
597
+ x402Version: paymentPayload.x402Version,
598
+ paymentPayload: this.toJsonSafe(paymentPayload),
599
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
600
+ })
601
+ });
602
+ if (!response.ok) {
603
+ const text = await response.text();
604
+ let data;
605
+ try {
606
+ data = JSON.parse(text);
607
+ } catch {
608
+ throw new Error(`Facilitator settle failed (${response.status}): ${responseExcerpt(text)}`);
609
+ }
610
+ if (typeof data === "object" && data !== null && "success" in data) {
611
+ throw new SettleError(response.status, data);
612
+ }
613
+ throw new Error(
614
+ `Facilitator settle failed (${response.status}): ${responseExcerpt(JSON.stringify(data))}`
615
+ );
616
+ }
617
+ const settleResult = await parseSuccessResponse(response, settleResponseSchema, "settle");
618
+ logExtensionResponsesHeader(response);
619
+ return settleResult;
620
+ }
621
+ /**
622
+ * Get supported payment kinds and extensions from the facilitator.
623
+ * Retries with exponential backoff on 429 rate limit errors.
624
+ *
625
+ * @returns Supported payment kinds and extensions
626
+ */
627
+ async getSupported() {
628
+ let headers = {
629
+ "Content-Type": "application/json"
630
+ };
631
+ if (this._createAuthHeaders) {
632
+ const authHeaders = await this.createAuthHeaders("supported");
633
+ headers = { ...headers, ...authHeaders.headers };
634
+ }
635
+ let lastError = null;
636
+ for (let attempt = 0; attempt < GET_SUPPORTED_RETRIES; attempt++) {
637
+ const response = await fetch(`${this.url}/supported`, {
638
+ method: "GET",
639
+ headers,
640
+ redirect: "follow"
641
+ });
642
+ if (response.ok) {
643
+ return parseSuccessResponse(response, supportedResponseSchema, "supported");
644
+ }
645
+ const errorText = await response.text().catch(() => response.statusText);
646
+ lastError = new Error(
647
+ `Facilitator getSupported failed (${response.status}): ${responseExcerpt(errorText)}`
648
+ );
649
+ if (response.status === 429 && attempt < GET_SUPPORTED_RETRIES - 1) {
650
+ const delay = computeRetryDelay(response.headers.get("Retry-After"), attempt);
651
+ await new Promise((resolve) => setTimeout(resolve, delay));
652
+ continue;
653
+ }
654
+ throw lastError;
655
+ }
656
+ throw lastError ?? new Error("Facilitator getSupported failed after retries");
657
+ }
658
+ /**
659
+ * Creates authentication headers for a specific path.
660
+ *
661
+ * @param path - The path to create authentication headers for (e.g., "verify", "settle", "supported")
662
+ * @returns An object containing the authentication headers for the specified path
663
+ */
664
+ async createAuthHeaders(path) {
665
+ if (this._createAuthHeaders) {
666
+ const authHeaders = await this._createAuthHeaders();
667
+ return {
668
+ headers: authHeaders[path] ?? {}
669
+ };
670
+ }
671
+ return {
672
+ headers: {}
673
+ };
674
+ }
675
+ /**
676
+ * Helper to convert objects to JSON-safe format.
677
+ * Handles BigInt and other non-JSON types.
678
+ *
679
+ * @param obj - The object to convert
680
+ * @returns The JSON-safe representation of the object
681
+ */
682
+ toJsonSafe(obj) {
683
+ return JSON.parse(
684
+ JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() : value)
685
+ );
686
+ }
687
+ };
688
+
689
+ // src/index.ts
690
+ var x402Version = 2;
691
+
692
+ // src/server/x402ResourceServer.ts
693
+ function resolveSettlementOverrideAmount(rawAmount, requirements, decimals = 6) {
694
+ const percentMatch = rawAmount.match(/^(\d+(?:\.\d{0,2})?)%$/);
695
+ if (percentMatch) {
696
+ const [intPart, decPart = ""] = percentMatch[1].split(".");
697
+ const scaledPercent = BigInt(intPart) * 100n + BigInt(decPart.padEnd(2, "0").slice(0, 2));
698
+ const base = BigInt(requirements.amount);
699
+ return (base * scaledPercent / 10000n).toString();
700
+ }
701
+ const dollarMatch = rawAmount.match(/^\$(\d+(?:\.\d+)?)$/);
702
+ if (dollarMatch) {
703
+ const dollars = parseFloat(dollarMatch[1]);
704
+ return Math.round(dollars * 10 ** decimals).toString();
705
+ }
706
+ return rawAmount;
707
+ }
708
+ var x402ResourceServer = class {
709
+ /**
710
+ * Creates a new x402ResourceServer instance.
711
+ *
712
+ * @param facilitatorClients - Optional facilitator client(s) for payment processing
713
+ */
714
+ constructor(facilitatorClients) {
715
+ this.registeredServerSchemes = /* @__PURE__ */ new Map();
716
+ this.schemeHookAdapters = /* @__PURE__ */ new Map();
717
+ this.supportedResponsesMap = /* @__PURE__ */ new Map();
718
+ this.facilitatorClientsMap = /* @__PURE__ */ new Map();
719
+ this.registeredExtensions = /* @__PURE__ */ new Map();
720
+ this.extensionHookAdapters = /* @__PURE__ */ new Map();
721
+ this.beforeVerifyHooks = [];
722
+ this.afterVerifyHooks = [];
723
+ this.onVerifyFailureHooks = [];
724
+ this.beforeSettleHooks = [];
725
+ this.afterSettleHooks = [];
726
+ this.onSettleFailureHooks = [];
727
+ this.onVerifiedPaymentCanceledHooks = [];
728
+ if (!facilitatorClients) {
729
+ this.facilitatorClients = [new HTTPFacilitatorClient()];
730
+ } else if (Array.isArray(facilitatorClients)) {
731
+ this.facilitatorClients = facilitatorClients.length > 0 ? facilitatorClients : [new HTTPFacilitatorClient()];
732
+ } else {
733
+ this.facilitatorClients = [facilitatorClients];
734
+ }
735
+ }
736
+ /**
737
+ * Register a scheme/network server implementation.
738
+ *
739
+ * @param network - The network identifier
740
+ * @param server - The scheme/network server implementation
741
+ * @returns The x402ResourceServer instance for chaining
742
+ */
743
+ register(network, server) {
744
+ if (!this.registeredServerSchemes.has(network)) {
745
+ this.registeredServerSchemes.set(network, /* @__PURE__ */ new Map());
746
+ }
747
+ const serverByScheme = this.registeredServerSchemes.get(network);
748
+ serverByScheme.set(server.scheme, server);
749
+ if (!this.schemeHookAdapters.has(network)) {
750
+ this.schemeHookAdapters.set(network, /* @__PURE__ */ new Map());
751
+ }
752
+ const hooksByScheme = this.schemeHookAdapters.get(network);
753
+ const hooks = server.schemeHooks;
754
+ if (!hooks) {
755
+ hooksByScheme.delete(server.scheme);
756
+ return this;
757
+ }
758
+ const handles = {};
759
+ if (hooks.onBeforeVerify) handles.beforeVerify = hooks.onBeforeVerify;
760
+ if (hooks.onAfterVerify) handles.afterVerify = hooks.onAfterVerify;
761
+ if (hooks.onVerifyFailure) handles.onVerifyFailure = hooks.onVerifyFailure;
762
+ if (hooks.onBeforeSettle) handles.beforeSettle = hooks.onBeforeSettle;
763
+ if (hooks.onAfterSettle) handles.afterSettle = hooks.onAfterSettle;
764
+ if (hooks.onSettleFailure) handles.onSettleFailure = hooks.onSettleFailure;
765
+ if (hooks.onVerifiedPaymentCanceled) {
766
+ handles.onVerifiedPaymentCanceled = hooks.onVerifiedPaymentCanceled;
767
+ }
768
+ if (Object.keys(handles).length > 0) {
769
+ hooksByScheme.set(server.scheme, handles);
770
+ } else {
771
+ hooksByScheme.delete(server.scheme);
772
+ }
773
+ return this;
774
+ }
775
+ /**
776
+ * Check if a scheme is registered for a given network.
777
+ *
778
+ * @param network - The network identifier
779
+ * @param scheme - The payment scheme name
780
+ * @returns True if the scheme is registered for the network, false otherwise
781
+ */
782
+ hasRegisteredScheme(network, scheme) {
783
+ return !!findByNetworkAndScheme(this.registeredServerSchemes, scheme, network);
784
+ }
785
+ /**
786
+ * Returns the decimal precision for the asset specified in the given payment requirements.
787
+ * Looks up the registered scheme for the network and delegates to its getAssetDecimals
788
+ * method if available. Falls back to 6 (standard for USDC stablecoins) when the scheme
789
+ * does not implement getAssetDecimals or is not registered.
790
+ *
791
+ * @param requirements - The payment requirements containing scheme, network, and asset
792
+ * @returns The number of decimal places for the asset
793
+ */
794
+ getAssetDecimalsForRequirements(requirements) {
795
+ const scheme = findByNetworkAndScheme(
796
+ this.registeredServerSchemes,
797
+ requirements.scheme,
798
+ requirements.network
799
+ );
800
+ return scheme?.getAssetDecimals?.(requirements.asset ?? "", requirements.network) ?? 6;
801
+ }
802
+ /**
803
+ * Registers a resource server extension (enrichment and optional verify/settle hooks).
804
+ * Re-registering the same key overwrites; omitting `hooks` removes adapter handles for that key.
805
+ *
806
+ * @param extension - Extension definition including `key` and optional `hooks`
807
+ * @returns This server instance for chaining
808
+ */
809
+ registerExtension(extension) {
810
+ this.registeredExtensions.set(extension.key, extension);
811
+ const extensionKey = extension.key;
812
+ const extensionHooks = extension.hooks;
813
+ if (!extensionHooks) {
814
+ this.extensionHookAdapters.delete(extensionKey);
815
+ return this;
816
+ }
817
+ const handles = {};
818
+ const bindExtensionHookAdapter = (extensionHookKey, adapterPhase) => {
819
+ const impl = extensionHooks[extensionHookKey];
820
+ if (!impl) return;
821
+ handles[adapterPhase] = (async (ctx) => {
822
+ if (ctx.declaredExtensions[extensionKey] === void 0) return;
823
+ return impl(
824
+ ctx.declaredExtensions[extensionKey],
825
+ ctx
826
+ );
827
+ });
828
+ };
829
+ bindExtensionHookAdapter("onBeforeVerify", "beforeVerify");
830
+ bindExtensionHookAdapter("onAfterVerify", "afterVerify");
831
+ bindExtensionHookAdapter("onVerifyFailure", "onVerifyFailure");
832
+ bindExtensionHookAdapter("onBeforeSettle", "beforeSettle");
833
+ bindExtensionHookAdapter("onAfterSettle", "afterSettle");
834
+ bindExtensionHookAdapter("onSettleFailure", "onSettleFailure");
835
+ bindExtensionHookAdapter("onVerifiedPaymentCanceled", "onVerifiedPaymentCanceled");
836
+ if (Object.keys(handles).length > 0) {
837
+ this.extensionHookAdapters.set(extensionKey, handles);
838
+ } else {
839
+ this.extensionHookAdapters.delete(extensionKey);
840
+ }
841
+ return this;
842
+ }
843
+ /**
844
+ * Check if an extension is registered.
845
+ *
846
+ * @param key - The extension key
847
+ * @returns True if the extension is registered
848
+ */
849
+ hasExtension(key) {
850
+ return this.registeredExtensions.has(key);
851
+ }
852
+ /**
853
+ * Get all registered extensions.
854
+ *
855
+ * @returns Array of registered extensions
856
+ */
857
+ getExtensions() {
858
+ return Array.from(this.registeredExtensions.values());
859
+ }
860
+ /**
861
+ * Enriches declared extensions using registered extension hooks.
862
+ *
863
+ * @param declaredExtensions - Extensions declared on the route
864
+ * @param transportContext - Transport-specific context (HTTP, A2A, MCP, etc.)
865
+ * @returns Enriched extensions map
866
+ */
867
+ enrichExtensions(declaredExtensions, transportContext) {
868
+ const enriched = {};
869
+ for (const [key, declaration] of Object.entries(declaredExtensions)) {
870
+ const extension = this.registeredExtensions.get(key);
871
+ if (extension?.enrichDeclaration) {
872
+ try {
873
+ enriched[key] = extension.enrichDeclaration(declaration, transportContext);
874
+ } catch (error) {
875
+ this.warnExtensionHookFailure(key, "enrichDeclaration", error);
876
+ enriched[key] = declaration;
877
+ }
878
+ } else {
879
+ enriched[key] = declaration;
880
+ }
881
+ }
882
+ return enriched;
883
+ }
884
+ /**
885
+ * Register a hook to execute before payment verification.
886
+ * Can abort verification by returning { abort: true, reason: string }
887
+ *
888
+ * @param hook - The hook function to register
889
+ * @returns The x402ResourceServer instance for chaining
890
+ */
891
+ onBeforeVerify(hook) {
892
+ this.beforeVerifyHooks.push(hook);
893
+ return this;
894
+ }
895
+ /**
896
+ * Register a hook to execute after successful payment verification.
897
+ *
898
+ * @param hook - The hook function to register
899
+ * @returns The x402ResourceServer instance for chaining
900
+ */
901
+ onAfterVerify(hook) {
902
+ this.afterVerifyHooks.push(hook);
903
+ return this;
904
+ }
905
+ /**
906
+ * Register a hook to execute when payment verification fails.
907
+ * Can recover from failure by returning { recovered: true, result: VerifyResponse }
908
+ *
909
+ * @param hook - The hook function to register
910
+ * @returns The x402ResourceServer instance for chaining
911
+ */
912
+ onVerifyFailure(hook) {
913
+ this.onVerifyFailureHooks.push(hook);
914
+ return this;
915
+ }
916
+ /**
917
+ * Register a hook to execute before payment settlement.
918
+ * Can abort settlement by returning { abort: true, reason: string }
919
+ *
920
+ * @param hook - The hook function to register
921
+ * @returns The x402ResourceServer instance for chaining
922
+ */
923
+ onBeforeSettle(hook) {
924
+ this.beforeSettleHooks.push(hook);
925
+ return this;
926
+ }
927
+ /**
928
+ * Register a hook to execute after successful payment settlement.
929
+ *
930
+ * @param hook - The hook function to register
931
+ * @returns The x402ResourceServer instance for chaining
932
+ */
933
+ onAfterSettle(hook) {
934
+ this.afterSettleHooks.push(hook);
935
+ return this;
936
+ }
937
+ /**
938
+ * Register a hook to execute when payment settlement fails.
939
+ * Can recover from failure by returning { recovered: true, result: SettleResponse }
940
+ *
941
+ * @param hook - The hook function to register
942
+ * @returns The x402ResourceServer instance for chaining
943
+ */
944
+ onSettleFailure(hook) {
945
+ this.onSettleFailureHooks.push(hook);
946
+ return this;
947
+ }
948
+ /**
949
+ * Register a hook to execute when verified payment work is canceled before settlement.
950
+ *
951
+ * @param hook - The hook function to register
952
+ * @returns The x402ResourceServer instance for chaining
953
+ */
954
+ onVerifiedPaymentCanceled(hook) {
955
+ this.onVerifiedPaymentCanceledHooks.push(hook);
956
+ return this;
957
+ }
958
+ /**
959
+ * Initialize by fetching supported kinds from all facilitators
960
+ * Creates mappings for supported responses and facilitator clients
961
+ * Earlier facilitators in the array get precedence
962
+ */
963
+ async initialize() {
964
+ this.supportedResponsesMap.clear();
965
+ this.facilitatorClientsMap.clear();
966
+ let lastError;
967
+ for (const facilitatorClient of this.facilitatorClients) {
968
+ try {
969
+ const supported = await facilitatorClient.getSupported();
970
+ for (const kind of supported.kinds) {
971
+ const x402Version2 = kind.x402Version;
972
+ if (!this.supportedResponsesMap.has(x402Version2)) {
973
+ this.supportedResponsesMap.set(x402Version2, /* @__PURE__ */ new Map());
974
+ }
975
+ const responseVersionMap = this.supportedResponsesMap.get(x402Version2);
976
+ if (!this.facilitatorClientsMap.has(x402Version2)) {
977
+ this.facilitatorClientsMap.set(x402Version2, /* @__PURE__ */ new Map());
978
+ }
979
+ const clientVersionMap = this.facilitatorClientsMap.get(x402Version2);
980
+ if (!responseVersionMap.has(kind.network)) {
981
+ responseVersionMap.set(kind.network, /* @__PURE__ */ new Map());
982
+ }
983
+ const responseNetworkMap = responseVersionMap.get(kind.network);
984
+ if (!clientVersionMap.has(kind.network)) {
985
+ clientVersionMap.set(kind.network, /* @__PURE__ */ new Map());
986
+ }
987
+ const clientNetworkMap = clientVersionMap.get(kind.network);
988
+ if (!responseNetworkMap.has(kind.scheme)) {
989
+ responseNetworkMap.set(kind.scheme, supported);
990
+ clientNetworkMap.set(kind.scheme, facilitatorClient);
991
+ }
992
+ }
993
+ } catch (error) {
994
+ lastError = error;
995
+ console.warn(`Failed to fetch supported kinds from facilitator: ${error}`);
996
+ }
997
+ }
998
+ if (this.supportedResponsesMap.size === 0) {
999
+ throw lastError ? new Error(
1000
+ "Failed to initialize: no supported payment kinds loaded from any facilitator.",
1001
+ {
1002
+ cause: lastError
1003
+ }
1004
+ ) : new Error(
1005
+ "Failed to initialize: no supported payment kinds loaded from any facilitator."
1006
+ );
1007
+ }
1008
+ }
1009
+ /**
1010
+ * Get supported kind for a specific version, network, and scheme
1011
+ *
1012
+ * @param x402Version - The x402 version
1013
+ * @param network - The network identifier
1014
+ * @param scheme - The payment scheme
1015
+ * @returns The supported kind or undefined if not found
1016
+ */
1017
+ getSupportedKind(x402Version2, network, scheme) {
1018
+ const versionMap = this.supportedResponsesMap.get(x402Version2);
1019
+ if (!versionMap) return void 0;
1020
+ const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
1021
+ if (!supportedResponse) return void 0;
1022
+ return supportedResponse.kinds.find(
1023
+ (kind) => kind.x402Version === x402Version2 && kind.network === network && kind.scheme === scheme
1024
+ );
1025
+ }
1026
+ /**
1027
+ * Get facilitator extensions for a specific version, network, and scheme
1028
+ *
1029
+ * @param x402Version - The x402 version
1030
+ * @param network - The network identifier
1031
+ * @param scheme - The payment scheme
1032
+ * @returns The facilitator extensions or empty array if not found
1033
+ */
1034
+ getFacilitatorExtensions(x402Version2, network, scheme) {
1035
+ const versionMap = this.supportedResponsesMap.get(x402Version2);
1036
+ if (!versionMap) return [];
1037
+ const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
1038
+ return supportedResponse?.extensions || [];
1039
+ }
1040
+ /**
1041
+ * Build payment requirements for a protected resource
1042
+ *
1043
+ * @param resourceConfig - Configuration for the protected resource
1044
+ * @returns Array of payment requirements
1045
+ */
1046
+ async buildPaymentRequirements(resourceConfig) {
1047
+ const requirements = [];
1048
+ const scheme = resourceConfig.scheme;
1049
+ const SchemeNetworkServer = findByNetworkAndScheme(
1050
+ this.registeredServerSchemes,
1051
+ scheme,
1052
+ resourceConfig.network
1053
+ );
1054
+ if (!SchemeNetworkServer) {
1055
+ console.warn(
1056
+ `No server implementation registered for scheme: ${scheme}, network: ${resourceConfig.network}`
1057
+ );
1058
+ return requirements;
1059
+ }
1060
+ const supportedKind = this.getSupportedKind(
1061
+ x402Version,
1062
+ resourceConfig.network,
1063
+ SchemeNetworkServer.scheme
1064
+ );
1065
+ if (!supportedKind) {
1066
+ throw new Error(
1067
+ `Facilitator does not support ${SchemeNetworkServer.scheme} on ${resourceConfig.network}. Make sure to call initialize() to fetch supported kinds from facilitators.`
1068
+ );
1069
+ }
1070
+ const facilitatorExtensions = this.getFacilitatorExtensions(
1071
+ x402Version,
1072
+ resourceConfig.network,
1073
+ SchemeNetworkServer.scheme
1074
+ );
1075
+ const parsedPrice = await SchemeNetworkServer.parsePrice(
1076
+ resourceConfig.price,
1077
+ resourceConfig.network
1078
+ );
1079
+ const baseRequirements = {
1080
+ scheme: SchemeNetworkServer.scheme,
1081
+ network: resourceConfig.network,
1082
+ amount: parsedPrice.amount,
1083
+ asset: parsedPrice.asset,
1084
+ payTo: resourceConfig.payTo,
1085
+ maxTimeoutSeconds: resourceConfig.maxTimeoutSeconds || 300,
1086
+ // Default 5 minutes
1087
+ extra: {
1088
+ ...parsedPrice.extra,
1089
+ ...resourceConfig.extra
1090
+ // Merge user-provided extra
1091
+ }
1092
+ };
1093
+ const requirement = await SchemeNetworkServer.enhancePaymentRequirements(
1094
+ baseRequirements,
1095
+ supportedKind,
1096
+ facilitatorExtensions
1097
+ );
1098
+ requirements.push(requirement);
1099
+ return requirements;
1100
+ }
1101
+ /**
1102
+ * Build payment requirements from multiple payment options
1103
+ * This method handles resolving dynamic payTo/price functions and builds requirements for each option
1104
+ *
1105
+ * @param paymentOptions - Array of payment options to convert
1106
+ * @param context - HTTP request context for resolving dynamic functions
1107
+ * @returns Array of payment requirements (one per option)
1108
+ */
1109
+ async buildPaymentRequirementsFromOptions(paymentOptions, context) {
1110
+ const allRequirements = [];
1111
+ for (const option of paymentOptions) {
1112
+ const resolvedPayTo = typeof option.payTo === "function" ? await option.payTo(context) : option.payTo;
1113
+ const resolvedPrice = typeof option.price === "function" ? await option.price(context) : option.price;
1114
+ const resourceConfig = {
1115
+ scheme: option.scheme,
1116
+ payTo: resolvedPayTo,
1117
+ price: resolvedPrice,
1118
+ network: option.network,
1119
+ maxTimeoutSeconds: option.maxTimeoutSeconds,
1120
+ extra: option.extra
1121
+ };
1122
+ const requirements = await this.buildPaymentRequirements(resourceConfig);
1123
+ allRequirements.push(...requirements);
1124
+ }
1125
+ return allRequirements;
1126
+ }
1127
+ /**
1128
+ * Create a payment required response
1129
+ *
1130
+ * @param requirements - Payment requirements
1131
+ * @param resourceInfo - Resource information
1132
+ * @param error - Error message
1133
+ * @param extensions - Optional declared extensions (for per-key enrichment)
1134
+ * @param transportContext - Optional transport-specific context (e.g., HTTP request, MCP tool context)
1135
+ * @param paymentPayload - Optional failed payment payload for response-time scheme enrichment
1136
+ * @returns Payment required response object
1137
+ */
1138
+ async createPaymentRequiredResponse(requirements, resourceInfo, error, extensions, transportContext, paymentPayload) {
1139
+ const acceptsClone = requirements.map((req) => ({
1140
+ ...req,
1141
+ extra: structuredClone(req.extra)
1142
+ }));
1143
+ let workingAccepts = acceptsClone;
1144
+ let baselineAccepts = snapshotPaymentRequirementsList(workingAccepts);
1145
+ let response = {
1146
+ x402Version: 2,
1147
+ error,
1148
+ resource: resourceInfo,
1149
+ accepts: workingAccepts
1150
+ };
1151
+ if (extensions && Object.keys(extensions).length > 0) {
1152
+ response.extensions = extensions;
1153
+ }
1154
+ for (let i = 0; i < workingAccepts.length; i++) {
1155
+ const accept = workingAccepts[i];
1156
+ const scheme = findByNetworkAndScheme(
1157
+ this.registeredServerSchemes,
1158
+ accept.scheme,
1159
+ accept.network
1160
+ );
1161
+ if (!scheme?.enrichPaymentRequiredResponse) {
1162
+ continue;
1163
+ }
1164
+ const context = {
1165
+ requirements: workingAccepts,
1166
+ paymentPayload,
1167
+ resourceInfo,
1168
+ error,
1169
+ paymentRequiredResponse: response,
1170
+ transportContext
1171
+ };
1172
+ const enrichedAccepts = await scheme.enrichPaymentRequiredResponse(context);
1173
+ if (enrichedAccepts !== void 0) {
1174
+ workingAccepts = enrichedAccepts;
1175
+ response.accepts = workingAccepts;
1176
+ }
1177
+ assertAcceptsAdditiveExtraAfterSchemeEnrich(
1178
+ baselineAccepts,
1179
+ response.accepts,
1180
+ accept.scheme,
1181
+ accept.network
1182
+ );
1183
+ baselineAccepts = snapshotPaymentRequirementsList(response.accepts);
1184
+ }
1185
+ if (extensions) {
1186
+ for (const [key, declaration] of Object.entries(extensions)) {
1187
+ const extension = this.registeredExtensions.get(key);
1188
+ if (extension?.enrichPaymentRequiredResponse) {
1189
+ try {
1190
+ const context = {
1191
+ requirements: workingAccepts,
1192
+ resourceInfo,
1193
+ error,
1194
+ paymentRequiredResponse: response,
1195
+ transportContext
1196
+ };
1197
+ const extensionData = await extension.enrichPaymentRequiredResponse(
1198
+ declaration,
1199
+ context
1200
+ );
1201
+ if (extensionData !== void 0) {
1202
+ if (!response.extensions) {
1203
+ response.extensions = {};
1204
+ }
1205
+ response.extensions[key] = extensionData;
1206
+ }
1207
+ } catch (error2) {
1208
+ this.warnExtensionHookFailure(key, "enrichPaymentRequiredResponse", error2);
1209
+ }
1210
+ assertAcceptsAllowlistedAfterExtensionEnrich(baselineAccepts, workingAccepts, key);
1211
+ baselineAccepts = snapshotPaymentRequirementsList(workingAccepts);
1212
+ }
1213
+ }
1214
+ }
1215
+ return response;
1216
+ }
1217
+ /**
1218
+ * Verifies a payment against requirements, running manual and in-use extension hooks.
1219
+ *
1220
+ * @param paymentPayload - Signed payment payload from the client
1221
+ * @param requirements - Requirements matched to the payload
1222
+ * @param declaredExtensions - Optional per-extension declarations for the request
1223
+ * @param transportContext - Optional transport-specific context (e.g. HTTP, MCP)
1224
+ * @returns Facilitator verify outcome (optionally carrying a `skipHandler` directive),
1225
+ * or abort/recovery as driven by hooks
1226
+ */
1227
+ async verifyPayment(paymentPayload, requirements, declaredExtensions, transportContext) {
1228
+ const resolvedDeclaredExtensions = declaredExtensions ?? {};
1229
+ const extensionKeysInUse = Object.keys(resolvedDeclaredExtensions);
1230
+ const matchedScheme = {
1231
+ network: requirements.network,
1232
+ scheme: requirements.scheme
1233
+ };
1234
+ const context = {
1235
+ paymentPayload,
1236
+ requirements,
1237
+ declaredExtensions: resolvedDeclaredExtensions,
1238
+ transportContext
1239
+ };
1240
+ for (const { label, hook } of this.getLabeledHooks(
1241
+ "beforeVerify",
1242
+ extensionKeysInUse,
1243
+ matchedScheme
1244
+ )) {
1245
+ try {
1246
+ const result = await hook(context);
1247
+ if (result && "abort" in result && result.abort) {
1248
+ return {
1249
+ isValid: false,
1250
+ invalidReason: result.reason,
1251
+ invalidMessage: result.message
1252
+ };
1253
+ }
1254
+ if (result && "skip" in result && result.skip) {
1255
+ return this.runAfterVerifyHooks(
1256
+ result.result,
1257
+ context,
1258
+ extensionKeysInUse,
1259
+ matchedScheme
1260
+ );
1261
+ }
1262
+ } catch (error) {
1263
+ this.warnResourceServerHookFailure("beforeVerify", label, error);
1264
+ }
1265
+ }
1266
+ try {
1267
+ const facilitatorClient = this.getFacilitatorClient(
1268
+ paymentPayload.x402Version,
1269
+ requirements.network,
1270
+ requirements.scheme
1271
+ );
1272
+ let verifyResult;
1273
+ if (!facilitatorClient) {
1274
+ let lastError;
1275
+ for (const client of this.facilitatorClients) {
1276
+ try {
1277
+ verifyResult = await client.verify(paymentPayload, requirements);
1278
+ break;
1279
+ } catch (error) {
1280
+ lastError = error;
1281
+ }
1282
+ }
1283
+ if (!verifyResult) {
1284
+ throw lastError || new Error(
1285
+ `No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
1286
+ );
1287
+ }
1288
+ } else {
1289
+ verifyResult = await facilitatorClient.verify(paymentPayload, requirements);
1290
+ }
1291
+ return this.runAfterVerifyHooks(verifyResult, context, extensionKeysInUse, matchedScheme);
1292
+ } catch (error) {
1293
+ const failureContext = {
1294
+ ...context,
1295
+ error
1296
+ };
1297
+ for (const { label, hook } of this.getLabeledHooks(
1298
+ "onVerifyFailure",
1299
+ extensionKeysInUse,
1300
+ matchedScheme
1301
+ )) {
1302
+ try {
1303
+ const result = await hook(failureContext);
1304
+ if (result && "recovered" in result && result.recovered) {
1305
+ return result.result;
1306
+ }
1307
+ } catch (error2) {
1308
+ this.warnResourceServerHookFailure("onVerifyFailure", label, error2);
1309
+ }
1310
+ }
1311
+ throw error;
1312
+ }
1313
+ }
1314
+ /**
1315
+ * Create cancellation controls for a verified payment attempt.
1316
+ *
1317
+ * @param paymentPayload - Signed payment payload from the client
1318
+ * @param requirements - Requirements matched to the payload
1319
+ * @param declaredExtensions - Optional per-extension declarations for the request
1320
+ * @param transportContext - Optional transport-specific context
1321
+ * @returns Cancellation controls for the verified payment attempt
1322
+ */
1323
+ createPaymentCancellationDispatcher(paymentPayload, requirements, declaredExtensions, transportContext) {
1324
+ const resolvedDeclaredExtensions = declaredExtensions ?? {};
1325
+ let cancelPromise;
1326
+ return {
1327
+ cancel: (options) => {
1328
+ if (!cancelPromise) {
1329
+ cancelPromise = this.dispatchVerifiedPaymentCanceled(
1330
+ paymentPayload,
1331
+ requirements,
1332
+ resolvedDeclaredExtensions,
1333
+ options,
1334
+ transportContext
1335
+ );
1336
+ }
1337
+ return cancelPromise;
1338
+ }
1339
+ };
1340
+ }
1341
+ /**
1342
+ * Settle a verified payment
1343
+ *
1344
+ * @param paymentPayload - The payment payload to settle
1345
+ * @param requirements - The payment requirements
1346
+ * @param declaredExtensions - Optional declared extensions (for per-key enrichment)
1347
+ * @param transportContext - Optional transport-specific context (e.g., HTTP request/response, MCP tool context)
1348
+ * @param settlementOverrides - Optional overrides for settlement parameters (e.g., partial settlement amount)
1349
+ * @returns Settlement response
1350
+ */
1351
+ async settlePayment(paymentPayload, requirements, declaredExtensions, transportContext, settlementOverrides) {
1352
+ const resolvedDeclaredExtensions = declaredExtensions ?? {};
1353
+ const extensionKeysInUse = Object.keys(resolvedDeclaredExtensions);
1354
+ let effectiveRequirements = requirements;
1355
+ if (settlementOverrides?.amount !== void 0) {
1356
+ const scheme = findByNetworkAndScheme(
1357
+ this.registeredServerSchemes,
1358
+ requirements.scheme,
1359
+ requirements.network
1360
+ );
1361
+ const decimals = scheme?.getAssetDecimals?.(requirements.asset ?? "", requirements.network) ?? 6;
1362
+ effectiveRequirements = {
1363
+ ...requirements,
1364
+ amount: resolveSettlementOverrideAmount(settlementOverrides.amount, requirements, decimals)
1365
+ };
1366
+ }
1367
+ const context = {
1368
+ paymentPayload,
1369
+ requirements: effectiveRequirements,
1370
+ declaredExtensions: resolvedDeclaredExtensions,
1371
+ transportContext
1372
+ };
1373
+ const matchedScheme = {
1374
+ network: effectiveRequirements.network,
1375
+ scheme: effectiveRequirements.scheme
1376
+ };
1377
+ for (const { label, hook } of this.getLabeledHooks(
1378
+ "beforeSettle",
1379
+ extensionKeysInUse,
1380
+ matchedScheme
1381
+ )) {
1382
+ try {
1383
+ const result = await hook(context);
1384
+ if (result && "abort" in result && result.abort) {
1385
+ throw new SettleError(400, {
1386
+ success: false,
1387
+ errorReason: result.reason,
1388
+ errorMessage: result.message,
1389
+ transaction: "",
1390
+ network: requirements.network
1391
+ });
1392
+ }
1393
+ if (result && "skip" in result && result.skip) {
1394
+ const settleResult = result.result;
1395
+ const skipResultContext = {
1396
+ ...context,
1397
+ result: settleResult,
1398
+ transportContext
1399
+ };
1400
+ for (const { label: label2, hook: hook2 } of this.getLabeledHooks(
1401
+ "afterSettle",
1402
+ extensionKeysInUse,
1403
+ matchedScheme
1404
+ )) {
1405
+ try {
1406
+ await hook2(skipResultContext);
1407
+ } catch (error) {
1408
+ this.warnResourceServerHookFailure("afterSettle", label2, error);
1409
+ }
1410
+ }
1411
+ await this.enrichSettlementResponse(
1412
+ settleResult,
1413
+ skipResultContext,
1414
+ resolvedDeclaredExtensions,
1415
+ matchedScheme
1416
+ );
1417
+ return settleResult;
1418
+ }
1419
+ } catch (error) {
1420
+ if (error instanceof SettleError) {
1421
+ throw error;
1422
+ }
1423
+ this.warnResourceServerHookFailure("beforeSettle", label, error);
1424
+ }
1425
+ }
1426
+ try {
1427
+ const scheme = findByNetworkAndScheme(
1428
+ this.registeredServerSchemes,
1429
+ matchedScheme.scheme,
1430
+ matchedScheme.network
1431
+ );
1432
+ const payloadEnrichmentHook = scheme?.enrichSettlementPayload;
1433
+ if (payloadEnrichmentHook) {
1434
+ const label = `scheme "${matchedScheme.scheme}" enrichSettlementPayload`;
1435
+ const enrichment = await payloadEnrichmentHook(context);
1436
+ if (enrichment !== void 0) {
1437
+ assertAdditivePayloadEnrichment(paymentPayload.payload, enrichment, label);
1438
+ paymentPayload.payload = { ...paymentPayload.payload, ...enrichment };
1439
+ }
1440
+ }
1441
+ const facilitatorClient = this.getFacilitatorClient(
1442
+ paymentPayload.x402Version,
1443
+ effectiveRequirements.network,
1444
+ effectiveRequirements.scheme
1445
+ );
1446
+ let settleResult;
1447
+ if (!facilitatorClient) {
1448
+ let lastError;
1449
+ for (const client of this.facilitatorClients) {
1450
+ try {
1451
+ settleResult = await client.settle(paymentPayload, effectiveRequirements);
1452
+ break;
1453
+ } catch (error) {
1454
+ lastError = error;
1455
+ }
1456
+ }
1457
+ if (!settleResult) {
1458
+ throw lastError || new Error(
1459
+ `No facilitator supports ${effectiveRequirements.scheme} on ${effectiveRequirements.network} for v${paymentPayload.x402Version}`
1460
+ );
1461
+ }
1462
+ } else {
1463
+ settleResult = await facilitatorClient.settle(paymentPayload, effectiveRequirements);
1464
+ }
1465
+ const resultContext = {
1466
+ ...context,
1467
+ result: settleResult
1468
+ };
1469
+ for (const { label, hook } of this.getLabeledHooks(
1470
+ "afterSettle",
1471
+ extensionKeysInUse,
1472
+ matchedScheme
1473
+ )) {
1474
+ try {
1475
+ await hook(resultContext);
1476
+ } catch (error) {
1477
+ this.warnResourceServerHookFailure("afterSettle", label, error);
1478
+ }
1479
+ }
1480
+ await this.enrichSettlementResponse(
1481
+ settleResult,
1482
+ resultContext,
1483
+ resolvedDeclaredExtensions,
1484
+ matchedScheme
1485
+ );
1486
+ return settleResult;
1487
+ } catch (error) {
1488
+ const failureContext = {
1489
+ ...context,
1490
+ error
1491
+ };
1492
+ for (const { label, hook } of this.getLabeledHooks(
1493
+ "onSettleFailure",
1494
+ extensionKeysInUse,
1495
+ matchedScheme
1496
+ )) {
1497
+ try {
1498
+ const result = await hook(failureContext);
1499
+ if (result && "recovered" in result && result.recovered) {
1500
+ return result.result;
1501
+ }
1502
+ } catch (error2) {
1503
+ this.warnResourceServerHookFailure("onSettleFailure", label, error2);
1504
+ }
1505
+ }
1506
+ throw error;
1507
+ }
1508
+ }
1509
+ /**
1510
+ * Find matching payment requirements for a payment
1511
+ *
1512
+ * @param availableRequirements - Array of available payment requirements
1513
+ * @param paymentPayload - The payment payload
1514
+ * @returns Matching payment requirements or undefined
1515
+ */
1516
+ /**
1517
+ * Validates optional client extension echoes against server-advertised extension info.
1518
+ * When the client omits extensions entirely, validation passes.
1519
+ *
1520
+ * @param paymentRequired - Server payment required response used for matching
1521
+ * @param paymentPayload - Client payment payload
1522
+ * @returns Whether echoed extension info preserves server-advertised values
1523
+ */
1524
+ validateExtensions(paymentRequired, paymentPayload) {
1525
+ if (paymentPayload.x402Version !== 2) {
1526
+ return { valid: true };
1527
+ }
1528
+ const serverExtensions = paymentRequired.extensions;
1529
+ if (!serverExtensions || Object.keys(serverExtensions).length === 0) {
1530
+ return { valid: true };
1531
+ }
1532
+ const clientExtensions = paymentPayload.extensions;
1533
+ if (!clientExtensions || Object.keys(clientExtensions).length === 0) {
1534
+ return { valid: true };
1535
+ }
1536
+ for (const [key, echoedValue] of Object.entries(clientExtensions)) {
1537
+ if (!Object.prototype.hasOwnProperty.call(serverExtensions, key)) {
1538
+ continue;
1539
+ }
1540
+ const advertisedInfo = getExtensionInfo(serverExtensions[key]);
1541
+ const echoedInfo = getExtensionInfo(echoedValue);
1542
+ if (!extensionInfoMatchesAdvertised(advertisedInfo, echoedInfo)) {
1543
+ return {
1544
+ valid: false,
1545
+ invalidReason: "extension_echo_mismatch",
1546
+ extensionKey: key
1547
+ };
1548
+ }
1549
+ }
1550
+ return { valid: true };
1551
+ }
1552
+ /**
1553
+ * Finds the server-advertised requirement that matches a client payment payload.
1554
+ *
1555
+ * @param availableRequirements - Payment requirements advertised for the resource.
1556
+ * @param paymentPayload - Signed payment payload from the client.
1557
+ * @returns The matching requirement, or undefined when none match.
1558
+ */
1559
+ findMatchingRequirements(availableRequirements, paymentPayload) {
1560
+ switch (paymentPayload.x402Version) {
1561
+ case 2:
1562
+ return availableRequirements.find(
1563
+ (paymentRequirements) => paymentRequirementsMatchAccepted(paymentRequirements, paymentPayload.accepted)
1564
+ );
1565
+ case 1:
1566
+ return availableRequirements.find(
1567
+ (req) => req.scheme === paymentPayload.accepted.scheme && req.network === paymentPayload.accepted.network
1568
+ );
1569
+ default:
1570
+ throw new Error(
1571
+ `Unsupported x402 version: ${paymentPayload.x402Version}`
1572
+ );
1573
+ }
1574
+ }
1575
+ /**
1576
+ * Logs a warning when a manual or extension adapter lifecycle hook throws.
1577
+ *
1578
+ * @param phase - Lifecycle phase name (e.g. `beforeVerify`)
1579
+ * @param label - Hook source label from {@link getLabeledHooks} (manual index or extension key)
1580
+ * @param error - Thrown value or rejection reason
1581
+ */
1582
+ warnResourceServerHookFailure(phase, label, error) {
1583
+ const detail = error instanceof Error ? error.message : String(error);
1584
+ console.warn(`[x402] Resource server ${phase} hook threw (${label}): ${detail}`);
1585
+ }
1586
+ /**
1587
+ * Logs a warning when a registered extension enrichment hook throws.
1588
+ *
1589
+ * @param extensionKey - Registered extension identifier
1590
+ * @param hookName - Hook method name (e.g. `enrichDeclaration`)
1591
+ * @param error - Thrown value or rejection reason
1592
+ */
1593
+ warnExtensionHookFailure(extensionKey, hookName, error) {
1594
+ const detail = error instanceof Error ? error.message : String(error);
1595
+ console.warn(`[x402] extension "${extensionKey}" ${hookName} threw: ${detail}`);
1596
+ }
1597
+ /**
1598
+ * Executes after-verify hooks for facilitator and hook-provided verify results.
1599
+ *
1600
+ * @param verifyResult - Verify response passed to after-verify hooks.
1601
+ * @param context - Verify context shared with before-verify hooks.
1602
+ * @param extensionKeysInUse - Declared extension keys for this request.
1603
+ * @param matchedScheme - Scheme/network selected for this payment.
1604
+ * @param matchedScheme.network - Matched payment network.
1605
+ * @param matchedScheme.scheme - Matched payment scheme.
1606
+ * @returns Verify response with any in-process skip handler directive.
1607
+ */
1608
+ async runAfterVerifyHooks(verifyResult, context, extensionKeysInUse, matchedScheme) {
1609
+ const resultContext = {
1610
+ ...context,
1611
+ result: verifyResult
1612
+ };
1613
+ let skipHandler;
1614
+ for (const { label, hook } of this.getLabeledHooks(
1615
+ "afterVerify",
1616
+ extensionKeysInUse,
1617
+ matchedScheme
1618
+ )) {
1619
+ try {
1620
+ const directive = await hook(resultContext);
1621
+ if (directive && "skipHandler" in directive && directive.skipHandler) {
1622
+ skipHandler = directive.response ?? {};
1623
+ }
1624
+ } catch (error) {
1625
+ this.warnResourceServerHookFailure("afterVerify", label, error);
1626
+ }
1627
+ }
1628
+ return skipHandler ? { ...verifyResult, skipHandler } : verifyResult;
1629
+ }
1630
+ /**
1631
+ * Runs response enrichment after settlement lifecycle hooks complete.
1632
+ *
1633
+ * @param settleResult - Mutable settlement result being returned to the caller
1634
+ * @param context - Read-only hook context for enrichment callbacks
1635
+ * @param declaredExtensions - Extension declarations present on this payment
1636
+ * @param matchedScheme - Scheme/network selected for this settlement
1637
+ * @param matchedScheme.network - Matched payment network
1638
+ * @param matchedScheme.scheme - Matched payment scheme
1639
+ */
1640
+ async enrichSettlementResponse(settleResult, context, declaredExtensions, matchedScheme) {
1641
+ if (Object.keys(declaredExtensions).length > 0) {
1642
+ const settleCoreSnapshot = snapshotSettleResponseCore(settleResult);
1643
+ for (const [key, declaration] of Object.entries(declaredExtensions)) {
1644
+ const extension = this.registeredExtensions.get(key);
1645
+ if (!extension?.enrichSettlementResponse) continue;
1646
+ try {
1647
+ const extensionData = await extension.enrichSettlementResponse(declaration, context);
1648
+ if (extensionData !== void 0) {
1649
+ if (!settleResult.extensions) {
1650
+ settleResult.extensions = {};
1651
+ }
1652
+ settleResult.extensions[key] = extensionData;
1653
+ }
1654
+ } catch (error) {
1655
+ this.warnExtensionHookFailure(key, "enrichSettlementResponse", error);
1656
+ }
1657
+ assertSettleResponseCoreUnchanged(settleCoreSnapshot, settleResult, key);
1658
+ }
1659
+ }
1660
+ const scheme = findByNetworkAndScheme(
1661
+ this.registeredServerSchemes,
1662
+ matchedScheme.scheme,
1663
+ matchedScheme.network
1664
+ );
1665
+ const hook = scheme?.enrichSettlementResponse;
1666
+ if (!hook) return;
1667
+ const label = `scheme "${matchedScheme.scheme}" enrichSettlementResponse`;
1668
+ try {
1669
+ const enrichment = await hook(context);
1670
+ if (enrichment === void 0) return;
1671
+ assertAdditiveSettlementExtra(settleResult.extra ?? {}, enrichment, label);
1672
+ settleResult.extra = mergeAdditiveSettlementExtra(settleResult.extra ?? {}, enrichment);
1673
+ } catch (error) {
1674
+ this.warnResourceServerHookFailure("enrichSettlementResponse", label, error);
1675
+ }
1676
+ }
1677
+ /**
1678
+ * Notify hooks that verified work ended before settlement.
1679
+ *
1680
+ * @param paymentPayload - Signed payment payload from the client
1681
+ * @param requirements - Requirements matched to the payload
1682
+ * @param declaredExtensions - Optional per-extension declarations for the request
1683
+ * @param options - Cancellation reason and optional diagnostics
1684
+ * @param fallbackTransportContext - Optional transport-specific context
1685
+ */
1686
+ async dispatchVerifiedPaymentCanceled(paymentPayload, requirements, declaredExtensions, options, fallbackTransportContext) {
1687
+ const extensionKeysInUse = Object.keys(declaredExtensions);
1688
+ const matchedScheme = {
1689
+ network: requirements.network,
1690
+ scheme: requirements.scheme
1691
+ };
1692
+ const context = {
1693
+ paymentPayload,
1694
+ requirements,
1695
+ declaredExtensions,
1696
+ transportContext: fallbackTransportContext,
1697
+ reason: options.reason,
1698
+ error: options.error,
1699
+ responseStatus: options.responseStatus
1700
+ };
1701
+ for (const { label, hook } of this.getLabeledHooks(
1702
+ "onVerifiedPaymentCanceled",
1703
+ extensionKeysInUse,
1704
+ matchedScheme
1705
+ )) {
1706
+ try {
1707
+ await hook(context);
1708
+ } catch (error) {
1709
+ this.warnResourceServerHookFailure("onVerifiedPaymentCanceled", label, error);
1710
+ }
1711
+ }
1712
+ }
1713
+ /**
1714
+ * Manual hooks first, then the matched scheme adapter, then extension adapters for keys in use.
1715
+ * Each entry carries a stable label for logging when a hook throws.
1716
+ *
1717
+ * @param phase - Hook slot (e.g. `beforeVerify`)
1718
+ * @param extensionKeysInUse - Declared extension keys for this request
1719
+ * @param matchedScheme - Scheme/network selected for this payment
1720
+ * @param matchedScheme.network - Matched payment network
1721
+ * @param matchedScheme.scheme - Matched payment scheme
1722
+ * @returns Hooks in invocation order with source labels
1723
+ */
1724
+ getLabeledHooks(phase, extensionKeysInUse, matchedScheme) {
1725
+ const manualKey = `${phase}Hooks`;
1726
+ const manual = this[manualKey];
1727
+ const out = [];
1728
+ manual.forEach((hook, index) => {
1729
+ out.push({ label: `manual ${phase} hook #${index}`, hook });
1730
+ });
1731
+ if (matchedScheme) {
1732
+ const schemeHandles = findByNetworkAndScheme(
1733
+ this.schemeHookAdapters,
1734
+ matchedScheme.scheme,
1735
+ matchedScheme.network
1736
+ );
1737
+ const hook = schemeHandles?.[phase];
1738
+ if (hook !== void 0) {
1739
+ out.push({
1740
+ label: `scheme "${matchedScheme.scheme}" ${phase}`,
1741
+ hook
1742
+ });
1743
+ }
1744
+ }
1745
+ const inUse = new Set(extensionKeysInUse);
1746
+ for (const [extensionKey, adapterHandles] of this.extensionHookAdapters.entries()) {
1747
+ if (!inUse.has(extensionKey)) continue;
1748
+ const hook = adapterHandles[phase];
1749
+ if (hook !== void 0) {
1750
+ out.push({ label: `extension "${extensionKey}" ${phase}`, hook });
1751
+ }
1752
+ }
1753
+ return out;
1754
+ }
1755
+ /**
1756
+ * Get facilitator client for a specific version, network, and scheme
1757
+ *
1758
+ * @param x402Version - The x402 version
1759
+ * @param network - The network identifier
1760
+ * @param scheme - The payment scheme
1761
+ * @returns The facilitator client or undefined if not found
1762
+ */
1763
+ getFacilitatorClient(x402Version2, network, scheme) {
1764
+ const versionMap = this.facilitatorClientsMap.get(x402Version2);
1765
+ if (!versionMap) return void 0;
1766
+ return findByNetworkAndScheme(versionMap, scheme, network);
1767
+ }
1768
+ };
1769
+ function getExtensionInfo(value) {
1770
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "info")) {
1771
+ return value.info;
1772
+ }
1773
+ return value;
1774
+ }
1775
+ function extensionInfoMatchesAdvertised(advertised, echoed) {
1776
+ return objectContainsSubset(advertised, echoed);
1777
+ }
1778
+ function paymentRequirementsMatchAccepted(required, accepted) {
1779
+ const { extra: requiredExtra, ...requiredCore } = required;
1780
+ const { extra: acceptedExtra, ...acceptedCore } = accepted;
1781
+ if (!deepEqual(requiredCore, acceptedCore)) {
1782
+ return false;
1783
+ }
1784
+ if (requiredExtra === void 0) {
1785
+ return true;
1786
+ }
1787
+ return objectContainsSubset(requiredExtra, acceptedExtra);
1788
+ }
1789
+ function objectContainsSubset(expected, actual) {
1790
+ if (expected === null || typeof expected !== "object" || Array.isArray(expected)) {
1791
+ return deepEqual(expected, actual);
1792
+ }
1793
+ if (actual === null || typeof actual !== "object" || Array.isArray(actual)) {
1794
+ return false;
1795
+ }
1796
+ const actualRecord = actual;
1797
+ return Object.entries(expected).every(([key, value]) => {
1798
+ const hasActualKey = Object.prototype.hasOwnProperty.call(actualRecord, key);
1799
+ if (!hasActualKey) {
1800
+ return value === void 0;
1801
+ }
1802
+ return objectContainsSubset(value, actualRecord[key]);
1803
+ });
1804
+ }
1805
+
1806
+ // src/http/index.ts
1807
+ function decodePaymentSignatureHeader(paymentSignatureHeader) {
1808
+ if (!Base64EncodedRegex.test(paymentSignatureHeader)) {
1809
+ throw new Error("Invalid payment signature header");
1810
+ }
1811
+ return JSON.parse(safeBase64Decode(paymentSignatureHeader));
1812
+ }
1813
+ function encodePaymentRequiredHeader(paymentRequired) {
1814
+ return safeBase64Encode(JSON.stringify(paymentRequired));
1815
+ }
1816
+ function encodePaymentResponseHeader(paymentResponse) {
1817
+ return safeBase64Encode(JSON.stringify(paymentResponse));
1818
+ }
1819
+
1820
+ // src/http/x402HTTPResourceServer.ts
1821
+ var SETTLEMENT_OVERRIDES_HEADER = "Settlement-Overrides";
1822
+ function checkIfBazaarNeeded(routes) {
1823
+ if ("accepts" in routes) {
1824
+ return !!(routes.extensions && "bazaar" in routes.extensions);
1825
+ }
1826
+ return Object.values(routes).some((routeConfig) => {
1827
+ return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions);
1828
+ });
1829
+ }
1830
+ var RouteConfigurationError = class extends Error {
1831
+ /**
1832
+ * Creates a new RouteConfigurationError with the given validation errors.
1833
+ *
1834
+ * @param errors - The validation errors that caused this exception.
1835
+ */
1836
+ constructor(errors) {
1837
+ const message = `x402 Route Configuration Errors:
1838
+ ${errors.map((e) => ` - ${e.message}`).join("\n")}`;
1839
+ super(message);
1840
+ this.name = "RouteConfigurationError";
1841
+ this.errors = errors;
1842
+ }
1843
+ };
1844
+ var FALLBACK_PAYWALL_HTML = `<!DOCTYPE html>
1845
+ <html>
1846
+ <head>
1847
+ <title>Payment Required</title>
1848
+ <meta charset="UTF-8">
1849
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1850
+ </head>
1851
+ <body>
1852
+ <div style="max-width: 600px; margin: 50px auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
1853
+ <h1>Payment Required</h1>
1854
+ <p>This resource is protected by the x402 payment protocol.</p>
1855
+ <p style="margin-top: 2rem; padding: 1rem; background: #fef3c7; border-radius: 0.5rem;">
1856
+ <strong>Note to developers:</strong> install <code>@bankofai/x402-paywall</code> to enable
1857
+ the in-browser wallet connection and payment UI. Programmatic clients should read
1858
+ the payment requirements from the 402 response headers and JSON body.
1859
+ </p>
1860
+ </div>
1861
+ </body>
1862
+ </html>`;
1863
+ var x402HTTPResourceServer = class {
1864
+ /**
1865
+ * Creates a new x402HTTPResourceServer instance.
1866
+ *
1867
+ * @param ResourceServer - The core x402ResourceServer instance to use
1868
+ * @param routes - Route configuration for payment-protected endpoints
1869
+ */
1870
+ constructor(ResourceServer, routes) {
1871
+ this.compiledRoutes = [];
1872
+ this.protectedRequestHooks = [];
1873
+ this.ResourceServer = ResourceServer;
1874
+ this.routesConfig = routes;
1875
+ const normalizedRoutes = typeof routes === "object" && !("accepts" in routes) ? routes : { "*": routes };
1876
+ for (const [pattern, config] of Object.entries(normalizedRoutes)) {
1877
+ const parsed = this.parseRoutePattern(pattern);
1878
+ this.compiledRoutes.push({
1879
+ verb: parsed.verb,
1880
+ regex: parsed.regex,
1881
+ config,
1882
+ pattern: parsed.path
1883
+ });
1884
+ }
1885
+ }
1886
+ /**
1887
+ * Get the underlying x402ResourceServer instance.
1888
+ *
1889
+ * @returns The underlying x402ResourceServer instance
1890
+ */
1891
+ get server() {
1892
+ return this.ResourceServer;
1893
+ }
1894
+ /**
1895
+ * Get the routes configuration.
1896
+ *
1897
+ * @returns The routes configuration
1898
+ */
1899
+ get routes() {
1900
+ return this.routesConfig;
1901
+ }
1902
+ /**
1903
+ * Initialize the HTTP resource server.
1904
+ *
1905
+ * This method initializes the underlying resource server (fetching facilitator support)
1906
+ * and then validates that all route payment configurations have corresponding
1907
+ * registered schemes and facilitator support.
1908
+ *
1909
+ * @throws RouteConfigurationError if any route's payment options don't have
1910
+ * corresponding registered schemes or facilitator support
1911
+ *
1912
+ * @example
1913
+ * ```typescript
1914
+ * const httpServer = new x402HTTPResourceServer(server, routes);
1915
+ * await httpServer.initialize();
1916
+ * ```
1917
+ */
1918
+ async initialize() {
1919
+ await this.ResourceServer.initialize();
1920
+ const errors = this.validateRouteConfiguration();
1921
+ if (errors.length > 0) {
1922
+ throw new RouteConfigurationError(errors);
1923
+ }
1924
+ }
1925
+ /**
1926
+ * Register a custom paywall provider for generating HTML
1927
+ *
1928
+ * @param provider - PaywallProvider instance
1929
+ * @returns This service instance for chaining
1930
+ */
1931
+ registerPaywallProvider(provider) {
1932
+ this.paywallProvider = provider;
1933
+ return this;
1934
+ }
1935
+ /**
1936
+ * Register a hook that runs on every request to a protected route, before payment processing.
1937
+ * Hooks are executed in order of registration. The first hook to return a non-void result wins.
1938
+ *
1939
+ * @param hook - The request hook function
1940
+ * @returns The x402HTTPResourceServer instance for chaining
1941
+ */
1942
+ onProtectedRequest(hook) {
1943
+ this.protectedRequestHooks.push(hook);
1944
+ return this;
1945
+ }
1946
+ /**
1947
+ * Process HTTP request and return response instructions
1948
+ * This is the main entry point for framework middleware
1949
+ *
1950
+ * @param context - HTTP request context
1951
+ * @param paywallConfig - Optional paywall configuration
1952
+ * @returns Process result indicating next action for middleware
1953
+ */
1954
+ async processHTTPRequest(context, paywallConfig) {
1955
+ const method = context.method || context.adapter.getMethod();
1956
+ context = { ...context, method };
1957
+ const { adapter, path } = context;
1958
+ const routeMatch = this.getRouteConfig(path, method);
1959
+ if (!routeMatch) {
1960
+ return { type: "no-payment-required" };
1961
+ }
1962
+ const { config: routeConfig, pattern: routePattern } = routeMatch;
1963
+ const enrichedContext = { ...context, routePattern };
1964
+ for (const hook of this.getProtectedRequestHooks(routeConfig)) {
1965
+ const result = await hook(enrichedContext, routeConfig);
1966
+ if (result && "grantAccess" in result) {
1967
+ return { type: "no-payment-required" };
1968
+ }
1969
+ if (result && "abort" in result) {
1970
+ return {
1971
+ type: "payment-error",
1972
+ response: {
1973
+ status: 403,
1974
+ headers: { "Content-Type": "application/json" },
1975
+ body: { error: result.reason }
1976
+ }
1977
+ };
1978
+ }
1979
+ }
1980
+ const paymentOptions = this.normalizePaymentOptions(routeConfig);
1981
+ const paymentPayload = this.extractPayment(adapter);
1982
+ const resourceInfo = {
1983
+ url: routeConfig.resource || enrichedContext.adapter.getUrl(),
1984
+ description: routeConfig.description || "",
1985
+ mimeType: routeConfig.mimeType || "",
1986
+ ...routeConfig.serviceName !== void 0 && { serviceName: routeConfig.serviceName },
1987
+ ...routeConfig.tags !== void 0 && { tags: routeConfig.tags },
1988
+ ...routeConfig.iconUrl !== void 0 && { iconUrl: routeConfig.iconUrl }
1989
+ };
1990
+ let requirements = await this.ResourceServer.buildPaymentRequirementsFromOptions(
1991
+ paymentOptions,
1992
+ enrichedContext
1993
+ );
1994
+ let extensions = routeConfig.extensions;
1995
+ if (extensions) {
1996
+ extensions = this.ResourceServer.enrichExtensions(extensions, enrichedContext);
1997
+ }
1998
+ const transportContext = { request: enrichedContext };
1999
+ const paymentRequired = await this.ResourceServer.createPaymentRequiredResponse(
2000
+ requirements,
2001
+ resourceInfo,
2002
+ !paymentPayload ? "Payment required" : void 0,
2003
+ extensions,
2004
+ transportContext
2005
+ );
2006
+ if (!paymentPayload) {
2007
+ const unpaidBody = routeConfig.unpaidResponseBody ? await routeConfig.unpaidResponseBody(enrichedContext) : void 0;
2008
+ return {
2009
+ type: "payment-error",
2010
+ response: this.createHTTPResponse(
2011
+ paymentRequired,
2012
+ this.isWebBrowser(adapter),
2013
+ paywallConfig,
2014
+ routeConfig.customPaywallHtml,
2015
+ unpaidBody
2016
+ )
2017
+ };
2018
+ }
2019
+ try {
2020
+ const matchingRequirements = this.ResourceServer.findMatchingRequirements(
2021
+ paymentRequired.accepts,
2022
+ paymentPayload
2023
+ );
2024
+ if (!matchingRequirements) {
2025
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
2026
+ requirements,
2027
+ resourceInfo,
2028
+ "No matching payment requirements",
2029
+ extensions,
2030
+ transportContext
2031
+ );
2032
+ return {
2033
+ type: "payment-error",
2034
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
2035
+ };
2036
+ }
2037
+ const extensionResult = this.ResourceServer.validateExtensions(
2038
+ paymentRequired,
2039
+ paymentPayload
2040
+ );
2041
+ if (!extensionResult.valid) {
2042
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
2043
+ requirements,
2044
+ resourceInfo,
2045
+ extensionResult.invalidReason,
2046
+ extensions,
2047
+ transportContext,
2048
+ paymentPayload
2049
+ );
2050
+ return {
2051
+ type: "payment-error",
2052
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
2053
+ };
2054
+ }
2055
+ const verifyResult = await this.ResourceServer.verifyPayment(
2056
+ paymentPayload,
2057
+ matchingRequirements,
2058
+ extensions,
2059
+ transportContext
2060
+ );
2061
+ if (!verifyResult.isValid) {
2062
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
2063
+ requirements,
2064
+ resourceInfo,
2065
+ verifyResult.invalidReason,
2066
+ extensions,
2067
+ transportContext,
2068
+ paymentPayload
2069
+ );
2070
+ return {
2071
+ type: "payment-error",
2072
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
2073
+ };
2074
+ }
2075
+ if (verifyResult.skipHandler) {
2076
+ return await this.processSkipHandlerSettlement(
2077
+ paymentPayload,
2078
+ matchingRequirements,
2079
+ extensions,
2080
+ transportContext,
2081
+ verifyResult.skipHandler
2082
+ );
2083
+ }
2084
+ const cancellationDispatcher = this.ResourceServer.createPaymentCancellationDispatcher(
2085
+ paymentPayload,
2086
+ matchingRequirements,
2087
+ extensions,
2088
+ transportContext
2089
+ );
2090
+ return {
2091
+ type: "payment-verified",
2092
+ cancellationDispatcher,
2093
+ paymentPayload,
2094
+ paymentRequirements: matchingRequirements,
2095
+ declaredExtensions: extensions
2096
+ };
2097
+ } catch (error) {
2098
+ if (error instanceof FacilitatorResponseError) {
2099
+ throw error;
2100
+ }
2101
+ const errorResponse = await this.ResourceServer.createPaymentRequiredResponse(
2102
+ requirements,
2103
+ resourceInfo,
2104
+ error instanceof Error ? error.message : "Payment verification failed",
2105
+ extensions,
2106
+ transportContext
2107
+ );
2108
+ return {
2109
+ type: "payment-error",
2110
+ response: this.createHTTPResponse(errorResponse, false, paywallConfig)
2111
+ };
2112
+ }
2113
+ }
2114
+ /**
2115
+ * Process settlement after successful response
2116
+ *
2117
+ * @param paymentPayload - The verified payment payload
2118
+ * @param requirements - The matching payment requirements
2119
+ * @param declaredExtensions - Optional declared extensions (for per-key enrichment)
2120
+ * @param transportContext - Optional HTTP transport context
2121
+ * @param settlementOverrides - Optional settlement overrides (e.g., partial settlement amount)
2122
+ * @returns ProcessSettleResultResponse - SettleResponse with headers if success or errorReason if failure
2123
+ */
2124
+ async processSettlement(paymentPayload, requirements, declaredExtensions, transportContext, settlementOverrides) {
2125
+ if (transportContext?.request && !transportContext.request.method) {
2126
+ transportContext = {
2127
+ ...transportContext,
2128
+ request: {
2129
+ ...transportContext.request,
2130
+ method: transportContext.request.adapter.getMethod()
2131
+ }
2132
+ };
2133
+ }
2134
+ try {
2135
+ let resolvedOverrides = settlementOverrides;
2136
+ if (!resolvedOverrides && transportContext?.responseHeaders) {
2137
+ const overridesKey = SETTLEMENT_OVERRIDES_HEADER.toLowerCase();
2138
+ const rawValue = Object.entries(transportContext.responseHeaders).find(
2139
+ ([key]) => key.toLowerCase() === overridesKey
2140
+ )?.[1];
2141
+ if (rawValue) {
2142
+ try {
2143
+ resolvedOverrides = JSON.parse(rawValue);
2144
+ } catch {
2145
+ }
2146
+ }
2147
+ }
2148
+ const settleResponse = await this.ResourceServer.settlePayment(
2149
+ paymentPayload,
2150
+ requirements,
2151
+ declaredExtensions,
2152
+ transportContext,
2153
+ resolvedOverrides
2154
+ );
2155
+ if (!settleResponse.success) {
2156
+ const failure = {
2157
+ ...settleResponse,
2158
+ success: false,
2159
+ errorReason: settleResponse.errorReason || "Settlement failed",
2160
+ errorMessage: settleResponse.errorMessage || settleResponse.errorReason || "Settlement failed",
2161
+ headers: this.createSettlementHeaders(settleResponse)
2162
+ };
2163
+ const response = await this.buildSettlementFailureResponse(failure, transportContext);
2164
+ return { ...failure, response };
2165
+ }
2166
+ return {
2167
+ ...settleResponse,
2168
+ success: true,
2169
+ headers: this.createSettlementHeaders(settleResponse),
2170
+ requirements
2171
+ };
2172
+ } catch (error) {
2173
+ if (error instanceof FacilitatorResponseError) {
2174
+ throw error;
2175
+ }
2176
+ if (error instanceof SettleError) {
2177
+ const errorReason2 = error.errorReason || error.message;
2178
+ const settleResponse2 = {
2179
+ success: false,
2180
+ errorReason: errorReason2,
2181
+ errorMessage: error.errorMessage || errorReason2,
2182
+ payer: error.payer,
2183
+ network: error.network,
2184
+ transaction: error.transaction
2185
+ };
2186
+ const failure2 = {
2187
+ ...settleResponse2,
2188
+ success: false,
2189
+ errorReason: errorReason2,
2190
+ headers: this.createSettlementHeaders(settleResponse2)
2191
+ };
2192
+ const response2 = await this.buildSettlementFailureResponse(failure2, transportContext);
2193
+ return { ...failure2, response: response2 };
2194
+ }
2195
+ const errorReason = error instanceof Error ? error.message : "Settlement failed";
2196
+ const settleResponse = {
2197
+ success: false,
2198
+ errorReason,
2199
+ errorMessage: errorReason,
2200
+ network: requirements.network,
2201
+ transaction: ""
2202
+ };
2203
+ const failure = {
2204
+ ...settleResponse,
2205
+ success: false,
2206
+ errorReason,
2207
+ headers: this.createSettlementHeaders(settleResponse)
2208
+ };
2209
+ const response = await this.buildSettlementFailureResponse(failure, transportContext);
2210
+ return { ...failure, response };
2211
+ }
2212
+ }
2213
+ /**
2214
+ * Check if a request requires payment based on route configuration
2215
+ *
2216
+ * @param context - HTTP request context
2217
+ * @returns True if the route requires payment, false otherwise
2218
+ */
2219
+ requiresPayment(context) {
2220
+ const method = context.method || context.adapter.getMethod();
2221
+ return this.getRouteConfig(context.path, method) !== void 0;
2222
+ }
2223
+ /**
2224
+ * Settle a verified payment that requested `skipHandler`, packaging the
2225
+ * result as a `payment-error` HTTPProcessResult so framework adapters can
2226
+ * write the response without invoking the route handler.
2227
+ *
2228
+ * - On success: status 200 + PAYMENT-RESPONSE header + configured body.
2229
+ * - On failure: the standard 402 settlement-failure response.
2230
+ *
2231
+ * @param paymentPayload - Verified payment payload.
2232
+ * @param requirements - Matched payment requirements.
2233
+ * @param declaredExtensions - Optional declared extensions for the route.
2234
+ * @param transportContext - Optional HTTP transport context.
2235
+ * @param skipHandlerResponse - Optional content type + body to return on success.
2236
+ * @returns A `payment-error` HTTPProcessResult carrying the final response.
2237
+ */
2238
+ async processSkipHandlerSettlement(paymentPayload, requirements, declaredExtensions, transportContext, skipHandlerResponse) {
2239
+ const settleResult = await this.processSettlement(
2240
+ paymentPayload,
2241
+ requirements,
2242
+ declaredExtensions,
2243
+ transportContext
2244
+ );
2245
+ if (!settleResult.success) {
2246
+ return { type: "payment-error", response: settleResult.response };
2247
+ }
2248
+ const contentType = skipHandlerResponse?.contentType ?? "application/json";
2249
+ const body = skipHandlerResponse?.body ?? {};
2250
+ return {
2251
+ type: "payment-error",
2252
+ response: {
2253
+ status: 200,
2254
+ headers: {
2255
+ "Content-Type": contentType,
2256
+ ...settleResult.headers
2257
+ },
2258
+ body,
2259
+ isHtml: contentType.includes("text/html")
2260
+ }
2261
+ };
2262
+ }
2263
+ /**
2264
+ * Build HTTPResponseInstructions for settlement failure.
2265
+ * Uses settlementFailedResponseBody hook if configured, otherwise defaults to empty body.
2266
+ *
2267
+ * @param failure - Settlement failure result with headers
2268
+ * @param transportContext - Optional HTTP transport context for the request
2269
+ * @returns HTTP response instructions for the 402 settlement failure response
2270
+ */
2271
+ async buildSettlementFailureResponse(failure, transportContext) {
2272
+ const settlementHeaders = failure.headers;
2273
+ const routeConfig = transportContext ? this.getRouteConfig(transportContext.request.path, transportContext.request.method) : void 0;
2274
+ const customBody = routeConfig?.config.settlementFailedResponseBody ? await routeConfig.config.settlementFailedResponseBody(transportContext.request, failure) : void 0;
2275
+ const contentType = customBody ? customBody.contentType : "application/json";
2276
+ const body = customBody ? customBody.body : {};
2277
+ return {
2278
+ status: 402,
2279
+ headers: {
2280
+ "Content-Type": contentType,
2281
+ ...settlementHeaders
2282
+ },
2283
+ body,
2284
+ isHtml: contentType.includes("text/html")
2285
+ };
2286
+ }
2287
+ /**
2288
+ * Normalizes a RouteConfig's accepts field into an array of PaymentOptions
2289
+ * Handles both single PaymentOption and array formats
2290
+ *
2291
+ * @param routeConfig - Route configuration
2292
+ * @returns Array of payment options
2293
+ */
2294
+ normalizePaymentOptions(routeConfig) {
2295
+ return Array.isArray(routeConfig.accepts) ? routeConfig.accepts : [routeConfig.accepts];
2296
+ }
2297
+ /**
2298
+ * Manual request hooks run before extension transport hooks for declared extensions.
2299
+ *
2300
+ * @param routeConfig - Route configuration for the matched request
2301
+ * @returns Hooks in invocation order
2302
+ */
2303
+ getProtectedRequestHooks(routeConfig) {
2304
+ const hooks = [...this.protectedRequestHooks];
2305
+ const declaredExtensions = routeConfig.extensions;
2306
+ if (!declaredExtensions) return hooks;
2307
+ for (const extension of this.ResourceServer.getExtensions()) {
2308
+ const hook = extension.transportHooks?.http?.onProtectedRequest;
2309
+ if (!hook || !(extension.key in declaredExtensions)) continue;
2310
+ hooks.push(
2311
+ (context, routeConfig2) => hook(declaredExtensions[extension.key], context, routeConfig2)
2312
+ );
2313
+ }
2314
+ return hooks;
2315
+ }
2316
+ /**
2317
+ * Validates that all payment options in routes have corresponding registered schemes
2318
+ * and facilitator support.
2319
+ *
2320
+ * @returns Array of validation errors (empty if all routes are valid)
2321
+ */
2322
+ validateRouteConfiguration() {
2323
+ const errors = [];
2324
+ const normalizedRoutes = typeof this.routesConfig === "object" && !("accepts" in this.routesConfig) ? Object.entries(this.routesConfig) : [["*", this.routesConfig]];
2325
+ for (const [pattern, config] of normalizedRoutes) {
2326
+ const pathPart = pattern.includes(" ") ? pattern.split(/\s+/)[1] : pattern;
2327
+ if (pathPart && pathPart.includes("*") && config.extensions && "bazaar" in config.extensions) {
2328
+ console.warn(
2329
+ `[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.`
2330
+ );
2331
+ }
2332
+ const paymentOptions = this.normalizePaymentOptions(config);
2333
+ for (const option of paymentOptions) {
2334
+ if (!this.ResourceServer.hasRegisteredScheme(option.network, option.scheme)) {
2335
+ errors.push({
2336
+ routePattern: pattern,
2337
+ scheme: option.scheme,
2338
+ network: option.network,
2339
+ reason: "missing_scheme",
2340
+ message: `Route "${pattern}": No scheme implementation registered for "${option.scheme}" on network "${option.network}"`
2341
+ });
2342
+ continue;
2343
+ }
2344
+ const supportedKind = this.ResourceServer.getSupportedKind(
2345
+ x402Version,
2346
+ option.network,
2347
+ option.scheme
2348
+ );
2349
+ if (!supportedKind) {
2350
+ errors.push({
2351
+ routePattern: pattern,
2352
+ scheme: option.scheme,
2353
+ network: option.network,
2354
+ reason: "missing_facilitator",
2355
+ message: `Route "${pattern}": Facilitator does not support scheme "${option.scheme}" on network "${option.network}"`
2356
+ });
2357
+ }
2358
+ }
2359
+ }
2360
+ return errors;
2361
+ }
2362
+ /**
2363
+ * Get route configuration for a request
2364
+ *
2365
+ * @param path - Request path
2366
+ * @param method - HTTP method
2367
+ * @returns Route configuration and pattern, or undefined if no match
2368
+ */
2369
+ getRouteConfig(path, method) {
2370
+ const normalizedPath = this.normalizePath(path);
2371
+ const upperMethod = method.toUpperCase();
2372
+ const matchingRoute = this.compiledRoutes.find(
2373
+ (route) => route.regex.test(normalizedPath) && (route.verb === "*" || route.verb === upperMethod)
2374
+ );
2375
+ if (!matchingRoute) return void 0;
2376
+ return { config: matchingRoute.config, pattern: matchingRoute.pattern };
2377
+ }
2378
+ /**
2379
+ * Extract payment from HTTP headers (handles v1 and v2)
2380
+ *
2381
+ * @param adapter - HTTP adapter
2382
+ * @returns Decoded payment payload or null
2383
+ */
2384
+ extractPayment(adapter) {
2385
+ const header = adapter.getHeader("payment-signature") || adapter.getHeader("PAYMENT-SIGNATURE");
2386
+ if (header) {
2387
+ try {
2388
+ return decodePaymentSignatureHeader(header);
2389
+ } catch (error) {
2390
+ console.warn("Failed to decode PAYMENT-SIGNATURE header:", error);
2391
+ }
2392
+ }
2393
+ return null;
2394
+ }
2395
+ /**
2396
+ * Check if request is from a web browser
2397
+ *
2398
+ * @param adapter - HTTP adapter
2399
+ * @returns True if request appears to be from a browser
2400
+ */
2401
+ isWebBrowser(adapter) {
2402
+ const accept = adapter.getAcceptHeader();
2403
+ const userAgent = adapter.getUserAgent();
2404
+ return accept.includes("text/html") && userAgent.includes("Mozilla");
2405
+ }
2406
+ /**
2407
+ * Create HTTP response instructions from payment required
2408
+ *
2409
+ * @param paymentRequired - Payment requirements
2410
+ * @param isWebBrowser - Whether request is from browser
2411
+ * @param paywallConfig - Paywall configuration
2412
+ * @param customHtml - Custom HTML template
2413
+ * @param unpaidResponse - Optional custom response (content type and body) for unpaid API requests
2414
+ * @returns Response instructions
2415
+ */
2416
+ createHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) {
2417
+ const status = paymentRequired.error === "permit2_allowance_required" ? 412 : 402;
2418
+ const response = this.createHTTPPaymentRequiredResponse(paymentRequired);
2419
+ if (isWebBrowser) {
2420
+ const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml);
2421
+ return {
2422
+ status,
2423
+ headers: {
2424
+ "Content-Type": "text/html",
2425
+ ...response.headers
2426
+ },
2427
+ body: html,
2428
+ isHtml: true
2429
+ };
2430
+ }
2431
+ const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json";
2432
+ const body = unpaidResponse ? unpaidResponse.body : {};
2433
+ return {
2434
+ status,
2435
+ headers: {
2436
+ "Content-Type": contentType,
2437
+ ...response.headers
2438
+ },
2439
+ body
2440
+ };
2441
+ }
2442
+ /**
2443
+ * Create HTTP payment required response (v1 puts in body, v2 puts in header)
2444
+ *
2445
+ * @param paymentRequired - Payment required object
2446
+ * @returns Headers and body for the HTTP response
2447
+ */
2448
+ createHTTPPaymentRequiredResponse(paymentRequired) {
2449
+ return {
2450
+ headers: {
2451
+ "PAYMENT-REQUIRED": encodePaymentRequiredHeader(paymentRequired)
2452
+ }
2453
+ };
2454
+ }
2455
+ /**
2456
+ * Create settlement response headers
2457
+ *
2458
+ * @param settleResponse - Settlement response
2459
+ * @returns Headers to add to response
2460
+ */
2461
+ createSettlementHeaders(settleResponse) {
2462
+ const encoded = encodePaymentResponseHeader(settleResponse);
2463
+ return { "PAYMENT-RESPONSE": encoded };
2464
+ }
2465
+ /**
2466
+ * Parse route pattern into verb and regex
2467
+ *
2468
+ * @param pattern - Route pattern like "GET /api/*", "/api/[id]", or "/api/:id"
2469
+ * @returns Parsed pattern with verb and regex
2470
+ */
2471
+ parseRoutePattern(pattern) {
2472
+ const [verb, path] = pattern.includes(" ") ? pattern.split(/\s+/) : ["*", pattern];
2473
+ const regex = new RegExp(
2474
+ `^${path.replace(/\\/g, "\\\\").replace(/[$()+.?^{|}]/g, "\\$&").replace(/\*/g, ".*?").replace(/\[([^\]]+)\]/g, "[^/]+").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "[^/]+").replace(/\//g, "\\/")}$`,
2475
+ "i"
2476
+ );
2477
+ return { verb: verb.toUpperCase(), regex, path };
2478
+ }
2479
+ /**
2480
+ * Normalize path for matching
2481
+ *
2482
+ * @param path - Raw path from request
2483
+ * @returns Normalized path
2484
+ */
2485
+ normalizePath(path) {
2486
+ const pathWithoutQuery = path.split(/[?#]/)[0];
2487
+ const parts = pathWithoutQuery.split(/(%2[fF]|%5[cC])/);
2488
+ const decoded = parts.map((part, i) => {
2489
+ if (i % 2 === 1) return part;
2490
+ try {
2491
+ return decodeURIComponent(part);
2492
+ } catch {
2493
+ return part;
2494
+ }
2495
+ }).join("");
2496
+ return decoded.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/(.+?)\/+$/, "$1");
2497
+ }
2498
+ /**
2499
+ * Generate paywall HTML for browser requests
2500
+ *
2501
+ * @param paymentRequired - Payment required response
2502
+ * @param paywallConfig - Optional paywall configuration
2503
+ * @param customHtml - Optional custom HTML template
2504
+ * @returns HTML string
2505
+ */
2506
+ generatePaywallHTML(paymentRequired, paywallConfig, customHtml) {
2507
+ if (customHtml) {
2508
+ return customHtml;
2509
+ }
2510
+ if (this.paywallProvider) {
2511
+ return this.paywallProvider.generateHtml(paymentRequired, paywallConfig);
2512
+ }
2513
+ try {
2514
+ const paywall = require("@bankofai/x402-paywall");
2515
+ const displayAmount = this.getDisplayAmount(paymentRequired);
2516
+ const resource = paymentRequired.resource;
2517
+ return paywall.getPaywallHtml({
2518
+ amount: displayAmount,
2519
+ paymentRequired,
2520
+ currentUrl: resource?.url || paywallConfig?.currentUrl || "",
2521
+ testnet: paywallConfig?.testnet ?? true,
2522
+ appName: paywallConfig?.appName,
2523
+ appLogo: paywallConfig?.appLogo,
2524
+ sessionTokenEndpoint: paywallConfig?.sessionTokenEndpoint
2525
+ });
2526
+ } catch {
2527
+ }
2528
+ return FALLBACK_PAYWALL_HTML;
2529
+ }
2530
+ /**
2531
+ * Extract display amount from payment requirements.
2532
+ * Uses the registered scheme's decimal precision for the asset, falling back to 6.
2533
+ *
2534
+ * @param paymentRequired - The payment required object
2535
+ * @returns The display amount in decimal format
2536
+ */
2537
+ getDisplayAmount(paymentRequired) {
2538
+ const accepts = paymentRequired.accepts;
2539
+ if (accepts && accepts.length > 0) {
2540
+ const firstReq = accepts[0];
2541
+ if ("amount" in firstReq) {
2542
+ const decimals = this.ResourceServer.getAssetDecimalsForRequirements(firstReq);
2543
+ return parseFloat(firstReq.amount) / 10 ** decimals;
2544
+ }
2545
+ }
2546
+ return 0;
2547
+ }
2548
+ };
2549
+ // Annotate the CommonJS export names for ESM import in node:
2550
+ 0 && (module.exports = {
2551
+ FacilitatorResponseError,
2552
+ HTTPFacilitatorClient,
2553
+ RouteConfigurationError,
2554
+ SETTLEMENT_OVERRIDES_HEADER,
2555
+ assertAcceptsAdditiveExtraAfterSchemeEnrich,
2556
+ assertAcceptsAllowlistedAfterExtensionEnrich,
2557
+ assertAdditivePayloadEnrichment,
2558
+ assertAdditiveSettlementExtra,
2559
+ assertSettleResponseCoreUnchanged,
2560
+ checkIfBazaarNeeded,
2561
+ getFacilitatorResponseError,
2562
+ isVacantStringField,
2563
+ snapshotPaymentRequirementsList,
2564
+ snapshotSettleResponseCore,
2565
+ x402HTTPResourceServer,
2566
+ x402ResourceServer
2567
+ });
2568
+ //# sourceMappingURL=index.js.map