@x402/core
Advanced tools
Sorry, the diff of this file is too big to display
| // src/utils/index.ts | ||
| function numberToDecimalString(n) { | ||
| const str = n.toString(); | ||
| if (!/[eE]/.test(str)) return str; | ||
| const [significand, exponentStr] = str.split(/[eE]/); | ||
| const exp = parseInt(exponentStr, 10); | ||
| const negative = significand.startsWith("-"); | ||
| const abs = negative ? significand.slice(1) : significand; | ||
| const [intDigits, fracDigits = ""] = abs.split("."); | ||
| const allDigits = intDigits + fracDigits; | ||
| const decimalPos = intDigits.length + exp; | ||
| let result; | ||
| if (decimalPos <= 0) { | ||
| result = "0." + "0".repeat(-decimalPos) + allDigits; | ||
| } else if (decimalPos >= allDigits.length) { | ||
| result = allDigits + "0".repeat(decimalPos - allDigits.length); | ||
| } else { | ||
| result = allDigits.slice(0, decimalPos) + "." + allDigits.slice(decimalPos); | ||
| } | ||
| return (negative ? "-" : "") + result; | ||
| } | ||
| function parseMoneyString(money) { | ||
| const cleaned = money.replace(/^\$/, "").trim(); | ||
| if (!/^-?\d+(?:\.\d+)?$/.test(cleaned) || /[eE]/.test(cleaned)) { | ||
| throw new Error(`Invalid money format: ${money}`); | ||
| } | ||
| const amount = Number(cleaned); | ||
| if (!Number.isFinite(amount) || amount < 0) { | ||
| throw new Error(`Invalid money format: ${money}`); | ||
| } | ||
| return amount; | ||
| } | ||
| function convertToTokenAmount(decimalAmount, decimals) { | ||
| if (/[eE]/.test(decimalAmount)) { | ||
| throw new Error( | ||
| `Invalid amount: ${decimalAmount} \u2014 use decimal notation, not scientific notation` | ||
| ); | ||
| } | ||
| if (!/^-?\d+\.?\d*$/.test(decimalAmount)) { | ||
| throw new Error(`Invalid amount: ${decimalAmount}`); | ||
| } | ||
| const [intPart, decPart = ""] = decimalAmount.split("."); | ||
| const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals); | ||
| const tokenAmount = (intPart + paddedDec).replace(/^0+/, "") || "0"; | ||
| if (tokenAmount === "0" && /[1-9]/.test(decimalAmount)) { | ||
| throw new Error( | ||
| `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places` | ||
| ); | ||
| } | ||
| return tokenAmount; | ||
| } | ||
| var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| var networkPatternToRegExp = (pattern) => { | ||
| const source = escapeRegExp(pattern).replace(/\\\*/g, ".*"); | ||
| return new RegExp(`^${source}$`); | ||
| }; | ||
| var networkMatchesPattern = (pattern, network) => { | ||
| return networkPatternToRegExp(pattern).test(network); | ||
| }; | ||
| var findSchemesByNetwork = (map, network) => { | ||
| let implementationsByScheme = map.get(network); | ||
| if (!implementationsByScheme) { | ||
| for (const [registeredNetworkPattern, implementations] of map.entries()) { | ||
| if (networkMatchesPattern(registeredNetworkPattern, network)) { | ||
| implementationsByScheme = implementations; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return implementationsByScheme; | ||
| }; | ||
| var findByNetworkAndScheme = (map, scheme, network) => { | ||
| return findSchemesByNetwork(map, network)?.get(scheme); | ||
| }; | ||
| var findFacilitatorBySchemeAndNetwork = (schemeMap, scheme, network) => { | ||
| const schemeData = schemeMap.get(scheme); | ||
| if (!schemeData) return void 0; | ||
| if (schemeData.networks.has(network)) { | ||
| return schemeData.facilitator; | ||
| } | ||
| if (networkMatchesPattern(schemeData.pattern, network)) { | ||
| return schemeData.facilitator; | ||
| } | ||
| return void 0; | ||
| }; | ||
| var Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/; | ||
| function safeBase64Encode(data) { | ||
| if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") { | ||
| const bytes = new TextEncoder().encode(data); | ||
| const binaryString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join(""); | ||
| return globalThis.btoa(binaryString); | ||
| } | ||
| return Buffer.from(data, "utf8").toString("base64"); | ||
| } | ||
| function safeBase64Decode(data) { | ||
| if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") { | ||
| const binaryString = globalThis.atob(data); | ||
| const bytes = new Uint8Array(binaryString.length); | ||
| for (let i = 0; i < binaryString.length; i++) { | ||
| bytes[i] = binaryString.charCodeAt(i); | ||
| } | ||
| const decoder = new TextDecoder("utf-8"); | ||
| return decoder.decode(bytes); | ||
| } | ||
| return Buffer.from(data, "base64").toString("utf-8"); | ||
| } | ||
| function deepEqual(obj1, obj2) { | ||
| const normalize = (obj) => { | ||
| if (obj === null || obj === void 0) return JSON.stringify(obj); | ||
| if (typeof obj !== "object") return JSON.stringify(obj); | ||
| if (Array.isArray(obj)) { | ||
| return JSON.stringify( | ||
| obj.map( | ||
| (item) => typeof item === "object" && item !== null ? JSON.parse(normalize(item)) : item | ||
| ) | ||
| ); | ||
| } | ||
| const sorted = {}; | ||
| Object.keys(obj).sort().forEach((key) => { | ||
| const value = obj[key]; | ||
| sorted[key] = typeof value === "object" && value !== null ? JSON.parse(normalize(value)) : value; | ||
| }); | ||
| return JSON.stringify(sorted); | ||
| }; | ||
| try { | ||
| return normalize(obj1) === normalize(obj2); | ||
| } catch { | ||
| return JSON.stringify(obj1) === JSON.stringify(obj2); | ||
| } | ||
| } | ||
| export { | ||
| numberToDecimalString, | ||
| parseMoneyString, | ||
| convertToTokenAmount, | ||
| networkMatchesPattern, | ||
| findSchemesByNetwork, | ||
| findByNetworkAndScheme, | ||
| findFacilitatorBySchemeAndNetwork, | ||
| Base64EncodedRegex, | ||
| safeBase64Encode, | ||
| safeBase64Decode, | ||
| deepEqual | ||
| }; | ||
| //# sourceMappingURL=chunk-ABS7D6VX.mjs.map |
| {"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["import { Network } from \"../types\";\n\n/**\n * Converts a JavaScript number to a plain decimal string, expanding scientific notation\n * via string manipulation rather than parseFloat round-tripping.\n *\n * e.g. 1e-7 → \"0.0000001\", 4.02 → \"4.02\"\n *\n * @param n - The number to convert\n * @returns A plain decimal string representation with no scientific notation\n */\nexport function numberToDecimalString(n: number): string {\n const str = n.toString();\n if (!/[eE]/.test(str)) return str;\n\n const [significand, exponentStr] = str.split(/[eE]/);\n const exp = parseInt(exponentStr, 10);\n const negative = significand.startsWith(\"-\");\n const abs = negative ? significand.slice(1) : significand;\n const [intDigits, fracDigits = \"\"] = abs.split(\".\");\n const allDigits = intDigits + fracDigits;\n const decimalPos = intDigits.length + exp;\n\n let result: string;\n if (decimalPos <= 0) {\n result = \"0.\" + \"0\".repeat(-decimalPos) + allDigits;\n } else if (decimalPos >= allDigits.length) {\n result = allDigits + \"0\".repeat(decimalPos - allDigits.length);\n } else {\n result = allDigits.slice(0, decimalPos) + \".\" + allDigits.slice(decimalPos);\n }\n return (negative ? \"-\" : \"\") + result;\n}\n\n/**\n * Parses a money string into a finite, non-negative decimal number.\n * Accepts plain decimal strings with an optional leading dollar sign.\n *\n * @param money - The money string to parse\n * @returns Decimal number\n */\nexport function parseMoneyString(money: string): number {\n const cleaned = money.replace(/^\\$/, \"\").trim();\n if (!/^-?\\d+(?:\\.\\d+)?$/.test(cleaned) || /[eE]/.test(cleaned)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n const amount = Number(cleaned);\n if (!Number.isFinite(amount) || amount < 0) {\n throw new Error(`Invalid money format: ${money}`);\n }\n return amount;\n}\n\n/**\n * Convert a decimal amount to token smallest units.\n * Accepts only plain decimal strings — scientific notation is not allowed.\n * Throws if the amount is non-zero but too small to represent with the given decimal precision.\n *\n * @param decimalAmount - The decimal amount as a plain string (e.g., \"0.10\")\n * @param decimals - The number of decimals for the token (e.g., 6 for USDC)\n * @returns The amount in smallest units as a string\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n if (/[eE]/.test(decimalAmount)) {\n throw new Error(\n `Invalid amount: ${decimalAmount} — use decimal notation, not scientific notation`,\n );\n }\n if (!/^-?\\d+\\.?\\d*$/.test(decimalAmount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n const [intPart, decPart = \"\"] = decimalAmount.split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n if (tokenAmount === \"0\" && /[1-9]/.test(decimalAmount)) {\n throw new Error(\n `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places`,\n );\n }\n return tokenAmount;\n}\n\n/**\n * Scheme data structure for facilitator storage\n */\nexport interface SchemeData<T> {\n facilitator: T;\n networks: Set<Network>;\n pattern: Network;\n}\n\nconst escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nconst networkPatternToRegExp = (pattern: Network): RegExp => {\n const source = escapeRegExp(pattern).replace(/\\\\\\*/g, \".*\");\n return new RegExp(`^${source}$`);\n};\n\nexport const networkMatchesPattern = (pattern: Network, network: Network): boolean => {\n return networkPatternToRegExp(pattern).test(network);\n};\n\nexport const findSchemesByNetwork = <T>(\n map: Map<string, Map<string, T>>,\n network: Network,\n): Map<string, T> | undefined => {\n // Direct match first\n let implementationsByScheme = map.get(network);\n\n if (!implementationsByScheme) {\n // Try pattern matching for registered network patterns\n for (const [registeredNetworkPattern, implementations] of map.entries()) {\n if (networkMatchesPattern(registeredNetworkPattern as Network, network)) {\n implementationsByScheme = implementations;\n break;\n }\n }\n }\n\n return implementationsByScheme;\n};\n\nexport const findByNetworkAndScheme = <T>(\n map: Map<string, Map<string, T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n return findSchemesByNetwork(map, network)?.get(scheme);\n};\n\n/**\n * Finds a facilitator by scheme and network using pattern matching.\n * Works with new SchemeData storage structure.\n *\n * @param schemeMap - Map of scheme names to SchemeData\n * @param scheme - The scheme to find\n * @param network - The network to match against\n * @returns The facilitator if found, undefined otherwise\n */\nexport const findFacilitatorBySchemeAndNetwork = <T>(\n schemeMap: Map<string, SchemeData<T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n const schemeData = schemeMap.get(scheme);\n if (!schemeData) return undefined;\n\n // Check if network is in the stored networks set\n if (schemeData.networks.has(network)) {\n return schemeData.facilitator;\n }\n\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, network)) {\n return schemeData.facilitator;\n }\n\n return undefined;\n};\n\nexport const Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n/**\n * Encodes a string to base64 format\n *\n * @param data - The string to be encoded to base64\n * @returns The base64 encoded string\n */\nexport function safeBase64Encode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.btoa === \"function\") {\n const bytes = new TextEncoder().encode(data);\n const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join(\"\");\n return globalThis.btoa(binaryString);\n }\n return Buffer.from(data, \"utf8\").toString(\"base64\");\n}\n\n/**\n * Decodes a base64 string back to its original format\n *\n * @param data - The base64 encoded string to be decoded\n * @returns The decoded string in UTF-8 format\n */\nexport function safeBase64Decode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.atob === \"function\") {\n const binaryString = globalThis.atob(data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder(\"utf-8\");\n return decoder.decode(bytes);\n }\n return Buffer.from(data, \"base64\").toString(\"utf-8\");\n}\n\n/**\n * Deep equality comparison for payment requirements\n * Uses a normalized JSON.stringify for consistent comparison\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are deeply equal\n */\nexport function deepEqual(obj1: unknown, obj2: unknown): boolean {\n // Normalize and stringify both objects for comparison\n // This handles nested objects, arrays, and different property orders\n const normalize = (obj: unknown): string => {\n // Handle primitives and null/undefined\n if (obj === null || obj === undefined) return JSON.stringify(obj);\n if (typeof obj !== \"object\") return JSON.stringify(obj);\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return JSON.stringify(\n obj.map(item =>\n typeof item === \"object\" && item !== null ? JSON.parse(normalize(item)) : item,\n ),\n );\n }\n\n // Handle objects - sort keys and recursively normalize values\n const sorted: Record<string, unknown> = {};\n Object.keys(obj as Record<string, unknown>)\n .sort()\n .forEach(key => {\n const value = (obj as Record<string, unknown>)[key];\n sorted[key] =\n typeof value === \"object\" && value !== null ? JSON.parse(normalize(value)) : value;\n });\n return JSON.stringify(sorted);\n };\n\n try {\n return normalize(obj1) === normalize(obj2);\n } catch {\n // Fallback to simple comparison if normalization fails\n return JSON.stringify(obj1) === JSON.stringify(obj2);\n }\n}\n"],"mappings":"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","names":[]} |
| import { | ||
| z | ||
| } from "./chunk-FPXAE3OS.mjs"; | ||
| import { | ||
| x402Version | ||
| } from "./chunk-VE37GDG2.mjs"; | ||
| import { | ||
| FacilitatorResponseError, | ||
| SettleError, | ||
| VerifyError | ||
| } from "./chunk-AGOUMC4P.mjs"; | ||
| import { | ||
| Base64EncodedRegex, | ||
| safeBase64Decode, | ||
| safeBase64Encode | ||
| } from "./chunk-ABS7D6VX.mjs"; | ||
| import { | ||
| __require | ||
| } from "./chunk-BJTO5JO5.mjs"; | ||
| // src/http/x402HTTPResourceServer.ts | ||
| var SETTLEMENT_OVERRIDES_HEADER = "Settlement-Overrides"; | ||
| function checkIfBazaarNeeded(routes) { | ||
| if ("accepts" in routes) { | ||
| return !!(routes.extensions && "bazaar" in routes.extensions); | ||
| } | ||
| return Object.values(routes).some((routeConfig) => { | ||
| return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions); | ||
| }); | ||
| } | ||
| var RouteConfigurationError = class extends Error { | ||
| /** | ||
| * Creates a new RouteConfigurationError with the given validation errors. | ||
| * | ||
| * @param errors - The validation errors that caused this exception. | ||
| */ | ||
| constructor(errors) { | ||
| const message = `x402 Route Configuration Errors: | ||
| ${errors.map((e) => ` - ${e.message}`).join("\n")}`; | ||
| super(message); | ||
| this.name = "RouteConfigurationError"; | ||
| this.errors = errors; | ||
| } | ||
| }; | ||
| var FALLBACK_PAYWALL_HTML = `<!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Payment Required</title> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| </head> | ||
| <body> | ||
| <div style="max-width: 600px; margin: 50px auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;"> | ||
| <h1>Payment Required</h1> | ||
| <p>This resource is protected by the x402 payment protocol.</p> | ||
| <p style="margin-top: 2rem; padding: 1rem; background: #fef3c7; border-radius: 0.5rem;"> | ||
| <strong>Note to developers:</strong> install <code>@x402/paywall</code> to enable | ||
| the in-browser wallet connection and payment UI. Programmatic clients should read | ||
| the payment requirements from the 402 response headers and JSON body. | ||
| </p> | ||
| </div> | ||
| </body> | ||
| </html>`; | ||
| var x402HTTPResourceServer = class { | ||
| /** | ||
| * Creates a new x402HTTPResourceServer instance. | ||
| * | ||
| * @param ResourceServer - The core x402ResourceServer instance to use | ||
| * @param routes - Route configuration for payment-protected endpoints | ||
| */ | ||
| constructor(ResourceServer, routes) { | ||
| this.compiledRoutes = []; | ||
| this.protectedRequestHooks = []; | ||
| this.ResourceServer = ResourceServer; | ||
| this.routesConfig = routes; | ||
| const normalizedRoutes = typeof routes === "object" && !("accepts" in routes) ? routes : { "*": routes }; | ||
| for (const [pattern, config] of Object.entries(normalizedRoutes)) { | ||
| const parsed = this.parseRoutePattern(pattern); | ||
| this.compiledRoutes.push({ | ||
| verb: parsed.verb, | ||
| regex: parsed.regex, | ||
| config, | ||
| pattern: parsed.path | ||
| }); | ||
| } | ||
| } | ||
| /** | ||
| * Get the underlying x402ResourceServer instance. | ||
| * | ||
| * @returns The underlying x402ResourceServer instance | ||
| */ | ||
| get server() { | ||
| return this.ResourceServer; | ||
| } | ||
| /** | ||
| * Get the routes configuration. | ||
| * | ||
| * @returns The routes configuration | ||
| */ | ||
| get routes() { | ||
| return this.routesConfig; | ||
| } | ||
| /** | ||
| * Initialize the HTTP resource server. | ||
| * | ||
| * This method initializes the underlying resource server (fetching facilitator support) | ||
| * and then validates that all route payment configurations have corresponding | ||
| * registered schemes and facilitator support. | ||
| * | ||
| * @throws RouteConfigurationError if any route's payment options don't have | ||
| * corresponding registered schemes or facilitator support | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const httpServer = new x402HTTPResourceServer(server, routes); | ||
| * await httpServer.initialize(); | ||
| * ``` | ||
| */ | ||
| async initialize() { | ||
| await this.ResourceServer.initialize(); | ||
| const errors = this.validateRouteConfiguration(); | ||
| if (errors.length > 0) { | ||
| throw new RouteConfigurationError(errors); | ||
| } | ||
| } | ||
| /** | ||
| * Register a custom paywall provider for generating HTML | ||
| * | ||
| * @param provider - PaywallProvider instance | ||
| * @returns This service instance for chaining | ||
| */ | ||
| registerPaywallProvider(provider) { | ||
| this.paywallProvider = provider; | ||
| return this; | ||
| } | ||
| /** | ||
| * Register a hook that runs on every request to a protected route, before payment processing. | ||
| * Hooks are executed in order of registration. The first hook to return a non-void result wins. | ||
| * | ||
| * @param hook - The request hook function | ||
| * @returns The x402HTTPResourceServer instance for chaining | ||
| */ | ||
| onProtectedRequest(hook) { | ||
| this.protectedRequestHooks.push(hook); | ||
| return this; | ||
| } | ||
| /** | ||
| * Process HTTP request and return response instructions | ||
| * This is the main entry point for framework middleware | ||
| * | ||
| * @param context - HTTP request context | ||
| * @param paywallConfig - Optional paywall configuration | ||
| * @returns Process result indicating next action for middleware | ||
| */ | ||
| async processHTTPRequest(context, paywallConfig) { | ||
| const method = context.method || context.adapter.getMethod(); | ||
| context = { ...context, method }; | ||
| const { adapter, path } = context; | ||
| const routeMatch = this.getRouteConfig(path, method); | ||
| if (!routeMatch) { | ||
| return { type: "no-payment-required" }; | ||
| } | ||
| const { config: routeConfig, pattern: routePattern } = routeMatch; | ||
| const enrichedContext = { ...context, routePattern }; | ||
| for (const hook of this.getProtectedRequestHooks(routeConfig)) { | ||
| const result = await hook(enrichedContext, routeConfig); | ||
| if (result && "grantAccess" in result) { | ||
| return { type: "no-payment-required" }; | ||
| } | ||
| if (result && "abort" in result) { | ||
| return { | ||
| type: "payment-error", | ||
| response: { | ||
| status: 403, | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: { error: result.reason } | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| const paymentOptions = this.normalizePaymentOptions(routeConfig); | ||
| const paymentPayload = this.extractPayment(adapter); | ||
| const resourceInfo = { | ||
| url: routeConfig.resource || enrichedContext.adapter.getUrl(), | ||
| description: routeConfig.description || "", | ||
| mimeType: routeConfig.mimeType || "", | ||
| ...routeConfig.serviceName !== void 0 && { serviceName: routeConfig.serviceName }, | ||
| ...routeConfig.tags !== void 0 && { tags: routeConfig.tags }, | ||
| ...routeConfig.iconUrl !== void 0 && { iconUrl: routeConfig.iconUrl } | ||
| }; | ||
| let requirements = await this.ResourceServer.buildPaymentRequirementsFromOptions( | ||
| paymentOptions, | ||
| enrichedContext | ||
| ); | ||
| let extensions = routeConfig.extensions; | ||
| if (extensions) { | ||
| extensions = this.ResourceServer.enrichExtensions(extensions, enrichedContext); | ||
| } | ||
| const transportContext = { request: enrichedContext }; | ||
| const paymentRequired = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| !paymentPayload ? "Payment required" : void 0, | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| if (!paymentPayload) { | ||
| const unpaidBody = routeConfig.unpaidResponseBody ? await routeConfig.unpaidResponseBody(enrichedContext) : void 0; | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse( | ||
| paymentRequired, | ||
| this.isWebBrowser(adapter), | ||
| paywallConfig, | ||
| routeConfig.customPaywallHtml, | ||
| unpaidBody | ||
| ) | ||
| }; | ||
| } | ||
| try { | ||
| const matchingRequirements = this.ResourceServer.findMatchingRequirements( | ||
| paymentRequired.accepts, | ||
| paymentPayload | ||
| ); | ||
| if (!matchingRequirements) { | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| "No matching payment requirements", | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| const extensionResult = this.ResourceServer.validateExtensions( | ||
| paymentRequired, | ||
| paymentPayload | ||
| ); | ||
| if (!extensionResult.valid) { | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| extensionResult.invalidReason, | ||
| extensions, | ||
| transportContext, | ||
| paymentPayload | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| const verifyResult = await this.ResourceServer.verifyPayment( | ||
| paymentPayload, | ||
| matchingRequirements, | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| if (!verifyResult.isValid) { | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| verifyResult.invalidReason, | ||
| extensions, | ||
| transportContext, | ||
| paymentPayload | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| if (verifyResult.skipHandler) { | ||
| return await this.processSkipHandlerSettlement( | ||
| paymentPayload, | ||
| matchingRequirements, | ||
| extensions, | ||
| transportContext, | ||
| verifyResult.skipHandler | ||
| ); | ||
| } | ||
| const cancellationDispatcher = this.ResourceServer.createPaymentCancellationDispatcher( | ||
| paymentPayload, | ||
| matchingRequirements, | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| return { | ||
| type: "payment-verified", | ||
| cancellationDispatcher, | ||
| paymentPayload, | ||
| paymentRequirements: matchingRequirements, | ||
| declaredExtensions: extensions | ||
| }; | ||
| } catch (error) { | ||
| if (error instanceof FacilitatorResponseError) { | ||
| throw error; | ||
| } | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| error instanceof Error ? error.message : "Payment verification failed", | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| } | ||
| /** | ||
| * Process settlement after successful response | ||
| * | ||
| * @param paymentPayload - The verified payment payload | ||
| * @param requirements - The matching payment requirements | ||
| * @param declaredExtensions - Optional declared extensions (for per-key enrichment) | ||
| * @param transportContext - Optional HTTP transport context | ||
| * @param settlementOverrides - Optional settlement overrides (e.g., partial settlement amount) | ||
| * @returns ProcessSettleResultResponse - SettleResponse with headers if success or errorReason if failure | ||
| */ | ||
| async processSettlement(paymentPayload, requirements, declaredExtensions, transportContext, settlementOverrides) { | ||
| if (transportContext?.request && !transportContext.request.method) { | ||
| transportContext = { | ||
| ...transportContext, | ||
| request: { | ||
| ...transportContext.request, | ||
| method: transportContext.request.adapter.getMethod() | ||
| } | ||
| }; | ||
| } | ||
| try { | ||
| let resolvedOverrides = settlementOverrides; | ||
| if (!resolvedOverrides && transportContext?.responseHeaders) { | ||
| const overridesKey = SETTLEMENT_OVERRIDES_HEADER.toLowerCase(); | ||
| const rawValue = Object.entries(transportContext.responseHeaders).find( | ||
| ([key]) => key.toLowerCase() === overridesKey | ||
| )?.[1]; | ||
| if (rawValue) { | ||
| try { | ||
| resolvedOverrides = JSON.parse(rawValue); | ||
| } catch { | ||
| } | ||
| } | ||
| } | ||
| const settleResponse = await this.ResourceServer.settlePayment( | ||
| paymentPayload, | ||
| requirements, | ||
| declaredExtensions, | ||
| transportContext, | ||
| resolvedOverrides | ||
| ); | ||
| if (!settleResponse.success) { | ||
| const failure = { | ||
| ...settleResponse, | ||
| success: false, | ||
| errorReason: settleResponse.errorReason || "Settlement failed", | ||
| errorMessage: settleResponse.errorMessage || settleResponse.errorReason || "Settlement failed", | ||
| headers: this.createSettlementHeaders(settleResponse) | ||
| }; | ||
| const response = await this.buildSettlementFailureResponse(failure, transportContext); | ||
| return { ...failure, response }; | ||
| } | ||
| return { | ||
| ...settleResponse, | ||
| success: true, | ||
| headers: this.createSettlementHeaders(settleResponse), | ||
| requirements | ||
| }; | ||
| } catch (error) { | ||
| if (error instanceof FacilitatorResponseError) { | ||
| throw error; | ||
| } | ||
| if (error instanceof SettleError) { | ||
| const errorReason2 = error.errorReason || error.message; | ||
| const settleResponse2 = { | ||
| success: false, | ||
| errorReason: errorReason2, | ||
| errorMessage: error.errorMessage || errorReason2, | ||
| payer: error.payer, | ||
| network: error.network, | ||
| transaction: error.transaction | ||
| }; | ||
| const failure2 = { | ||
| ...settleResponse2, | ||
| success: false, | ||
| errorReason: errorReason2, | ||
| headers: this.createSettlementHeaders(settleResponse2) | ||
| }; | ||
| const response2 = await this.buildSettlementFailureResponse(failure2, transportContext); | ||
| return { ...failure2, response: response2 }; | ||
| } | ||
| const errorReason = error instanceof Error ? error.message : "Settlement failed"; | ||
| const settleResponse = { | ||
| success: false, | ||
| errorReason, | ||
| errorMessage: errorReason, | ||
| network: requirements.network, | ||
| transaction: "" | ||
| }; | ||
| const failure = { | ||
| ...settleResponse, | ||
| success: false, | ||
| errorReason, | ||
| headers: this.createSettlementHeaders(settleResponse) | ||
| }; | ||
| const response = await this.buildSettlementFailureResponse(failure, transportContext); | ||
| return { ...failure, response }; | ||
| } | ||
| } | ||
| /** | ||
| * Check if a request requires payment based on route configuration | ||
| * | ||
| * @param context - HTTP request context | ||
| * @returns True if the route requires payment, false otherwise | ||
| */ | ||
| requiresPayment(context) { | ||
| const method = context.method || context.adapter.getMethod(); | ||
| return this.getRouteConfig(context.path, method) !== void 0; | ||
| } | ||
| /** | ||
| * Settle a verified payment that requested `skipHandler`, packaging the | ||
| * result as a `payment-error` HTTPProcessResult so framework adapters can | ||
| * write the response without invoking the route handler. | ||
| * | ||
| * - On success: status 200 + PAYMENT-RESPONSE header + configured body. | ||
| * - On failure: the standard 402 settlement-failure response. | ||
| * | ||
| * @param paymentPayload - Verified payment payload. | ||
| * @param requirements - Matched payment requirements. | ||
| * @param declaredExtensions - Optional declared extensions for the route. | ||
| * @param transportContext - Optional HTTP transport context. | ||
| * @param skipHandlerResponse - Optional content type + body to return on success. | ||
| * @returns A `payment-error` HTTPProcessResult carrying the final response. | ||
| */ | ||
| async processSkipHandlerSettlement(paymentPayload, requirements, declaredExtensions, transportContext, skipHandlerResponse) { | ||
| const settleResult = await this.processSettlement( | ||
| paymentPayload, | ||
| requirements, | ||
| declaredExtensions, | ||
| transportContext | ||
| ); | ||
| if (!settleResult.success) { | ||
| return { type: "payment-error", response: settleResult.response }; | ||
| } | ||
| const contentType = skipHandlerResponse?.contentType ?? "application/json"; | ||
| const body = skipHandlerResponse?.body ?? {}; | ||
| return { | ||
| type: "payment-error", | ||
| response: { | ||
| status: 200, | ||
| headers: { | ||
| "Content-Type": contentType, | ||
| ...settleResult.headers | ||
| }, | ||
| body, | ||
| isHtml: contentType.includes("text/html") | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Build HTTPResponseInstructions for settlement failure. | ||
| * Uses settlementFailedResponseBody hook if configured, otherwise defaults to empty body. | ||
| * | ||
| * @param failure - Settlement failure result with headers | ||
| * @param transportContext - Optional HTTP transport context for the request | ||
| * @returns HTTP response instructions for the 402 settlement failure response | ||
| */ | ||
| async buildSettlementFailureResponse(failure, transportContext) { | ||
| const settlementHeaders = failure.headers; | ||
| const routeConfig = transportContext ? this.getRouteConfig(transportContext.request.path, transportContext.request.method) : void 0; | ||
| const customBody = routeConfig?.config.settlementFailedResponseBody ? await routeConfig.config.settlementFailedResponseBody(transportContext.request, failure) : void 0; | ||
| const contentType = customBody ? customBody.contentType : "application/json"; | ||
| const body = customBody ? customBody.body : {}; | ||
| return { | ||
| status: 402, | ||
| headers: { | ||
| "Content-Type": contentType, | ||
| ...settlementHeaders | ||
| }, | ||
| body, | ||
| isHtml: contentType.includes("text/html") | ||
| }; | ||
| } | ||
| /** | ||
| * Normalizes a RouteConfig's accepts field into an array of PaymentOptions | ||
| * Handles both single PaymentOption and array formats | ||
| * | ||
| * @param routeConfig - Route configuration | ||
| * @returns Array of payment options | ||
| */ | ||
| normalizePaymentOptions(routeConfig) { | ||
| return Array.isArray(routeConfig.accepts) ? routeConfig.accepts : [routeConfig.accepts]; | ||
| } | ||
| /** | ||
| * Manual request hooks run before extension transport hooks for declared extensions. | ||
| * | ||
| * @param routeConfig - Route configuration for the matched request | ||
| * @returns Hooks in invocation order | ||
| */ | ||
| getProtectedRequestHooks(routeConfig) { | ||
| const hooks = [...this.protectedRequestHooks]; | ||
| const declaredExtensions = routeConfig.extensions; | ||
| if (!declaredExtensions) return hooks; | ||
| for (const extension of this.ResourceServer.getExtensions()) { | ||
| const hook = extension.transportHooks?.http?.onProtectedRequest; | ||
| if (!hook || !(extension.key in declaredExtensions)) continue; | ||
| hooks.push( | ||
| (context, routeConfig2) => hook(declaredExtensions[extension.key], context, routeConfig2) | ||
| ); | ||
| } | ||
| return hooks; | ||
| } | ||
| /** | ||
| * Validates that all payment options in routes have corresponding registered schemes | ||
| * and facilitator support. | ||
| * | ||
| * @returns Array of validation errors (empty if all routes are valid) | ||
| */ | ||
| validateRouteConfiguration() { | ||
| const errors = []; | ||
| const normalizedRoutes = typeof this.routesConfig === "object" && !("accepts" in this.routesConfig) ? Object.entries(this.routesConfig) : [["*", this.routesConfig]]; | ||
| for (const [pattern, config] of normalizedRoutes) { | ||
| const pathPart = pattern.includes(" ") ? pattern.split(/\s+/)[1] : pattern; | ||
| if (pathPart && pathPart.includes("*") && config.extensions && "bazaar" in config.extensions) { | ||
| console.warn( | ||
| `[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.` | ||
| ); | ||
| } | ||
| const paymentOptions = this.normalizePaymentOptions(config); | ||
| for (const option of paymentOptions) { | ||
| if (!this.ResourceServer.hasRegisteredScheme(option.network, option.scheme)) { | ||
| errors.push({ | ||
| routePattern: pattern, | ||
| scheme: option.scheme, | ||
| network: option.network, | ||
| reason: "missing_scheme", | ||
| message: `Route "${pattern}": No scheme implementation registered for "${option.scheme}" on network "${option.network}"` | ||
| }); | ||
| continue; | ||
| } | ||
| const supportedKind = this.ResourceServer.getSupportedKind( | ||
| x402Version, | ||
| option.network, | ||
| option.scheme | ||
| ); | ||
| if (!supportedKind) { | ||
| errors.push({ | ||
| routePattern: pattern, | ||
| scheme: option.scheme, | ||
| network: option.network, | ||
| reason: "missing_facilitator", | ||
| message: `Route "${pattern}": Facilitator does not support scheme "${option.scheme}" on network "${option.network}"` | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| return errors; | ||
| } | ||
| /** | ||
| * Get route configuration for a request | ||
| * | ||
| * @param path - Request path | ||
| * @param method - HTTP method | ||
| * @returns Route configuration and pattern, or undefined if no match | ||
| */ | ||
| getRouteConfig(path, method) { | ||
| const normalizedPath = this.normalizePath(path); | ||
| const upperMethod = method.toUpperCase(); | ||
| const matchingRoute = this.compiledRoutes.find( | ||
| (route) => route.regex.test(normalizedPath) && (route.verb === "*" || route.verb === upperMethod) | ||
| ); | ||
| if (!matchingRoute) return void 0; | ||
| return { config: matchingRoute.config, pattern: matchingRoute.pattern }; | ||
| } | ||
| /** | ||
| * Extract payment from HTTP headers (handles v1 and v2) | ||
| * | ||
| * @param adapter - HTTP adapter | ||
| * @returns Decoded payment payload or null | ||
| */ | ||
| extractPayment(adapter) { | ||
| const header = adapter.getHeader("payment-signature") || adapter.getHeader("PAYMENT-SIGNATURE"); | ||
| if (header) { | ||
| try { | ||
| return decodePaymentSignatureHeader(header); | ||
| } catch (error) { | ||
| console.warn("Failed to decode PAYMENT-SIGNATURE header:", error); | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| /** | ||
| * Check if request is from a web browser | ||
| * | ||
| * @param adapter - HTTP adapter | ||
| * @returns True if request appears to be from a browser | ||
| */ | ||
| isWebBrowser(adapter) { | ||
| const accept = adapter.getAcceptHeader(); | ||
| const userAgent = adapter.getUserAgent(); | ||
| return accept.includes("text/html") && userAgent.includes("Mozilla"); | ||
| } | ||
| /** | ||
| * Create HTTP response instructions from payment required | ||
| * | ||
| * @param paymentRequired - Payment requirements | ||
| * @param isWebBrowser - Whether request is from browser | ||
| * @param paywallConfig - Paywall configuration | ||
| * @param customHtml - Custom HTML template | ||
| * @param unpaidResponse - Optional custom response (content type and body) for unpaid API requests | ||
| * @returns Response instructions | ||
| */ | ||
| createHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) { | ||
| const status = paymentRequired.error === "permit2_allowance_required" ? 412 : 402; | ||
| const response = this.createHTTPPaymentRequiredResponse(paymentRequired); | ||
| if (isWebBrowser) { | ||
| const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml); | ||
| return { | ||
| status, | ||
| headers: { | ||
| "Content-Type": "text/html", | ||
| ...response.headers | ||
| }, | ||
| body: html, | ||
| isHtml: true | ||
| }; | ||
| } | ||
| const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json"; | ||
| const body = unpaidResponse ? unpaidResponse.body : {}; | ||
| return { | ||
| status, | ||
| headers: { | ||
| "Content-Type": contentType, | ||
| ...response.headers | ||
| }, | ||
| body | ||
| }; | ||
| } | ||
| /** | ||
| * Create HTTP payment required response (v1 puts in body, v2 puts in header) | ||
| * | ||
| * @param paymentRequired - Payment required object | ||
| * @returns Headers and body for the HTTP response | ||
| */ | ||
| createHTTPPaymentRequiredResponse(paymentRequired) { | ||
| return { | ||
| headers: { | ||
| "PAYMENT-REQUIRED": encodePaymentRequiredHeader(paymentRequired) | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Create settlement response headers | ||
| * | ||
| * @param settleResponse - Settlement response | ||
| * @returns Headers to add to response | ||
| */ | ||
| createSettlementHeaders(settleResponse) { | ||
| const encoded = encodePaymentResponseHeader(settleResponse); | ||
| return { "PAYMENT-RESPONSE": encoded }; | ||
| } | ||
| /** | ||
| * Parse route pattern into verb and regex | ||
| * | ||
| * @param pattern - Route pattern like "GET /api/*", "/api/[id]", or "/api/:id" | ||
| * @returns Parsed pattern with verb and regex | ||
| */ | ||
| parseRoutePattern(pattern) { | ||
| const [verb, path] = pattern.includes(" ") ? pattern.split(/\s+/) : ["*", pattern]; | ||
| const regex = new RegExp( | ||
| `^${path.replace(/\\/g, "\\\\").replace(/[$()+.?^{|}]/g, "\\$&").replace(/\*/g, ".*?").replace(/\[([^\]]+)\]/g, "[^/]+").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "[^/]+").replace(/\//g, "\\/")}$`, | ||
| "i" | ||
| ); | ||
| return { verb: verb.toUpperCase(), regex, path }; | ||
| } | ||
| /** | ||
| * Normalize path for matching | ||
| * | ||
| * @param path - Raw path from request | ||
| * @returns Normalized path | ||
| */ | ||
| normalizePath(path) { | ||
| const pathWithoutQuery = path.split(/[?#]/)[0]; | ||
| const parts = pathWithoutQuery.split(/(%2[fF]|%5[cC])/); | ||
| const decoded = parts.map((part, i) => { | ||
| if (i % 2 === 1) return part; | ||
| try { | ||
| return decodeURIComponent(part); | ||
| } catch { | ||
| return part; | ||
| } | ||
| }).join(""); | ||
| return decoded.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/(.+?)\/+$/, "$1"); | ||
| } | ||
| /** | ||
| * Generate paywall HTML for browser requests | ||
| * | ||
| * @param paymentRequired - Payment required response | ||
| * @param paywallConfig - Optional paywall configuration | ||
| * @param customHtml - Optional custom HTML template | ||
| * @returns HTML string | ||
| */ | ||
| generatePaywallHTML(paymentRequired, paywallConfig, customHtml) { | ||
| if (customHtml) { | ||
| return customHtml; | ||
| } | ||
| if (this.paywallProvider) { | ||
| return this.paywallProvider.generateHtml(paymentRequired, paywallConfig); | ||
| } | ||
| try { | ||
| const paywall = __require("@x402/paywall"); | ||
| const displayAmount = this.getDisplayAmount(paymentRequired); | ||
| const resource = paymentRequired.resource; | ||
| return paywall.getPaywallHtml({ | ||
| amount: displayAmount, | ||
| paymentRequired, | ||
| currentUrl: resource?.url || paywallConfig?.currentUrl || "", | ||
| testnet: paywallConfig?.testnet ?? true, | ||
| appName: paywallConfig?.appName, | ||
| appLogo: paywallConfig?.appLogo, | ||
| sessionTokenEndpoint: paywallConfig?.sessionTokenEndpoint | ||
| }); | ||
| } catch { | ||
| } | ||
| return FALLBACK_PAYWALL_HTML; | ||
| } | ||
| /** | ||
| * Extract display amount from payment requirements. | ||
| * Uses the registered scheme's decimal precision for the asset, falling back to 6. | ||
| * | ||
| * @param paymentRequired - The payment required object | ||
| * @returns The display amount in decimal format | ||
| */ | ||
| getDisplayAmount(paymentRequired) { | ||
| const accepts = paymentRequired.accepts; | ||
| if (accepts && accepts.length > 0) { | ||
| const firstReq = accepts[0]; | ||
| if ("amount" in firstReq) { | ||
| const decimals = this.ResourceServer.getAssetDecimalsForRequirements(firstReq); | ||
| return parseFloat(firstReq.amount) / 10 ** decimals; | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
| }; | ||
| // src/http/httpFacilitatorClient.ts | ||
| var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator"; | ||
| var GET_SUPPORTED_RETRIES = 3; | ||
| var GET_SUPPORTED_RETRY_DELAY_MS = 1e3; | ||
| var MAX_RETRY_DELAY_MS = 3e4; | ||
| function computeRetryDelay(retryAfter, attempt) { | ||
| let delay = null; | ||
| if (retryAfter !== null) { | ||
| const seconds = Number(retryAfter); | ||
| if (!isNaN(seconds)) { | ||
| delay = seconds * 1e3; | ||
| } else { | ||
| const retryDate = Date.parse(retryAfter); | ||
| if (!isNaN(retryDate)) { | ||
| delay = retryDate - Date.now(); | ||
| } | ||
| } | ||
| } | ||
| if (delay === null || delay <= 0) { | ||
| delay = GET_SUPPORTED_RETRY_DELAY_MS * Math.pow(2, attempt); | ||
| } | ||
| return Math.min(delay, MAX_RETRY_DELAY_MS); | ||
| } | ||
| var verifyResponseSchema = z.object({ | ||
| isValid: z.boolean(), | ||
| invalidReason: z.string().nullish().transform((v) => v ?? void 0), | ||
| invalidMessage: z.string().nullish().transform((v) => v ?? void 0), | ||
| payer: z.string().nullish().transform((v) => v ?? void 0), | ||
| extensions: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0), | ||
| extra: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0) | ||
| }); | ||
| var settleResponseSchema = z.object({ | ||
| success: z.boolean(), | ||
| errorReason: z.string().nullish().transform((v) => v ?? void 0), | ||
| errorMessage: z.string().nullish().transform((v) => v ?? void 0), | ||
| payer: z.string().nullish().transform((v) => v ?? void 0), | ||
| transaction: z.string(), | ||
| network: z.custom((value) => typeof value === "string"), | ||
| amount: z.string().nullish().transform((v) => v ?? void 0), | ||
| extensions: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0), | ||
| extra: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0) | ||
| }); | ||
| var supportedKindSchema = z.object({ | ||
| x402Version: z.number(), | ||
| scheme: z.string(), | ||
| network: z.custom( | ||
| (value) => typeof value === "string" | ||
| ), | ||
| extra: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0) | ||
| }); | ||
| var supportedResponseSchema = z.object({ | ||
| kinds: z.array(supportedKindSchema), | ||
| extensions: z.array(z.string()).default([]), | ||
| signers: z.record(z.string(), z.array(z.string())).default({}) | ||
| }); | ||
| function responseExcerpt(text, limit = 200) { | ||
| const compact = text.trim().replace(/\s+/g, " "); | ||
| if (!compact) { | ||
| return "<empty response>"; | ||
| } | ||
| if (compact.length <= limit) { | ||
| return compact; | ||
| } | ||
| return `${compact.slice(0, limit - 3)}...`; | ||
| } | ||
| var EXTENSION_RESPONSE_LOG_FIELD_ALLOWLIST = ["status", "rejectedReason", "reason", "code"]; | ||
| function logExtensionResponsesHeader(response) { | ||
| const header = response.headers.get("EXTENSION-RESPONSES"); | ||
| if (!header) return; | ||
| try { | ||
| const decoded = JSON.parse(safeBase64Decode(header)); | ||
| if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return; | ||
| const sanitized = {}; | ||
| for (const [extensionKey, payload] of Object.entries(decoded)) { | ||
| const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {}; | ||
| const filtered = {}; | ||
| for (const key of EXTENSION_RESPONSE_LOG_FIELD_ALLOWLIST) { | ||
| if (source[key] !== void 0) { | ||
| filtered[key] = source[key]; | ||
| } | ||
| } | ||
| sanitized[extensionKey] = filtered; | ||
| } | ||
| console.log(`[x402] extension responses: ${JSON.stringify(sanitized)}`); | ||
| } catch { | ||
| } | ||
| } | ||
| async function parseSuccessResponse(response, schema, operation) { | ||
| const text = await response.text(); | ||
| let data; | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| throw new FacilitatorResponseError( | ||
| `Facilitator ${operation} returned invalid JSON: ${responseExcerpt(text)}` | ||
| ); | ||
| } | ||
| const parsed = schema.safeParse(data); | ||
| if (!parsed.success) { | ||
| throw new FacilitatorResponseError( | ||
| `Facilitator ${operation} returned invalid data: ${responseExcerpt(text)}` | ||
| ); | ||
| } | ||
| return parsed.data; | ||
| } | ||
| var HTTPFacilitatorClient = class { | ||
| /** | ||
| * Creates a new HTTPFacilitatorClient instance. | ||
| * | ||
| * @param config - Configuration options for the facilitator client | ||
| */ | ||
| constructor(config) { | ||
| this.url = (config?.url || DEFAULT_FACILITATOR_URL).replace(/\/+$/, ""); | ||
| this._createAuthHeaders = config?.createAuthHeaders; | ||
| } | ||
| /** | ||
| * Verify a payment with the facilitator | ||
| * | ||
| * @param paymentPayload - The payment to verify | ||
| * @param paymentRequirements - The requirements to verify against | ||
| * @returns Verification response | ||
| */ | ||
| async verify(paymentPayload, paymentRequirements) { | ||
| let headers = { | ||
| "Content-Type": "application/json" | ||
| }; | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this.createAuthHeaders("verify"); | ||
| headers = { ...headers, ...authHeaders.headers }; | ||
| } | ||
| const response = await fetch(`${this.url}/verify`, { | ||
| method: "POST", | ||
| headers, | ||
| redirect: "follow", | ||
| body: JSON.stringify({ | ||
| x402Version: paymentPayload.x402Version, | ||
| paymentPayload: this.toJsonSafe(paymentPayload), | ||
| paymentRequirements: this.toJsonSafe(paymentRequirements) | ||
| }) | ||
| }); | ||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| let data; | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| throw new Error(`Facilitator verify failed (${response.status}): ${responseExcerpt(text)}`); | ||
| } | ||
| if (typeof data === "object" && data !== null && "isValid" in data) { | ||
| throw new VerifyError(response.status, data); | ||
| } | ||
| throw new Error( | ||
| `Facilitator verify failed (${response.status}): ${responseExcerpt(JSON.stringify(data))}` | ||
| ); | ||
| } | ||
| const verifyResult = await parseSuccessResponse(response, verifyResponseSchema, "verify"); | ||
| logExtensionResponsesHeader(response); | ||
| return verifyResult; | ||
| } | ||
| /** | ||
| * Settle a payment with the facilitator | ||
| * | ||
| * @param paymentPayload - The payment to settle | ||
| * @param paymentRequirements - The requirements for settlement | ||
| * @returns Settlement response | ||
| */ | ||
| async settle(paymentPayload, paymentRequirements) { | ||
| let headers = { | ||
| "Content-Type": "application/json" | ||
| }; | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this.createAuthHeaders("settle"); | ||
| headers = { ...headers, ...authHeaders.headers }; | ||
| } | ||
| const response = await fetch(`${this.url}/settle`, { | ||
| method: "POST", | ||
| headers, | ||
| redirect: "follow", | ||
| body: JSON.stringify({ | ||
| x402Version: paymentPayload.x402Version, | ||
| paymentPayload: this.toJsonSafe(paymentPayload), | ||
| paymentRequirements: this.toJsonSafe(paymentRequirements) | ||
| }) | ||
| }); | ||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| let data; | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| throw new Error(`Facilitator settle failed (${response.status}): ${responseExcerpt(text)}`); | ||
| } | ||
| if (typeof data === "object" && data !== null && "success" in data) { | ||
| throw new SettleError(response.status, data); | ||
| } | ||
| throw new Error( | ||
| `Facilitator settle failed (${response.status}): ${responseExcerpt(JSON.stringify(data))}` | ||
| ); | ||
| } | ||
| const settleResult = await parseSuccessResponse(response, settleResponseSchema, "settle"); | ||
| logExtensionResponsesHeader(response); | ||
| return settleResult; | ||
| } | ||
| /** | ||
| * Get supported payment kinds and extensions from the facilitator. | ||
| * Retries with exponential backoff on 429 rate limit errors. | ||
| * | ||
| * @returns Supported payment kinds and extensions | ||
| */ | ||
| async getSupported() { | ||
| let headers = { | ||
| "Content-Type": "application/json" | ||
| }; | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this.createAuthHeaders("supported"); | ||
| headers = { ...headers, ...authHeaders.headers }; | ||
| } | ||
| let lastError = null; | ||
| for (let attempt = 0; attempt < GET_SUPPORTED_RETRIES; attempt++) { | ||
| const response = await fetch(`${this.url}/supported`, { | ||
| method: "GET", | ||
| headers, | ||
| redirect: "follow" | ||
| }); | ||
| if (response.ok) { | ||
| return parseSuccessResponse(response, supportedResponseSchema, "supported"); | ||
| } | ||
| const errorText = await response.text().catch(() => response.statusText); | ||
| lastError = new Error( | ||
| `Facilitator getSupported failed (${response.status}): ${responseExcerpt(errorText)}` | ||
| ); | ||
| if (response.status === 429 && attempt < GET_SUPPORTED_RETRIES - 1) { | ||
| const delay = computeRetryDelay(response.headers.get("Retry-After"), attempt); | ||
| await new Promise((resolve) => setTimeout(resolve, delay)); | ||
| continue; | ||
| } | ||
| throw lastError; | ||
| } | ||
| throw lastError ?? new Error("Facilitator getSupported failed after retries"); | ||
| } | ||
| /** | ||
| * Creates authentication headers for a specific path. | ||
| * | ||
| * @param path - The path to create authentication headers for (e.g., "verify", "settle", "supported") | ||
| * @returns An object containing the authentication headers for the specified path | ||
| */ | ||
| async createAuthHeaders(path) { | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this._createAuthHeaders(); | ||
| return { | ||
| headers: authHeaders[path] ?? {} | ||
| }; | ||
| } | ||
| return { | ||
| headers: {} | ||
| }; | ||
| } | ||
| /** | ||
| * Helper to convert objects to JSON-safe format. | ||
| * Handles BigInt and other non-JSON types. | ||
| * | ||
| * @param obj - The object to convert | ||
| * @returns The JSON-safe representation of the object | ||
| */ | ||
| toJsonSafe(obj) { | ||
| return JSON.parse( | ||
| JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() : value) | ||
| ); | ||
| } | ||
| }; | ||
| // src/http/x402HTTPClient.ts | ||
| var x402HTTPClient = class { | ||
| /** | ||
| * Creates a new x402HTTPClient instance. | ||
| * | ||
| * @param client - The underlying x402Client for payment logic | ||
| */ | ||
| constructor(client) { | ||
| this.client = client; | ||
| this.paymentRequiredHooks = []; | ||
| } | ||
| /** | ||
| * Register a hook to handle 402 responses before payment. | ||
| * Hooks run in order; first to return headers wins. | ||
| * | ||
| * @param hook - The hook function to register | ||
| * @returns This instance for chaining | ||
| */ | ||
| onPaymentRequired(hook) { | ||
| this.paymentRequiredHooks.push(hook); | ||
| return this; | ||
| } | ||
| /** | ||
| * Run hooks and return headers if any hook provides them. | ||
| * | ||
| * @param paymentRequired - The payment required response from the server | ||
| * @returns Headers to use for retry, or null to proceed to payment | ||
| */ | ||
| async handlePaymentRequired(paymentRequired) { | ||
| for (const hook of this.getPaymentRequiredHooks(paymentRequired)) { | ||
| const result = await hook({ paymentRequired }); | ||
| if (result?.headers) { | ||
| return result.headers; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| /** | ||
| * Encodes a payment payload into appropriate HTTP headers based on version. | ||
| * | ||
| * @param paymentPayload - The payment payload to encode | ||
| * @returns HTTP headers containing the encoded payment signature | ||
| */ | ||
| encodePaymentSignatureHeader(paymentPayload) { | ||
| switch (paymentPayload.x402Version) { | ||
| case 2: | ||
| return { | ||
| "PAYMENT-SIGNATURE": encodePaymentSignatureHeader(paymentPayload) | ||
| }; | ||
| case 1: | ||
| return { | ||
| "X-PAYMENT": encodePaymentSignatureHeader(paymentPayload) | ||
| }; | ||
| default: | ||
| throw new Error( | ||
| `Unsupported x402 version: ${paymentPayload.x402Version}` | ||
| ); | ||
| } | ||
| } | ||
| /** | ||
| * Extracts payment required information from HTTP response. | ||
| * | ||
| * @param getHeader - Function to retrieve header value by name (case-insensitive) | ||
| * @param body - Optional response body for v1 compatibility | ||
| * @returns The payment required object | ||
| */ | ||
| getPaymentRequiredResponse(getHeader, body) { | ||
| const paymentRequired = getHeader("PAYMENT-REQUIRED"); | ||
| if (paymentRequired) { | ||
| return decodePaymentRequiredHeader(paymentRequired); | ||
| } | ||
| if (body && body instanceof Object && "x402Version" in body && body.x402Version === 1) { | ||
| return body; | ||
| } | ||
| throw new Error("Invalid payment required response"); | ||
| } | ||
| /** | ||
| * Extracts payment settlement response from HTTP headers. | ||
| * | ||
| * @param getHeader - Function to retrieve header value by name (case-insensitive) | ||
| * @returns The settlement response object | ||
| */ | ||
| getPaymentSettleResponse(getHeader) { | ||
| const paymentResponse = getHeader("PAYMENT-RESPONSE"); | ||
| if (paymentResponse) { | ||
| return decodePaymentResponseHeader(paymentResponse); | ||
| } | ||
| const xPaymentResponse = getHeader("X-PAYMENT-RESPONSE"); | ||
| if (xPaymentResponse) { | ||
| return decodePaymentResponseHeader(xPaymentResponse); | ||
| } | ||
| throw new Error("Payment response header not found"); | ||
| } | ||
| /** | ||
| * Creates a payment payload for the given payment requirements. | ||
| * Delegates to the underlying x402Client. | ||
| * | ||
| * @param paymentRequired - The payment required response from the server | ||
| * @returns Promise resolving to the payment payload | ||
| */ | ||
| async createPaymentPayload(paymentRequired) { | ||
| return this.client.createPaymentPayload(paymentRequired); | ||
| } | ||
| /** | ||
| * Parses response headers into protocol types, fires payment response hooks (v2 only), | ||
| * and returns whether a hook signaled recovery. | ||
| * | ||
| * Called by transport wrappers (fetch, axios) after the paid request completes. | ||
| * | ||
| * @param paymentPayload - The payload that was sent with the request | ||
| * @param getHeader - Function to retrieve a response header by name | ||
| * @param status - The HTTP status code of the response | ||
| * @returns Whether a hook recovered and the parsed settle response (if any) | ||
| */ | ||
| async processPaymentResult(paymentPayload, getHeader, status) { | ||
| let settleResponse; | ||
| try { | ||
| settleResponse = this.getPaymentSettleResponse(getHeader); | ||
| } catch { | ||
| } | ||
| if (paymentPayload.x402Version === 1) { | ||
| return { recovered: false, settleResponse }; | ||
| } | ||
| let paymentRequired; | ||
| if (!settleResponse && status === 402) { | ||
| try { | ||
| paymentRequired = this.getPaymentRequiredResponse(getHeader); | ||
| } catch { | ||
| } | ||
| } | ||
| const requirements = paymentPayload.accepted; | ||
| if (!requirements) { | ||
| throw new Error("Invalid x402 v2 payment payload: missing `accepted`"); | ||
| } | ||
| const ctx = { | ||
| paymentPayload, | ||
| requirements, | ||
| ...settleResponse ? { settleResponse } : {}, | ||
| ...paymentRequired ? { paymentRequired } : {} | ||
| }; | ||
| const result = await this.client.handlePaymentResponse(ctx); | ||
| return { recovered: result?.recovered === true, settleResponse }; | ||
| } | ||
| /** | ||
| * Parses HTTP status, headers, and body into an `HTTPResourceResponse`. | ||
| * | ||
| * Decodes the x402 payment header into `header`: the `PAYMENT-RESPONSE` | ||
| * settlement if present, otherwise the `PAYMENT-REQUIRED` declaration on | ||
| * 402 responses (whose `error` field carries the server's failure reason). | ||
| * | ||
| * @param args - Normalized response inputs from any HTTP transport | ||
| * @param args.status - HTTP response status code | ||
| * @param args.getHeader - Callback to read response headers by name | ||
| * @param args.body - Response body payload | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| parsePaymentResult(args) { | ||
| const { status, getHeader, body } = args; | ||
| let header; | ||
| try { | ||
| header = this.getPaymentSettleResponse(getHeader); | ||
| } catch { | ||
| if (status === 402) { | ||
| try { | ||
| header = this.getPaymentRequiredResponse(getHeader, body); | ||
| } catch { | ||
| } | ||
| } | ||
| } | ||
| let paymentStatus = "none"; | ||
| if (header && !("success" in header)) { | ||
| paymentStatus = "payment_required"; | ||
| } | ||
| if (header && "success" in header) { | ||
| paymentStatus = header.success ? "settled" : "settle_failed"; | ||
| } | ||
| return { status, paymentStatus, body, header }; | ||
| } | ||
| /** | ||
| * Parses a fetch Response into an `HTTPResourceResponse` for app-level convenience. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| async processResponse(response) { | ||
| const getHeader = (name) => response.headers.get(name); | ||
| const contentType = response.headers.get("content-type") ?? ""; | ||
| const body = contentType.includes("application/json") ? await response.json() : await response.text(); | ||
| return this.parsePaymentResult({ status: response.status, getHeader, body }); | ||
| } | ||
| /** | ||
| * Manual HTTP hooks run before extension hooks scoped to the 402 response. | ||
| * | ||
| * @param paymentRequired - The payment required response from the server | ||
| * @returns Hooks in invocation order | ||
| */ | ||
| getPaymentRequiredHooks(paymentRequired) { | ||
| const hooks = [...this.paymentRequiredHooks]; | ||
| const declaredExtensions = paymentRequired.extensions; | ||
| if (!declaredExtensions) return hooks; | ||
| for (const extension of this.client.getExtensions()) { | ||
| const httpExtension = extension; | ||
| const hook = httpExtension.transportHooks?.http?.onPaymentRequired; | ||
| if (!hook || !(extension.key in declaredExtensions)) continue; | ||
| hooks.push((context) => hook(declaredExtensions[extension.key], context)); | ||
| } | ||
| return hooks; | ||
| } | ||
| }; | ||
| // src/http/index.ts | ||
| function encodePaymentSignatureHeader(paymentPayload) { | ||
| return safeBase64Encode(JSON.stringify(paymentPayload)); | ||
| } | ||
| function decodePaymentSignatureHeader(paymentSignatureHeader) { | ||
| if (!Base64EncodedRegex.test(paymentSignatureHeader)) { | ||
| throw new Error("Invalid payment signature header"); | ||
| } | ||
| return JSON.parse(safeBase64Decode(paymentSignatureHeader)); | ||
| } | ||
| function encodePaymentRequiredHeader(paymentRequired) { | ||
| return safeBase64Encode(JSON.stringify(paymentRequired)); | ||
| } | ||
| function decodePaymentRequiredHeader(paymentRequiredHeader) { | ||
| if (!Base64EncodedRegex.test(paymentRequiredHeader)) { | ||
| throw new Error("Invalid payment required header"); | ||
| } | ||
| return JSON.parse(safeBase64Decode(paymentRequiredHeader)); | ||
| } | ||
| function encodePaymentResponseHeader(paymentResponse) { | ||
| return safeBase64Encode(JSON.stringify(paymentResponse)); | ||
| } | ||
| function decodePaymentResponseHeader(paymentResponseHeader) { | ||
| if (!Base64EncodedRegex.test(paymentResponseHeader)) { | ||
| throw new Error("Invalid payment response header"); | ||
| } | ||
| return JSON.parse(safeBase64Decode(paymentResponseHeader)); | ||
| } | ||
| export { | ||
| SETTLEMENT_OVERRIDES_HEADER, | ||
| checkIfBazaarNeeded, | ||
| RouteConfigurationError, | ||
| x402HTTPResourceServer, | ||
| HTTPFacilitatorClient, | ||
| encodePaymentSignatureHeader, | ||
| decodePaymentSignatureHeader, | ||
| encodePaymentRequiredHeader, | ||
| decodePaymentRequiredHeader, | ||
| encodePaymentResponseHeader, | ||
| decodePaymentResponseHeader, | ||
| x402HTTPClient | ||
| }; | ||
| //# sourceMappingURL=chunk-YSNRPV3S.mjs.map |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
@@ -1,3 +0,3 @@ | ||
| import { c as PaymentRequired, ad as x402Client, P as PaymentPayload, S as SettleResponse } from '../x402Client-BSWPuNYe.js'; | ||
| export { ai as AfterPaymentCreationHook, ah as BeforePaymentCreationHook, ap as ClientExtension, an as ClientExtensionHooks, ao as ClientTransportExtensionHooks, aj as OnPaymentCreationFailureHook, al as OnPaymentResponseHook, af as PaymentCreatedContext, ae as PaymentCreationContext, ag as PaymentCreationFailureContext, aq as PaymentPolicy, ak as PaymentResponseContext, ar as SchemeRegistration, am as SelectPaymentRequirements, as as x402ClientConfig } from '../x402Client-BSWPuNYe.js'; | ||
| import { c as PaymentRequired, ae as x402Client, P as PaymentPayload, S as SettleResponse } from '../x402Client-TQHctrG7.js'; | ||
| export { aj as AfterPaymentCreationHook, ai as BeforePaymentCreationHook, aq as ClientExtension, ao as ClientExtensionHooks, ap as ClientTransportExtensionHooks, ak as OnPaymentCreationFailureHook, am as OnPaymentResponseHook, ag as PaymentCreatedContext, af as PaymentCreationContext, ah as PaymentCreationFailureContext, ar as PaymentPolicy, al as PaymentResponseContext, as as SchemeRegistration, an as SelectPaymentRequirements, at as x402ClientConfig } from '../x402Client-TQHctrG7.js'; | ||
@@ -98,8 +98,26 @@ /** | ||
| /** | ||
| * Parses a fetch Response into a discriminated `x402PaymentResult` for app-level convenience. | ||
| * Parses HTTP status, headers, and body into an `HTTPResourceResponse`. | ||
| * | ||
| * Decodes the x402 payment header into `header`: the `PAYMENT-RESPONSE` | ||
| * settlement if present, otherwise the `PAYMENT-REQUIRED` declaration on | ||
| * 402 responses (whose `error` field carries the server's failure reason). | ||
| * | ||
| * @param args - Normalized response inputs from any HTTP transport | ||
| * @param args.status - HTTP response status code | ||
| * @param args.getHeader - Callback to read response headers by name | ||
| * @param args.body - Response body payload | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| parsePaymentResult(args: { | ||
| status: number; | ||
| getHeader: (name: string) => string | null | undefined; | ||
| body: unknown; | ||
| }): HTTPResourceResponse; | ||
| /** | ||
| * Parses a fetch Response into an `HTTPResourceResponse` for app-level convenience. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns A discriminated union describing the payment outcome | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| processResponse(response: Response): Promise<x402PaymentResult>; | ||
| processResponse(response: Response): Promise<HTTPResourceResponse>; | ||
| /** | ||
@@ -114,29 +132,20 @@ * Manual HTTP hooks run before extension hooks scoped to the 402 response. | ||
| /** | ||
| * Discriminated union describing the outcome of a payment-enabled request. | ||
| * Parsed result of an HTTP request to an x402 resource. | ||
| */ | ||
| type x402PaymentResult = { | ||
| kind: "success"; | ||
| response: Response; | ||
| body: unknown; | ||
| settleResponse: SettleResponse; | ||
| } | { | ||
| kind: "settle_failed"; | ||
| response: Response; | ||
| body: unknown; | ||
| settleResponse: SettleResponse; | ||
| } | { | ||
| kind: "payment_required"; | ||
| response: Response; | ||
| paymentRequired: PaymentRequired; | ||
| } | { | ||
| kind: "error"; | ||
| response: Response; | ||
| type HTTPResourceResponse = { | ||
| /** HTTP status code. */ | ||
| status: number; | ||
| /** x402 payment outcome. */ | ||
| paymentStatus: HTTPPaymentStatus; | ||
| /** Parsed response body. */ | ||
| body: unknown; | ||
| } | { | ||
| kind: "passthrough"; | ||
| response: Response; | ||
| body: unknown; | ||
| /** | ||
| * Decoded x402 payment header, if present: | ||
| * - SettleResponse (from PAYMENT-RESPONSE / X-PAYMENT-RESPONSE) | ||
| * - PaymentRequired (from PAYMENT-REQUIRED; its `error` carries the server reason) | ||
| */ | ||
| header?: SettleResponse | PaymentRequired; | ||
| }; | ||
| type HTTPPaymentStatus = "settled" | "settle_failed" | "payment_required" | "none"; | ||
| export { type HTTPClientExtensionHooks, type PaymentRequiredContext, type PaymentRequiredHook, x402Client, x402HTTPClient, type x402PaymentResult }; | ||
| export { type HTTPClientExtensionHooks, type HTTPPaymentStatus, type HTTPResourceResponse, type PaymentRequiredContext, type PaymentRequiredHook, x402Client, x402HTTPClient }; |
+83
-40
@@ -32,2 +32,10 @@ "use strict"; | ||
| // src/utils/index.ts | ||
| var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| var networkPatternToRegExp = (pattern) => { | ||
| const source = escapeRegExp(pattern).replace(/\\\*/g, ".*"); | ||
| return new RegExp(`^${source}$`); | ||
| }; | ||
| var networkMatchesPattern = (pattern, network) => { | ||
| return networkPatternToRegExp(pattern).test(network); | ||
| }; | ||
| var findSchemesByNetwork = (map, network) => { | ||
@@ -37,5 +45,3 @@ let implementationsByScheme = map.get(network); | ||
| for (const [registeredNetworkPattern, implementations] of map.entries()) { | ||
| const pattern = registeredNetworkPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*"); | ||
| const regex = new RegExp(`^${pattern}$`); | ||
| if (regex.test(network)) { | ||
| if (networkMatchesPattern(registeredNetworkPattern, network)) { | ||
| implementationsByScheme = implementations; | ||
@@ -334,21 +340,41 @@ break; | ||
| /** | ||
| * Merges server-declared extensions with scheme-provided extensions. | ||
| * Scheme extensions overlay on top of server extensions at each key, | ||
| * preserving server-provided schema while overlaying scheme-provided info. | ||
| * Merges server-declared extensions with client extension echoes. | ||
| * Client extension data may add fields, but server-declared fields remain intact. | ||
| * | ||
| * @param serverExtensions - Extensions declared by the server in the 402 response | ||
| * @param schemeExtensions - Extensions provided by the scheme client (e.g. EIP-2612) | ||
| * @param clientExtensions - Extensions provided by the client or scheme | ||
| * @returns The merged extensions object, or undefined if both inputs are undefined | ||
| */ | ||
| mergeExtensions(serverExtensions, schemeExtensions) { | ||
| if (!schemeExtensions) return serverExtensions; | ||
| if (!serverExtensions) return schemeExtensions; | ||
| mergeExtensions(serverExtensions, clientExtensions) { | ||
| if (!clientExtensions) return serverExtensions; | ||
| if (!serverExtensions) return clientExtensions; | ||
| const merged = { ...serverExtensions }; | ||
| for (const [key, schemeValue] of Object.entries(schemeExtensions)) { | ||
| for (const [key, clientValue] of Object.entries(clientExtensions)) { | ||
| const serverValue = merged[key]; | ||
| if (serverValue && typeof serverValue === "object" && schemeValue && typeof schemeValue === "object") { | ||
| merged[key] = { ...serverValue, ...schemeValue }; | ||
| } else { | ||
| merged[key] = schemeValue; | ||
| if (serverValue === null || typeof serverValue !== "object" || Array.isArray(serverValue) || clientValue === null || typeof clientValue !== "object" || Array.isArray(clientValue)) { | ||
| merged[key] = clientValue; | ||
| continue; | ||
| } | ||
| const serverRecord = serverValue; | ||
| const clientRecord = clientValue; | ||
| const extensionValue = { ...serverRecord }; | ||
| const pending = [{ target: extensionValue, source: clientRecord }]; | ||
| for (const item of pending) { | ||
| for (const [fieldKey, clientFieldValue] of Object.entries(item.source)) { | ||
| const serverFieldValue = item.target[fieldKey]; | ||
| if (serverFieldValue !== null && typeof serverFieldValue === "object" && !Array.isArray(serverFieldValue) && clientFieldValue !== null && typeof clientFieldValue === "object" && !Array.isArray(clientFieldValue)) { | ||
| const nestedValue = { ...serverFieldValue }; | ||
| item.target[fieldKey] = nestedValue; | ||
| pending.push({ | ||
| target: nestedValue, | ||
| source: clientFieldValue | ||
| }); | ||
| continue; | ||
| } | ||
| if (!Object.prototype.hasOwnProperty.call(item.target, fieldKey)) { | ||
| item.target[fieldKey] = clientFieldValue; | ||
| } | ||
| } | ||
| } | ||
| merged[key] = extensionValue; | ||
| } | ||
@@ -376,3 +402,6 @@ return merged; | ||
| } | ||
| return enriched; | ||
| return { | ||
| ...enriched, | ||
| extensions: this.mergeExtensions(paymentRequired.extensions, enriched.extensions) | ||
| }; | ||
| } | ||
@@ -814,35 +843,49 @@ /** | ||
| /** | ||
| * Parses a fetch Response into a discriminated `x402PaymentResult` for app-level convenience. | ||
| * Parses HTTP status, headers, and body into an `HTTPResourceResponse`. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns A discriminated union describing the payment outcome | ||
| * Decodes the x402 payment header into `header`: the `PAYMENT-RESPONSE` | ||
| * settlement if present, otherwise the `PAYMENT-REQUIRED` declaration on | ||
| * 402 responses (whose `error` field carries the server's failure reason). | ||
| * | ||
| * @param args - Normalized response inputs from any HTTP transport | ||
| * @param args.status - HTTP response status code | ||
| * @param args.getHeader - Callback to read response headers by name | ||
| * @param args.body - Response body payload | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| async processResponse(response) { | ||
| const getHeader = (name) => response.headers.get(name); | ||
| let settleResponse; | ||
| parsePaymentResult(args) { | ||
| const { status, getHeader, body } = args; | ||
| let header; | ||
| try { | ||
| settleResponse = this.getPaymentSettleResponse(getHeader); | ||
| header = this.getPaymentSettleResponse(getHeader); | ||
| } catch { | ||
| if (status === 402) { | ||
| try { | ||
| header = this.getPaymentRequiredResponse(getHeader, body); | ||
| } catch { | ||
| } | ||
| } | ||
| } | ||
| const contentType = response.headers.get("content-type") ?? ""; | ||
| const body = contentType.includes("application/json") ? await response.json() : await response.text(); | ||
| if (settleResponse && settleResponse.success) { | ||
| return { kind: "success", response, body, settleResponse }; | ||
| let paymentStatus = "none"; | ||
| if (header && !("success" in header)) { | ||
| paymentStatus = "payment_required"; | ||
| } | ||
| if (settleResponse && !settleResponse.success) { | ||
| return { kind: "settle_failed", response, body, settleResponse }; | ||
| if (header && "success" in header) { | ||
| paymentStatus = header.success ? "settled" : "settle_failed"; | ||
| } | ||
| if (response.status === 402) { | ||
| try { | ||
| const paymentRequired = this.getPaymentRequiredResponse(getHeader, body); | ||
| return { kind: "payment_required", response, paymentRequired }; | ||
| } catch { | ||
| } | ||
| } | ||
| if (response.ok) { | ||
| return { kind: "passthrough", response, body }; | ||
| } | ||
| return { kind: "error", response, status: response.status, body }; | ||
| return { status, paymentStatus, body, header }; | ||
| } | ||
| /** | ||
| * Parses a fetch Response into an `HTTPResourceResponse` for app-level convenience. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| async processResponse(response) { | ||
| const getHeader = (name) => response.headers.get(name); | ||
| const contentType = response.headers.get("content-type") ?? ""; | ||
| const body = contentType.includes("application/json") ? await response.json() : await response.text(); | ||
| return this.parsePaymentResult({ status: response.status, getHeader, body }); | ||
| } | ||
| /** | ||
| * Manual HTTP hooks run before extension hooks scoped to the 402 response. | ||
@@ -849,0 +892,0 @@ * |
@@ -1,2 +0,2 @@ | ||
| import { P as PaymentPayload, a as PaymentRequirements, V as VerifyResponse, S as SettleResponse, N as Network, b as SchemeNetworkFacilitator, F as FacilitatorExtension } from '../x402Client-BSWPuNYe.js'; | ||
| import { P as PaymentPayload, a as PaymentRequirements, V as VerifyResponse, S as SettleResponse, N as Network, b as SchemeNetworkFacilitator, F as FacilitatorExtension } from '../x402Client-TQHctrG7.js'; | ||
@@ -3,0 +3,0 @@ /** |
@@ -30,2 +30,12 @@ "use strict"; | ||
| // src/utils/index.ts | ||
| var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| var networkPatternToRegExp = (pattern) => { | ||
| const source = escapeRegExp(pattern).replace(/\\\*/g, ".*"); | ||
| return new RegExp(`^${source}$`); | ||
| }; | ||
| var networkMatchesPattern = (pattern, network) => { | ||
| return networkPatternToRegExp(pattern).test(network); | ||
| }; | ||
| // src/facilitator/x402Facilitator.ts | ||
@@ -235,4 +245,3 @@ var x402Facilitator = class { | ||
| } | ||
| const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); | ||
| if (patternRegex.test(paymentRequirements.network)) { | ||
| if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) { | ||
| schemeNetworkFacilitator = schemeData.facilitator; | ||
@@ -328,4 +337,3 @@ break; | ||
| } | ||
| const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); | ||
| if (patternRegex.test(paymentRequirements.network)) { | ||
| if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) { | ||
| schemeNetworkFacilitator = schemeData.facilitator; | ||
@@ -332,0 +340,0 @@ break; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../../src/facilitator/index.ts","../../../src/index.ts","../../../src/facilitator/x402Facilitator.ts"],"sourcesContent":["export * from \"./x402Facilitator\";\n","export const x402Version = 2;\n","import { x402Version } from \"..\";\nimport { SettleResponse, VerifyResponse } from \"../types/facilitator\";\nimport { FacilitatorExtension } from \"../types/extensions\";\nimport { SchemeNetworkFacilitator, FacilitatorContext } from \"../types/mechanisms\";\nimport { PaymentPayload, PaymentRequirements } from \"../types/payments\";\nimport { Network } from \"../types\";\nimport { type SchemeData } from \"../utils\";\n\n/**\n * Facilitator Hook Context Interfaces\n */\n\nexport interface FacilitatorVerifyContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorVerifyResultContext extends FacilitatorVerifyContext {\n result: VerifyResponse;\n}\n\nexport interface FacilitatorVerifyFailureContext extends FacilitatorVerifyContext {\n error: Error;\n}\n\nexport interface FacilitatorSettleContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorSettleResultContext extends FacilitatorSettleContext {\n result: SettleResponse;\n}\n\nexport interface FacilitatorSettleFailureContext extends FacilitatorSettleContext {\n error: Error;\n}\n\n/**\n * Facilitator Hook Type Definitions\n */\n\nexport type FacilitatorBeforeVerifyHook = (\n context: FacilitatorVerifyContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterVerifyHook = (context: FacilitatorVerifyResultContext) => Promise<void>;\n\nexport type FacilitatorOnVerifyFailureHook = (\n context: FacilitatorVerifyFailureContext,\n) => Promise<void | { recovered: true; result: VerifyResponse }>;\n\nexport type FacilitatorBeforeSettleHook = (\n context: FacilitatorSettleContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterSettleHook = (context: FacilitatorSettleResultContext) => Promise<void>;\n\nexport type FacilitatorOnSettleFailureHook = (\n context: FacilitatorSettleFailureContext,\n) => Promise<void | { recovered: true; result: SettleResponse }>;\n\n/**\n * Facilitator client for the x402 payment protocol.\n * Manages payment scheme registration, verification, and settlement.\n */\nexport class x402Facilitator {\n private readonly registeredFacilitatorSchemes: Map<\n number,\n SchemeData<SchemeNetworkFacilitator>[] // Array to support multiple facilitators per version\n > = new Map();\n private readonly extensions: Map<string, FacilitatorExtension> = new Map();\n\n private beforeVerifyHooks: FacilitatorBeforeVerifyHook[] = [];\n private afterVerifyHooks: FacilitatorAfterVerifyHook[] = [];\n private onVerifyFailureHooks: FacilitatorOnVerifyFailureHook[] = [];\n private beforeSettleHooks: FacilitatorBeforeSettleHook[] = [];\n private afterSettleHooks: FacilitatorAfterSettleHook[] = [];\n private onSettleFailureHooks: FacilitatorOnSettleFailureHook[] = [];\n\n /**\n * Registers a scheme facilitator for the current x402 version.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n register(networks: Network | Network[], facilitator: SchemeNetworkFacilitator): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(x402Version, networksArray, facilitator);\n }\n\n /**\n * Registers a scheme facilitator for x402 version 1.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n registerV1(\n networks: Network | Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(1, networksArray, facilitator);\n }\n\n /**\n * Registers a protocol extension.\n *\n * @param extension - The extension object to register\n * @returns The x402Facilitator instance for chaining\n */\n registerExtension(extension: FacilitatorExtension): x402Facilitator {\n this.extensions.set(extension.key, extension);\n return this;\n }\n\n /**\n * Gets the list of registered extension keys.\n *\n * @returns Array of extension key strings\n */\n getExtensions(): string[] {\n return Array.from(this.extensions.keys());\n }\n\n /**\n * Gets a registered extension by key.\n *\n * @param key - The extension key to look up\n * @returns The extension object, or undefined if not registered\n */\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(key: string): T | undefined {\n return this.extensions.get(key) as T | undefined;\n }\n\n /**\n * Register a hook to execute before facilitator payment verification.\n * Can abort verification by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeVerify(hook: FacilitatorBeforeVerifyHook): x402Facilitator {\n this.beforeVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment verification (isValid: true).\n * This hook is NOT called when verification fails (isValid: false) - use onVerifyFailure for that.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterVerify(hook: FacilitatorAfterVerifyHook): x402Facilitator {\n this.afterVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment verification fails.\n * Called when: verification returns isValid: false, or an exception is thrown during verification.\n * Can recover from failure by returning { recovered: true, result: VerifyResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onVerifyFailure(hook: FacilitatorOnVerifyFailureHook): x402Facilitator {\n this.onVerifyFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute before facilitator payment settlement.\n * Can abort settlement by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeSettle(hook: FacilitatorBeforeSettleHook): x402Facilitator {\n this.beforeSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment settlement.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterSettle(hook: FacilitatorAfterSettleHook): x402Facilitator {\n this.afterSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment settlement fails.\n * Can recover from failure by returning { recovered: true, result: SettleResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onSettleFailure(hook: FacilitatorOnSettleFailureHook): x402Facilitator {\n this.onSettleFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Gets supported payment kinds, extensions, and signers.\n * Uses networks registered during register() calls - no parameters needed.\n * Returns flat array format for backward compatibility with V1 clients.\n *\n * @returns Supported response with kinds as array (with version in each element), extensions, and signers\n */\n getSupported(): {\n kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }>;\n extensions: string[];\n signers: Record<string, string[]>;\n } {\n const kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }> = [];\n const signersByFamily: Record<string, Set<string>> = {};\n\n // Iterate over registered scheme data (array supports multiple facilitators per version)\n for (const [version, schemeDataArray] of this.registeredFacilitatorSchemes) {\n for (const schemeData of schemeDataArray) {\n const { facilitator, networks } = schemeData;\n const scheme = facilitator.scheme;\n\n // Iterate over stored concrete networks\n for (const network of networks) {\n const extra = facilitator.getExtra(network);\n kinds.push({\n x402Version: version,\n scheme,\n network,\n ...(extra && { extra }),\n });\n\n // Collect signers by CAIP family for this network\n const family = facilitator.caipFamily;\n if (!signersByFamily[family]) {\n signersByFamily[family] = new Set();\n }\n facilitator.getSigners(network).forEach(signer => signersByFamily[family].add(signer));\n }\n }\n }\n\n // Convert signer sets to arrays\n const signers: Record<string, string[]> = {};\n for (const [family, signerSet] of Object.entries(signersByFamily)) {\n signers[family] = Array.from(signerSet);\n }\n\n return {\n kinds,\n extensions: this.getExtensions(),\n signers,\n };\n }\n\n /**\n * Verifies a payment payload against requirements.\n *\n * @param paymentPayload - The payment payload to verify\n * @param paymentRequirements - The payment requirements to verify against\n * @returns Promise resolving to the verification response\n */\n async verify(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const context: FacilitatorVerifyContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeVerify hooks\n for (const hook of this.beforeVerifyHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n return {\n isValid: false,\n invalidReason: result.reason,\n };\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n const patternRegex = new RegExp(\"^\" + schemeData.pattern.replace(\"*\", \".*\") + \"$\");\n if (patternRegex.test(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const verifyResult = await schemeNetworkFacilitator.verify(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Check if verification failed (isValid: false)\n if (!verifyResult.isValid) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: new Error(verifyResult.invalidReason || \"Verification failed\"),\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n // If recovered, execute afterVerify hooks with recovered result\n const recoveredContext: FacilitatorVerifyResultContext = {\n ...context,\n result: result.result,\n };\n for (const hook of this.afterVerifyHooks) {\n await hook(recoveredContext);\n }\n return result.result;\n }\n }\n\n return verifyResult;\n }\n\n // Execute afterVerify hooks only for successful verification\n const resultContext: FacilitatorVerifyResultContext = {\n ...context,\n result: verifyResult,\n };\n\n for (const hook of this.afterVerifyHooks) {\n await hook(resultContext);\n }\n\n return verifyResult;\n } catch (error) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Settles a payment based on the payload and requirements.\n *\n * @param paymentPayload - The payment payload to settle\n * @param paymentRequirements - The payment requirements for settlement\n * @returns Promise resolving to the settlement response\n */\n async settle(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const context: FacilitatorSettleContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeSettle hooks\n for (const hook of this.beforeSettleHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n throw new Error(`Settlement aborted: ${result.reason}`);\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n const patternRegex = new RegExp(\"^\" + schemeData.pattern.replace(\"*\", \".*\") + \"$\");\n if (patternRegex.test(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const settleResult = await schemeNetworkFacilitator.settle(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Execute afterSettle hooks\n const resultContext: FacilitatorSettleResultContext = {\n ...context,\n result: settleResult,\n };\n\n for (const hook of this.afterSettleHooks) {\n await hook(resultContext);\n }\n\n return settleResult;\n } catch (error) {\n const failureContext: FacilitatorSettleFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onSettleFailure hooks\n for (const hook of this.onSettleFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Builds a FacilitatorContext from the registered extensions map.\n * Passed to mechanism verify/settle so they can access extension capabilities.\n *\n * @returns A FacilitatorContext backed by this facilitator's registered extensions\n */\n private buildFacilitatorContext(): FacilitatorContext {\n const extensionsMap = this.extensions;\n return {\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(\n key: string,\n ): T | undefined {\n return extensionsMap.get(key) as T | undefined;\n },\n };\n }\n\n /**\n * Internal method to register a scheme facilitator.\n *\n * @param x402Version - The x402 protocol version\n * @param networks - Array of concrete networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n private _registerScheme(\n x402Version: number,\n networks: Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n if (!this.registeredFacilitatorSchemes.has(x402Version)) {\n this.registeredFacilitatorSchemes.set(x402Version, []);\n }\n const schemeDataArray = this.registeredFacilitatorSchemes.get(x402Version)!;\n\n // Add new scheme data (supports multiple facilitators with same scheme name)\n schemeDataArray.push({\n facilitator,\n networks: new Set(networks),\n pattern: this.derivePattern(networks),\n });\n\n return this;\n }\n\n /**\n * Derives a wildcard pattern from an array of networks.\n * If all networks share the same namespace, returns wildcard pattern.\n * Otherwise returns the first network for exact matching.\n *\n * @param networks - Array of networks\n * @returns Derived pattern for matching\n */\n private derivePattern(networks: Network[]): Network {\n if (networks.length === 0) return \"\" as Network;\n if (networks.length === 1) return networks[0];\n\n // Extract namespaces (e.g., \"eip155\" from \"eip155:84532\")\n const namespaces = networks.map(n => n.split(\":\")[0]);\n const uniqueNamespaces = new Set(namespaces);\n\n // If all same namespace, use wildcard\n if (uniqueNamespaces.size === 1) {\n return `${namespaces[0]}:*` as Network;\n }\n\n // Mixed namespaces - use first network for exact matching\n return networks[0];\n }\n}\n"],"mappings":"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","names":["hook","x402Version"]} | ||
| {"version":3,"sources":["../../../src/facilitator/index.ts","../../../src/index.ts","../../../src/utils/index.ts","../../../src/facilitator/x402Facilitator.ts"],"sourcesContent":["export * from \"./x402Facilitator\";\n","export const x402Version = 2;\n","import { Network } from \"../types\";\n\n/**\n * Converts a JavaScript number to a plain decimal string, expanding scientific notation\n * via string manipulation rather than parseFloat round-tripping.\n *\n * e.g. 1e-7 → \"0.0000001\", 4.02 → \"4.02\"\n *\n * @param n - The number to convert\n * @returns A plain decimal string representation with no scientific notation\n */\nexport function numberToDecimalString(n: number): string {\n const str = n.toString();\n if (!/[eE]/.test(str)) return str;\n\n const [significand, exponentStr] = str.split(/[eE]/);\n const exp = parseInt(exponentStr, 10);\n const negative = significand.startsWith(\"-\");\n const abs = negative ? significand.slice(1) : significand;\n const [intDigits, fracDigits = \"\"] = abs.split(\".\");\n const allDigits = intDigits + fracDigits;\n const decimalPos = intDigits.length + exp;\n\n let result: string;\n if (decimalPos <= 0) {\n result = \"0.\" + \"0\".repeat(-decimalPos) + allDigits;\n } else if (decimalPos >= allDigits.length) {\n result = allDigits + \"0\".repeat(decimalPos - allDigits.length);\n } else {\n result = allDigits.slice(0, decimalPos) + \".\" + allDigits.slice(decimalPos);\n }\n return (negative ? \"-\" : \"\") + result;\n}\n\n/**\n * Parses a money string into a finite, non-negative decimal number.\n * Accepts plain decimal strings with an optional leading dollar sign.\n *\n * @param money - The money string to parse\n * @returns Decimal number\n */\nexport function parseMoneyString(money: string): number {\n const cleaned = money.replace(/^\\$/, \"\").trim();\n if (!/^-?\\d+(?:\\.\\d+)?$/.test(cleaned) || /[eE]/.test(cleaned)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n const amount = Number(cleaned);\n if (!Number.isFinite(amount) || amount < 0) {\n throw new Error(`Invalid money format: ${money}`);\n }\n return amount;\n}\n\n/**\n * Convert a decimal amount to token smallest units.\n * Accepts only plain decimal strings — scientific notation is not allowed.\n * Throws if the amount is non-zero but too small to represent with the given decimal precision.\n *\n * @param decimalAmount - The decimal amount as a plain string (e.g., \"0.10\")\n * @param decimals - The number of decimals for the token (e.g., 6 for USDC)\n * @returns The amount in smallest units as a string\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n if (/[eE]/.test(decimalAmount)) {\n throw new Error(\n `Invalid amount: ${decimalAmount} — use decimal notation, not scientific notation`,\n );\n }\n if (!/^-?\\d+\\.?\\d*$/.test(decimalAmount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n const [intPart, decPart = \"\"] = decimalAmount.split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n if (tokenAmount === \"0\" && /[1-9]/.test(decimalAmount)) {\n throw new Error(\n `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places`,\n );\n }\n return tokenAmount;\n}\n\n/**\n * Scheme data structure for facilitator storage\n */\nexport interface SchemeData<T> {\n facilitator: T;\n networks: Set<Network>;\n pattern: Network;\n}\n\nconst escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nconst networkPatternToRegExp = (pattern: Network): RegExp => {\n const source = escapeRegExp(pattern).replace(/\\\\\\*/g, \".*\");\n return new RegExp(`^${source}$`);\n};\n\nexport const networkMatchesPattern = (pattern: Network, network: Network): boolean => {\n return networkPatternToRegExp(pattern).test(network);\n};\n\nexport const findSchemesByNetwork = <T>(\n map: Map<string, Map<string, T>>,\n network: Network,\n): Map<string, T> | undefined => {\n // Direct match first\n let implementationsByScheme = map.get(network);\n\n if (!implementationsByScheme) {\n // Try pattern matching for registered network patterns\n for (const [registeredNetworkPattern, implementations] of map.entries()) {\n if (networkMatchesPattern(registeredNetworkPattern as Network, network)) {\n implementationsByScheme = implementations;\n break;\n }\n }\n }\n\n return implementationsByScheme;\n};\n\nexport const findByNetworkAndScheme = <T>(\n map: Map<string, Map<string, T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n return findSchemesByNetwork(map, network)?.get(scheme);\n};\n\n/**\n * Finds a facilitator by scheme and network using pattern matching.\n * Works with new SchemeData storage structure.\n *\n * @param schemeMap - Map of scheme names to SchemeData\n * @param scheme - The scheme to find\n * @param network - The network to match against\n * @returns The facilitator if found, undefined otherwise\n */\nexport const findFacilitatorBySchemeAndNetwork = <T>(\n schemeMap: Map<string, SchemeData<T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n const schemeData = schemeMap.get(scheme);\n if (!schemeData) return undefined;\n\n // Check if network is in the stored networks set\n if (schemeData.networks.has(network)) {\n return schemeData.facilitator;\n }\n\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, network)) {\n return schemeData.facilitator;\n }\n\n return undefined;\n};\n\nexport const Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n/**\n * Encodes a string to base64 format\n *\n * @param data - The string to be encoded to base64\n * @returns The base64 encoded string\n */\nexport function safeBase64Encode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.btoa === \"function\") {\n const bytes = new TextEncoder().encode(data);\n const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join(\"\");\n return globalThis.btoa(binaryString);\n }\n return Buffer.from(data, \"utf8\").toString(\"base64\");\n}\n\n/**\n * Decodes a base64 string back to its original format\n *\n * @param data - The base64 encoded string to be decoded\n * @returns The decoded string in UTF-8 format\n */\nexport function safeBase64Decode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.atob === \"function\") {\n const binaryString = globalThis.atob(data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder(\"utf-8\");\n return decoder.decode(bytes);\n }\n return Buffer.from(data, \"base64\").toString(\"utf-8\");\n}\n\n/**\n * Deep equality comparison for payment requirements\n * Uses a normalized JSON.stringify for consistent comparison\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are deeply equal\n */\nexport function deepEqual(obj1: unknown, obj2: unknown): boolean {\n // Normalize and stringify both objects for comparison\n // This handles nested objects, arrays, and different property orders\n const normalize = (obj: unknown): string => {\n // Handle primitives and null/undefined\n if (obj === null || obj === undefined) return JSON.stringify(obj);\n if (typeof obj !== \"object\") return JSON.stringify(obj);\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return JSON.stringify(\n obj.map(item =>\n typeof item === \"object\" && item !== null ? JSON.parse(normalize(item)) : item,\n ),\n );\n }\n\n // Handle objects - sort keys and recursively normalize values\n const sorted: Record<string, unknown> = {};\n Object.keys(obj as Record<string, unknown>)\n .sort()\n .forEach(key => {\n const value = (obj as Record<string, unknown>)[key];\n sorted[key] =\n typeof value === \"object\" && value !== null ? JSON.parse(normalize(value)) : value;\n });\n return JSON.stringify(sorted);\n };\n\n try {\n return normalize(obj1) === normalize(obj2);\n } catch {\n // Fallback to simple comparison if normalization fails\n return JSON.stringify(obj1) === JSON.stringify(obj2);\n }\n}\n","import { x402Version } from \"..\";\nimport { SettleResponse, VerifyResponse } from \"../types/facilitator\";\nimport { FacilitatorExtension } from \"../types/extensions\";\nimport { SchemeNetworkFacilitator, FacilitatorContext } from \"../types/mechanisms\";\nimport { PaymentPayload, PaymentRequirements } from \"../types/payments\";\nimport { Network } from \"../types\";\nimport { networkMatchesPattern, type SchemeData } from \"../utils\";\n\n/**\n * Facilitator Hook Context Interfaces\n */\n\nexport interface FacilitatorVerifyContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorVerifyResultContext extends FacilitatorVerifyContext {\n result: VerifyResponse;\n}\n\nexport interface FacilitatorVerifyFailureContext extends FacilitatorVerifyContext {\n error: Error;\n}\n\nexport interface FacilitatorSettleContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorSettleResultContext extends FacilitatorSettleContext {\n result: SettleResponse;\n}\n\nexport interface FacilitatorSettleFailureContext extends FacilitatorSettleContext {\n error: Error;\n}\n\n/**\n * Facilitator Hook Type Definitions\n */\n\nexport type FacilitatorBeforeVerifyHook = (\n context: FacilitatorVerifyContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterVerifyHook = (context: FacilitatorVerifyResultContext) => Promise<void>;\n\nexport type FacilitatorOnVerifyFailureHook = (\n context: FacilitatorVerifyFailureContext,\n) => Promise<void | { recovered: true; result: VerifyResponse }>;\n\nexport type FacilitatorBeforeSettleHook = (\n context: FacilitatorSettleContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterSettleHook = (context: FacilitatorSettleResultContext) => Promise<void>;\n\nexport type FacilitatorOnSettleFailureHook = (\n context: FacilitatorSettleFailureContext,\n) => Promise<void | { recovered: true; result: SettleResponse }>;\n\n/**\n * Facilitator client for the x402 payment protocol.\n * Manages payment scheme registration, verification, and settlement.\n */\nexport class x402Facilitator {\n private readonly registeredFacilitatorSchemes: Map<\n number,\n SchemeData<SchemeNetworkFacilitator>[] // Array to support multiple facilitators per version\n > = new Map();\n private readonly extensions: Map<string, FacilitatorExtension> = new Map();\n\n private beforeVerifyHooks: FacilitatorBeforeVerifyHook[] = [];\n private afterVerifyHooks: FacilitatorAfterVerifyHook[] = [];\n private onVerifyFailureHooks: FacilitatorOnVerifyFailureHook[] = [];\n private beforeSettleHooks: FacilitatorBeforeSettleHook[] = [];\n private afterSettleHooks: FacilitatorAfterSettleHook[] = [];\n private onSettleFailureHooks: FacilitatorOnSettleFailureHook[] = [];\n\n /**\n * Registers a scheme facilitator for the current x402 version.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n register(networks: Network | Network[], facilitator: SchemeNetworkFacilitator): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(x402Version, networksArray, facilitator);\n }\n\n /**\n * Registers a scheme facilitator for x402 version 1.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n registerV1(\n networks: Network | Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(1, networksArray, facilitator);\n }\n\n /**\n * Registers a protocol extension.\n *\n * @param extension - The extension object to register\n * @returns The x402Facilitator instance for chaining\n */\n registerExtension(extension: FacilitatorExtension): x402Facilitator {\n this.extensions.set(extension.key, extension);\n return this;\n }\n\n /**\n * Gets the list of registered extension keys.\n *\n * @returns Array of extension key strings\n */\n getExtensions(): string[] {\n return Array.from(this.extensions.keys());\n }\n\n /**\n * Gets a registered extension by key.\n *\n * @param key - The extension key to look up\n * @returns The extension object, or undefined if not registered\n */\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(key: string): T | undefined {\n return this.extensions.get(key) as T | undefined;\n }\n\n /**\n * Register a hook to execute before facilitator payment verification.\n * Can abort verification by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeVerify(hook: FacilitatorBeforeVerifyHook): x402Facilitator {\n this.beforeVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment verification (isValid: true).\n * This hook is NOT called when verification fails (isValid: false) - use onVerifyFailure for that.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterVerify(hook: FacilitatorAfterVerifyHook): x402Facilitator {\n this.afterVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment verification fails.\n * Called when: verification returns isValid: false, or an exception is thrown during verification.\n * Can recover from failure by returning { recovered: true, result: VerifyResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onVerifyFailure(hook: FacilitatorOnVerifyFailureHook): x402Facilitator {\n this.onVerifyFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute before facilitator payment settlement.\n * Can abort settlement by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeSettle(hook: FacilitatorBeforeSettleHook): x402Facilitator {\n this.beforeSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment settlement.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterSettle(hook: FacilitatorAfterSettleHook): x402Facilitator {\n this.afterSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment settlement fails.\n * Can recover from failure by returning { recovered: true, result: SettleResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onSettleFailure(hook: FacilitatorOnSettleFailureHook): x402Facilitator {\n this.onSettleFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Gets supported payment kinds, extensions, and signers.\n * Uses networks registered during register() calls - no parameters needed.\n * Returns flat array format for backward compatibility with V1 clients.\n *\n * @returns Supported response with kinds as array (with version in each element), extensions, and signers\n */\n getSupported(): {\n kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }>;\n extensions: string[];\n signers: Record<string, string[]>;\n } {\n const kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }> = [];\n const signersByFamily: Record<string, Set<string>> = {};\n\n // Iterate over registered scheme data (array supports multiple facilitators per version)\n for (const [version, schemeDataArray] of this.registeredFacilitatorSchemes) {\n for (const schemeData of schemeDataArray) {\n const { facilitator, networks } = schemeData;\n const scheme = facilitator.scheme;\n\n // Iterate over stored concrete networks\n for (const network of networks) {\n const extra = facilitator.getExtra(network);\n kinds.push({\n x402Version: version,\n scheme,\n network,\n ...(extra && { extra }),\n });\n\n // Collect signers by CAIP family for this network\n const family = facilitator.caipFamily;\n if (!signersByFamily[family]) {\n signersByFamily[family] = new Set();\n }\n facilitator.getSigners(network).forEach(signer => signersByFamily[family].add(signer));\n }\n }\n }\n\n // Convert signer sets to arrays\n const signers: Record<string, string[]> = {};\n for (const [family, signerSet] of Object.entries(signersByFamily)) {\n signers[family] = Array.from(signerSet);\n }\n\n return {\n kinds,\n extensions: this.getExtensions(),\n signers,\n };\n }\n\n /**\n * Verifies a payment payload against requirements.\n *\n * @param paymentPayload - The payment payload to verify\n * @param paymentRequirements - The payment requirements to verify against\n * @returns Promise resolving to the verification response\n */\n async verify(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const context: FacilitatorVerifyContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeVerify hooks\n for (const hook of this.beforeVerifyHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n return {\n isValid: false,\n invalidReason: result.reason,\n };\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const verifyResult = await schemeNetworkFacilitator.verify(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Check if verification failed (isValid: false)\n if (!verifyResult.isValid) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: new Error(verifyResult.invalidReason || \"Verification failed\"),\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n // If recovered, execute afterVerify hooks with recovered result\n const recoveredContext: FacilitatorVerifyResultContext = {\n ...context,\n result: result.result,\n };\n for (const hook of this.afterVerifyHooks) {\n await hook(recoveredContext);\n }\n return result.result;\n }\n }\n\n return verifyResult;\n }\n\n // Execute afterVerify hooks only for successful verification\n const resultContext: FacilitatorVerifyResultContext = {\n ...context,\n result: verifyResult,\n };\n\n for (const hook of this.afterVerifyHooks) {\n await hook(resultContext);\n }\n\n return verifyResult;\n } catch (error) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Settles a payment based on the payload and requirements.\n *\n * @param paymentPayload - The payment payload to settle\n * @param paymentRequirements - The payment requirements for settlement\n * @returns Promise resolving to the settlement response\n */\n async settle(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const context: FacilitatorSettleContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeSettle hooks\n for (const hook of this.beforeSettleHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n throw new Error(`Settlement aborted: ${result.reason}`);\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const settleResult = await schemeNetworkFacilitator.settle(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Execute afterSettle hooks\n const resultContext: FacilitatorSettleResultContext = {\n ...context,\n result: settleResult,\n };\n\n for (const hook of this.afterSettleHooks) {\n await hook(resultContext);\n }\n\n return settleResult;\n } catch (error) {\n const failureContext: FacilitatorSettleFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onSettleFailure hooks\n for (const hook of this.onSettleFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Builds a FacilitatorContext from the registered extensions map.\n * Passed to mechanism verify/settle so they can access extension capabilities.\n *\n * @returns A FacilitatorContext backed by this facilitator's registered extensions\n */\n private buildFacilitatorContext(): FacilitatorContext {\n const extensionsMap = this.extensions;\n return {\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(\n key: string,\n ): T | undefined {\n return extensionsMap.get(key) as T | undefined;\n },\n };\n }\n\n /**\n * Internal method to register a scheme facilitator.\n *\n * @param x402Version - The x402 protocol version\n * @param networks - Array of concrete networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n private _registerScheme(\n x402Version: number,\n networks: Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n if (!this.registeredFacilitatorSchemes.has(x402Version)) {\n this.registeredFacilitatorSchemes.set(x402Version, []);\n }\n const schemeDataArray = this.registeredFacilitatorSchemes.get(x402Version)!;\n\n // Add new scheme data (supports multiple facilitators with same scheme name)\n schemeDataArray.push({\n facilitator,\n networks: new Set(networks),\n pattern: this.derivePattern(networks),\n });\n\n return this;\n }\n\n /**\n * Derives a wildcard pattern from an array of networks.\n * If all networks share the same namespace, returns wildcard pattern.\n * Otherwise returns the first network for exact matching.\n *\n * @param networks - Array of networks\n * @returns Derived pattern for matching\n */\n private derivePattern(networks: Network[]): Network {\n if (networks.length === 0) return \"\" as Network;\n if (networks.length === 1) return networks[0];\n\n // Extract namespaces (e.g., \"eip155\" from \"eip155:84532\")\n const namespaces = networks.map(n => n.split(\":\")[0]);\n const uniqueNamespaces = new Set(namespaces);\n\n // If all same namespace, use wildcard\n if (uniqueNamespaces.size === 1) {\n return `${namespaces[0]}:*` as Network;\n }\n\n // Mixed namespaces - use first network for exact matching\n return networks[0];\n }\n}\n"],"mappings":"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","names":["hook","x402Version"]} |
@@ -1,4 +0,4 @@ | ||
| import { P as PaymentPayload, c as PaymentRequired, S as SettleResponse } from '../x402Client-BSWPuNYe.js'; | ||
| export { C as CompiledRoute, D as DynamicPayTo, l as DynamicPrice, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, u as HTTPResourceServerExtensionHooks, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, j as PaymentOption, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, v as ResourceServerTransportExtensionHooks, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, n as SettlementFailedResponseBody, U as UnpaidResponseBody, B as getFacilitatorResponseError, x as x402HTTPResourceServer } from '../x402Client-BSWPuNYe.js'; | ||
| export { HTTPClientExtensionHooks, PaymentRequiredContext, PaymentRequiredHook, x402HTTPClient } from '../client/index.js'; | ||
| import { P as PaymentPayload, c as PaymentRequired, S as SettleResponse } from '../x402Client-TQHctrG7.js'; | ||
| export { C as CompiledRoute, D as DynamicPayTo, l as DynamicPrice, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, u as HTTPResourceServerExtensionHooks, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, j as PaymentOption, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, v as ResourceServerTransportExtensionHooks, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, n as SettlementFailedResponseBody, U as UnpaidResponseBody, B as getFacilitatorResponseError, x as x402HTTPResourceServer } from '../x402Client-TQHctrG7.js'; | ||
| export { HTTPClientExtensionHooks, HTTPPaymentStatus, HTTPResourceResponse, PaymentRequiredContext, PaymentRequiredHook, x402HTTPClient } from '../client/index.js'; | ||
@@ -5,0 +5,0 @@ type QueryParamMethods = "GET" | "HEAD" | "DELETE"; |
+56
-24
@@ -334,2 +334,20 @@ "use strict"; | ||
| } | ||
| const extensionResult = this.ResourceServer.validateExtensions( | ||
| paymentRequired, | ||
| paymentPayload | ||
| ); | ||
| if (!extensionResult.valid) { | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| extensionResult.invalidReason, | ||
| extensions, | ||
| transportContext, | ||
| paymentPayload | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| const verifyResult = await this.ResourceServer.verifyPayment( | ||
@@ -1326,35 +1344,49 @@ paymentPayload, | ||
| /** | ||
| * Parses a fetch Response into a discriminated `x402PaymentResult` for app-level convenience. | ||
| * Parses HTTP status, headers, and body into an `HTTPResourceResponse`. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns A discriminated union describing the payment outcome | ||
| * Decodes the x402 payment header into `header`: the `PAYMENT-RESPONSE` | ||
| * settlement if present, otherwise the `PAYMENT-REQUIRED` declaration on | ||
| * 402 responses (whose `error` field carries the server's failure reason). | ||
| * | ||
| * @param args - Normalized response inputs from any HTTP transport | ||
| * @param args.status - HTTP response status code | ||
| * @param args.getHeader - Callback to read response headers by name | ||
| * @param args.body - Response body payload | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| async processResponse(response) { | ||
| const getHeader = (name) => response.headers.get(name); | ||
| let settleResponse; | ||
| parsePaymentResult(args) { | ||
| const { status, getHeader, body } = args; | ||
| let header; | ||
| try { | ||
| settleResponse = this.getPaymentSettleResponse(getHeader); | ||
| header = this.getPaymentSettleResponse(getHeader); | ||
| } catch { | ||
| if (status === 402) { | ||
| try { | ||
| header = this.getPaymentRequiredResponse(getHeader, body); | ||
| } catch { | ||
| } | ||
| } | ||
| } | ||
| const contentType = response.headers.get("content-type") ?? ""; | ||
| const body = contentType.includes("application/json") ? await response.json() : await response.text(); | ||
| if (settleResponse && settleResponse.success) { | ||
| return { kind: "success", response, body, settleResponse }; | ||
| let paymentStatus = "none"; | ||
| if (header && !("success" in header)) { | ||
| paymentStatus = "payment_required"; | ||
| } | ||
| if (settleResponse && !settleResponse.success) { | ||
| return { kind: "settle_failed", response, body, settleResponse }; | ||
| if (header && "success" in header) { | ||
| paymentStatus = header.success ? "settled" : "settle_failed"; | ||
| } | ||
| if (response.status === 402) { | ||
| try { | ||
| const paymentRequired = this.getPaymentRequiredResponse(getHeader, body); | ||
| return { kind: "payment_required", response, paymentRequired }; | ||
| } catch { | ||
| } | ||
| } | ||
| if (response.ok) { | ||
| return { kind: "passthrough", response, body }; | ||
| } | ||
| return { kind: "error", response, status: response.status, body }; | ||
| return { status, paymentStatus, body, header }; | ||
| } | ||
| /** | ||
| * Parses a fetch Response into an `HTTPResourceResponse` for app-level convenience. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| async processResponse(response) { | ||
| const getHeader = (name) => response.headers.get(name); | ||
| const contentType = response.headers.get("content-type") ?? ""; | ||
| const body = contentType.includes("application/json") ? await response.json() : await response.text(); | ||
| return this.parsePaymentResult({ status: response.status, getHeader, body }); | ||
| } | ||
| /** | ||
| * Manual HTTP hooks run before extension hooks scoped to the 402 response. | ||
@@ -1361,0 +1393,0 @@ * |
@@ -1,3 +0,3 @@ | ||
| import { a as PaymentRequirements, S as SettleResponse } from '../x402Client-BSWPuNYe.js'; | ||
| export { a4 as AfterSettleHook, a1 as AfterVerifyHook, a3 as BeforeSettleHook, a0 as BeforeVerifyHook, C as CompiledRoute, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, a5 as OnSettleFailureHook, a6 as OnVerifiedPaymentCanceledHook, a2 as OnVerifyFailureHook, Y as PaymentCancellationDispatcher, I as PaymentRequiredContext, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, G as ResourceConfig, $ as ResourceVerifyRespone, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, ab as SETTLEMENT_OVERRIDES_HEADER, a7 as SchemeEnrichPaymentRequiredResponseHook, a9 as SchemeEnrichSettlementPayloadHook, aa as SchemeEnrichSettlementResponseHook, a8 as SchemePaymentRequiredContext, M as SettleContext, Q as SettleFailureContext, O as SettleResultContext, n as SettlementFailedResponseBody, Z as SettlementOverrides, _ as SkipHandlerDirective, U as UnpaidResponseBody, X as VerifiedPaymentCancelOptions, T as VerifiedPaymentCanceledContext, W as VerifiedPaymentCancellationReason, J as VerifyContext, L as VerifyFailureContext, K as VerifyResultContext, ac as checkIfBazaarNeeded, B as getFacilitatorResponseError, x as x402HTTPResourceServer, E as x402ResourceServer } from '../x402Client-BSWPuNYe.js'; | ||
| import { a as PaymentRequirements, S as SettleResponse } from '../x402Client-TQHctrG7.js'; | ||
| export { a5 as AfterSettleHook, a2 as AfterVerifyHook, a4 as BeforeSettleHook, a1 as BeforeVerifyHook, C as CompiledRoute, _ as ExtensionValidationResult, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, a6 as OnSettleFailureHook, a7 as OnVerifiedPaymentCanceledHook, a3 as OnVerifyFailureHook, Y as PaymentCancellationDispatcher, I as PaymentRequiredContext, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, G as ResourceConfig, a0 as ResourceVerifyRespone, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, ac as SETTLEMENT_OVERRIDES_HEADER, a8 as SchemeEnrichPaymentRequiredResponseHook, aa as SchemeEnrichSettlementPayloadHook, ab as SchemeEnrichSettlementResponseHook, a9 as SchemePaymentRequiredContext, M as SettleContext, Q as SettleFailureContext, O as SettleResultContext, n as SettlementFailedResponseBody, Z as SettlementOverrides, $ as SkipHandlerDirective, U as UnpaidResponseBody, X as VerifiedPaymentCancelOptions, T as VerifiedPaymentCanceledContext, W as VerifiedPaymentCancellationReason, J as VerifyContext, L as VerifyFailureContext, K as VerifyResultContext, ad as checkIfBazaarNeeded, B as getFacilitatorResponseError, x as x402HTTPResourceServer, E as x402ResourceServer } from '../x402Client-TQHctrG7.js'; | ||
@@ -4,0 +4,0 @@ /** |
@@ -1,1 +0,1 @@ | ||
| export { aB as AssetAmount, aU as DeepReadonly, aR as FacilitatorContext, F as FacilitatorExtension, A as FacilitatorResponseError, u as HTTPResourceServerExtensionHooks, aA as Money, aO as MoneyParser, N as Network, P as PaymentPayload, aQ as PaymentPayloadContext, aP as PaymentPayloadResult, av as PaymentPayloadV1, c as PaymentRequired, I as PaymentRequiredContext, au as PaymentRequiredV1, a as PaymentRequirements, at as PaymentRequirementsV1, aC as Price, aJ as ResourceInfo, aS as ResourceServerExtension, aT as ResourceServerExtensionHooks, v as ResourceServerTransportExtensionHooks, aL as SchemeClientHooks, a7 as SchemeEnrichPaymentRequiredResponseHook, aK as SchemeNetworkClient, b as SchemeNetworkFacilitator, aM as SchemeNetworkServer, a8 as SchemePaymentRequiredContext, aN as SchemeServerHooks, M as SettleContext, aI as SettleError, Q as SettleFailureContext, aE as SettleRequest, S as SettleResponse, O as SettleResultContext, aG as SupportedKind, aF as SupportedResponse, T as VerifiedPaymentCanceledContext, J as VerifyContext, aH as VerifyError, L as VerifyFailureContext, aD as VerifyRequest, V as VerifyResponse, K as VerifyResultContext, B as getFacilitatorResponseError } from '../x402Client-BSWPuNYe.js'; | ||
| export { aC as AssetAmount, aV as DeepReadonly, aS as FacilitatorContext, F as FacilitatorExtension, A as FacilitatorResponseError, u as HTTPResourceServerExtensionHooks, aB as Money, aP as MoneyParser, N as Network, P as PaymentPayload, aR as PaymentPayloadContext, aQ as PaymentPayloadResult, aw as PaymentPayloadV1, c as PaymentRequired, I as PaymentRequiredContext, av as PaymentRequiredV1, a as PaymentRequirements, au as PaymentRequirementsV1, aD as Price, aK as ResourceInfo, aT as ResourceServerExtension, aU as ResourceServerExtensionHooks, v as ResourceServerTransportExtensionHooks, aM as SchemeClientHooks, a8 as SchemeEnrichPaymentRequiredResponseHook, aL as SchemeNetworkClient, b as SchemeNetworkFacilitator, aN as SchemeNetworkServer, a9 as SchemePaymentRequiredContext, aO as SchemeServerHooks, M as SettleContext, aJ as SettleError, Q as SettleFailureContext, aF as SettleRequest, S as SettleResponse, O as SettleResultContext, aH as SupportedKind, aG as SupportedResponse, T as VerifiedPaymentCanceledContext, J as VerifyContext, aI as VerifyError, L as VerifyFailureContext, aE as VerifyRequest, V as VerifyResponse, K as VerifyResultContext, B as getFacilitatorResponseError } from '../x402Client-TQHctrG7.js'; |
@@ -1,1 +0,1 @@ | ||
| export { av as PaymentPayloadV1, au as PaymentRequiredV1, at as PaymentRequirementsV1, ax as SettleRequestV1, ay as SettleResponseV1, az as SupportedResponseV1, aw as VerifyRequestV1 } from '../../x402Client-BSWPuNYe.js'; | ||
| export { aw as PaymentPayloadV1, av as PaymentRequiredV1, au as PaymentRequirementsV1, ay as SettleRequestV1, az as SettleResponseV1, aA as SupportedResponseV1, ax as VerifyRequestV1 } from '../../x402Client-TQHctrG7.js'; |
@@ -1,2 +0,2 @@ | ||
| import { N as Network } from '../x402Client-BSWPuNYe.js'; | ||
| import { N as Network } from '../x402Client-TQHctrG7.js'; | ||
@@ -14,2 +14,10 @@ /** | ||
| /** | ||
| * Parses a money string into a finite, non-negative decimal number. | ||
| * Accepts plain decimal strings with an optional leading dollar sign. | ||
| * | ||
| * @param money - The money string to parse | ||
| * @returns Decimal number | ||
| */ | ||
| declare function parseMoneyString(money: string): number; | ||
| /** | ||
| * Convert a decimal amount to token smallest units. | ||
@@ -32,2 +40,3 @@ * Accepts only plain decimal strings — scientific notation is not allowed. | ||
| } | ||
| declare const networkMatchesPattern: (pattern: Network, network: Network) => boolean; | ||
| declare const findSchemesByNetwork: <T>(map: Map<string, Map<string, T>>, network: Network) => Map<string, T> | undefined; | ||
@@ -70,2 +79,2 @@ declare const findByNetworkAndScheme: <T>(map: Map<string, Map<string, T>>, scheme: string, network: Network) => T | undefined; | ||
| export { Base64EncodedRegex, type SchemeData, convertToTokenAmount, deepEqual, findByNetworkAndScheme, findFacilitatorBySchemeAndNetwork, findSchemesByNetwork, numberToDecimalString, safeBase64Decode, safeBase64Encode }; | ||
| export { Base64EncodedRegex, type SchemeData, convertToTokenAmount, deepEqual, findByNetworkAndScheme, findFacilitatorBySchemeAndNetwork, findSchemesByNetwork, networkMatchesPattern, numberToDecimalString, parseMoneyString, safeBase64Decode, safeBase64Encode }; |
@@ -29,3 +29,5 @@ "use strict"; | ||
| findSchemesByNetwork: () => findSchemesByNetwork, | ||
| networkMatchesPattern: () => networkMatchesPattern, | ||
| numberToDecimalString: () => numberToDecimalString, | ||
| parseMoneyString: () => parseMoneyString, | ||
| safeBase64Decode: () => safeBase64Decode, | ||
@@ -55,2 +57,13 @@ safeBase64Encode: () => safeBase64Encode | ||
| } | ||
| function parseMoneyString(money) { | ||
| const cleaned = money.replace(/^\$/, "").trim(); | ||
| if (!/^-?\d+(?:\.\d+)?$/.test(cleaned) || /[eE]/.test(cleaned)) { | ||
| throw new Error(`Invalid money format: ${money}`); | ||
| } | ||
| const amount = Number(cleaned); | ||
| if (!Number.isFinite(amount) || amount < 0) { | ||
| throw new Error(`Invalid money format: ${money}`); | ||
| } | ||
| return amount; | ||
| } | ||
| function convertToTokenAmount(decimalAmount, decimals) { | ||
@@ -75,2 +88,10 @@ if (/[eE]/.test(decimalAmount)) { | ||
| } | ||
| var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| var networkPatternToRegExp = (pattern) => { | ||
| const source = escapeRegExp(pattern).replace(/\\\*/g, ".*"); | ||
| return new RegExp(`^${source}$`); | ||
| }; | ||
| var networkMatchesPattern = (pattern, network) => { | ||
| return networkPatternToRegExp(pattern).test(network); | ||
| }; | ||
| var findSchemesByNetwork = (map, network) => { | ||
@@ -80,5 +101,3 @@ let implementationsByScheme = map.get(network); | ||
| for (const [registeredNetworkPattern, implementations] of map.entries()) { | ||
| const pattern = registeredNetworkPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*"); | ||
| const regex = new RegExp(`^${pattern}$`); | ||
| if (regex.test(network)) { | ||
| if (networkMatchesPattern(registeredNetworkPattern, network)) { | ||
| implementationsByScheme = implementations; | ||
@@ -100,4 +119,3 @@ break; | ||
| } | ||
| const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); | ||
| if (patternRegex.test(network)) { | ||
| if (networkMatchesPattern(schemeData.pattern, network)) { | ||
| return schemeData.facilitator; | ||
@@ -160,3 +178,5 @@ } | ||
| findSchemesByNetwork, | ||
| networkMatchesPattern, | ||
| numberToDecimalString, | ||
| parseMoneyString, | ||
| safeBase64Decode, | ||
@@ -163,0 +183,0 @@ safeBase64Encode |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../../src/utils/index.ts"],"sourcesContent":["import { Network } from \"../types\";\n\n/**\n * Converts a JavaScript number to a plain decimal string, expanding scientific notation\n * via string manipulation rather than parseFloat round-tripping.\n *\n * e.g. 1e-7 → \"0.0000001\", 4.02 → \"4.02\"\n *\n * @param n - The number to convert\n * @returns A plain decimal string representation with no scientific notation\n */\nexport function numberToDecimalString(n: number): string {\n const str = n.toString();\n if (!/[eE]/.test(str)) return str;\n\n const [significand, exponentStr] = str.split(/[eE]/);\n const exp = parseInt(exponentStr, 10);\n const negative = significand.startsWith(\"-\");\n const abs = negative ? significand.slice(1) : significand;\n const [intDigits, fracDigits = \"\"] = abs.split(\".\");\n const allDigits = intDigits + fracDigits;\n const decimalPos = intDigits.length + exp;\n\n let result: string;\n if (decimalPos <= 0) {\n result = \"0.\" + \"0\".repeat(-decimalPos) + allDigits;\n } else if (decimalPos >= allDigits.length) {\n result = allDigits + \"0\".repeat(decimalPos - allDigits.length);\n } else {\n result = allDigits.slice(0, decimalPos) + \".\" + allDigits.slice(decimalPos);\n }\n return (negative ? \"-\" : \"\") + result;\n}\n\n/**\n * Convert a decimal amount to token smallest units.\n * Accepts only plain decimal strings — scientific notation is not allowed.\n * Throws if the amount is non-zero but too small to represent with the given decimal precision.\n *\n * @param decimalAmount - The decimal amount as a plain string (e.g., \"0.10\")\n * @param decimals - The number of decimals for the token (e.g., 6 for USDC)\n * @returns The amount in smallest units as a string\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n if (/[eE]/.test(decimalAmount)) {\n throw new Error(\n `Invalid amount: ${decimalAmount} — use decimal notation, not scientific notation`,\n );\n }\n if (!/^-?\\d+\\.?\\d*$/.test(decimalAmount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n const [intPart, decPart = \"\"] = decimalAmount.split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n if (tokenAmount === \"0\" && /[1-9]/.test(decimalAmount)) {\n throw new Error(\n `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places`,\n );\n }\n return tokenAmount;\n}\n\n/**\n * Scheme data structure for facilitator storage\n */\nexport interface SchemeData<T> {\n facilitator: T;\n networks: Set<Network>;\n pattern: Network;\n}\n\nexport const findSchemesByNetwork = <T>(\n map: Map<string, Map<string, T>>,\n network: Network,\n): Map<string, T> | undefined => {\n // Direct match first\n let implementationsByScheme = map.get(network);\n\n if (!implementationsByScheme) {\n // Try pattern matching for registered network patterns\n for (const [registeredNetworkPattern, implementations] of map.entries()) {\n // Convert the registered network pattern to a regex\n // e.g., \"eip155:*\" becomes /^eip155:.*$/\n const pattern = registeredNetworkPattern\n .replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape special regex chars except *\n .replace(/\\\\\\*/g, \".*\"); // Replace escaped * with .*\n\n const regex = new RegExp(`^${pattern}$`);\n\n if (regex.test(network)) {\n implementationsByScheme = implementations;\n break;\n }\n }\n }\n\n return implementationsByScheme;\n};\n\nexport const findByNetworkAndScheme = <T>(\n map: Map<string, Map<string, T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n return findSchemesByNetwork(map, network)?.get(scheme);\n};\n\n/**\n * Finds a facilitator by scheme and network using pattern matching.\n * Works with new SchemeData storage structure.\n *\n * @param schemeMap - Map of scheme names to SchemeData\n * @param scheme - The scheme to find\n * @param network - The network to match against\n * @returns The facilitator if found, undefined otherwise\n */\nexport const findFacilitatorBySchemeAndNetwork = <T>(\n schemeMap: Map<string, SchemeData<T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n const schemeData = schemeMap.get(scheme);\n if (!schemeData) return undefined;\n\n // Check if network is in the stored networks set\n if (schemeData.networks.has(network)) {\n return schemeData.facilitator;\n }\n\n // Try pattern matching\n const patternRegex = new RegExp(\"^\" + schemeData.pattern.replace(\"*\", \".*\") + \"$\");\n if (patternRegex.test(network)) {\n return schemeData.facilitator;\n }\n\n return undefined;\n};\n\nexport const Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n/**\n * Encodes a string to base64 format\n *\n * @param data - The string to be encoded to base64\n * @returns The base64 encoded string\n */\nexport function safeBase64Encode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.btoa === \"function\") {\n const bytes = new TextEncoder().encode(data);\n const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join(\"\");\n return globalThis.btoa(binaryString);\n }\n return Buffer.from(data, \"utf8\").toString(\"base64\");\n}\n\n/**\n * Decodes a base64 string back to its original format\n *\n * @param data - The base64 encoded string to be decoded\n * @returns The decoded string in UTF-8 format\n */\nexport function safeBase64Decode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.atob === \"function\") {\n const binaryString = globalThis.atob(data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder(\"utf-8\");\n return decoder.decode(bytes);\n }\n return Buffer.from(data, \"base64\").toString(\"utf-8\");\n}\n\n/**\n * Deep equality comparison for payment requirements\n * Uses a normalized JSON.stringify for consistent comparison\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are deeply equal\n */\nexport function deepEqual(obj1: unknown, obj2: unknown): boolean {\n // Normalize and stringify both objects for comparison\n // This handles nested objects, arrays, and different property orders\n const normalize = (obj: unknown): string => {\n // Handle primitives and null/undefined\n if (obj === null || obj === undefined) return JSON.stringify(obj);\n if (typeof obj !== \"object\") return JSON.stringify(obj);\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return JSON.stringify(\n obj.map(item =>\n typeof item === \"object\" && item !== null ? JSON.parse(normalize(item)) : item,\n ),\n );\n }\n\n // Handle objects - sort keys and recursively normalize values\n const sorted: Record<string, unknown> = {};\n Object.keys(obj as Record<string, unknown>)\n .sort()\n .forEach(key => {\n const value = (obj as Record<string, unknown>)[key];\n sorted[key] =\n typeof value === \"object\" && value !== null ? JSON.parse(normalize(value)) : value;\n });\n return JSON.stringify(sorted);\n };\n\n try {\n return normalize(obj1) === normalize(obj2);\n } catch {\n // Fallback to simple comparison if normalization fails\n return JSON.stringify(obj1) === JSON.stringify(obj2);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWO,SAAS,sBAAsB,GAAmB;AACvD,QAAM,MAAM,EAAE,SAAS;AACvB,MAAI,CAAC,OAAO,KAAK,GAAG,EAAG,QAAO;AAE9B,QAAM,CAAC,aAAa,WAAW,IAAI,IAAI,MAAM,MAAM;AACnD,QAAM,MAAM,SAAS,aAAa,EAAE;AACpC,QAAM,WAAW,YAAY,WAAW,GAAG;AAC3C,QAAM,MAAM,WAAW,YAAY,MAAM,CAAC,IAAI;AAC9C,QAAM,CAAC,WAAW,aAAa,EAAE,IAAI,IAAI,MAAM,GAAG;AAClD,QAAM,YAAY,YAAY;AAC9B,QAAM,aAAa,UAAU,SAAS;AAEtC,MAAI;AACJ,MAAI,cAAc,GAAG;AACnB,aAAS,OAAO,IAAI,OAAO,CAAC,UAAU,IAAI;AAAA,EAC5C,WAAW,cAAc,UAAU,QAAQ;AACzC,aAAS,YAAY,IAAI,OAAO,aAAa,UAAU,MAAM;AAAA,EAC/D,OAAO;AACL,aAAS,UAAU,MAAM,GAAG,UAAU,IAAI,MAAM,UAAU,MAAM,UAAU;AAAA,EAC5E;AACA,UAAQ,WAAW,MAAM,MAAM;AACjC;AAWO,SAAS,qBAAqB,eAAuB,UAA0B;AACpF,MAAI,OAAO,KAAK,aAAa,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR,mBAAmB,aAAa;AAAA,IAClC;AAAA,EACF;AACA,MAAI,CAAC,gBAAgB,KAAK,aAAa,GAAG;AACxC,UAAM,IAAI,MAAM,mBAAmB,aAAa,EAAE;AAAA,EACpD;AACA,QAAM,CAAC,SAAS,UAAU,EAAE,IAAI,cAAc,MAAM,GAAG;AACvD,QAAM,YAAY,QAAQ,OAAO,UAAU,GAAG,EAAE,MAAM,GAAG,QAAQ;AACjE,QAAM,eAAe,UAAU,WAAW,QAAQ,OAAO,EAAE,KAAK;AAChE,MAAI,gBAAgB,OAAO,QAAQ,KAAK,aAAa,GAAG;AACtD,UAAM,IAAI;AAAA,MACR,UAAU,aAAa,mCAAmC,QAAQ;AAAA,IACpE;AAAA,EACF;AACA,SAAO;AACT;AAWO,IAAM,uBAAuB,CAClC,KACA,YAC+B;AAE/B,MAAI,0BAA0B,IAAI,IAAI,OAAO;AAE7C,MAAI,CAAC,yBAAyB;AAE5B,eAAW,CAAC,0BAA0B,eAAe,KAAK,IAAI,QAAQ,GAAG;AAGvE,YAAM,UAAU,yBACb,QAAQ,uBAAuB,MAAM,EACrC,QAAQ,SAAS,IAAI;AAExB,YAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,GAAG;AAEvC,UAAI,MAAM,KAAK,OAAO,GAAG;AACvB,kCAA0B;AAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,yBAAyB,CACpC,KACA,QACA,YACkB;AAClB,SAAO,qBAAqB,KAAK,OAAO,GAAG,IAAI,MAAM;AACvD;AAWO,IAAM,oCAAoC,CAC/C,WACA,QACA,YACkB;AAClB,QAAM,aAAa,UAAU,IAAI,MAAM;AACvC,MAAI,CAAC,WAAY,QAAO;AAGxB,MAAI,WAAW,SAAS,IAAI,OAAO,GAAG;AACpC,WAAO,WAAW;AAAA,EACpB;AAGA,QAAM,eAAe,IAAI,OAAO,MAAM,WAAW,QAAQ,QAAQ,KAAK,IAAI,IAAI,GAAG;AACjF,MAAI,aAAa,KAAK,OAAO,GAAG;AAC9B,WAAO,WAAW;AAAA,EACpB;AAEA,SAAO;AACT;AAEO,IAAM,qBAAqB;AAQ3B,SAAS,iBAAiB,MAAsB;AACrD,MAAI,OAAO,eAAe,eAAe,OAAO,WAAW,SAAS,YAAY;AAC9E,UAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI;AAC3C,UAAM,eAAe,MAAM,KAAK,OAAO,UAAQ,OAAO,aAAa,IAAI,CAAC,EAAE,KAAK,EAAE;AACjF,WAAO,WAAW,KAAK,YAAY;AAAA,EACrC;AACA,SAAO,OAAO,KAAK,MAAM,MAAM,EAAE,SAAS,QAAQ;AACpD;AAQO,SAAS,iBAAiB,MAAsB;AACrD,MAAI,OAAO,eAAe,eAAe,OAAO,WAAW,SAAS,YAAY;AAC9E,UAAM,eAAe,WAAW,KAAK,IAAI;AACzC,UAAM,QAAQ,IAAI,WAAW,aAAa,MAAM;AAChD,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,YAAM,CAAC,IAAI,aAAa,WAAW,CAAC;AAAA,IACtC;AACA,UAAM,UAAU,IAAI,YAAY,OAAO;AACvC,WAAO,QAAQ,OAAO,KAAK;AAAA,EAC7B;AACA,SAAO,OAAO,KAAK,MAAM,QAAQ,EAAE,SAAS,OAAO;AACrD;AAUO,SAAS,UAAU,MAAe,MAAwB;AAG/D,QAAM,YAAY,CAAC,QAAyB;AAE1C,QAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO,KAAK,UAAU,GAAG;AAChE,QAAI,OAAO,QAAQ,SAAU,QAAO,KAAK,UAAU,GAAG;AAGtD,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,aAAO,KAAK;AAAA,QACV,IAAI;AAAA,UAAI,UACN,OAAO,SAAS,YAAY,SAAS,OAAO,KAAK,MAAM,UAAU,IAAI,CAAC,IAAI;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAGA,UAAM,SAAkC,CAAC;AACzC,WAAO,KAAK,GAA8B,EACvC,KAAK,EACL,QAAQ,SAAO;AACd,YAAM,QAAS,IAAgC,GAAG;AAClD,aAAO,GAAG,IACR,OAAO,UAAU,YAAY,UAAU,OAAO,KAAK,MAAM,UAAU,KAAK,CAAC,IAAI;AAAA,IACjF,CAAC;AACH,WAAO,KAAK,UAAU,MAAM;AAAA,EAC9B;AAEA,MAAI;AACF,WAAO,UAAU,IAAI,MAAM,UAAU,IAAI;AAAA,EAC3C,QAAQ;AAEN,WAAO,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI;AAAA,EACrD;AACF;","names":[]} | ||
| {"version":3,"sources":["../../../src/utils/index.ts"],"sourcesContent":["import { Network } from \"../types\";\n\n/**\n * Converts a JavaScript number to a plain decimal string, expanding scientific notation\n * via string manipulation rather than parseFloat round-tripping.\n *\n * e.g. 1e-7 → \"0.0000001\", 4.02 → \"4.02\"\n *\n * @param n - The number to convert\n * @returns A plain decimal string representation with no scientific notation\n */\nexport function numberToDecimalString(n: number): string {\n const str = n.toString();\n if (!/[eE]/.test(str)) return str;\n\n const [significand, exponentStr] = str.split(/[eE]/);\n const exp = parseInt(exponentStr, 10);\n const negative = significand.startsWith(\"-\");\n const abs = negative ? significand.slice(1) : significand;\n const [intDigits, fracDigits = \"\"] = abs.split(\".\");\n const allDigits = intDigits + fracDigits;\n const decimalPos = intDigits.length + exp;\n\n let result: string;\n if (decimalPos <= 0) {\n result = \"0.\" + \"0\".repeat(-decimalPos) + allDigits;\n } else if (decimalPos >= allDigits.length) {\n result = allDigits + \"0\".repeat(decimalPos - allDigits.length);\n } else {\n result = allDigits.slice(0, decimalPos) + \".\" + allDigits.slice(decimalPos);\n }\n return (negative ? \"-\" : \"\") + result;\n}\n\n/**\n * Parses a money string into a finite, non-negative decimal number.\n * Accepts plain decimal strings with an optional leading dollar sign.\n *\n * @param money - The money string to parse\n * @returns Decimal number\n */\nexport function parseMoneyString(money: string): number {\n const cleaned = money.replace(/^\\$/, \"\").trim();\n if (!/^-?\\d+(?:\\.\\d+)?$/.test(cleaned) || /[eE]/.test(cleaned)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n const amount = Number(cleaned);\n if (!Number.isFinite(amount) || amount < 0) {\n throw new Error(`Invalid money format: ${money}`);\n }\n return amount;\n}\n\n/**\n * Convert a decimal amount to token smallest units.\n * Accepts only plain decimal strings — scientific notation is not allowed.\n * Throws if the amount is non-zero but too small to represent with the given decimal precision.\n *\n * @param decimalAmount - The decimal amount as a plain string (e.g., \"0.10\")\n * @param decimals - The number of decimals for the token (e.g., 6 for USDC)\n * @returns The amount in smallest units as a string\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n if (/[eE]/.test(decimalAmount)) {\n throw new Error(\n `Invalid amount: ${decimalAmount} — use decimal notation, not scientific notation`,\n );\n }\n if (!/^-?\\d+\\.?\\d*$/.test(decimalAmount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n const [intPart, decPart = \"\"] = decimalAmount.split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n if (tokenAmount === \"0\" && /[1-9]/.test(decimalAmount)) {\n throw new Error(\n `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places`,\n );\n }\n return tokenAmount;\n}\n\n/**\n * Scheme data structure for facilitator storage\n */\nexport interface SchemeData<T> {\n facilitator: T;\n networks: Set<Network>;\n pattern: Network;\n}\n\nconst escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nconst networkPatternToRegExp = (pattern: Network): RegExp => {\n const source = escapeRegExp(pattern).replace(/\\\\\\*/g, \".*\");\n return new RegExp(`^${source}$`);\n};\n\nexport const networkMatchesPattern = (pattern: Network, network: Network): boolean => {\n return networkPatternToRegExp(pattern).test(network);\n};\n\nexport const findSchemesByNetwork = <T>(\n map: Map<string, Map<string, T>>,\n network: Network,\n): Map<string, T> | undefined => {\n // Direct match first\n let implementationsByScheme = map.get(network);\n\n if (!implementationsByScheme) {\n // Try pattern matching for registered network patterns\n for (const [registeredNetworkPattern, implementations] of map.entries()) {\n if (networkMatchesPattern(registeredNetworkPattern as Network, network)) {\n implementationsByScheme = implementations;\n break;\n }\n }\n }\n\n return implementationsByScheme;\n};\n\nexport const findByNetworkAndScheme = <T>(\n map: Map<string, Map<string, T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n return findSchemesByNetwork(map, network)?.get(scheme);\n};\n\n/**\n * Finds a facilitator by scheme and network using pattern matching.\n * Works with new SchemeData storage structure.\n *\n * @param schemeMap - Map of scheme names to SchemeData\n * @param scheme - The scheme to find\n * @param network - The network to match against\n * @returns The facilitator if found, undefined otherwise\n */\nexport const findFacilitatorBySchemeAndNetwork = <T>(\n schemeMap: Map<string, SchemeData<T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n const schemeData = schemeMap.get(scheme);\n if (!schemeData) return undefined;\n\n // Check if network is in the stored networks set\n if (schemeData.networks.has(network)) {\n return schemeData.facilitator;\n }\n\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, network)) {\n return schemeData.facilitator;\n }\n\n return undefined;\n};\n\nexport const Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n/**\n * Encodes a string to base64 format\n *\n * @param data - The string to be encoded to base64\n * @returns The base64 encoded string\n */\nexport function safeBase64Encode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.btoa === \"function\") {\n const bytes = new TextEncoder().encode(data);\n const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join(\"\");\n return globalThis.btoa(binaryString);\n }\n return Buffer.from(data, \"utf8\").toString(\"base64\");\n}\n\n/**\n * Decodes a base64 string back to its original format\n *\n * @param data - The base64 encoded string to be decoded\n * @returns The decoded string in UTF-8 format\n */\nexport function safeBase64Decode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.atob === \"function\") {\n const binaryString = globalThis.atob(data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder(\"utf-8\");\n return decoder.decode(bytes);\n }\n return Buffer.from(data, \"base64\").toString(\"utf-8\");\n}\n\n/**\n * Deep equality comparison for payment requirements\n * Uses a normalized JSON.stringify for consistent comparison\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are deeply equal\n */\nexport function deepEqual(obj1: unknown, obj2: unknown): boolean {\n // Normalize and stringify both objects for comparison\n // This handles nested objects, arrays, and different property orders\n const normalize = (obj: unknown): string => {\n // Handle primitives and null/undefined\n if (obj === null || obj === undefined) return JSON.stringify(obj);\n if (typeof obj !== \"object\") return JSON.stringify(obj);\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return JSON.stringify(\n obj.map(item =>\n typeof item === \"object\" && item !== null ? JSON.parse(normalize(item)) : item,\n ),\n );\n }\n\n // Handle objects - sort keys and recursively normalize values\n const sorted: Record<string, unknown> = {};\n Object.keys(obj as Record<string, unknown>)\n .sort()\n .forEach(key => {\n const value = (obj as Record<string, unknown>)[key];\n sorted[key] =\n typeof value === \"object\" && value !== null ? JSON.parse(normalize(value)) : value;\n });\n return JSON.stringify(sorted);\n };\n\n try {\n return normalize(obj1) === normalize(obj2);\n } catch {\n // Fallback to simple comparison if normalization fails\n return JSON.stringify(obj1) === JSON.stringify(obj2);\n }\n}\n"],"mappings":"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","names":[]} |
@@ -1,3 +0,3 @@ | ||
| import { c as PaymentRequired, ad as x402Client, P as PaymentPayload, S as SettleResponse } from '../x402Client-BSWPuNYe.mjs'; | ||
| export { ai as AfterPaymentCreationHook, ah as BeforePaymentCreationHook, ap as ClientExtension, an as ClientExtensionHooks, ao as ClientTransportExtensionHooks, aj as OnPaymentCreationFailureHook, al as OnPaymentResponseHook, af as PaymentCreatedContext, ae as PaymentCreationContext, ag as PaymentCreationFailureContext, aq as PaymentPolicy, ak as PaymentResponseContext, ar as SchemeRegistration, am as SelectPaymentRequirements, as as x402ClientConfig } from '../x402Client-BSWPuNYe.mjs'; | ||
| import { c as PaymentRequired, ae as x402Client, P as PaymentPayload, S as SettleResponse } from '../x402Client-TQHctrG7.mjs'; | ||
| export { aj as AfterPaymentCreationHook, ai as BeforePaymentCreationHook, aq as ClientExtension, ao as ClientExtensionHooks, ap as ClientTransportExtensionHooks, ak as OnPaymentCreationFailureHook, am as OnPaymentResponseHook, ag as PaymentCreatedContext, af as PaymentCreationContext, ah as PaymentCreationFailureContext, ar as PaymentPolicy, al as PaymentResponseContext, as as SchemeRegistration, an as SelectPaymentRequirements, at as x402ClientConfig } from '../x402Client-TQHctrG7.mjs'; | ||
@@ -98,8 +98,26 @@ /** | ||
| /** | ||
| * Parses a fetch Response into a discriminated `x402PaymentResult` for app-level convenience. | ||
| * Parses HTTP status, headers, and body into an `HTTPResourceResponse`. | ||
| * | ||
| * Decodes the x402 payment header into `header`: the `PAYMENT-RESPONSE` | ||
| * settlement if present, otherwise the `PAYMENT-REQUIRED` declaration on | ||
| * 402 responses (whose `error` field carries the server's failure reason). | ||
| * | ||
| * @param args - Normalized response inputs from any HTTP transport | ||
| * @param args.status - HTTP response status code | ||
| * @param args.getHeader - Callback to read response headers by name | ||
| * @param args.body - Response body payload | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| parsePaymentResult(args: { | ||
| status: number; | ||
| getHeader: (name: string) => string | null | undefined; | ||
| body: unknown; | ||
| }): HTTPResourceResponse; | ||
| /** | ||
| * Parses a fetch Response into an `HTTPResourceResponse` for app-level convenience. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns A discriminated union describing the payment outcome | ||
| * @returns The parsed status, body, and decoded payment header | ||
| */ | ||
| processResponse(response: Response): Promise<x402PaymentResult>; | ||
| processResponse(response: Response): Promise<HTTPResourceResponse>; | ||
| /** | ||
@@ -114,29 +132,20 @@ * Manual HTTP hooks run before extension hooks scoped to the 402 response. | ||
| /** | ||
| * Discriminated union describing the outcome of a payment-enabled request. | ||
| * Parsed result of an HTTP request to an x402 resource. | ||
| */ | ||
| type x402PaymentResult = { | ||
| kind: "success"; | ||
| response: Response; | ||
| body: unknown; | ||
| settleResponse: SettleResponse; | ||
| } | { | ||
| kind: "settle_failed"; | ||
| response: Response; | ||
| body: unknown; | ||
| settleResponse: SettleResponse; | ||
| } | { | ||
| kind: "payment_required"; | ||
| response: Response; | ||
| paymentRequired: PaymentRequired; | ||
| } | { | ||
| kind: "error"; | ||
| response: Response; | ||
| type HTTPResourceResponse = { | ||
| /** HTTP status code. */ | ||
| status: number; | ||
| /** x402 payment outcome. */ | ||
| paymentStatus: HTTPPaymentStatus; | ||
| /** Parsed response body. */ | ||
| body: unknown; | ||
| } | { | ||
| kind: "passthrough"; | ||
| response: Response; | ||
| body: unknown; | ||
| /** | ||
| * Decoded x402 payment header, if present: | ||
| * - SettleResponse (from PAYMENT-RESPONSE / X-PAYMENT-RESPONSE) | ||
| * - PaymentRequired (from PAYMENT-REQUIRED; its `error` carries the server reason) | ||
| */ | ||
| header?: SettleResponse | PaymentRequired; | ||
| }; | ||
| type HTTPPaymentStatus = "settled" | "settle_failed" | "payment_required" | "none"; | ||
| export { type HTTPClientExtensionHooks, type PaymentRequiredContext, type PaymentRequiredHook, x402Client, x402HTTPClient, type x402PaymentResult }; | ||
| export { type HTTPClientExtensionHooks, type HTTPPaymentStatus, type HTTPResourceResponse, type PaymentRequiredContext, type PaymentRequiredHook, x402Client, x402HTTPClient }; |
| import { | ||
| x402HTTPClient | ||
| } from "../chunk-W4OPBTK7.mjs"; | ||
| } from "../chunk-YSNRPV3S.mjs"; | ||
| import "../chunk-FPXAE3OS.mjs"; | ||
@@ -12,3 +12,3 @@ import { | ||
| findSchemesByNetwork | ||
| } from "../chunk-4BKQ2IT7.mjs"; | ||
| } from "../chunk-ABS7D6VX.mjs"; | ||
| import "../chunk-BJTO5JO5.mjs"; | ||
@@ -277,21 +277,41 @@ | ||
| /** | ||
| * Merges server-declared extensions with scheme-provided extensions. | ||
| * Scheme extensions overlay on top of server extensions at each key, | ||
| * preserving server-provided schema while overlaying scheme-provided info. | ||
| * Merges server-declared extensions with client extension echoes. | ||
| * Client extension data may add fields, but server-declared fields remain intact. | ||
| * | ||
| * @param serverExtensions - Extensions declared by the server in the 402 response | ||
| * @param schemeExtensions - Extensions provided by the scheme client (e.g. EIP-2612) | ||
| * @param clientExtensions - Extensions provided by the client or scheme | ||
| * @returns The merged extensions object, or undefined if both inputs are undefined | ||
| */ | ||
| mergeExtensions(serverExtensions, schemeExtensions) { | ||
| if (!schemeExtensions) return serverExtensions; | ||
| if (!serverExtensions) return schemeExtensions; | ||
| mergeExtensions(serverExtensions, clientExtensions) { | ||
| if (!clientExtensions) return serverExtensions; | ||
| if (!serverExtensions) return clientExtensions; | ||
| const merged = { ...serverExtensions }; | ||
| for (const [key, schemeValue] of Object.entries(schemeExtensions)) { | ||
| for (const [key, clientValue] of Object.entries(clientExtensions)) { | ||
| const serverValue = merged[key]; | ||
| if (serverValue && typeof serverValue === "object" && schemeValue && typeof schemeValue === "object") { | ||
| merged[key] = { ...serverValue, ...schemeValue }; | ||
| } else { | ||
| merged[key] = schemeValue; | ||
| if (serverValue === null || typeof serverValue !== "object" || Array.isArray(serverValue) || clientValue === null || typeof clientValue !== "object" || Array.isArray(clientValue)) { | ||
| merged[key] = clientValue; | ||
| continue; | ||
| } | ||
| const serverRecord = serverValue; | ||
| const clientRecord = clientValue; | ||
| const extensionValue = { ...serverRecord }; | ||
| const pending = [{ target: extensionValue, source: clientRecord }]; | ||
| for (const item of pending) { | ||
| for (const [fieldKey, clientFieldValue] of Object.entries(item.source)) { | ||
| const serverFieldValue = item.target[fieldKey]; | ||
| if (serverFieldValue !== null && typeof serverFieldValue === "object" && !Array.isArray(serverFieldValue) && clientFieldValue !== null && typeof clientFieldValue === "object" && !Array.isArray(clientFieldValue)) { | ||
| const nestedValue = { ...serverFieldValue }; | ||
| item.target[fieldKey] = nestedValue; | ||
| pending.push({ | ||
| target: nestedValue, | ||
| source: clientFieldValue | ||
| }); | ||
| continue; | ||
| } | ||
| if (!Object.prototype.hasOwnProperty.call(item.target, fieldKey)) { | ||
| item.target[fieldKey] = clientFieldValue; | ||
| } | ||
| } | ||
| } | ||
| merged[key] = extensionValue; | ||
| } | ||
@@ -319,3 +339,6 @@ return merged; | ||
| } | ||
| return enriched; | ||
| return { | ||
| ...enriched, | ||
| extensions: this.mergeExtensions(paymentRequired.extensions, enriched.extensions) | ||
| }; | ||
| } | ||
@@ -322,0 +345,0 @@ /** |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../../src/client/x402Client.ts"],"sourcesContent":["import { x402Version } from \"..\";\nimport { SchemeNetworkClient } from \"../types/mechanisms\";\nimport { PaymentPayload, PaymentRequirements } from \"../types/payments\";\nimport { Network, PaymentRequired, SettleResponse } from \"../types\";\nimport { findByNetworkAndScheme, findSchemesByNetwork } from \"../utils\";\n\n/**\n * Client Hook Context Interfaces\n */\n\nexport interface PaymentCreationContext {\n paymentRequired: PaymentRequired;\n selectedRequirements: PaymentRequirements;\n}\n\nexport interface PaymentCreatedContext extends PaymentCreationContext {\n paymentPayload: PaymentPayload;\n}\n\nexport interface PaymentCreationFailureContext extends PaymentCreationContext {\n error: Error;\n}\n\n/**\n * Client Hook Type Definitions\n */\n\nexport type BeforePaymentCreationHook = (\n context: PaymentCreationContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type AfterPaymentCreationHook = (context: PaymentCreatedContext) => Promise<void>;\n\nexport type OnPaymentCreationFailureHook = (\n context: PaymentCreationFailureContext,\n) => Promise<void | { recovered: true; payload: PaymentPayload }>;\n\n/**\n * Context provided to payment response hooks after the paid request completes.\n *\n * Discriminate by what's present:\n * - `settleResponse` with `success: true` → settle succeeded\n * - `settleResponse` with `success: false` → settle failed\n * - `paymentRequired` (no `settleResponse`) → verify failed\n * - `error` → transport or parse error\n */\nexport interface PaymentResponseContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n settleResponse?: SettleResponse;\n paymentRequired?: PaymentRequired;\n error?: Error;\n}\n\n/**\n * Hook fired after a paid request completes.\n * Return `{ recovered: true }` to signal the transport should retry with a fresh payload.\n */\nexport type OnPaymentResponseHook = (\n ctx: PaymentResponseContext,\n) => Promise<void | { recovered: true }>;\n\nexport type SelectPaymentRequirements = (x402Version: number, paymentRequirements: PaymentRequirements[]) => PaymentRequirements;\n\ntype ClientHookAdapterHandles = {\n beforePaymentCreation?: BeforePaymentCreationHook;\n afterPaymentCreation?: AfterPaymentCreationHook;\n onPaymentCreationFailure?: OnPaymentCreationFailureHook;\n onPaymentResponse?: OnPaymentResponseHook;\n};\n\ntype ClientHookPhase = keyof ClientHookAdapterHandles;\n\nexport interface ClientExtensionHooks {\n onBeforePaymentCreation?: (\n declaration: unknown,\n context: PaymentCreationContext,\n ) => Promise<void | { abort: true; reason: string }>;\n onAfterPaymentCreation?: (\n declaration: unknown,\n context: PaymentCreatedContext,\n ) => Promise<void>;\n onPaymentCreationFailure?: (\n declaration: unknown,\n context: PaymentCreationFailureContext,\n ) => Promise<void | { recovered: true; payload: PaymentPayload }>;\n onPaymentResponse?: (\n declaration: unknown,\n context: PaymentResponseContext,\n ) => Promise<void | { recovered: true }>;\n}\n\nexport interface ClientTransportExtensionHooks {\n [transport: string]: unknown;\n}\n\n/**\n * Extension that can enrich payment payloads on the client side.\n *\n * Client extensions are invoked after the scheme creates the base payment payload\n * but before it is returned. This allows mechanism-specific logic (e.g., EVM EIP-2612\n * permit signing) to enrich the payload's extensions data.\n */\nexport interface ClientExtension {\n /**\n * Unique key identifying this extension (e.g., \"eip2612GasSponsoring\").\n * Must match the extension key used in PaymentRequired.extensions.\n */\n key: string;\n\n /**\n * Called after payload creation when the extension key is present in\n * paymentRequired.extensions. Allows the extension to enrich the payload\n * with extension-specific data (e.g., signing an EIP-2612 permit).\n *\n * @param paymentPayload - The payment payload to enrich\n * @param paymentRequired - The original PaymentRequired response\n * @returns The enriched payment payload\n */\n enrichPaymentPayload?: (\n paymentPayload: PaymentPayload,\n paymentRequired: PaymentRequired,\n ) => Promise<PaymentPayload>;\n\n hooks?: ClientExtensionHooks;\n transportHooks?: ClientTransportExtensionHooks;\n}\n\n/**\n * A policy function that filters or transforms payment requirements.\n * Policies are applied in order before the selector chooses the final option.\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - Array of payment requirements to filter/transform\n * @returns Filtered array of payment requirements\n */\nexport type PaymentPolicy = (x402Version: number, paymentRequirements: PaymentRequirements[]) => PaymentRequirements[];\n\n\n/**\n * Configuration for registering a payment scheme with a specific network\n */\nexport interface SchemeRegistration {\n /**\n * The network identifier (e.g., 'eip155:8453', 'solana:mainnet')\n */\n network: Network;\n\n /**\n * The scheme client implementation for this network\n */\n client: SchemeNetworkClient;\n\n /**\n * The x402 protocol version to use for this scheme\n *\n * @default 2\n */\n x402Version?: number;\n}\n\n/**\n * Configuration options for the fetch wrapper\n */\nexport interface x402ClientConfig {\n /**\n * Array of scheme registrations defining which payment methods are supported\n */\n schemes: SchemeRegistration[];\n\n /**\n * Policies to apply to the client\n */\n policies?: PaymentPolicy[];\n\n /**\n * Custom payment requirements selector function\n * If not provided, uses the default selector (first available option)\n */\n paymentRequirementsSelector?: SelectPaymentRequirements;\n}\n\n/**\n * Core client for managing x402 payment schemes and creating payment payloads.\n *\n * Handles registration of payment schemes, policy-based filtering of payment requirements,\n * and creation of payment payloads based on server requirements.\n */\nexport class x402Client {\n private readonly paymentRequirementsSelector: SelectPaymentRequirements;\n private readonly registeredClientSchemes: Map<number, Map<string, Map<string, SchemeNetworkClient>>> = new Map();\n private readonly schemeClientHookAdapters: Map<number, Map<string, Map<string, ClientHookAdapterHandles>>> = new Map();\n private readonly policies: PaymentPolicy[] = [];\n private readonly registeredExtensions: Map<string, ClientExtension> = new Map();\n\n private beforePaymentCreationHooks: BeforePaymentCreationHook[] = [];\n private afterPaymentCreationHooks: AfterPaymentCreationHook[] = [];\n private onPaymentCreationFailureHooks: OnPaymentCreationFailureHook[] = [];\n private paymentResponseHooks: OnPaymentResponseHook[] = [];\n\n /**\n * Creates a new x402Client instance.\n *\n * @param paymentRequirementsSelector - Function to select payment requirements from available options\n */\n constructor(paymentRequirementsSelector?: SelectPaymentRequirements) {\n this.paymentRequirementsSelector = paymentRequirementsSelector || ((x402Version, accepts) => accepts[0]);\n }\n\n /**\n * Creates a new x402Client instance from a configuration object.\n *\n * @param config - The client configuration including schemes, policies, and payment requirements selector\n * @returns A configured x402Client instance\n */\n static fromConfig(config: x402ClientConfig): x402Client {\n const client = new x402Client(config.paymentRequirementsSelector);\n config.schemes.forEach(scheme => {\n if (scheme.x402Version === 1) {\n client.registerV1(scheme.network, scheme.client);\n } else {\n client.register(scheme.network, scheme.client);\n }\n });\n config.policies?.forEach(policy => {\n client.registerPolicy(policy);\n });\n return client;\n }\n\n /**\n * Registers a scheme client for the current x402 version.\n *\n * @param network - The network to register the client for\n * @param client - The scheme network client to register\n * @returns The x402Client instance for chaining\n */\n register(network: Network, client: SchemeNetworkClient): x402Client {\n return this._registerScheme(x402Version, network, client);\n }\n\n /**\n * Registers a scheme client for x402 version 1.\n *\n * @param network - The v1 network identifier (e.g., 'base-sepolia', 'solana-devnet')\n * @param client - The scheme network client to register\n * @returns The x402Client instance for chaining\n */\n registerV1(network: string, client: SchemeNetworkClient): x402Client {\n return this._registerScheme(1, network as Network, client);\n }\n\n /**\n * Registers a policy to filter or transform payment requirements.\n *\n * Policies are applied in order after filtering by registered schemes\n * and before the selector chooses the final payment requirement.\n *\n * @param policy - Function to filter/transform payment requirements\n * @returns The x402Client instance for chaining\n *\n * @example\n * ```typescript\n * // Prefer cheaper options\n * client.registerPolicy((version, reqs) =>\n * reqs.filter(r => BigInt(r.value) < BigInt('1000000'))\n * );\n *\n * // Prefer specific networks\n * client.registerPolicy((version, reqs) =>\n * reqs.filter(r => r.network.startsWith('eip155:'))\n * );\n * ```\n */\n registerPolicy(policy: PaymentPolicy): x402Client {\n this.policies.push(policy);\n return this;\n }\n\n /**\n * Registers a client extension that can enrich payment payloads.\n *\n * Extensions are invoked after the scheme creates the base payload and the\n * payload is wrapped with extensions/resource/accepted data. If the extension's\n * key is present in `paymentRequired.extensions`, the extension's\n * `enrichPaymentPayload` hook is called to modify the payload.\n *\n * @param extension - The client extension to register\n * @returns The x402Client instance for chaining\n */\n registerExtension(extension: ClientExtension): x402Client {\n this.registeredExtensions.set(extension.key, extension);\n return this;\n }\n\n /**\n * Get all registered client extensions.\n *\n * @returns Array of registered extensions\n */\n getExtensions(): ClientExtension[] {\n return Array.from(this.registeredExtensions.values());\n }\n\n /**\n * Register a hook to execute before payment payload creation.\n * Can abort creation by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onBeforePaymentCreation(hook: BeforePaymentCreationHook): x402Client {\n this.beforePaymentCreationHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful payment payload creation.\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onAfterPaymentCreation(hook: AfterPaymentCreationHook): x402Client {\n this.afterPaymentCreationHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when payment payload creation fails.\n * Can recover from failure by returning { recovered: true, payload: PaymentPayload }\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onPaymentCreationFailure(hook: OnPaymentCreationFailureHook): x402Client {\n this.onPaymentCreationFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after a paid request completes.\n * Can signal recovery by returning { recovered: true }, causing the transport to retry.\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onPaymentResponse(hook: OnPaymentResponseHook): x402Client {\n this.paymentResponseHooks.push(hook);\n return this;\n }\n\n /**\n * Fires all registered payment response hooks in order.\n * Returns `{ recovered: true }` if any hook signals recovery (first wins).\n *\n * @param ctx - The payment response context\n * @returns Recovery signal or undefined\n */\n async handlePaymentResponse(\n ctx: PaymentResponseContext,\n ): Promise<{ recovered: true } | undefined> {\n for (const hook of this.getLabeledHooks(\n \"onPaymentResponse\",\n ctx.paymentPayload.x402Version,\n ctx.requirements,\n ctx.paymentRequired?.extensions ?? ctx.paymentPayload.extensions,\n )) {\n const result = await hook(ctx);\n if (result && \"recovered\" in result && result.recovered) {\n return { recovered: true };\n }\n }\n return undefined;\n }\n\n /**\n * Creates a payment payload based on a PaymentRequired response.\n *\n * Automatically extracts x402Version, resource, and extensions from the PaymentRequired\n * response and constructs a complete PaymentPayload with the accepted requirements.\n *\n * @param paymentRequired - The PaymentRequired response from the server\n * @returns Promise resolving to the complete payment payload\n */\n async createPaymentPayload(\n paymentRequired: PaymentRequired,\n ): Promise<PaymentPayload> {\n const clientSchemesByNetwork = this.registeredClientSchemes.get(paymentRequired.x402Version);\n if (!clientSchemesByNetwork) {\n throw new Error(`No client registered for x402 version: ${paymentRequired.x402Version}`);\n }\n\n const requirements = this.selectPaymentRequirements(paymentRequired.x402Version, paymentRequired.accepts);\n\n const context: PaymentCreationContext = {\n paymentRequired,\n selectedRequirements: requirements,\n };\n\n for (const hook of this.getLabeledHooks(\n \"beforePaymentCreation\",\n paymentRequired.x402Version,\n requirements,\n paymentRequired.extensions,\n )) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n throw new Error(`Payment creation aborted: ${result.reason}`);\n }\n }\n\n try {\n const schemeNetworkClient = findByNetworkAndScheme(clientSchemesByNetwork, requirements.scheme, requirements.network);\n if (!schemeNetworkClient) {\n throw new Error(`No client registered for scheme: ${requirements.scheme} and network: ${requirements.network}`);\n }\n\n const partialPayload = await schemeNetworkClient.createPaymentPayload(\n paymentRequired.x402Version,\n requirements,\n { extensions: paymentRequired.extensions },\n );\n\n let paymentPayload: PaymentPayload;\n if (partialPayload.x402Version == 1) {\n paymentPayload = partialPayload as PaymentPayload;\n } else {\n // Merge server-declared extensions with any scheme-provided extensions.\n // Scheme extensions overlay on top (e.g., EIP-2612 info enriches server declaration).\n const mergedExtensions = this.mergeExtensions(\n paymentRequired.extensions,\n partialPayload.extensions,\n );\n\n paymentPayload = {\n x402Version: partialPayload.x402Version,\n payload: partialPayload.payload,\n extensions: mergedExtensions,\n resource: paymentRequired.resource,\n accepted: requirements,\n };\n }\n\n // Enrich payload via registered client extensions (for non-scheme extensions)\n paymentPayload = await this.enrichPaymentPayloadWithExtensions(paymentPayload, paymentRequired);\n\n const createdContext: PaymentCreatedContext = {\n ...context,\n paymentPayload,\n };\n\n for (const hook of this.getLabeledHooks(\n \"afterPaymentCreation\",\n paymentRequired.x402Version,\n requirements,\n paymentRequired.extensions,\n )) {\n await hook(createdContext);\n }\n\n return paymentPayload;\n } catch (error) {\n const failureContext: PaymentCreationFailureContext = {\n ...context,\n error: error as Error,\n };\n\n for (const hook of this.getLabeledHooks(\n \"onPaymentCreationFailure\",\n paymentRequired.x402Version,\n requirements,\n paymentRequired.extensions,\n )) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.payload;\n }\n }\n\n throw error;\n }\n }\n\n\n\n /**\n * Merges server-declared extensions with scheme-provided extensions.\n * Scheme extensions overlay on top of server extensions at each key,\n * preserving server-provided schema while overlaying scheme-provided info.\n *\n * @param serverExtensions - Extensions declared by the server in the 402 response\n * @param schemeExtensions - Extensions provided by the scheme client (e.g. EIP-2612)\n * @returns The merged extensions object, or undefined if both inputs are undefined\n */\n private mergeExtensions(\n serverExtensions?: Record<string, unknown>,\n schemeExtensions?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!schemeExtensions) return serverExtensions;\n if (!serverExtensions) return schemeExtensions;\n\n const merged = { ...serverExtensions };\n for (const [key, schemeValue] of Object.entries(schemeExtensions)) {\n const serverValue = merged[key];\n if (\n serverValue &&\n typeof serverValue === \"object\" &&\n schemeValue &&\n typeof schemeValue === \"object\"\n ) {\n // Deep merge: scheme info overlays server info, schema preserved\n merged[key] = { ...serverValue as Record<string, unknown>, ...schemeValue as Record<string, unknown> };\n } else {\n merged[key] = schemeValue;\n }\n }\n return merged;\n }\n\n /**\n * Enriches a payment payload by calling registered extension hooks.\n * For each extension key present in the PaymentRequired response,\n * invokes the corresponding extension's enrichPaymentPayload callback.\n *\n * @param paymentPayload - The payment payload to enrich with extension data\n * @param paymentRequired - The PaymentRequired response containing extension declarations\n * @returns The enriched payment payload with extension data applied\n */\n private async enrichPaymentPayloadWithExtensions(\n paymentPayload: PaymentPayload,\n paymentRequired: PaymentRequired,\n ): Promise<PaymentPayload> {\n if (!paymentRequired.extensions || this.registeredExtensions.size === 0) {\n return paymentPayload;\n }\n\n let enriched = paymentPayload;\n for (const [key, extension] of this.registeredExtensions) {\n if (key in paymentRequired.extensions && extension.enrichPaymentPayload) {\n enriched = await extension.enrichPaymentPayload(enriched, paymentRequired);\n }\n }\n\n return enriched;\n }\n\n /**\n * Selects appropriate payment requirements based on registered clients and policies.\n *\n * Selection process:\n * 1. Filter by registered schemes (network + scheme support)\n * 2. Apply all registered policies in order\n * 3. Use selector to choose final requirement\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - Array of available payment requirements\n * @returns The selected payment requirements\n */\n private selectPaymentRequirements(x402Version: number, paymentRequirements: PaymentRequirements[]): PaymentRequirements {\n const clientSchemesByNetwork = this.registeredClientSchemes.get(x402Version);\n if (!clientSchemesByNetwork) {\n throw new Error(`No client registered for x402 version: ${x402Version}`);\n }\n\n // Step 1: Filter by registered schemes\n const supportedPaymentRequirements = paymentRequirements.filter(requirement => {\n let clientSchemes = findSchemesByNetwork(clientSchemesByNetwork, requirement.network);\n if (!clientSchemes) {\n return false;\n }\n\n return clientSchemes.has(requirement.scheme);\n })\n\n if (supportedPaymentRequirements.length === 0) {\n throw new Error(`No network/scheme registered for x402 version: ${x402Version} which comply with the payment requirements. ${JSON.stringify({\n x402Version,\n paymentRequirements,\n x402Versions: Array.from(this.registeredClientSchemes.keys()),\n networks: Array.from(clientSchemesByNetwork.keys()),\n schemes: Array.from(clientSchemesByNetwork.values()).map(schemes => Array.from(schemes.keys())).flat(),\n })}`);\n }\n\n // Step 2: Apply all policies in order\n let filteredRequirements = supportedPaymentRequirements;\n for (const policy of this.policies) {\n filteredRequirements = policy(x402Version, filteredRequirements);\n\n if (filteredRequirements.length === 0) {\n throw new Error(`All payment requirements were filtered out by policies for x402 version: ${x402Version}`);\n }\n }\n\n // Step 3: Use selector to choose final requirement\n return this.paymentRequirementsSelector(x402Version, filteredRequirements);\n }\n\n /**\n * Internal method to register a scheme client.\n *\n * @param x402Version - The x402 protocol version\n * @param network - The network to register the client for\n * @param client - The scheme network client to register\n * @returns The x402Client instance for chaining\n */\n private _registerScheme(x402Version: number, network: Network, client: SchemeNetworkClient): x402Client {\n if (!this.registeredClientSchemes.has(x402Version)) {\n this.registeredClientSchemes.set(x402Version, new Map());\n }\n const clientSchemesByNetwork = this.registeredClientSchemes.get(x402Version)!;\n if (!clientSchemesByNetwork.has(network)) {\n clientSchemesByNetwork.set(network, new Map());\n }\n\n const clientByScheme = clientSchemesByNetwork.get(network)!;\n clientByScheme.set(client.scheme, client);\n\n if (!this.schemeClientHookAdapters.has(x402Version)) {\n this.schemeClientHookAdapters.set(x402Version, new Map());\n }\n const adaptersByNetwork = this.schemeClientHookAdapters.get(x402Version)!;\n if (!adaptersByNetwork.has(network)) {\n adaptersByNetwork.set(network, new Map());\n }\n\n const adaptersByScheme = adaptersByNetwork.get(network)!;\n const hooks = client.schemeHooks;\n if (!hooks) {\n adaptersByScheme.delete(client.scheme);\n return this;\n }\n\n const handles: ClientHookAdapterHandles = {};\n if (hooks.onBeforePaymentCreation) {\n handles.beforePaymentCreation = hooks.onBeforePaymentCreation;\n }\n if (hooks.onAfterPaymentCreation) {\n handles.afterPaymentCreation = hooks.onAfterPaymentCreation;\n }\n if (hooks.onPaymentCreationFailure) {\n handles.onPaymentCreationFailure = hooks.onPaymentCreationFailure;\n }\n if (hooks.onPaymentResponse) {\n handles.onPaymentResponse = hooks.onPaymentResponse;\n }\n\n if (Object.keys(handles).length > 0) {\n adaptersByScheme.set(client.scheme, handles);\n } else {\n adaptersByScheme.delete(client.scheme);\n }\n\n return this;\n }\n\n /**\n * Returns manual hooks followed by the selected scheme hook and declared extension hooks.\n *\n * @param phase - Hook slot to collect\n * @param x402Version - Protocol version for the selected requirement\n * @param requirements - Selected payment requirement\n * @param declaredExtensions - Extension declarations that scope extension hooks\n * @returns Hooks in invocation order\n */\n private getLabeledHooks<P extends ClientHookPhase>(\n phase: P,\n x402Version: number,\n requirements: PaymentRequirements,\n declaredExtensions?: Record<string, unknown>,\n ): Array<NonNullable<ClientHookAdapterHandles[P]>> {\n let manual: Array<NonNullable<ClientHookAdapterHandles[P]>>;\n switch (phase) {\n case \"beforePaymentCreation\":\n manual = this.beforePaymentCreationHooks as Array<\n NonNullable<ClientHookAdapterHandles[P]>\n >;\n break;\n case \"afterPaymentCreation\":\n manual = this.afterPaymentCreationHooks as Array<\n NonNullable<ClientHookAdapterHandles[P]>\n >;\n break;\n case \"onPaymentCreationFailure\":\n manual = this.onPaymentCreationFailureHooks as Array<\n NonNullable<ClientHookAdapterHandles[P]>\n >;\n break;\n case \"onPaymentResponse\":\n manual = this.paymentResponseHooks as Array<NonNullable<ClientHookAdapterHandles[P]>>;\n break;\n }\n\n const out: Array<NonNullable<ClientHookAdapterHandles[P]>> = [...manual];\n const adaptersByNetwork = this.schemeClientHookAdapters.get(x402Version);\n const schemeAdapter = adaptersByNetwork\n ? findByNetworkAndScheme(adaptersByNetwork, requirements.scheme, requirements.network)\n : undefined;\n const hook = schemeAdapter?.[phase];\n if (hook !== undefined) {\n out.push(hook);\n }\n if (!declaredExtensions) {\n return out;\n }\n\n const extensionHookKey = this.getClientExtensionHookKey(phase);\n for (const [extensionKey, extension] of this.registeredExtensions) {\n if (!(extensionKey in declaredExtensions)) continue;\n\n const extensionHook = extension.hooks?.[extensionHookKey];\n if (!extensionHook) continue;\n\n type HookFn = NonNullable<ClientHookAdapterHandles[P]>;\n type HookContext = Parameters<HookFn>[0];\n out.push((async (ctx: HookContext) => {\n return (\n extensionHook as (\n declaration: unknown,\n context: HookContext,\n ) => ReturnType<HookFn>\n )(declaredExtensions[extensionKey], ctx);\n }) as HookFn);\n }\n return out;\n }\n\n /**\n * Maps internal hook phases to extension hook names.\n *\n * @param phase - Internal hook phase\n * @returns Extension hook key for the phase\n */\n private getClientExtensionHookKey<P extends ClientHookPhase>(\n phase: P,\n ): keyof ClientExtensionHooks {\n switch (phase) {\n case \"beforePaymentCreation\":\n return \"onBeforePaymentCreation\";\n case \"afterPaymentCreation\":\n return \"onAfterPaymentCreation\";\n case \"onPaymentCreationFailure\":\n return \"onPaymentCreationFailure\";\n case \"onPaymentResponse\":\n return \"onPaymentResponse\";\n }\n }\n}\n"],"mappings":"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","names":["x402Version"]} | ||
| {"version":3,"sources":["../../../src/client/x402Client.ts"],"sourcesContent":["import { x402Version } from \"..\";\nimport { SchemeNetworkClient } from \"../types/mechanisms\";\nimport { PaymentPayload, PaymentRequirements } from \"../types/payments\";\nimport { Network, PaymentRequired, SettleResponse } from \"../types\";\nimport { findByNetworkAndScheme, findSchemesByNetwork } from \"../utils\";\n\n/**\n * Client Hook Context Interfaces\n */\n\nexport interface PaymentCreationContext {\n paymentRequired: PaymentRequired;\n selectedRequirements: PaymentRequirements;\n}\n\nexport interface PaymentCreatedContext extends PaymentCreationContext {\n paymentPayload: PaymentPayload;\n}\n\nexport interface PaymentCreationFailureContext extends PaymentCreationContext {\n error: Error;\n}\n\n/**\n * Client Hook Type Definitions\n */\n\nexport type BeforePaymentCreationHook = (\n context: PaymentCreationContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type AfterPaymentCreationHook = (context: PaymentCreatedContext) => Promise<void>;\n\nexport type OnPaymentCreationFailureHook = (\n context: PaymentCreationFailureContext,\n) => Promise<void | { recovered: true; payload: PaymentPayload }>;\n\n/**\n * Context provided to payment response hooks after the paid request completes.\n *\n * Discriminate by what's present:\n * - `settleResponse` with `success: true` → settle succeeded\n * - `settleResponse` with `success: false` → settle failed\n * - `paymentRequired` (no `settleResponse`) → verify failed\n * - `error` → transport or parse error\n */\nexport interface PaymentResponseContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n settleResponse?: SettleResponse;\n paymentRequired?: PaymentRequired;\n error?: Error;\n}\n\n/**\n * Hook fired after a paid request completes.\n * Return `{ recovered: true }` to signal the transport should retry with a fresh payload.\n */\nexport type OnPaymentResponseHook = (\n ctx: PaymentResponseContext,\n) => Promise<void | { recovered: true }>;\n\nexport type SelectPaymentRequirements = (x402Version: number, paymentRequirements: PaymentRequirements[]) => PaymentRequirements;\n\ntype ClientHookAdapterHandles = {\n beforePaymentCreation?: BeforePaymentCreationHook;\n afterPaymentCreation?: AfterPaymentCreationHook;\n onPaymentCreationFailure?: OnPaymentCreationFailureHook;\n onPaymentResponse?: OnPaymentResponseHook;\n};\n\ntype ClientHookPhase = keyof ClientHookAdapterHandles;\n\nexport interface ClientExtensionHooks {\n onBeforePaymentCreation?: (\n declaration: unknown,\n context: PaymentCreationContext,\n ) => Promise<void | { abort: true; reason: string }>;\n onAfterPaymentCreation?: (\n declaration: unknown,\n context: PaymentCreatedContext,\n ) => Promise<void>;\n onPaymentCreationFailure?: (\n declaration: unknown,\n context: PaymentCreationFailureContext,\n ) => Promise<void | { recovered: true; payload: PaymentPayload }>;\n onPaymentResponse?: (\n declaration: unknown,\n context: PaymentResponseContext,\n ) => Promise<void | { recovered: true }>;\n}\n\nexport interface ClientTransportExtensionHooks {\n [transport: string]: unknown;\n}\n\n/**\n * Extension that can enrich payment payloads on the client side.\n *\n * Client extensions are invoked after the scheme creates the base payment payload\n * but before it is returned. This allows mechanism-specific logic (e.g., EVM EIP-2612\n * permit signing) to enrich the payload's extensions data.\n */\nexport interface ClientExtension {\n /**\n * Unique key identifying this extension (e.g., \"eip2612GasSponsoring\").\n * Must match the extension key used in PaymentRequired.extensions.\n */\n key: string;\n\n /**\n * Called after payload creation when the extension key is present in\n * paymentRequired.extensions. Allows the extension to enrich the payload\n * with extension-specific data (e.g., signing an EIP-2612 permit).\n *\n * @param paymentPayload - The payment payload to enrich\n * @param paymentRequired - The original PaymentRequired response\n * @returns The enriched payment payload\n */\n enrichPaymentPayload?: (\n paymentPayload: PaymentPayload,\n paymentRequired: PaymentRequired,\n ) => Promise<PaymentPayload>;\n\n hooks?: ClientExtensionHooks;\n transportHooks?: ClientTransportExtensionHooks;\n}\n\n/**\n * A policy function that filters or transforms payment requirements.\n * Policies are applied in order before the selector chooses the final option.\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - Array of payment requirements to filter/transform\n * @returns Filtered array of payment requirements\n */\nexport type PaymentPolicy = (x402Version: number, paymentRequirements: PaymentRequirements[]) => PaymentRequirements[];\n\n\n/**\n * Configuration for registering a payment scheme with a specific network\n */\nexport interface SchemeRegistration {\n /**\n * The network identifier (e.g., 'eip155:8453', 'solana:mainnet')\n */\n network: Network;\n\n /**\n * The scheme client implementation for this network\n */\n client: SchemeNetworkClient;\n\n /**\n * The x402 protocol version to use for this scheme\n *\n * @default 2\n */\n x402Version?: number;\n}\n\n/**\n * Configuration options for the fetch wrapper\n */\nexport interface x402ClientConfig {\n /**\n * Array of scheme registrations defining which payment methods are supported\n */\n schemes: SchemeRegistration[];\n\n /**\n * Policies to apply to the client\n */\n policies?: PaymentPolicy[];\n\n /**\n * Custom payment requirements selector function\n * If not provided, uses the default selector (first available option)\n */\n paymentRequirementsSelector?: SelectPaymentRequirements;\n}\n\n/**\n * Core client for managing x402 payment schemes and creating payment payloads.\n *\n * Handles registration of payment schemes, policy-based filtering of payment requirements,\n * and creation of payment payloads based on server requirements.\n */\nexport class x402Client {\n private readonly paymentRequirementsSelector: SelectPaymentRequirements;\n private readonly registeredClientSchemes: Map<number, Map<string, Map<string, SchemeNetworkClient>>> = new Map();\n private readonly schemeClientHookAdapters: Map<number, Map<string, Map<string, ClientHookAdapterHandles>>> = new Map();\n private readonly policies: PaymentPolicy[] = [];\n private readonly registeredExtensions: Map<string, ClientExtension> = new Map();\n\n private beforePaymentCreationHooks: BeforePaymentCreationHook[] = [];\n private afterPaymentCreationHooks: AfterPaymentCreationHook[] = [];\n private onPaymentCreationFailureHooks: OnPaymentCreationFailureHook[] = [];\n private paymentResponseHooks: OnPaymentResponseHook[] = [];\n\n /**\n * Creates a new x402Client instance.\n *\n * @param paymentRequirementsSelector - Function to select payment requirements from available options\n */\n constructor(paymentRequirementsSelector?: SelectPaymentRequirements) {\n this.paymentRequirementsSelector = paymentRequirementsSelector || ((x402Version, accepts) => accepts[0]);\n }\n\n /**\n * Creates a new x402Client instance from a configuration object.\n *\n * @param config - The client configuration including schemes, policies, and payment requirements selector\n * @returns A configured x402Client instance\n */\n static fromConfig(config: x402ClientConfig): x402Client {\n const client = new x402Client(config.paymentRequirementsSelector);\n config.schemes.forEach(scheme => {\n if (scheme.x402Version === 1) {\n client.registerV1(scheme.network, scheme.client);\n } else {\n client.register(scheme.network, scheme.client);\n }\n });\n config.policies?.forEach(policy => {\n client.registerPolicy(policy);\n });\n return client;\n }\n\n /**\n * Registers a scheme client for the current x402 version.\n *\n * @param network - The network to register the client for\n * @param client - The scheme network client to register\n * @returns The x402Client instance for chaining\n */\n register(network: Network, client: SchemeNetworkClient): x402Client {\n return this._registerScheme(x402Version, network, client);\n }\n\n /**\n * Registers a scheme client for x402 version 1.\n *\n * @param network - The v1 network identifier (e.g., 'base-sepolia', 'solana-devnet')\n * @param client - The scheme network client to register\n * @returns The x402Client instance for chaining\n */\n registerV1(network: string, client: SchemeNetworkClient): x402Client {\n return this._registerScheme(1, network as Network, client);\n }\n\n /**\n * Registers a policy to filter or transform payment requirements.\n *\n * Policies are applied in order after filtering by registered schemes\n * and before the selector chooses the final payment requirement.\n *\n * @param policy - Function to filter/transform payment requirements\n * @returns The x402Client instance for chaining\n *\n * @example\n * ```typescript\n * // Prefer cheaper options\n * client.registerPolicy((version, reqs) =>\n * reqs.filter(r => BigInt(r.value) < BigInt('1000000'))\n * );\n *\n * // Prefer specific networks\n * client.registerPolicy((version, reqs) =>\n * reqs.filter(r => r.network.startsWith('eip155:'))\n * );\n * ```\n */\n registerPolicy(policy: PaymentPolicy): x402Client {\n this.policies.push(policy);\n return this;\n }\n\n /**\n * Registers a client extension that can enrich payment payloads.\n *\n * Extensions are invoked after the scheme creates the base payload and the\n * payload is wrapped with extensions/resource/accepted data. If the extension's\n * key is present in `paymentRequired.extensions`, the extension's\n * `enrichPaymentPayload` hook is called to modify the payload.\n *\n * @param extension - The client extension to register\n * @returns The x402Client instance for chaining\n */\n registerExtension(extension: ClientExtension): x402Client {\n this.registeredExtensions.set(extension.key, extension);\n return this;\n }\n\n /**\n * Get all registered client extensions.\n *\n * @returns Array of registered extensions\n */\n getExtensions(): ClientExtension[] {\n return Array.from(this.registeredExtensions.values());\n }\n\n /**\n * Register a hook to execute before payment payload creation.\n * Can abort creation by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onBeforePaymentCreation(hook: BeforePaymentCreationHook): x402Client {\n this.beforePaymentCreationHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful payment payload creation.\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onAfterPaymentCreation(hook: AfterPaymentCreationHook): x402Client {\n this.afterPaymentCreationHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when payment payload creation fails.\n * Can recover from failure by returning { recovered: true, payload: PaymentPayload }\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onPaymentCreationFailure(hook: OnPaymentCreationFailureHook): x402Client {\n this.onPaymentCreationFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after a paid request completes.\n * Can signal recovery by returning { recovered: true }, causing the transport to retry.\n *\n * @param hook - The hook function to register\n * @returns The x402Client instance for chaining\n */\n onPaymentResponse(hook: OnPaymentResponseHook): x402Client {\n this.paymentResponseHooks.push(hook);\n return this;\n }\n\n /**\n * Fires all registered payment response hooks in order.\n * Returns `{ recovered: true }` if any hook signals recovery (first wins).\n *\n * @param ctx - The payment response context\n * @returns Recovery signal or undefined\n */\n async handlePaymentResponse(\n ctx: PaymentResponseContext,\n ): Promise<{ recovered: true } | undefined> {\n for (const hook of this.getLabeledHooks(\n \"onPaymentResponse\",\n ctx.paymentPayload.x402Version,\n ctx.requirements,\n ctx.paymentRequired?.extensions ?? ctx.paymentPayload.extensions,\n )) {\n const result = await hook(ctx);\n if (result && \"recovered\" in result && result.recovered) {\n return { recovered: true };\n }\n }\n return undefined;\n }\n\n /**\n * Creates a payment payload based on a PaymentRequired response.\n *\n * Automatically extracts x402Version, resource, and extensions from the PaymentRequired\n * response and constructs a complete PaymentPayload with the accepted requirements.\n *\n * @param paymentRequired - The PaymentRequired response from the server\n * @returns Promise resolving to the complete payment payload\n */\n async createPaymentPayload(\n paymentRequired: PaymentRequired,\n ): Promise<PaymentPayload> {\n const clientSchemesByNetwork = this.registeredClientSchemes.get(paymentRequired.x402Version);\n if (!clientSchemesByNetwork) {\n throw new Error(`No client registered for x402 version: ${paymentRequired.x402Version}`);\n }\n\n const requirements = this.selectPaymentRequirements(paymentRequired.x402Version, paymentRequired.accepts);\n\n const context: PaymentCreationContext = {\n paymentRequired,\n selectedRequirements: requirements,\n };\n\n for (const hook of this.getLabeledHooks(\n \"beforePaymentCreation\",\n paymentRequired.x402Version,\n requirements,\n paymentRequired.extensions,\n )) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n throw new Error(`Payment creation aborted: ${result.reason}`);\n }\n }\n\n try {\n const schemeNetworkClient = findByNetworkAndScheme(clientSchemesByNetwork, requirements.scheme, requirements.network);\n if (!schemeNetworkClient) {\n throw new Error(`No client registered for scheme: ${requirements.scheme} and network: ${requirements.network}`);\n }\n\n const partialPayload = await schemeNetworkClient.createPaymentPayload(\n paymentRequired.x402Version,\n requirements,\n { extensions: paymentRequired.extensions },\n );\n\n let paymentPayload: PaymentPayload;\n if (partialPayload.x402Version == 1) {\n paymentPayload = partialPayload as PaymentPayload;\n } else {\n // Merge server-declared extensions with any scheme-provided extensions.\n // Scheme extensions overlay on top (e.g., EIP-2612 info enriches server declaration).\n const mergedExtensions = this.mergeExtensions(\n paymentRequired.extensions,\n partialPayload.extensions,\n );\n\n paymentPayload = {\n x402Version: partialPayload.x402Version,\n payload: partialPayload.payload,\n extensions: mergedExtensions,\n resource: paymentRequired.resource,\n accepted: requirements,\n };\n }\n\n // Enrich payload via registered client extensions (for non-scheme extensions)\n paymentPayload = await this.enrichPaymentPayloadWithExtensions(paymentPayload, paymentRequired);\n\n const createdContext: PaymentCreatedContext = {\n ...context,\n paymentPayload,\n };\n\n for (const hook of this.getLabeledHooks(\n \"afterPaymentCreation\",\n paymentRequired.x402Version,\n requirements,\n paymentRequired.extensions,\n )) {\n await hook(createdContext);\n }\n\n return paymentPayload;\n } catch (error) {\n const failureContext: PaymentCreationFailureContext = {\n ...context,\n error: error as Error,\n };\n\n for (const hook of this.getLabeledHooks(\n \"onPaymentCreationFailure\",\n paymentRequired.x402Version,\n requirements,\n paymentRequired.extensions,\n )) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.payload;\n }\n }\n\n throw error;\n }\n }\n\n\n\n /**\n * Merges server-declared extensions with client extension echoes.\n * Client extension data may add fields, but server-declared fields remain intact.\n *\n * @param serverExtensions - Extensions declared by the server in the 402 response\n * @param clientExtensions - Extensions provided by the client or scheme\n * @returns The merged extensions object, or undefined if both inputs are undefined\n */\n private mergeExtensions(\n serverExtensions?: Record<string, unknown>,\n clientExtensions?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!clientExtensions) return serverExtensions;\n if (!serverExtensions) return clientExtensions;\n\n const merged = { ...serverExtensions };\n for (const [key, clientValue] of Object.entries(clientExtensions)) {\n const serverValue = merged[key];\n if (\n serverValue === null ||\n typeof serverValue !== \"object\" ||\n Array.isArray(serverValue) ||\n clientValue === null ||\n typeof clientValue !== \"object\" ||\n Array.isArray(clientValue)\n ) {\n merged[key] = clientValue;\n continue;\n }\n\n const serverRecord = serverValue as Record<string, unknown>;\n const clientRecord = clientValue as Record<string, unknown>;\n const extensionValue = { ...serverRecord };\n const pending = [{ target: extensionValue, source: clientRecord }];\n for (const item of pending) {\n for (const [fieldKey, clientFieldValue] of Object.entries(item.source)) {\n const serverFieldValue = item.target[fieldKey];\n if (\n serverFieldValue !== null &&\n typeof serverFieldValue === \"object\" &&\n !Array.isArray(serverFieldValue) &&\n clientFieldValue !== null &&\n typeof clientFieldValue === \"object\" &&\n !Array.isArray(clientFieldValue)\n ) {\n const nestedValue = { ...(serverFieldValue as Record<string, unknown>) };\n item.target[fieldKey] = nestedValue;\n pending.push({\n target: nestedValue,\n source: clientFieldValue as Record<string, unknown>,\n });\n continue;\n }\n\n if (!Object.prototype.hasOwnProperty.call(item.target, fieldKey)) {\n item.target[fieldKey] = clientFieldValue;\n }\n }\n }\n\n merged[key] = extensionValue;\n }\n return merged;\n }\n\n /**\n * Enriches a payment payload by calling registered extension hooks.\n * For each extension key present in the PaymentRequired response,\n * invokes the corresponding extension's enrichPaymentPayload callback.\n *\n * @param paymentPayload - The payment payload to enrich with extension data\n * @param paymentRequired - The PaymentRequired response containing extension declarations\n * @returns The enriched payment payload with extension data applied\n */\n private async enrichPaymentPayloadWithExtensions(\n paymentPayload: PaymentPayload,\n paymentRequired: PaymentRequired,\n ): Promise<PaymentPayload> {\n if (!paymentRequired.extensions || this.registeredExtensions.size === 0) {\n return paymentPayload;\n }\n\n let enriched = paymentPayload;\n for (const [key, extension] of this.registeredExtensions) {\n if (key in paymentRequired.extensions && extension.enrichPaymentPayload) {\n enriched = await extension.enrichPaymentPayload(enriched, paymentRequired);\n }\n }\n\n return {\n ...enriched,\n extensions: this.mergeExtensions(paymentRequired.extensions, enriched.extensions),\n };\n }\n\n /**\n * Selects appropriate payment requirements based on registered clients and policies.\n *\n * Selection process:\n * 1. Filter by registered schemes (network + scheme support)\n * 2. Apply all registered policies in order\n * 3. Use selector to choose final requirement\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - Array of available payment requirements\n * @returns The selected payment requirements\n */\n private selectPaymentRequirements(x402Version: number, paymentRequirements: PaymentRequirements[]): PaymentRequirements {\n const clientSchemesByNetwork = this.registeredClientSchemes.get(x402Version);\n if (!clientSchemesByNetwork) {\n throw new Error(`No client registered for x402 version: ${x402Version}`);\n }\n\n // Step 1: Filter by registered schemes\n const supportedPaymentRequirements = paymentRequirements.filter(requirement => {\n let clientSchemes = findSchemesByNetwork(clientSchemesByNetwork, requirement.network);\n if (!clientSchemes) {\n return false;\n }\n\n return clientSchemes.has(requirement.scheme);\n })\n\n if (supportedPaymentRequirements.length === 0) {\n throw new Error(`No network/scheme registered for x402 version: ${x402Version} which comply with the payment requirements. ${JSON.stringify({\n x402Version,\n paymentRequirements,\n x402Versions: Array.from(this.registeredClientSchemes.keys()),\n networks: Array.from(clientSchemesByNetwork.keys()),\n schemes: Array.from(clientSchemesByNetwork.values()).map(schemes => Array.from(schemes.keys())).flat(),\n })}`);\n }\n\n // Step 2: Apply all policies in order\n let filteredRequirements = supportedPaymentRequirements;\n for (const policy of this.policies) {\n filteredRequirements = policy(x402Version, filteredRequirements);\n\n if (filteredRequirements.length === 0) {\n throw new Error(`All payment requirements were filtered out by policies for x402 version: ${x402Version}`);\n }\n }\n\n // Step 3: Use selector to choose final requirement\n return this.paymentRequirementsSelector(x402Version, filteredRequirements);\n }\n\n /**\n * Internal method to register a scheme client.\n *\n * @param x402Version - The x402 protocol version\n * @param network - The network to register the client for\n * @param client - The scheme network client to register\n * @returns The x402Client instance for chaining\n */\n private _registerScheme(x402Version: number, network: Network, client: SchemeNetworkClient): x402Client {\n if (!this.registeredClientSchemes.has(x402Version)) {\n this.registeredClientSchemes.set(x402Version, new Map());\n }\n const clientSchemesByNetwork = this.registeredClientSchemes.get(x402Version)!;\n if (!clientSchemesByNetwork.has(network)) {\n clientSchemesByNetwork.set(network, new Map());\n }\n\n const clientByScheme = clientSchemesByNetwork.get(network)!;\n clientByScheme.set(client.scheme, client);\n\n if (!this.schemeClientHookAdapters.has(x402Version)) {\n this.schemeClientHookAdapters.set(x402Version, new Map());\n }\n const adaptersByNetwork = this.schemeClientHookAdapters.get(x402Version)!;\n if (!adaptersByNetwork.has(network)) {\n adaptersByNetwork.set(network, new Map());\n }\n\n const adaptersByScheme = adaptersByNetwork.get(network)!;\n const hooks = client.schemeHooks;\n if (!hooks) {\n adaptersByScheme.delete(client.scheme);\n return this;\n }\n\n const handles: ClientHookAdapterHandles = {};\n if (hooks.onBeforePaymentCreation) {\n handles.beforePaymentCreation = hooks.onBeforePaymentCreation;\n }\n if (hooks.onAfterPaymentCreation) {\n handles.afterPaymentCreation = hooks.onAfterPaymentCreation;\n }\n if (hooks.onPaymentCreationFailure) {\n handles.onPaymentCreationFailure = hooks.onPaymentCreationFailure;\n }\n if (hooks.onPaymentResponse) {\n handles.onPaymentResponse = hooks.onPaymentResponse;\n }\n\n if (Object.keys(handles).length > 0) {\n adaptersByScheme.set(client.scheme, handles);\n } else {\n adaptersByScheme.delete(client.scheme);\n }\n\n return this;\n }\n\n /**\n * Returns manual hooks followed by the selected scheme hook and declared extension hooks.\n *\n * @param phase - Hook slot to collect\n * @param x402Version - Protocol version for the selected requirement\n * @param requirements - Selected payment requirement\n * @param declaredExtensions - Extension declarations that scope extension hooks\n * @returns Hooks in invocation order\n */\n private getLabeledHooks<P extends ClientHookPhase>(\n phase: P,\n x402Version: number,\n requirements: PaymentRequirements,\n declaredExtensions?: Record<string, unknown>,\n ): Array<NonNullable<ClientHookAdapterHandles[P]>> {\n let manual: Array<NonNullable<ClientHookAdapterHandles[P]>>;\n switch (phase) {\n case \"beforePaymentCreation\":\n manual = this.beforePaymentCreationHooks as Array<\n NonNullable<ClientHookAdapterHandles[P]>\n >;\n break;\n case \"afterPaymentCreation\":\n manual = this.afterPaymentCreationHooks as Array<\n NonNullable<ClientHookAdapterHandles[P]>\n >;\n break;\n case \"onPaymentCreationFailure\":\n manual = this.onPaymentCreationFailureHooks as Array<\n NonNullable<ClientHookAdapterHandles[P]>\n >;\n break;\n case \"onPaymentResponse\":\n manual = this.paymentResponseHooks as Array<NonNullable<ClientHookAdapterHandles[P]>>;\n break;\n }\n\n const out: Array<NonNullable<ClientHookAdapterHandles[P]>> = [...manual];\n const adaptersByNetwork = this.schemeClientHookAdapters.get(x402Version);\n const schemeAdapter = adaptersByNetwork\n ? findByNetworkAndScheme(adaptersByNetwork, requirements.scheme, requirements.network)\n : undefined;\n const hook = schemeAdapter?.[phase];\n if (hook !== undefined) {\n out.push(hook);\n }\n if (!declaredExtensions) {\n return out;\n }\n\n const extensionHookKey = this.getClientExtensionHookKey(phase);\n for (const [extensionKey, extension] of this.registeredExtensions) {\n if (!(extensionKey in declaredExtensions)) continue;\n\n const extensionHook = extension.hooks?.[extensionHookKey];\n if (!extensionHook) continue;\n\n type HookFn = NonNullable<ClientHookAdapterHandles[P]>;\n type HookContext = Parameters<HookFn>[0];\n out.push((async (ctx: HookContext) => {\n return (\n extensionHook as (\n declaration: unknown,\n context: HookContext,\n ) => ReturnType<HookFn>\n )(declaredExtensions[extensionKey], ctx);\n }) as HookFn);\n }\n return out;\n }\n\n /**\n * Maps internal hook phases to extension hook names.\n *\n * @param phase - Internal hook phase\n * @returns Extension hook key for the phase\n */\n private getClientExtensionHookKey<P extends ClientHookPhase>(\n phase: P,\n ): keyof ClientExtensionHooks {\n switch (phase) {\n case \"beforePaymentCreation\":\n return \"onBeforePaymentCreation\";\n case \"afterPaymentCreation\":\n return \"onAfterPaymentCreation\";\n case \"onPaymentCreationFailure\":\n return \"onPaymentCreationFailure\";\n case \"onPaymentResponse\":\n return \"onPaymentResponse\";\n }\n }\n}\n"],"mappings":"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","names":["x402Version"]} |
@@ -1,2 +0,2 @@ | ||
| import { P as PaymentPayload, a as PaymentRequirements, V as VerifyResponse, S as SettleResponse, N as Network, b as SchemeNetworkFacilitator, F as FacilitatorExtension } from '../x402Client-BSWPuNYe.mjs'; | ||
| import { P as PaymentPayload, a as PaymentRequirements, V as VerifyResponse, S as SettleResponse, N as Network, b as SchemeNetworkFacilitator, F as FacilitatorExtension } from '../x402Client-TQHctrG7.mjs'; | ||
@@ -3,0 +3,0 @@ /** |
| import { | ||
| x402Version | ||
| } from "../chunk-VE37GDG2.mjs"; | ||
| import { | ||
| networkMatchesPattern | ||
| } from "../chunk-ABS7D6VX.mjs"; | ||
| import "../chunk-BJTO5JO5.mjs"; | ||
@@ -210,4 +213,3 @@ | ||
| } | ||
| const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); | ||
| if (patternRegex.test(paymentRequirements.network)) { | ||
| if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) { | ||
| schemeNetworkFacilitator = schemeData.facilitator; | ||
@@ -303,4 +305,3 @@ break; | ||
| } | ||
| const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); | ||
| if (patternRegex.test(paymentRequirements.network)) { | ||
| if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) { | ||
| schemeNetworkFacilitator = schemeData.facilitator; | ||
@@ -307,0 +308,0 @@ break; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../../src/facilitator/x402Facilitator.ts"],"sourcesContent":["import { x402Version } from \"..\";\nimport { SettleResponse, VerifyResponse } from \"../types/facilitator\";\nimport { FacilitatorExtension } from \"../types/extensions\";\nimport { SchemeNetworkFacilitator, FacilitatorContext } from \"../types/mechanisms\";\nimport { PaymentPayload, PaymentRequirements } from \"../types/payments\";\nimport { Network } from \"../types\";\nimport { type SchemeData } from \"../utils\";\n\n/**\n * Facilitator Hook Context Interfaces\n */\n\nexport interface FacilitatorVerifyContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorVerifyResultContext extends FacilitatorVerifyContext {\n result: VerifyResponse;\n}\n\nexport interface FacilitatorVerifyFailureContext extends FacilitatorVerifyContext {\n error: Error;\n}\n\nexport interface FacilitatorSettleContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorSettleResultContext extends FacilitatorSettleContext {\n result: SettleResponse;\n}\n\nexport interface FacilitatorSettleFailureContext extends FacilitatorSettleContext {\n error: Error;\n}\n\n/**\n * Facilitator Hook Type Definitions\n */\n\nexport type FacilitatorBeforeVerifyHook = (\n context: FacilitatorVerifyContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterVerifyHook = (context: FacilitatorVerifyResultContext) => Promise<void>;\n\nexport type FacilitatorOnVerifyFailureHook = (\n context: FacilitatorVerifyFailureContext,\n) => Promise<void | { recovered: true; result: VerifyResponse }>;\n\nexport type FacilitatorBeforeSettleHook = (\n context: FacilitatorSettleContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterSettleHook = (context: FacilitatorSettleResultContext) => Promise<void>;\n\nexport type FacilitatorOnSettleFailureHook = (\n context: FacilitatorSettleFailureContext,\n) => Promise<void | { recovered: true; result: SettleResponse }>;\n\n/**\n * Facilitator client for the x402 payment protocol.\n * Manages payment scheme registration, verification, and settlement.\n */\nexport class x402Facilitator {\n private readonly registeredFacilitatorSchemes: Map<\n number,\n SchemeData<SchemeNetworkFacilitator>[] // Array to support multiple facilitators per version\n > = new Map();\n private readonly extensions: Map<string, FacilitatorExtension> = new Map();\n\n private beforeVerifyHooks: FacilitatorBeforeVerifyHook[] = [];\n private afterVerifyHooks: FacilitatorAfterVerifyHook[] = [];\n private onVerifyFailureHooks: FacilitatorOnVerifyFailureHook[] = [];\n private beforeSettleHooks: FacilitatorBeforeSettleHook[] = [];\n private afterSettleHooks: FacilitatorAfterSettleHook[] = [];\n private onSettleFailureHooks: FacilitatorOnSettleFailureHook[] = [];\n\n /**\n * Registers a scheme facilitator for the current x402 version.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n register(networks: Network | Network[], facilitator: SchemeNetworkFacilitator): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(x402Version, networksArray, facilitator);\n }\n\n /**\n * Registers a scheme facilitator for x402 version 1.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n registerV1(\n networks: Network | Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(1, networksArray, facilitator);\n }\n\n /**\n * Registers a protocol extension.\n *\n * @param extension - The extension object to register\n * @returns The x402Facilitator instance for chaining\n */\n registerExtension(extension: FacilitatorExtension): x402Facilitator {\n this.extensions.set(extension.key, extension);\n return this;\n }\n\n /**\n * Gets the list of registered extension keys.\n *\n * @returns Array of extension key strings\n */\n getExtensions(): string[] {\n return Array.from(this.extensions.keys());\n }\n\n /**\n * Gets a registered extension by key.\n *\n * @param key - The extension key to look up\n * @returns The extension object, or undefined if not registered\n */\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(key: string): T | undefined {\n return this.extensions.get(key) as T | undefined;\n }\n\n /**\n * Register a hook to execute before facilitator payment verification.\n * Can abort verification by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeVerify(hook: FacilitatorBeforeVerifyHook): x402Facilitator {\n this.beforeVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment verification (isValid: true).\n * This hook is NOT called when verification fails (isValid: false) - use onVerifyFailure for that.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterVerify(hook: FacilitatorAfterVerifyHook): x402Facilitator {\n this.afterVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment verification fails.\n * Called when: verification returns isValid: false, or an exception is thrown during verification.\n * Can recover from failure by returning { recovered: true, result: VerifyResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onVerifyFailure(hook: FacilitatorOnVerifyFailureHook): x402Facilitator {\n this.onVerifyFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute before facilitator payment settlement.\n * Can abort settlement by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeSettle(hook: FacilitatorBeforeSettleHook): x402Facilitator {\n this.beforeSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment settlement.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterSettle(hook: FacilitatorAfterSettleHook): x402Facilitator {\n this.afterSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment settlement fails.\n * Can recover from failure by returning { recovered: true, result: SettleResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onSettleFailure(hook: FacilitatorOnSettleFailureHook): x402Facilitator {\n this.onSettleFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Gets supported payment kinds, extensions, and signers.\n * Uses networks registered during register() calls - no parameters needed.\n * Returns flat array format for backward compatibility with V1 clients.\n *\n * @returns Supported response with kinds as array (with version in each element), extensions, and signers\n */\n getSupported(): {\n kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }>;\n extensions: string[];\n signers: Record<string, string[]>;\n } {\n const kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }> = [];\n const signersByFamily: Record<string, Set<string>> = {};\n\n // Iterate over registered scheme data (array supports multiple facilitators per version)\n for (const [version, schemeDataArray] of this.registeredFacilitatorSchemes) {\n for (const schemeData of schemeDataArray) {\n const { facilitator, networks } = schemeData;\n const scheme = facilitator.scheme;\n\n // Iterate over stored concrete networks\n for (const network of networks) {\n const extra = facilitator.getExtra(network);\n kinds.push({\n x402Version: version,\n scheme,\n network,\n ...(extra && { extra }),\n });\n\n // Collect signers by CAIP family for this network\n const family = facilitator.caipFamily;\n if (!signersByFamily[family]) {\n signersByFamily[family] = new Set();\n }\n facilitator.getSigners(network).forEach(signer => signersByFamily[family].add(signer));\n }\n }\n }\n\n // Convert signer sets to arrays\n const signers: Record<string, string[]> = {};\n for (const [family, signerSet] of Object.entries(signersByFamily)) {\n signers[family] = Array.from(signerSet);\n }\n\n return {\n kinds,\n extensions: this.getExtensions(),\n signers,\n };\n }\n\n /**\n * Verifies a payment payload against requirements.\n *\n * @param paymentPayload - The payment payload to verify\n * @param paymentRequirements - The payment requirements to verify against\n * @returns Promise resolving to the verification response\n */\n async verify(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const context: FacilitatorVerifyContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeVerify hooks\n for (const hook of this.beforeVerifyHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n return {\n isValid: false,\n invalidReason: result.reason,\n };\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n const patternRegex = new RegExp(\"^\" + schemeData.pattern.replace(\"*\", \".*\") + \"$\");\n if (patternRegex.test(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const verifyResult = await schemeNetworkFacilitator.verify(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Check if verification failed (isValid: false)\n if (!verifyResult.isValid) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: new Error(verifyResult.invalidReason || \"Verification failed\"),\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n // If recovered, execute afterVerify hooks with recovered result\n const recoveredContext: FacilitatorVerifyResultContext = {\n ...context,\n result: result.result,\n };\n for (const hook of this.afterVerifyHooks) {\n await hook(recoveredContext);\n }\n return result.result;\n }\n }\n\n return verifyResult;\n }\n\n // Execute afterVerify hooks only for successful verification\n const resultContext: FacilitatorVerifyResultContext = {\n ...context,\n result: verifyResult,\n };\n\n for (const hook of this.afterVerifyHooks) {\n await hook(resultContext);\n }\n\n return verifyResult;\n } catch (error) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Settles a payment based on the payload and requirements.\n *\n * @param paymentPayload - The payment payload to settle\n * @param paymentRequirements - The payment requirements for settlement\n * @returns Promise resolving to the settlement response\n */\n async settle(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const context: FacilitatorSettleContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeSettle hooks\n for (const hook of this.beforeSettleHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n throw new Error(`Settlement aborted: ${result.reason}`);\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n const patternRegex = new RegExp(\"^\" + schemeData.pattern.replace(\"*\", \".*\") + \"$\");\n if (patternRegex.test(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const settleResult = await schemeNetworkFacilitator.settle(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Execute afterSettle hooks\n const resultContext: FacilitatorSettleResultContext = {\n ...context,\n result: settleResult,\n };\n\n for (const hook of this.afterSettleHooks) {\n await hook(resultContext);\n }\n\n return settleResult;\n } catch (error) {\n const failureContext: FacilitatorSettleFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onSettleFailure hooks\n for (const hook of this.onSettleFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Builds a FacilitatorContext from the registered extensions map.\n * Passed to mechanism verify/settle so they can access extension capabilities.\n *\n * @returns A FacilitatorContext backed by this facilitator's registered extensions\n */\n private buildFacilitatorContext(): FacilitatorContext {\n const extensionsMap = this.extensions;\n return {\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(\n key: string,\n ): T | undefined {\n return extensionsMap.get(key) as T | undefined;\n },\n };\n }\n\n /**\n * Internal method to register a scheme facilitator.\n *\n * @param x402Version - The x402 protocol version\n * @param networks - Array of concrete networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n private _registerScheme(\n x402Version: number,\n networks: Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n if (!this.registeredFacilitatorSchemes.has(x402Version)) {\n this.registeredFacilitatorSchemes.set(x402Version, []);\n }\n const schemeDataArray = this.registeredFacilitatorSchemes.get(x402Version)!;\n\n // Add new scheme data (supports multiple facilitators with same scheme name)\n schemeDataArray.push({\n facilitator,\n networks: new Set(networks),\n pattern: this.derivePattern(networks),\n });\n\n return this;\n }\n\n /**\n * Derives a wildcard pattern from an array of networks.\n * If all networks share the same namespace, returns wildcard pattern.\n * Otherwise returns the first network for exact matching.\n *\n * @param networks - Array of networks\n * @returns Derived pattern for matching\n */\n private derivePattern(networks: Network[]): Network {\n if (networks.length === 0) return \"\" as Network;\n if (networks.length === 1) return networks[0];\n\n // Extract namespaces (e.g., \"eip155\" from \"eip155:84532\")\n const namespaces = networks.map(n => n.split(\":\")[0]);\n const uniqueNamespaces = new Set(namespaces);\n\n // If all same namespace, use wildcard\n if (uniqueNamespaces.size === 1) {\n return `${namespaces[0]}:*` as Network;\n }\n\n // Mixed namespaces - use first network for exact matching\n return networks[0];\n }\n}\n"],"mappings":"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","names":["hook","x402Version"]} | ||
| {"version":3,"sources":["../../../src/facilitator/x402Facilitator.ts"],"sourcesContent":["import { x402Version } from \"..\";\nimport { SettleResponse, VerifyResponse } from \"../types/facilitator\";\nimport { FacilitatorExtension } from \"../types/extensions\";\nimport { SchemeNetworkFacilitator, FacilitatorContext } from \"../types/mechanisms\";\nimport { PaymentPayload, PaymentRequirements } from \"../types/payments\";\nimport { Network } from \"../types\";\nimport { networkMatchesPattern, type SchemeData } from \"../utils\";\n\n/**\n * Facilitator Hook Context Interfaces\n */\n\nexport interface FacilitatorVerifyContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorVerifyResultContext extends FacilitatorVerifyContext {\n result: VerifyResponse;\n}\n\nexport interface FacilitatorVerifyFailureContext extends FacilitatorVerifyContext {\n error: Error;\n}\n\nexport interface FacilitatorSettleContext {\n paymentPayload: PaymentPayload;\n requirements: PaymentRequirements;\n}\n\nexport interface FacilitatorSettleResultContext extends FacilitatorSettleContext {\n result: SettleResponse;\n}\n\nexport interface FacilitatorSettleFailureContext extends FacilitatorSettleContext {\n error: Error;\n}\n\n/**\n * Facilitator Hook Type Definitions\n */\n\nexport type FacilitatorBeforeVerifyHook = (\n context: FacilitatorVerifyContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterVerifyHook = (context: FacilitatorVerifyResultContext) => Promise<void>;\n\nexport type FacilitatorOnVerifyFailureHook = (\n context: FacilitatorVerifyFailureContext,\n) => Promise<void | { recovered: true; result: VerifyResponse }>;\n\nexport type FacilitatorBeforeSettleHook = (\n context: FacilitatorSettleContext,\n) => Promise<void | { abort: true; reason: string }>;\n\nexport type FacilitatorAfterSettleHook = (context: FacilitatorSettleResultContext) => Promise<void>;\n\nexport type FacilitatorOnSettleFailureHook = (\n context: FacilitatorSettleFailureContext,\n) => Promise<void | { recovered: true; result: SettleResponse }>;\n\n/**\n * Facilitator client for the x402 payment protocol.\n * Manages payment scheme registration, verification, and settlement.\n */\nexport class x402Facilitator {\n private readonly registeredFacilitatorSchemes: Map<\n number,\n SchemeData<SchemeNetworkFacilitator>[] // Array to support multiple facilitators per version\n > = new Map();\n private readonly extensions: Map<string, FacilitatorExtension> = new Map();\n\n private beforeVerifyHooks: FacilitatorBeforeVerifyHook[] = [];\n private afterVerifyHooks: FacilitatorAfterVerifyHook[] = [];\n private onVerifyFailureHooks: FacilitatorOnVerifyFailureHook[] = [];\n private beforeSettleHooks: FacilitatorBeforeSettleHook[] = [];\n private afterSettleHooks: FacilitatorAfterSettleHook[] = [];\n private onSettleFailureHooks: FacilitatorOnSettleFailureHook[] = [];\n\n /**\n * Registers a scheme facilitator for the current x402 version.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n register(networks: Network | Network[], facilitator: SchemeNetworkFacilitator): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(x402Version, networksArray, facilitator);\n }\n\n /**\n * Registers a scheme facilitator for x402 version 1.\n * Networks are stored and used for getSupported() - no need to specify them later.\n *\n * @param networks - Single network or array of networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n registerV1(\n networks: Network | Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n const networksArray = Array.isArray(networks) ? networks : [networks];\n return this._registerScheme(1, networksArray, facilitator);\n }\n\n /**\n * Registers a protocol extension.\n *\n * @param extension - The extension object to register\n * @returns The x402Facilitator instance for chaining\n */\n registerExtension(extension: FacilitatorExtension): x402Facilitator {\n this.extensions.set(extension.key, extension);\n return this;\n }\n\n /**\n * Gets the list of registered extension keys.\n *\n * @returns Array of extension key strings\n */\n getExtensions(): string[] {\n return Array.from(this.extensions.keys());\n }\n\n /**\n * Gets a registered extension by key.\n *\n * @param key - The extension key to look up\n * @returns The extension object, or undefined if not registered\n */\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(key: string): T | undefined {\n return this.extensions.get(key) as T | undefined;\n }\n\n /**\n * Register a hook to execute before facilitator payment verification.\n * Can abort verification by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeVerify(hook: FacilitatorBeforeVerifyHook): x402Facilitator {\n this.beforeVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment verification (isValid: true).\n * This hook is NOT called when verification fails (isValid: false) - use onVerifyFailure for that.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterVerify(hook: FacilitatorAfterVerifyHook): x402Facilitator {\n this.afterVerifyHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment verification fails.\n * Called when: verification returns isValid: false, or an exception is thrown during verification.\n * Can recover from failure by returning { recovered: true, result: VerifyResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onVerifyFailure(hook: FacilitatorOnVerifyFailureHook): x402Facilitator {\n this.onVerifyFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute before facilitator payment settlement.\n * Can abort settlement by returning { abort: true, reason: string }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onBeforeSettle(hook: FacilitatorBeforeSettleHook): x402Facilitator {\n this.beforeSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute after successful facilitator payment settlement.\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onAfterSettle(hook: FacilitatorAfterSettleHook): x402Facilitator {\n this.afterSettleHooks.push(hook);\n return this;\n }\n\n /**\n * Register a hook to execute when facilitator payment settlement fails.\n * Can recover from failure by returning { recovered: true, result: SettleResponse }\n *\n * @param hook - The hook function to register\n * @returns The x402Facilitator instance for chaining\n */\n onSettleFailure(hook: FacilitatorOnSettleFailureHook): x402Facilitator {\n this.onSettleFailureHooks.push(hook);\n return this;\n }\n\n /**\n * Gets supported payment kinds, extensions, and signers.\n * Uses networks registered during register() calls - no parameters needed.\n * Returns flat array format for backward compatibility with V1 clients.\n *\n * @returns Supported response with kinds as array (with version in each element), extensions, and signers\n */\n getSupported(): {\n kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }>;\n extensions: string[];\n signers: Record<string, string[]>;\n } {\n const kinds: Array<{\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n }> = [];\n const signersByFamily: Record<string, Set<string>> = {};\n\n // Iterate over registered scheme data (array supports multiple facilitators per version)\n for (const [version, schemeDataArray] of this.registeredFacilitatorSchemes) {\n for (const schemeData of schemeDataArray) {\n const { facilitator, networks } = schemeData;\n const scheme = facilitator.scheme;\n\n // Iterate over stored concrete networks\n for (const network of networks) {\n const extra = facilitator.getExtra(network);\n kinds.push({\n x402Version: version,\n scheme,\n network,\n ...(extra && { extra }),\n });\n\n // Collect signers by CAIP family for this network\n const family = facilitator.caipFamily;\n if (!signersByFamily[family]) {\n signersByFamily[family] = new Set();\n }\n facilitator.getSigners(network).forEach(signer => signersByFamily[family].add(signer));\n }\n }\n }\n\n // Convert signer sets to arrays\n const signers: Record<string, string[]> = {};\n for (const [family, signerSet] of Object.entries(signersByFamily)) {\n signers[family] = Array.from(signerSet);\n }\n\n return {\n kinds,\n extensions: this.getExtensions(),\n signers,\n };\n }\n\n /**\n * Verifies a payment payload against requirements.\n *\n * @param paymentPayload - The payment payload to verify\n * @param paymentRequirements - The payment requirements to verify against\n * @returns Promise resolving to the verification response\n */\n async verify(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const context: FacilitatorVerifyContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeVerify hooks\n for (const hook of this.beforeVerifyHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n return {\n isValid: false,\n invalidReason: result.reason,\n };\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const verifyResult = await schemeNetworkFacilitator.verify(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Check if verification failed (isValid: false)\n if (!verifyResult.isValid) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: new Error(verifyResult.invalidReason || \"Verification failed\"),\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n // If recovered, execute afterVerify hooks with recovered result\n const recoveredContext: FacilitatorVerifyResultContext = {\n ...context,\n result: result.result,\n };\n for (const hook of this.afterVerifyHooks) {\n await hook(recoveredContext);\n }\n return result.result;\n }\n }\n\n return verifyResult;\n }\n\n // Execute afterVerify hooks only for successful verification\n const resultContext: FacilitatorVerifyResultContext = {\n ...context,\n result: verifyResult,\n };\n\n for (const hook of this.afterVerifyHooks) {\n await hook(resultContext);\n }\n\n return verifyResult;\n } catch (error) {\n const failureContext: FacilitatorVerifyFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onVerifyFailure hooks\n for (const hook of this.onVerifyFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Settles a payment based on the payload and requirements.\n *\n * @param paymentPayload - The payment payload to settle\n * @param paymentRequirements - The payment requirements for settlement\n * @returns Promise resolving to the settlement response\n */\n async settle(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const context: FacilitatorSettleContext = {\n paymentPayload,\n requirements: paymentRequirements,\n };\n\n // Execute beforeSettle hooks\n for (const hook of this.beforeSettleHooks) {\n const result = await hook(context);\n if (result && \"abort\" in result && result.abort) {\n throw new Error(`Settlement aborted: ${result.reason}`);\n }\n }\n\n try {\n const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version);\n if (!schemeDataArray) {\n throw new Error(\n `No facilitator registered for x402 version: ${paymentPayload.x402Version}`,\n );\n }\n\n // Find matching facilitator from array\n let schemeNetworkFacilitator: SchemeNetworkFacilitator | undefined;\n for (const schemeData of schemeDataArray) {\n if (schemeData.facilitator.scheme === paymentRequirements.scheme) {\n // Check if network matches\n if (schemeData.networks.has(paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n // Try pattern matching\n if (networkMatchesPattern(schemeData.pattern, paymentRequirements.network)) {\n schemeNetworkFacilitator = schemeData.facilitator;\n break;\n }\n }\n }\n\n if (!schemeNetworkFacilitator) {\n throw new Error(\n `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}`,\n );\n }\n\n const facilitatorContext = this.buildFacilitatorContext();\n const settleResult = await schemeNetworkFacilitator.settle(\n paymentPayload,\n paymentRequirements,\n facilitatorContext,\n );\n\n // Execute afterSettle hooks\n const resultContext: FacilitatorSettleResultContext = {\n ...context,\n result: settleResult,\n };\n\n for (const hook of this.afterSettleHooks) {\n await hook(resultContext);\n }\n\n return settleResult;\n } catch (error) {\n const failureContext: FacilitatorSettleFailureContext = {\n ...context,\n error: error as Error,\n };\n\n // Execute onSettleFailure hooks\n for (const hook of this.onSettleFailureHooks) {\n const result = await hook(failureContext);\n if (result && \"recovered\" in result && result.recovered) {\n return result.result;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Builds a FacilitatorContext from the registered extensions map.\n * Passed to mechanism verify/settle so they can access extension capabilities.\n *\n * @returns A FacilitatorContext backed by this facilitator's registered extensions\n */\n private buildFacilitatorContext(): FacilitatorContext {\n const extensionsMap = this.extensions;\n return {\n getExtension<T extends FacilitatorExtension = FacilitatorExtension>(\n key: string,\n ): T | undefined {\n return extensionsMap.get(key) as T | undefined;\n },\n };\n }\n\n /**\n * Internal method to register a scheme facilitator.\n *\n * @param x402Version - The x402 protocol version\n * @param networks - Array of concrete networks this facilitator supports\n * @param facilitator - The scheme network facilitator to register\n * @returns The x402Facilitator instance for chaining\n */\n private _registerScheme(\n x402Version: number,\n networks: Network[],\n facilitator: SchemeNetworkFacilitator,\n ): x402Facilitator {\n if (!this.registeredFacilitatorSchemes.has(x402Version)) {\n this.registeredFacilitatorSchemes.set(x402Version, []);\n }\n const schemeDataArray = this.registeredFacilitatorSchemes.get(x402Version)!;\n\n // Add new scheme data (supports multiple facilitators with same scheme name)\n schemeDataArray.push({\n facilitator,\n networks: new Set(networks),\n pattern: this.derivePattern(networks),\n });\n\n return this;\n }\n\n /**\n * Derives a wildcard pattern from an array of networks.\n * If all networks share the same namespace, returns wildcard pattern.\n * Otherwise returns the first network for exact matching.\n *\n * @param networks - Array of networks\n * @returns Derived pattern for matching\n */\n private derivePattern(networks: Network[]): Network {\n if (networks.length === 0) return \"\" as Network;\n if (networks.length === 1) return networks[0];\n\n // Extract namespaces (e.g., \"eip155\" from \"eip155:84532\")\n const namespaces = networks.map(n => n.split(\":\")[0]);\n const uniqueNamespaces = new Set(namespaces);\n\n // If all same namespace, use wildcard\n if (uniqueNamespaces.size === 1) {\n return `${namespaces[0]}:*` as Network;\n }\n\n // Mixed namespaces - use first network for exact matching\n return networks[0];\n }\n}\n"],"mappings":"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","names":["hook","x402Version"]} |
@@ -1,4 +0,4 @@ | ||
| import { P as PaymentPayload, c as PaymentRequired, S as SettleResponse } from '../x402Client-BSWPuNYe.mjs'; | ||
| export { C as CompiledRoute, D as DynamicPayTo, l as DynamicPrice, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, u as HTTPResourceServerExtensionHooks, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, j as PaymentOption, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, v as ResourceServerTransportExtensionHooks, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, n as SettlementFailedResponseBody, U as UnpaidResponseBody, B as getFacilitatorResponseError, x as x402HTTPResourceServer } from '../x402Client-BSWPuNYe.mjs'; | ||
| export { HTTPClientExtensionHooks, PaymentRequiredContext, PaymentRequiredHook, x402HTTPClient } from '../client/index.mjs'; | ||
| import { P as PaymentPayload, c as PaymentRequired, S as SettleResponse } from '../x402Client-TQHctrG7.mjs'; | ||
| export { C as CompiledRoute, D as DynamicPayTo, l as DynamicPrice, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, u as HTTPResourceServerExtensionHooks, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, j as PaymentOption, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, v as ResourceServerTransportExtensionHooks, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, n as SettlementFailedResponseBody, U as UnpaidResponseBody, B as getFacilitatorResponseError, x as x402HTTPResourceServer } from '../x402Client-TQHctrG7.mjs'; | ||
| export { HTTPClientExtensionHooks, HTTPPaymentStatus, HTTPResourceResponse, PaymentRequiredContext, PaymentRequiredHook, x402HTTPClient } from '../client/index.mjs'; | ||
@@ -5,0 +5,0 @@ type QueryParamMethods = "GET" | "HEAD" | "DELETE"; |
@@ -12,3 +12,3 @@ import { | ||
| x402HTTPResourceServer | ||
| } from "../chunk-W4OPBTK7.mjs"; | ||
| } from "../chunk-YSNRPV3S.mjs"; | ||
| import "../chunk-FPXAE3OS.mjs"; | ||
@@ -20,3 +20,3 @@ import "../chunk-VE37GDG2.mjs"; | ||
| } from "../chunk-AGOUMC4P.mjs"; | ||
| import "../chunk-4BKQ2IT7.mjs"; | ||
| import "../chunk-ABS7D6VX.mjs"; | ||
| import "../chunk-BJTO5JO5.mjs"; | ||
@@ -23,0 +23,0 @@ export { |
@@ -1,3 +0,3 @@ | ||
| import { a as PaymentRequirements, S as SettleResponse } from '../x402Client-BSWPuNYe.mjs'; | ||
| export { a4 as AfterSettleHook, a1 as AfterVerifyHook, a3 as BeforeSettleHook, a0 as BeforeVerifyHook, C as CompiledRoute, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, a5 as OnSettleFailureHook, a6 as OnVerifiedPaymentCanceledHook, a2 as OnVerifyFailureHook, Y as PaymentCancellationDispatcher, I as PaymentRequiredContext, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, G as ResourceConfig, $ as ResourceVerifyRespone, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, ab as SETTLEMENT_OVERRIDES_HEADER, a7 as SchemeEnrichPaymentRequiredResponseHook, a9 as SchemeEnrichSettlementPayloadHook, aa as SchemeEnrichSettlementResponseHook, a8 as SchemePaymentRequiredContext, M as SettleContext, Q as SettleFailureContext, O as SettleResultContext, n as SettlementFailedResponseBody, Z as SettlementOverrides, _ as SkipHandlerDirective, U as UnpaidResponseBody, X as VerifiedPaymentCancelOptions, T as VerifiedPaymentCanceledContext, W as VerifiedPaymentCancellationReason, J as VerifyContext, L as VerifyFailureContext, K as VerifyResultContext, ac as checkIfBazaarNeeded, B as getFacilitatorResponseError, x as x402HTTPResourceServer, E as x402ResourceServer } from '../x402Client-BSWPuNYe.mjs'; | ||
| import { a as PaymentRequirements, S as SettleResponse } from '../x402Client-TQHctrG7.mjs'; | ||
| export { a5 as AfterSettleHook, a2 as AfterVerifyHook, a4 as BeforeSettleHook, a1 as BeforeVerifyHook, C as CompiledRoute, _ as ExtensionValidationResult, y as FacilitatorClient, z as FacilitatorConfig, A as FacilitatorResponseError, H as HTTPAdapter, w as HTTPFacilitatorClient, g as HTTPProcessResult, d as HTTPRequestContext, m as HTTPResponseBody, f as HTTPResponseInstructions, e as HTTPTransportContext, a6 as OnSettleFailureHook, a7 as OnVerifiedPaymentCanceledHook, a3 as OnVerifyFailureHook, Y as PaymentCancellationDispatcher, I as PaymentRequiredContext, h as PaywallConfig, i as PaywallProvider, q as ProcessSettleFailureResponse, o as ProcessSettleResultResponse, p as ProcessSettleSuccessResponse, t as ProtectedRequestHook, G as ResourceConfig, a0 as ResourceVerifyRespone, R as RouteConfig, s as RouteConfigurationError, r as RouteValidationError, k as RoutesConfig, ac as SETTLEMENT_OVERRIDES_HEADER, a8 as SchemeEnrichPaymentRequiredResponseHook, aa as SchemeEnrichSettlementPayloadHook, ab as SchemeEnrichSettlementResponseHook, a9 as SchemePaymentRequiredContext, M as SettleContext, Q as SettleFailureContext, O as SettleResultContext, n as SettlementFailedResponseBody, Z as SettlementOverrides, $ as SkipHandlerDirective, U as UnpaidResponseBody, X as VerifiedPaymentCancelOptions, T as VerifiedPaymentCanceledContext, W as VerifiedPaymentCancellationReason, J as VerifyContext, L as VerifyFailureContext, K as VerifyResultContext, ad as checkIfBazaarNeeded, B as getFacilitatorResponseError, x as x402HTTPResourceServer, E as x402ResourceServer } from '../x402Client-TQHctrG7.mjs'; | ||
@@ -4,0 +4,0 @@ /** |
@@ -7,3 +7,3 @@ import { | ||
| x402HTTPResourceServer | ||
| } from "../chunk-W4OPBTK7.mjs"; | ||
| } from "../chunk-YSNRPV3S.mjs"; | ||
| import "../chunk-FPXAE3OS.mjs"; | ||
@@ -21,3 +21,3 @@ import { | ||
| findByNetworkAndScheme | ||
| } from "../chunk-4BKQ2IT7.mjs"; | ||
| } from "../chunk-ABS7D6VX.mjs"; | ||
| import "../chunk-BJTO5JO5.mjs"; | ||
@@ -1013,2 +1013,45 @@ | ||
| */ | ||
| /** | ||
| * Validates optional client extension echoes against server-advertised extension info. | ||
| * When the client omits extensions entirely, validation passes. | ||
| * | ||
| * @param paymentRequired - Server payment required response used for matching | ||
| * @param paymentPayload - Client payment payload | ||
| * @returns Whether echoed extension info preserves server-advertised values | ||
| */ | ||
| validateExtensions(paymentRequired, paymentPayload) { | ||
| if (paymentPayload.x402Version !== 2) { | ||
| return { valid: true }; | ||
| } | ||
| const serverExtensions = paymentRequired.extensions; | ||
| if (!serverExtensions || Object.keys(serverExtensions).length === 0) { | ||
| return { valid: true }; | ||
| } | ||
| const clientExtensions = paymentPayload.extensions; | ||
| if (!clientExtensions || Object.keys(clientExtensions).length === 0) { | ||
| return { valid: true }; | ||
| } | ||
| for (const [key, echoedValue] of Object.entries(clientExtensions)) { | ||
| if (!Object.prototype.hasOwnProperty.call(serverExtensions, key)) { | ||
| continue; | ||
| } | ||
| const advertisedInfo = getExtensionInfo(serverExtensions[key]); | ||
| const echoedInfo = getExtensionInfo(echoedValue); | ||
| if (!extensionInfoMatchesAdvertised(advertisedInfo, echoedInfo)) { | ||
| return { | ||
| valid: false, | ||
| invalidReason: "extension_echo_mismatch", | ||
| extensionKey: key | ||
| }; | ||
| } | ||
| } | ||
| return { valid: true }; | ||
| } | ||
| /** | ||
| * Finds the server-advertised requirement that matches a client payment payload. | ||
| * | ||
| * @param availableRequirements - Payment requirements advertised for the resource. | ||
| * @param paymentPayload - Signed payment payload from the client. | ||
| * @returns The matching requirement, or undefined when none match. | ||
| */ | ||
| findMatchingRequirements(availableRequirements, paymentPayload) { | ||
@@ -1224,2 +1267,11 @@ switch (paymentPayload.x402Version) { | ||
| }; | ||
| function getExtensionInfo(value) { | ||
| if (value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "info")) { | ||
| return value.info; | ||
| } | ||
| return value; | ||
| } | ||
| function extensionInfoMatchesAdvertised(advertised, echoed) { | ||
| return objectContainsSubset(advertised, echoed); | ||
| } | ||
| function paymentRequirementsMatchAccepted(required, accepted) { | ||
@@ -1226,0 +1278,0 @@ const { extra: requiredExtra, ...requiredCore } = required; |
@@ -1,1 +0,1 @@ | ||
| export { aB as AssetAmount, aU as DeepReadonly, aR as FacilitatorContext, F as FacilitatorExtension, A as FacilitatorResponseError, u as HTTPResourceServerExtensionHooks, aA as Money, aO as MoneyParser, N as Network, P as PaymentPayload, aQ as PaymentPayloadContext, aP as PaymentPayloadResult, av as PaymentPayloadV1, c as PaymentRequired, I as PaymentRequiredContext, au as PaymentRequiredV1, a as PaymentRequirements, at as PaymentRequirementsV1, aC as Price, aJ as ResourceInfo, aS as ResourceServerExtension, aT as ResourceServerExtensionHooks, v as ResourceServerTransportExtensionHooks, aL as SchemeClientHooks, a7 as SchemeEnrichPaymentRequiredResponseHook, aK as SchemeNetworkClient, b as SchemeNetworkFacilitator, aM as SchemeNetworkServer, a8 as SchemePaymentRequiredContext, aN as SchemeServerHooks, M as SettleContext, aI as SettleError, Q as SettleFailureContext, aE as SettleRequest, S as SettleResponse, O as SettleResultContext, aG as SupportedKind, aF as SupportedResponse, T as VerifiedPaymentCanceledContext, J as VerifyContext, aH as VerifyError, L as VerifyFailureContext, aD as VerifyRequest, V as VerifyResponse, K as VerifyResultContext, B as getFacilitatorResponseError } from '../x402Client-BSWPuNYe.mjs'; | ||
| export { aC as AssetAmount, aV as DeepReadonly, aS as FacilitatorContext, F as FacilitatorExtension, A as FacilitatorResponseError, u as HTTPResourceServerExtensionHooks, aB as Money, aP as MoneyParser, N as Network, P as PaymentPayload, aR as PaymentPayloadContext, aQ as PaymentPayloadResult, aw as PaymentPayloadV1, c as PaymentRequired, I as PaymentRequiredContext, av as PaymentRequiredV1, a as PaymentRequirements, au as PaymentRequirementsV1, aD as Price, aK as ResourceInfo, aT as ResourceServerExtension, aU as ResourceServerExtensionHooks, v as ResourceServerTransportExtensionHooks, aM as SchemeClientHooks, a8 as SchemeEnrichPaymentRequiredResponseHook, aL as SchemeNetworkClient, b as SchemeNetworkFacilitator, aN as SchemeNetworkServer, a9 as SchemePaymentRequiredContext, aO as SchemeServerHooks, M as SettleContext, aJ as SettleError, Q as SettleFailureContext, aF as SettleRequest, S as SettleResponse, O as SettleResultContext, aH as SupportedKind, aG as SupportedResponse, T as VerifiedPaymentCanceledContext, J as VerifyContext, aI as VerifyError, L as VerifyFailureContext, aE as VerifyRequest, V as VerifyResponse, K as VerifyResultContext, B as getFacilitatorResponseError } from '../x402Client-TQHctrG7.mjs'; |
@@ -1,1 +0,1 @@ | ||
| export { av as PaymentPayloadV1, au as PaymentRequiredV1, at as PaymentRequirementsV1, ax as SettleRequestV1, ay as SettleResponseV1, az as SupportedResponseV1, aw as VerifyRequestV1 } from '../../x402Client-BSWPuNYe.mjs'; | ||
| export { aw as PaymentPayloadV1, av as PaymentRequiredV1, au as PaymentRequirementsV1, ay as SettleRequestV1, az as SettleResponseV1, aA as SupportedResponseV1, ax as VerifyRequestV1 } from '../../x402Client-TQHctrG7.mjs'; |
@@ -1,2 +0,2 @@ | ||
| import { N as Network } from '../x402Client-BSWPuNYe.mjs'; | ||
| import { N as Network } from '../x402Client-TQHctrG7.mjs'; | ||
@@ -14,2 +14,10 @@ /** | ||
| /** | ||
| * Parses a money string into a finite, non-negative decimal number. | ||
| * Accepts plain decimal strings with an optional leading dollar sign. | ||
| * | ||
| * @param money - The money string to parse | ||
| * @returns Decimal number | ||
| */ | ||
| declare function parseMoneyString(money: string): number; | ||
| /** | ||
| * Convert a decimal amount to token smallest units. | ||
@@ -32,2 +40,3 @@ * Accepts only plain decimal strings — scientific notation is not allowed. | ||
| } | ||
| declare const networkMatchesPattern: (pattern: Network, network: Network) => boolean; | ||
| declare const findSchemesByNetwork: <T>(map: Map<string, Map<string, T>>, network: Network) => Map<string, T> | undefined; | ||
@@ -70,2 +79,2 @@ declare const findByNetworkAndScheme: <T>(map: Map<string, Map<string, T>>, scheme: string, network: Network) => T | undefined; | ||
| export { Base64EncodedRegex, type SchemeData, convertToTokenAmount, deepEqual, findByNetworkAndScheme, findFacilitatorBySchemeAndNetwork, findSchemesByNetwork, numberToDecimalString, safeBase64Decode, safeBase64Encode }; | ||
| export { Base64EncodedRegex, type SchemeData, convertToTokenAmount, deepEqual, findByNetworkAndScheme, findFacilitatorBySchemeAndNetwork, findSchemesByNetwork, networkMatchesPattern, numberToDecimalString, parseMoneyString, safeBase64Decode, safeBase64Encode }; |
@@ -8,6 +8,8 @@ import { | ||
| findSchemesByNetwork, | ||
| networkMatchesPattern, | ||
| numberToDecimalString, | ||
| parseMoneyString, | ||
| safeBase64Decode, | ||
| safeBase64Encode | ||
| } from "../chunk-4BKQ2IT7.mjs"; | ||
| } from "../chunk-ABS7D6VX.mjs"; | ||
| import "../chunk-BJTO5JO5.mjs"; | ||
@@ -21,3 +23,5 @@ export { | ||
| findSchemesByNetwork, | ||
| networkMatchesPattern, | ||
| numberToDecimalString, | ||
| parseMoneyString, | ||
| safeBase64Decode, | ||
@@ -24,0 +28,0 @@ safeBase64Encode |
+1
-1
| { | ||
| "name": "@x402/core", | ||
| "version": "2.14.0", | ||
| "version": "2.15.0", | ||
| "main": "./dist/cjs/index.js", | ||
@@ -5,0 +5,0 @@ "module": "./dist/esm/index.js", |
Sorry, the diff of this file is too big to display
| // src/utils/index.ts | ||
| function numberToDecimalString(n) { | ||
| const str = n.toString(); | ||
| if (!/[eE]/.test(str)) return str; | ||
| const [significand, exponentStr] = str.split(/[eE]/); | ||
| const exp = parseInt(exponentStr, 10); | ||
| const negative = significand.startsWith("-"); | ||
| const abs = negative ? significand.slice(1) : significand; | ||
| const [intDigits, fracDigits = ""] = abs.split("."); | ||
| const allDigits = intDigits + fracDigits; | ||
| const decimalPos = intDigits.length + exp; | ||
| let result; | ||
| if (decimalPos <= 0) { | ||
| result = "0." + "0".repeat(-decimalPos) + allDigits; | ||
| } else if (decimalPos >= allDigits.length) { | ||
| result = allDigits + "0".repeat(decimalPos - allDigits.length); | ||
| } else { | ||
| result = allDigits.slice(0, decimalPos) + "." + allDigits.slice(decimalPos); | ||
| } | ||
| return (negative ? "-" : "") + result; | ||
| } | ||
| function convertToTokenAmount(decimalAmount, decimals) { | ||
| if (/[eE]/.test(decimalAmount)) { | ||
| throw new Error( | ||
| `Invalid amount: ${decimalAmount} \u2014 use decimal notation, not scientific notation` | ||
| ); | ||
| } | ||
| if (!/^-?\d+\.?\d*$/.test(decimalAmount)) { | ||
| throw new Error(`Invalid amount: ${decimalAmount}`); | ||
| } | ||
| const [intPart, decPart = ""] = decimalAmount.split("."); | ||
| const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals); | ||
| const tokenAmount = (intPart + paddedDec).replace(/^0+/, "") || "0"; | ||
| if (tokenAmount === "0" && /[1-9]/.test(decimalAmount)) { | ||
| throw new Error( | ||
| `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places` | ||
| ); | ||
| } | ||
| return tokenAmount; | ||
| } | ||
| var findSchemesByNetwork = (map, network) => { | ||
| let implementationsByScheme = map.get(network); | ||
| if (!implementationsByScheme) { | ||
| for (const [registeredNetworkPattern, implementations] of map.entries()) { | ||
| const pattern = registeredNetworkPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*"); | ||
| const regex = new RegExp(`^${pattern}$`); | ||
| if (regex.test(network)) { | ||
| implementationsByScheme = implementations; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return implementationsByScheme; | ||
| }; | ||
| var findByNetworkAndScheme = (map, scheme, network) => { | ||
| return findSchemesByNetwork(map, network)?.get(scheme); | ||
| }; | ||
| var findFacilitatorBySchemeAndNetwork = (schemeMap, scheme, network) => { | ||
| const schemeData = schemeMap.get(scheme); | ||
| if (!schemeData) return void 0; | ||
| if (schemeData.networks.has(network)) { | ||
| return schemeData.facilitator; | ||
| } | ||
| const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); | ||
| if (patternRegex.test(network)) { | ||
| return schemeData.facilitator; | ||
| } | ||
| return void 0; | ||
| }; | ||
| var Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/; | ||
| function safeBase64Encode(data) { | ||
| if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") { | ||
| const bytes = new TextEncoder().encode(data); | ||
| const binaryString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join(""); | ||
| return globalThis.btoa(binaryString); | ||
| } | ||
| return Buffer.from(data, "utf8").toString("base64"); | ||
| } | ||
| function safeBase64Decode(data) { | ||
| if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") { | ||
| const binaryString = globalThis.atob(data); | ||
| const bytes = new Uint8Array(binaryString.length); | ||
| for (let i = 0; i < binaryString.length; i++) { | ||
| bytes[i] = binaryString.charCodeAt(i); | ||
| } | ||
| const decoder = new TextDecoder("utf-8"); | ||
| return decoder.decode(bytes); | ||
| } | ||
| return Buffer.from(data, "base64").toString("utf-8"); | ||
| } | ||
| function deepEqual(obj1, obj2) { | ||
| const normalize = (obj) => { | ||
| if (obj === null || obj === void 0) return JSON.stringify(obj); | ||
| if (typeof obj !== "object") return JSON.stringify(obj); | ||
| if (Array.isArray(obj)) { | ||
| return JSON.stringify( | ||
| obj.map( | ||
| (item) => typeof item === "object" && item !== null ? JSON.parse(normalize(item)) : item | ||
| ) | ||
| ); | ||
| } | ||
| const sorted = {}; | ||
| Object.keys(obj).sort().forEach((key) => { | ||
| const value = obj[key]; | ||
| sorted[key] = typeof value === "object" && value !== null ? JSON.parse(normalize(value)) : value; | ||
| }); | ||
| return JSON.stringify(sorted); | ||
| }; | ||
| try { | ||
| return normalize(obj1) === normalize(obj2); | ||
| } catch { | ||
| return JSON.stringify(obj1) === JSON.stringify(obj2); | ||
| } | ||
| } | ||
| export { | ||
| numberToDecimalString, | ||
| convertToTokenAmount, | ||
| findSchemesByNetwork, | ||
| findByNetworkAndScheme, | ||
| findFacilitatorBySchemeAndNetwork, | ||
| Base64EncodedRegex, | ||
| safeBase64Encode, | ||
| safeBase64Decode, | ||
| deepEqual | ||
| }; | ||
| //# sourceMappingURL=chunk-4BKQ2IT7.mjs.map |
| {"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["import { Network } from \"../types\";\n\n/**\n * Converts a JavaScript number to a plain decimal string, expanding scientific notation\n * via string manipulation rather than parseFloat round-tripping.\n *\n * e.g. 1e-7 → \"0.0000001\", 4.02 → \"4.02\"\n *\n * @param n - The number to convert\n * @returns A plain decimal string representation with no scientific notation\n */\nexport function numberToDecimalString(n: number): string {\n const str = n.toString();\n if (!/[eE]/.test(str)) return str;\n\n const [significand, exponentStr] = str.split(/[eE]/);\n const exp = parseInt(exponentStr, 10);\n const negative = significand.startsWith(\"-\");\n const abs = negative ? significand.slice(1) : significand;\n const [intDigits, fracDigits = \"\"] = abs.split(\".\");\n const allDigits = intDigits + fracDigits;\n const decimalPos = intDigits.length + exp;\n\n let result: string;\n if (decimalPos <= 0) {\n result = \"0.\" + \"0\".repeat(-decimalPos) + allDigits;\n } else if (decimalPos >= allDigits.length) {\n result = allDigits + \"0\".repeat(decimalPos - allDigits.length);\n } else {\n result = allDigits.slice(0, decimalPos) + \".\" + allDigits.slice(decimalPos);\n }\n return (negative ? \"-\" : \"\") + result;\n}\n\n/**\n * Convert a decimal amount to token smallest units.\n * Accepts only plain decimal strings — scientific notation is not allowed.\n * Throws if the amount is non-zero but too small to represent with the given decimal precision.\n *\n * @param decimalAmount - The decimal amount as a plain string (e.g., \"0.10\")\n * @param decimals - The number of decimals for the token (e.g., 6 for USDC)\n * @returns The amount in smallest units as a string\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n if (/[eE]/.test(decimalAmount)) {\n throw new Error(\n `Invalid amount: ${decimalAmount} — use decimal notation, not scientific notation`,\n );\n }\n if (!/^-?\\d+\\.?\\d*$/.test(decimalAmount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n const [intPart, decPart = \"\"] = decimalAmount.split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n if (tokenAmount === \"0\" && /[1-9]/.test(decimalAmount)) {\n throw new Error(\n `Amount ${decimalAmount} is too small to represent with ${decimals} decimal places`,\n );\n }\n return tokenAmount;\n}\n\n/**\n * Scheme data structure for facilitator storage\n */\nexport interface SchemeData<T> {\n facilitator: T;\n networks: Set<Network>;\n pattern: Network;\n}\n\nexport const findSchemesByNetwork = <T>(\n map: Map<string, Map<string, T>>,\n network: Network,\n): Map<string, T> | undefined => {\n // Direct match first\n let implementationsByScheme = map.get(network);\n\n if (!implementationsByScheme) {\n // Try pattern matching for registered network patterns\n for (const [registeredNetworkPattern, implementations] of map.entries()) {\n // Convert the registered network pattern to a regex\n // e.g., \"eip155:*\" becomes /^eip155:.*$/\n const pattern = registeredNetworkPattern\n .replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape special regex chars except *\n .replace(/\\\\\\*/g, \".*\"); // Replace escaped * with .*\n\n const regex = new RegExp(`^${pattern}$`);\n\n if (regex.test(network)) {\n implementationsByScheme = implementations;\n break;\n }\n }\n }\n\n return implementationsByScheme;\n};\n\nexport const findByNetworkAndScheme = <T>(\n map: Map<string, Map<string, T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n return findSchemesByNetwork(map, network)?.get(scheme);\n};\n\n/**\n * Finds a facilitator by scheme and network using pattern matching.\n * Works with new SchemeData storage structure.\n *\n * @param schemeMap - Map of scheme names to SchemeData\n * @param scheme - The scheme to find\n * @param network - The network to match against\n * @returns The facilitator if found, undefined otherwise\n */\nexport const findFacilitatorBySchemeAndNetwork = <T>(\n schemeMap: Map<string, SchemeData<T>>,\n scheme: string,\n network: Network,\n): T | undefined => {\n const schemeData = schemeMap.get(scheme);\n if (!schemeData) return undefined;\n\n // Check if network is in the stored networks set\n if (schemeData.networks.has(network)) {\n return schemeData.facilitator;\n }\n\n // Try pattern matching\n const patternRegex = new RegExp(\"^\" + schemeData.pattern.replace(\"*\", \".*\") + \"$\");\n if (patternRegex.test(network)) {\n return schemeData.facilitator;\n }\n\n return undefined;\n};\n\nexport const Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n/**\n * Encodes a string to base64 format\n *\n * @param data - The string to be encoded to base64\n * @returns The base64 encoded string\n */\nexport function safeBase64Encode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.btoa === \"function\") {\n const bytes = new TextEncoder().encode(data);\n const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join(\"\");\n return globalThis.btoa(binaryString);\n }\n return Buffer.from(data, \"utf8\").toString(\"base64\");\n}\n\n/**\n * Decodes a base64 string back to its original format\n *\n * @param data - The base64 encoded string to be decoded\n * @returns The decoded string in UTF-8 format\n */\nexport function safeBase64Decode(data: string): string {\n if (typeof globalThis !== \"undefined\" && typeof globalThis.atob === \"function\") {\n const binaryString = globalThis.atob(data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n const decoder = new TextDecoder(\"utf-8\");\n return decoder.decode(bytes);\n }\n return Buffer.from(data, \"base64\").toString(\"utf-8\");\n}\n\n/**\n * Deep equality comparison for payment requirements\n * Uses a normalized JSON.stringify for consistent comparison\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are deeply equal\n */\nexport function deepEqual(obj1: unknown, obj2: unknown): boolean {\n // Normalize and stringify both objects for comparison\n // This handles nested objects, arrays, and different property orders\n const normalize = (obj: unknown): string => {\n // Handle primitives and null/undefined\n if (obj === null || obj === undefined) return JSON.stringify(obj);\n if (typeof obj !== \"object\") return JSON.stringify(obj);\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return JSON.stringify(\n obj.map(item =>\n typeof item === \"object\" && item !== null ? JSON.parse(normalize(item)) : item,\n ),\n );\n }\n\n // Handle objects - sort keys and recursively normalize values\n const sorted: Record<string, unknown> = {};\n Object.keys(obj as Record<string, unknown>)\n .sort()\n .forEach(key => {\n const value = (obj as Record<string, unknown>)[key];\n sorted[key] =\n typeof value === \"object\" && value !== null ? JSON.parse(normalize(value)) : value;\n });\n return JSON.stringify(sorted);\n };\n\n try {\n return normalize(obj1) === normalize(obj2);\n } catch {\n // Fallback to simple comparison if normalization fails\n return JSON.stringify(obj1) === JSON.stringify(obj2);\n }\n}\n"],"mappings":"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","names":[]} |
| import { | ||
| z | ||
| } from "./chunk-FPXAE3OS.mjs"; | ||
| import { | ||
| x402Version | ||
| } from "./chunk-VE37GDG2.mjs"; | ||
| import { | ||
| FacilitatorResponseError, | ||
| SettleError, | ||
| VerifyError | ||
| } from "./chunk-AGOUMC4P.mjs"; | ||
| import { | ||
| Base64EncodedRegex, | ||
| safeBase64Decode, | ||
| safeBase64Encode | ||
| } from "./chunk-4BKQ2IT7.mjs"; | ||
| import { | ||
| __require | ||
| } from "./chunk-BJTO5JO5.mjs"; | ||
| // src/http/x402HTTPResourceServer.ts | ||
| var SETTLEMENT_OVERRIDES_HEADER = "Settlement-Overrides"; | ||
| function checkIfBazaarNeeded(routes) { | ||
| if ("accepts" in routes) { | ||
| return !!(routes.extensions && "bazaar" in routes.extensions); | ||
| } | ||
| return Object.values(routes).some((routeConfig) => { | ||
| return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions); | ||
| }); | ||
| } | ||
| var RouteConfigurationError = class extends Error { | ||
| /** | ||
| * Creates a new RouteConfigurationError with the given validation errors. | ||
| * | ||
| * @param errors - The validation errors that caused this exception. | ||
| */ | ||
| constructor(errors) { | ||
| const message = `x402 Route Configuration Errors: | ||
| ${errors.map((e) => ` - ${e.message}`).join("\n")}`; | ||
| super(message); | ||
| this.name = "RouteConfigurationError"; | ||
| this.errors = errors; | ||
| } | ||
| }; | ||
| var FALLBACK_PAYWALL_HTML = `<!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Payment Required</title> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| </head> | ||
| <body> | ||
| <div style="max-width: 600px; margin: 50px auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;"> | ||
| <h1>Payment Required</h1> | ||
| <p>This resource is protected by the x402 payment protocol.</p> | ||
| <p style="margin-top: 2rem; padding: 1rem; background: #fef3c7; border-radius: 0.5rem;"> | ||
| <strong>Note to developers:</strong> install <code>@x402/paywall</code> to enable | ||
| the in-browser wallet connection and payment UI. Programmatic clients should read | ||
| the payment requirements from the 402 response headers and JSON body. | ||
| </p> | ||
| </div> | ||
| </body> | ||
| </html>`; | ||
| var x402HTTPResourceServer = class { | ||
| /** | ||
| * Creates a new x402HTTPResourceServer instance. | ||
| * | ||
| * @param ResourceServer - The core x402ResourceServer instance to use | ||
| * @param routes - Route configuration for payment-protected endpoints | ||
| */ | ||
| constructor(ResourceServer, routes) { | ||
| this.compiledRoutes = []; | ||
| this.protectedRequestHooks = []; | ||
| this.ResourceServer = ResourceServer; | ||
| this.routesConfig = routes; | ||
| const normalizedRoutes = typeof routes === "object" && !("accepts" in routes) ? routes : { "*": routes }; | ||
| for (const [pattern, config] of Object.entries(normalizedRoutes)) { | ||
| const parsed = this.parseRoutePattern(pattern); | ||
| this.compiledRoutes.push({ | ||
| verb: parsed.verb, | ||
| regex: parsed.regex, | ||
| config, | ||
| pattern: parsed.path | ||
| }); | ||
| } | ||
| } | ||
| /** | ||
| * Get the underlying x402ResourceServer instance. | ||
| * | ||
| * @returns The underlying x402ResourceServer instance | ||
| */ | ||
| get server() { | ||
| return this.ResourceServer; | ||
| } | ||
| /** | ||
| * Get the routes configuration. | ||
| * | ||
| * @returns The routes configuration | ||
| */ | ||
| get routes() { | ||
| return this.routesConfig; | ||
| } | ||
| /** | ||
| * Initialize the HTTP resource server. | ||
| * | ||
| * This method initializes the underlying resource server (fetching facilitator support) | ||
| * and then validates that all route payment configurations have corresponding | ||
| * registered schemes and facilitator support. | ||
| * | ||
| * @throws RouteConfigurationError if any route's payment options don't have | ||
| * corresponding registered schemes or facilitator support | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const httpServer = new x402HTTPResourceServer(server, routes); | ||
| * await httpServer.initialize(); | ||
| * ``` | ||
| */ | ||
| async initialize() { | ||
| await this.ResourceServer.initialize(); | ||
| const errors = this.validateRouteConfiguration(); | ||
| if (errors.length > 0) { | ||
| throw new RouteConfigurationError(errors); | ||
| } | ||
| } | ||
| /** | ||
| * Register a custom paywall provider for generating HTML | ||
| * | ||
| * @param provider - PaywallProvider instance | ||
| * @returns This service instance for chaining | ||
| */ | ||
| registerPaywallProvider(provider) { | ||
| this.paywallProvider = provider; | ||
| return this; | ||
| } | ||
| /** | ||
| * Register a hook that runs on every request to a protected route, before payment processing. | ||
| * Hooks are executed in order of registration. The first hook to return a non-void result wins. | ||
| * | ||
| * @param hook - The request hook function | ||
| * @returns The x402HTTPResourceServer instance for chaining | ||
| */ | ||
| onProtectedRequest(hook) { | ||
| this.protectedRequestHooks.push(hook); | ||
| return this; | ||
| } | ||
| /** | ||
| * Process HTTP request and return response instructions | ||
| * This is the main entry point for framework middleware | ||
| * | ||
| * @param context - HTTP request context | ||
| * @param paywallConfig - Optional paywall configuration | ||
| * @returns Process result indicating next action for middleware | ||
| */ | ||
| async processHTTPRequest(context, paywallConfig) { | ||
| const method = context.method || context.adapter.getMethod(); | ||
| context = { ...context, method }; | ||
| const { adapter, path } = context; | ||
| const routeMatch = this.getRouteConfig(path, method); | ||
| if (!routeMatch) { | ||
| return { type: "no-payment-required" }; | ||
| } | ||
| const { config: routeConfig, pattern: routePattern } = routeMatch; | ||
| const enrichedContext = { ...context, routePattern }; | ||
| for (const hook of this.getProtectedRequestHooks(routeConfig)) { | ||
| const result = await hook(enrichedContext, routeConfig); | ||
| if (result && "grantAccess" in result) { | ||
| return { type: "no-payment-required" }; | ||
| } | ||
| if (result && "abort" in result) { | ||
| return { | ||
| type: "payment-error", | ||
| response: { | ||
| status: 403, | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: { error: result.reason } | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| const paymentOptions = this.normalizePaymentOptions(routeConfig); | ||
| const paymentPayload = this.extractPayment(adapter); | ||
| const resourceInfo = { | ||
| url: routeConfig.resource || enrichedContext.adapter.getUrl(), | ||
| description: routeConfig.description || "", | ||
| mimeType: routeConfig.mimeType || "", | ||
| ...routeConfig.serviceName !== void 0 && { serviceName: routeConfig.serviceName }, | ||
| ...routeConfig.tags !== void 0 && { tags: routeConfig.tags }, | ||
| ...routeConfig.iconUrl !== void 0 && { iconUrl: routeConfig.iconUrl } | ||
| }; | ||
| let requirements = await this.ResourceServer.buildPaymentRequirementsFromOptions( | ||
| paymentOptions, | ||
| enrichedContext | ||
| ); | ||
| let extensions = routeConfig.extensions; | ||
| if (extensions) { | ||
| extensions = this.ResourceServer.enrichExtensions(extensions, enrichedContext); | ||
| } | ||
| const transportContext = { request: enrichedContext }; | ||
| const paymentRequired = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| !paymentPayload ? "Payment required" : void 0, | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| if (!paymentPayload) { | ||
| const unpaidBody = routeConfig.unpaidResponseBody ? await routeConfig.unpaidResponseBody(enrichedContext) : void 0; | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse( | ||
| paymentRequired, | ||
| this.isWebBrowser(adapter), | ||
| paywallConfig, | ||
| routeConfig.customPaywallHtml, | ||
| unpaidBody | ||
| ) | ||
| }; | ||
| } | ||
| try { | ||
| const matchingRequirements = this.ResourceServer.findMatchingRequirements( | ||
| paymentRequired.accepts, | ||
| paymentPayload | ||
| ); | ||
| if (!matchingRequirements) { | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| "No matching payment requirements", | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| const verifyResult = await this.ResourceServer.verifyPayment( | ||
| paymentPayload, | ||
| matchingRequirements, | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| if (!verifyResult.isValid) { | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| verifyResult.invalidReason, | ||
| extensions, | ||
| transportContext, | ||
| paymentPayload | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| if (verifyResult.skipHandler) { | ||
| return await this.processSkipHandlerSettlement( | ||
| paymentPayload, | ||
| matchingRequirements, | ||
| extensions, | ||
| transportContext, | ||
| verifyResult.skipHandler | ||
| ); | ||
| } | ||
| const cancellationDispatcher = this.ResourceServer.createPaymentCancellationDispatcher( | ||
| paymentPayload, | ||
| matchingRequirements, | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| return { | ||
| type: "payment-verified", | ||
| cancellationDispatcher, | ||
| paymentPayload, | ||
| paymentRequirements: matchingRequirements, | ||
| declaredExtensions: extensions | ||
| }; | ||
| } catch (error) { | ||
| if (error instanceof FacilitatorResponseError) { | ||
| throw error; | ||
| } | ||
| const errorResponse = await this.ResourceServer.createPaymentRequiredResponse( | ||
| requirements, | ||
| resourceInfo, | ||
| error instanceof Error ? error.message : "Payment verification failed", | ||
| extensions, | ||
| transportContext | ||
| ); | ||
| return { | ||
| type: "payment-error", | ||
| response: this.createHTTPResponse(errorResponse, false, paywallConfig) | ||
| }; | ||
| } | ||
| } | ||
| /** | ||
| * Process settlement after successful response | ||
| * | ||
| * @param paymentPayload - The verified payment payload | ||
| * @param requirements - The matching payment requirements | ||
| * @param declaredExtensions - Optional declared extensions (for per-key enrichment) | ||
| * @param transportContext - Optional HTTP transport context | ||
| * @param settlementOverrides - Optional settlement overrides (e.g., partial settlement amount) | ||
| * @returns ProcessSettleResultResponse - SettleResponse with headers if success or errorReason if failure | ||
| */ | ||
| async processSettlement(paymentPayload, requirements, declaredExtensions, transportContext, settlementOverrides) { | ||
| if (transportContext?.request && !transportContext.request.method) { | ||
| transportContext = { | ||
| ...transportContext, | ||
| request: { | ||
| ...transportContext.request, | ||
| method: transportContext.request.adapter.getMethod() | ||
| } | ||
| }; | ||
| } | ||
| try { | ||
| let resolvedOverrides = settlementOverrides; | ||
| if (!resolvedOverrides && transportContext?.responseHeaders) { | ||
| const overridesKey = SETTLEMENT_OVERRIDES_HEADER.toLowerCase(); | ||
| const rawValue = Object.entries(transportContext.responseHeaders).find( | ||
| ([key]) => key.toLowerCase() === overridesKey | ||
| )?.[1]; | ||
| if (rawValue) { | ||
| try { | ||
| resolvedOverrides = JSON.parse(rawValue); | ||
| } catch { | ||
| } | ||
| } | ||
| } | ||
| const settleResponse = await this.ResourceServer.settlePayment( | ||
| paymentPayload, | ||
| requirements, | ||
| declaredExtensions, | ||
| transportContext, | ||
| resolvedOverrides | ||
| ); | ||
| if (!settleResponse.success) { | ||
| const failure = { | ||
| ...settleResponse, | ||
| success: false, | ||
| errorReason: settleResponse.errorReason || "Settlement failed", | ||
| errorMessage: settleResponse.errorMessage || settleResponse.errorReason || "Settlement failed", | ||
| headers: this.createSettlementHeaders(settleResponse) | ||
| }; | ||
| const response = await this.buildSettlementFailureResponse(failure, transportContext); | ||
| return { ...failure, response }; | ||
| } | ||
| return { | ||
| ...settleResponse, | ||
| success: true, | ||
| headers: this.createSettlementHeaders(settleResponse), | ||
| requirements | ||
| }; | ||
| } catch (error) { | ||
| if (error instanceof FacilitatorResponseError) { | ||
| throw error; | ||
| } | ||
| if (error instanceof SettleError) { | ||
| const errorReason2 = error.errorReason || error.message; | ||
| const settleResponse2 = { | ||
| success: false, | ||
| errorReason: errorReason2, | ||
| errorMessage: error.errorMessage || errorReason2, | ||
| payer: error.payer, | ||
| network: error.network, | ||
| transaction: error.transaction | ||
| }; | ||
| const failure2 = { | ||
| ...settleResponse2, | ||
| success: false, | ||
| errorReason: errorReason2, | ||
| headers: this.createSettlementHeaders(settleResponse2) | ||
| }; | ||
| const response2 = await this.buildSettlementFailureResponse(failure2, transportContext); | ||
| return { ...failure2, response: response2 }; | ||
| } | ||
| const errorReason = error instanceof Error ? error.message : "Settlement failed"; | ||
| const settleResponse = { | ||
| success: false, | ||
| errorReason, | ||
| errorMessage: errorReason, | ||
| network: requirements.network, | ||
| transaction: "" | ||
| }; | ||
| const failure = { | ||
| ...settleResponse, | ||
| success: false, | ||
| errorReason, | ||
| headers: this.createSettlementHeaders(settleResponse) | ||
| }; | ||
| const response = await this.buildSettlementFailureResponse(failure, transportContext); | ||
| return { ...failure, response }; | ||
| } | ||
| } | ||
| /** | ||
| * Check if a request requires payment based on route configuration | ||
| * | ||
| * @param context - HTTP request context | ||
| * @returns True if the route requires payment, false otherwise | ||
| */ | ||
| requiresPayment(context) { | ||
| const method = context.method || context.adapter.getMethod(); | ||
| return this.getRouteConfig(context.path, method) !== void 0; | ||
| } | ||
| /** | ||
| * Settle a verified payment that requested `skipHandler`, packaging the | ||
| * result as a `payment-error` HTTPProcessResult so framework adapters can | ||
| * write the response without invoking the route handler. | ||
| * | ||
| * - On success: status 200 + PAYMENT-RESPONSE header + configured body. | ||
| * - On failure: the standard 402 settlement-failure response. | ||
| * | ||
| * @param paymentPayload - Verified payment payload. | ||
| * @param requirements - Matched payment requirements. | ||
| * @param declaredExtensions - Optional declared extensions for the route. | ||
| * @param transportContext - Optional HTTP transport context. | ||
| * @param skipHandlerResponse - Optional content type + body to return on success. | ||
| * @returns A `payment-error` HTTPProcessResult carrying the final response. | ||
| */ | ||
| async processSkipHandlerSettlement(paymentPayload, requirements, declaredExtensions, transportContext, skipHandlerResponse) { | ||
| const settleResult = await this.processSettlement( | ||
| paymentPayload, | ||
| requirements, | ||
| declaredExtensions, | ||
| transportContext | ||
| ); | ||
| if (!settleResult.success) { | ||
| return { type: "payment-error", response: settleResult.response }; | ||
| } | ||
| const contentType = skipHandlerResponse?.contentType ?? "application/json"; | ||
| const body = skipHandlerResponse?.body ?? {}; | ||
| return { | ||
| type: "payment-error", | ||
| response: { | ||
| status: 200, | ||
| headers: { | ||
| "Content-Type": contentType, | ||
| ...settleResult.headers | ||
| }, | ||
| body, | ||
| isHtml: contentType.includes("text/html") | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Build HTTPResponseInstructions for settlement failure. | ||
| * Uses settlementFailedResponseBody hook if configured, otherwise defaults to empty body. | ||
| * | ||
| * @param failure - Settlement failure result with headers | ||
| * @param transportContext - Optional HTTP transport context for the request | ||
| * @returns HTTP response instructions for the 402 settlement failure response | ||
| */ | ||
| async buildSettlementFailureResponse(failure, transportContext) { | ||
| const settlementHeaders = failure.headers; | ||
| const routeConfig = transportContext ? this.getRouteConfig(transportContext.request.path, transportContext.request.method) : void 0; | ||
| const customBody = routeConfig?.config.settlementFailedResponseBody ? await routeConfig.config.settlementFailedResponseBody(transportContext.request, failure) : void 0; | ||
| const contentType = customBody ? customBody.contentType : "application/json"; | ||
| const body = customBody ? customBody.body : {}; | ||
| return { | ||
| status: 402, | ||
| headers: { | ||
| "Content-Type": contentType, | ||
| ...settlementHeaders | ||
| }, | ||
| body, | ||
| isHtml: contentType.includes("text/html") | ||
| }; | ||
| } | ||
| /** | ||
| * Normalizes a RouteConfig's accepts field into an array of PaymentOptions | ||
| * Handles both single PaymentOption and array formats | ||
| * | ||
| * @param routeConfig - Route configuration | ||
| * @returns Array of payment options | ||
| */ | ||
| normalizePaymentOptions(routeConfig) { | ||
| return Array.isArray(routeConfig.accepts) ? routeConfig.accepts : [routeConfig.accepts]; | ||
| } | ||
| /** | ||
| * Manual request hooks run before extension transport hooks for declared extensions. | ||
| * | ||
| * @param routeConfig - Route configuration for the matched request | ||
| * @returns Hooks in invocation order | ||
| */ | ||
| getProtectedRequestHooks(routeConfig) { | ||
| const hooks = [...this.protectedRequestHooks]; | ||
| const declaredExtensions = routeConfig.extensions; | ||
| if (!declaredExtensions) return hooks; | ||
| for (const extension of this.ResourceServer.getExtensions()) { | ||
| const hook = extension.transportHooks?.http?.onProtectedRequest; | ||
| if (!hook || !(extension.key in declaredExtensions)) continue; | ||
| hooks.push( | ||
| (context, routeConfig2) => hook(declaredExtensions[extension.key], context, routeConfig2) | ||
| ); | ||
| } | ||
| return hooks; | ||
| } | ||
| /** | ||
| * Validates that all payment options in routes have corresponding registered schemes | ||
| * and facilitator support. | ||
| * | ||
| * @returns Array of validation errors (empty if all routes are valid) | ||
| */ | ||
| validateRouteConfiguration() { | ||
| const errors = []; | ||
| const normalizedRoutes = typeof this.routesConfig === "object" && !("accepts" in this.routesConfig) ? Object.entries(this.routesConfig) : [["*", this.routesConfig]]; | ||
| for (const [pattern, config] of normalizedRoutes) { | ||
| const pathPart = pattern.includes(" ") ? pattern.split(/\s+/)[1] : pattern; | ||
| if (pathPart && pathPart.includes("*") && config.extensions && "bazaar" in config.extensions) { | ||
| console.warn( | ||
| `[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.` | ||
| ); | ||
| } | ||
| const paymentOptions = this.normalizePaymentOptions(config); | ||
| for (const option of paymentOptions) { | ||
| if (!this.ResourceServer.hasRegisteredScheme(option.network, option.scheme)) { | ||
| errors.push({ | ||
| routePattern: pattern, | ||
| scheme: option.scheme, | ||
| network: option.network, | ||
| reason: "missing_scheme", | ||
| message: `Route "${pattern}": No scheme implementation registered for "${option.scheme}" on network "${option.network}"` | ||
| }); | ||
| continue; | ||
| } | ||
| const supportedKind = this.ResourceServer.getSupportedKind( | ||
| x402Version, | ||
| option.network, | ||
| option.scheme | ||
| ); | ||
| if (!supportedKind) { | ||
| errors.push({ | ||
| routePattern: pattern, | ||
| scheme: option.scheme, | ||
| network: option.network, | ||
| reason: "missing_facilitator", | ||
| message: `Route "${pattern}": Facilitator does not support scheme "${option.scheme}" on network "${option.network}"` | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| return errors; | ||
| } | ||
| /** | ||
| * Get route configuration for a request | ||
| * | ||
| * @param path - Request path | ||
| * @param method - HTTP method | ||
| * @returns Route configuration and pattern, or undefined if no match | ||
| */ | ||
| getRouteConfig(path, method) { | ||
| const normalizedPath = this.normalizePath(path); | ||
| const upperMethod = method.toUpperCase(); | ||
| const matchingRoute = this.compiledRoutes.find( | ||
| (route) => route.regex.test(normalizedPath) && (route.verb === "*" || route.verb === upperMethod) | ||
| ); | ||
| if (!matchingRoute) return void 0; | ||
| return { config: matchingRoute.config, pattern: matchingRoute.pattern }; | ||
| } | ||
| /** | ||
| * Extract payment from HTTP headers (handles v1 and v2) | ||
| * | ||
| * @param adapter - HTTP adapter | ||
| * @returns Decoded payment payload or null | ||
| */ | ||
| extractPayment(adapter) { | ||
| const header = adapter.getHeader("payment-signature") || adapter.getHeader("PAYMENT-SIGNATURE"); | ||
| if (header) { | ||
| try { | ||
| return decodePaymentSignatureHeader(header); | ||
| } catch (error) { | ||
| console.warn("Failed to decode PAYMENT-SIGNATURE header:", error); | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| /** | ||
| * Check if request is from a web browser | ||
| * | ||
| * @param adapter - HTTP adapter | ||
| * @returns True if request appears to be from a browser | ||
| */ | ||
| isWebBrowser(adapter) { | ||
| const accept = adapter.getAcceptHeader(); | ||
| const userAgent = adapter.getUserAgent(); | ||
| return accept.includes("text/html") && userAgent.includes("Mozilla"); | ||
| } | ||
| /** | ||
| * Create HTTP response instructions from payment required | ||
| * | ||
| * @param paymentRequired - Payment requirements | ||
| * @param isWebBrowser - Whether request is from browser | ||
| * @param paywallConfig - Paywall configuration | ||
| * @param customHtml - Custom HTML template | ||
| * @param unpaidResponse - Optional custom response (content type and body) for unpaid API requests | ||
| * @returns Response instructions | ||
| */ | ||
| createHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) { | ||
| const status = paymentRequired.error === "permit2_allowance_required" ? 412 : 402; | ||
| const response = this.createHTTPPaymentRequiredResponse(paymentRequired); | ||
| if (isWebBrowser) { | ||
| const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml); | ||
| return { | ||
| status, | ||
| headers: { | ||
| "Content-Type": "text/html", | ||
| ...response.headers | ||
| }, | ||
| body: html, | ||
| isHtml: true | ||
| }; | ||
| } | ||
| const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json"; | ||
| const body = unpaidResponse ? unpaidResponse.body : {}; | ||
| return { | ||
| status, | ||
| headers: { | ||
| "Content-Type": contentType, | ||
| ...response.headers | ||
| }, | ||
| body | ||
| }; | ||
| } | ||
| /** | ||
| * Create HTTP payment required response (v1 puts in body, v2 puts in header) | ||
| * | ||
| * @param paymentRequired - Payment required object | ||
| * @returns Headers and body for the HTTP response | ||
| */ | ||
| createHTTPPaymentRequiredResponse(paymentRequired) { | ||
| return { | ||
| headers: { | ||
| "PAYMENT-REQUIRED": encodePaymentRequiredHeader(paymentRequired) | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Create settlement response headers | ||
| * | ||
| * @param settleResponse - Settlement response | ||
| * @returns Headers to add to response | ||
| */ | ||
| createSettlementHeaders(settleResponse) { | ||
| const encoded = encodePaymentResponseHeader(settleResponse); | ||
| return { "PAYMENT-RESPONSE": encoded }; | ||
| } | ||
| /** | ||
| * Parse route pattern into verb and regex | ||
| * | ||
| * @param pattern - Route pattern like "GET /api/*", "/api/[id]", or "/api/:id" | ||
| * @returns Parsed pattern with verb and regex | ||
| */ | ||
| parseRoutePattern(pattern) { | ||
| const [verb, path] = pattern.includes(" ") ? pattern.split(/\s+/) : ["*", pattern]; | ||
| const regex = new RegExp( | ||
| `^${path.replace(/\\/g, "\\\\").replace(/[$()+.?^{|}]/g, "\\$&").replace(/\*/g, ".*?").replace(/\[([^\]]+)\]/g, "[^/]+").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "[^/]+").replace(/\//g, "\\/")}$`, | ||
| "i" | ||
| ); | ||
| return { verb: verb.toUpperCase(), regex, path }; | ||
| } | ||
| /** | ||
| * Normalize path for matching | ||
| * | ||
| * @param path - Raw path from request | ||
| * @returns Normalized path | ||
| */ | ||
| normalizePath(path) { | ||
| const pathWithoutQuery = path.split(/[?#]/)[0]; | ||
| const parts = pathWithoutQuery.split(/(%2[fF]|%5[cC])/); | ||
| const decoded = parts.map((part, i) => { | ||
| if (i % 2 === 1) return part; | ||
| try { | ||
| return decodeURIComponent(part); | ||
| } catch { | ||
| return part; | ||
| } | ||
| }).join(""); | ||
| return decoded.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/(.+?)\/+$/, "$1"); | ||
| } | ||
| /** | ||
| * Generate paywall HTML for browser requests | ||
| * | ||
| * @param paymentRequired - Payment required response | ||
| * @param paywallConfig - Optional paywall configuration | ||
| * @param customHtml - Optional custom HTML template | ||
| * @returns HTML string | ||
| */ | ||
| generatePaywallHTML(paymentRequired, paywallConfig, customHtml) { | ||
| if (customHtml) { | ||
| return customHtml; | ||
| } | ||
| if (this.paywallProvider) { | ||
| return this.paywallProvider.generateHtml(paymentRequired, paywallConfig); | ||
| } | ||
| try { | ||
| const paywall = __require("@x402/paywall"); | ||
| const displayAmount = this.getDisplayAmount(paymentRequired); | ||
| const resource = paymentRequired.resource; | ||
| return paywall.getPaywallHtml({ | ||
| amount: displayAmount, | ||
| paymentRequired, | ||
| currentUrl: resource?.url || paywallConfig?.currentUrl || "", | ||
| testnet: paywallConfig?.testnet ?? true, | ||
| appName: paywallConfig?.appName, | ||
| appLogo: paywallConfig?.appLogo, | ||
| sessionTokenEndpoint: paywallConfig?.sessionTokenEndpoint | ||
| }); | ||
| } catch { | ||
| } | ||
| return FALLBACK_PAYWALL_HTML; | ||
| } | ||
| /** | ||
| * Extract display amount from payment requirements. | ||
| * Uses the registered scheme's decimal precision for the asset, falling back to 6. | ||
| * | ||
| * @param paymentRequired - The payment required object | ||
| * @returns The display amount in decimal format | ||
| */ | ||
| getDisplayAmount(paymentRequired) { | ||
| const accepts = paymentRequired.accepts; | ||
| if (accepts && accepts.length > 0) { | ||
| const firstReq = accepts[0]; | ||
| if ("amount" in firstReq) { | ||
| const decimals = this.ResourceServer.getAssetDecimalsForRequirements(firstReq); | ||
| return parseFloat(firstReq.amount) / 10 ** decimals; | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
| }; | ||
| // src/http/httpFacilitatorClient.ts | ||
| var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator"; | ||
| var GET_SUPPORTED_RETRIES = 3; | ||
| var GET_SUPPORTED_RETRY_DELAY_MS = 1e3; | ||
| var MAX_RETRY_DELAY_MS = 3e4; | ||
| function computeRetryDelay(retryAfter, attempt) { | ||
| let delay = null; | ||
| if (retryAfter !== null) { | ||
| const seconds = Number(retryAfter); | ||
| if (!isNaN(seconds)) { | ||
| delay = seconds * 1e3; | ||
| } else { | ||
| const retryDate = Date.parse(retryAfter); | ||
| if (!isNaN(retryDate)) { | ||
| delay = retryDate - Date.now(); | ||
| } | ||
| } | ||
| } | ||
| if (delay === null || delay <= 0) { | ||
| delay = GET_SUPPORTED_RETRY_DELAY_MS * Math.pow(2, attempt); | ||
| } | ||
| return Math.min(delay, MAX_RETRY_DELAY_MS); | ||
| } | ||
| var verifyResponseSchema = z.object({ | ||
| isValid: z.boolean(), | ||
| invalidReason: z.string().nullish().transform((v) => v ?? void 0), | ||
| invalidMessage: z.string().nullish().transform((v) => v ?? void 0), | ||
| payer: z.string().nullish().transform((v) => v ?? void 0), | ||
| extensions: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0), | ||
| extra: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0) | ||
| }); | ||
| var settleResponseSchema = z.object({ | ||
| success: z.boolean(), | ||
| errorReason: z.string().nullish().transform((v) => v ?? void 0), | ||
| errorMessage: z.string().nullish().transform((v) => v ?? void 0), | ||
| payer: z.string().nullish().transform((v) => v ?? void 0), | ||
| transaction: z.string(), | ||
| network: z.custom((value) => typeof value === "string"), | ||
| amount: z.string().nullish().transform((v) => v ?? void 0), | ||
| extensions: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0), | ||
| extra: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0) | ||
| }); | ||
| var supportedKindSchema = z.object({ | ||
| x402Version: z.number(), | ||
| scheme: z.string(), | ||
| network: z.custom( | ||
| (value) => typeof value === "string" | ||
| ), | ||
| extra: z.record(z.string(), z.unknown()).nullish().transform((v) => v ?? void 0) | ||
| }); | ||
| var supportedResponseSchema = z.object({ | ||
| kinds: z.array(supportedKindSchema), | ||
| extensions: z.array(z.string()).default([]), | ||
| signers: z.record(z.string(), z.array(z.string())).default({}) | ||
| }); | ||
| function responseExcerpt(text, limit = 200) { | ||
| const compact = text.trim().replace(/\s+/g, " "); | ||
| if (!compact) { | ||
| return "<empty response>"; | ||
| } | ||
| if (compact.length <= limit) { | ||
| return compact; | ||
| } | ||
| return `${compact.slice(0, limit - 3)}...`; | ||
| } | ||
| var EXTENSION_RESPONSE_LOG_FIELD_ALLOWLIST = ["status", "rejectedReason", "reason", "code"]; | ||
| function logExtensionResponsesHeader(response) { | ||
| const header = response.headers.get("EXTENSION-RESPONSES"); | ||
| if (!header) return; | ||
| try { | ||
| const decoded = JSON.parse(safeBase64Decode(header)); | ||
| if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return; | ||
| const sanitized = {}; | ||
| for (const [extensionKey, payload] of Object.entries(decoded)) { | ||
| const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {}; | ||
| const filtered = {}; | ||
| for (const key of EXTENSION_RESPONSE_LOG_FIELD_ALLOWLIST) { | ||
| if (source[key] !== void 0) { | ||
| filtered[key] = source[key]; | ||
| } | ||
| } | ||
| sanitized[extensionKey] = filtered; | ||
| } | ||
| console.log(`[x402] extension responses: ${JSON.stringify(sanitized)}`); | ||
| } catch { | ||
| } | ||
| } | ||
| async function parseSuccessResponse(response, schema, operation) { | ||
| const text = await response.text(); | ||
| let data; | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| throw new FacilitatorResponseError( | ||
| `Facilitator ${operation} returned invalid JSON: ${responseExcerpt(text)}` | ||
| ); | ||
| } | ||
| const parsed = schema.safeParse(data); | ||
| if (!parsed.success) { | ||
| throw new FacilitatorResponseError( | ||
| `Facilitator ${operation} returned invalid data: ${responseExcerpt(text)}` | ||
| ); | ||
| } | ||
| return parsed.data; | ||
| } | ||
| var HTTPFacilitatorClient = class { | ||
| /** | ||
| * Creates a new HTTPFacilitatorClient instance. | ||
| * | ||
| * @param config - Configuration options for the facilitator client | ||
| */ | ||
| constructor(config) { | ||
| this.url = (config?.url || DEFAULT_FACILITATOR_URL).replace(/\/+$/, ""); | ||
| this._createAuthHeaders = config?.createAuthHeaders; | ||
| } | ||
| /** | ||
| * Verify a payment with the facilitator | ||
| * | ||
| * @param paymentPayload - The payment to verify | ||
| * @param paymentRequirements - The requirements to verify against | ||
| * @returns Verification response | ||
| */ | ||
| async verify(paymentPayload, paymentRequirements) { | ||
| let headers = { | ||
| "Content-Type": "application/json" | ||
| }; | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this.createAuthHeaders("verify"); | ||
| headers = { ...headers, ...authHeaders.headers }; | ||
| } | ||
| const response = await fetch(`${this.url}/verify`, { | ||
| method: "POST", | ||
| headers, | ||
| redirect: "follow", | ||
| body: JSON.stringify({ | ||
| x402Version: paymentPayload.x402Version, | ||
| paymentPayload: this.toJsonSafe(paymentPayload), | ||
| paymentRequirements: this.toJsonSafe(paymentRequirements) | ||
| }) | ||
| }); | ||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| let data; | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| throw new Error(`Facilitator verify failed (${response.status}): ${responseExcerpt(text)}`); | ||
| } | ||
| if (typeof data === "object" && data !== null && "isValid" in data) { | ||
| throw new VerifyError(response.status, data); | ||
| } | ||
| throw new Error( | ||
| `Facilitator verify failed (${response.status}): ${responseExcerpt(JSON.stringify(data))}` | ||
| ); | ||
| } | ||
| const verifyResult = await parseSuccessResponse(response, verifyResponseSchema, "verify"); | ||
| logExtensionResponsesHeader(response); | ||
| return verifyResult; | ||
| } | ||
| /** | ||
| * Settle a payment with the facilitator | ||
| * | ||
| * @param paymentPayload - The payment to settle | ||
| * @param paymentRequirements - The requirements for settlement | ||
| * @returns Settlement response | ||
| */ | ||
| async settle(paymentPayload, paymentRequirements) { | ||
| let headers = { | ||
| "Content-Type": "application/json" | ||
| }; | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this.createAuthHeaders("settle"); | ||
| headers = { ...headers, ...authHeaders.headers }; | ||
| } | ||
| const response = await fetch(`${this.url}/settle`, { | ||
| method: "POST", | ||
| headers, | ||
| redirect: "follow", | ||
| body: JSON.stringify({ | ||
| x402Version: paymentPayload.x402Version, | ||
| paymentPayload: this.toJsonSafe(paymentPayload), | ||
| paymentRequirements: this.toJsonSafe(paymentRequirements) | ||
| }) | ||
| }); | ||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| let data; | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| throw new Error(`Facilitator settle failed (${response.status}): ${responseExcerpt(text)}`); | ||
| } | ||
| if (typeof data === "object" && data !== null && "success" in data) { | ||
| throw new SettleError(response.status, data); | ||
| } | ||
| throw new Error( | ||
| `Facilitator settle failed (${response.status}): ${responseExcerpt(JSON.stringify(data))}` | ||
| ); | ||
| } | ||
| const settleResult = await parseSuccessResponse(response, settleResponseSchema, "settle"); | ||
| logExtensionResponsesHeader(response); | ||
| return settleResult; | ||
| } | ||
| /** | ||
| * Get supported payment kinds and extensions from the facilitator. | ||
| * Retries with exponential backoff on 429 rate limit errors. | ||
| * | ||
| * @returns Supported payment kinds and extensions | ||
| */ | ||
| async getSupported() { | ||
| let headers = { | ||
| "Content-Type": "application/json" | ||
| }; | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this.createAuthHeaders("supported"); | ||
| headers = { ...headers, ...authHeaders.headers }; | ||
| } | ||
| let lastError = null; | ||
| for (let attempt = 0; attempt < GET_SUPPORTED_RETRIES; attempt++) { | ||
| const response = await fetch(`${this.url}/supported`, { | ||
| method: "GET", | ||
| headers, | ||
| redirect: "follow" | ||
| }); | ||
| if (response.ok) { | ||
| return parseSuccessResponse(response, supportedResponseSchema, "supported"); | ||
| } | ||
| const errorText = await response.text().catch(() => response.statusText); | ||
| lastError = new Error( | ||
| `Facilitator getSupported failed (${response.status}): ${responseExcerpt(errorText)}` | ||
| ); | ||
| if (response.status === 429 && attempt < GET_SUPPORTED_RETRIES - 1) { | ||
| const delay = computeRetryDelay(response.headers.get("Retry-After"), attempt); | ||
| await new Promise((resolve) => setTimeout(resolve, delay)); | ||
| continue; | ||
| } | ||
| throw lastError; | ||
| } | ||
| throw lastError ?? new Error("Facilitator getSupported failed after retries"); | ||
| } | ||
| /** | ||
| * Creates authentication headers for a specific path. | ||
| * | ||
| * @param path - The path to create authentication headers for (e.g., "verify", "settle", "supported") | ||
| * @returns An object containing the authentication headers for the specified path | ||
| */ | ||
| async createAuthHeaders(path) { | ||
| if (this._createAuthHeaders) { | ||
| const authHeaders = await this._createAuthHeaders(); | ||
| return { | ||
| headers: authHeaders[path] ?? {} | ||
| }; | ||
| } | ||
| return { | ||
| headers: {} | ||
| }; | ||
| } | ||
| /** | ||
| * Helper to convert objects to JSON-safe format. | ||
| * Handles BigInt and other non-JSON types. | ||
| * | ||
| * @param obj - The object to convert | ||
| * @returns The JSON-safe representation of the object | ||
| */ | ||
| toJsonSafe(obj) { | ||
| return JSON.parse( | ||
| JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() : value) | ||
| ); | ||
| } | ||
| }; | ||
| // src/http/x402HTTPClient.ts | ||
| var x402HTTPClient = class { | ||
| /** | ||
| * Creates a new x402HTTPClient instance. | ||
| * | ||
| * @param client - The underlying x402Client for payment logic | ||
| */ | ||
| constructor(client) { | ||
| this.client = client; | ||
| this.paymentRequiredHooks = []; | ||
| } | ||
| /** | ||
| * Register a hook to handle 402 responses before payment. | ||
| * Hooks run in order; first to return headers wins. | ||
| * | ||
| * @param hook - The hook function to register | ||
| * @returns This instance for chaining | ||
| */ | ||
| onPaymentRequired(hook) { | ||
| this.paymentRequiredHooks.push(hook); | ||
| return this; | ||
| } | ||
| /** | ||
| * Run hooks and return headers if any hook provides them. | ||
| * | ||
| * @param paymentRequired - The payment required response from the server | ||
| * @returns Headers to use for retry, or null to proceed to payment | ||
| */ | ||
| async handlePaymentRequired(paymentRequired) { | ||
| for (const hook of this.getPaymentRequiredHooks(paymentRequired)) { | ||
| const result = await hook({ paymentRequired }); | ||
| if (result?.headers) { | ||
| return result.headers; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| /** | ||
| * Encodes a payment payload into appropriate HTTP headers based on version. | ||
| * | ||
| * @param paymentPayload - The payment payload to encode | ||
| * @returns HTTP headers containing the encoded payment signature | ||
| */ | ||
| encodePaymentSignatureHeader(paymentPayload) { | ||
| switch (paymentPayload.x402Version) { | ||
| case 2: | ||
| return { | ||
| "PAYMENT-SIGNATURE": encodePaymentSignatureHeader(paymentPayload) | ||
| }; | ||
| case 1: | ||
| return { | ||
| "X-PAYMENT": encodePaymentSignatureHeader(paymentPayload) | ||
| }; | ||
| default: | ||
| throw new Error( | ||
| `Unsupported x402 version: ${paymentPayload.x402Version}` | ||
| ); | ||
| } | ||
| } | ||
| /** | ||
| * Extracts payment required information from HTTP response. | ||
| * | ||
| * @param getHeader - Function to retrieve header value by name (case-insensitive) | ||
| * @param body - Optional response body for v1 compatibility | ||
| * @returns The payment required object | ||
| */ | ||
| getPaymentRequiredResponse(getHeader, body) { | ||
| const paymentRequired = getHeader("PAYMENT-REQUIRED"); | ||
| if (paymentRequired) { | ||
| return decodePaymentRequiredHeader(paymentRequired); | ||
| } | ||
| if (body && body instanceof Object && "x402Version" in body && body.x402Version === 1) { | ||
| return body; | ||
| } | ||
| throw new Error("Invalid payment required response"); | ||
| } | ||
| /** | ||
| * Extracts payment settlement response from HTTP headers. | ||
| * | ||
| * @param getHeader - Function to retrieve header value by name (case-insensitive) | ||
| * @returns The settlement response object | ||
| */ | ||
| getPaymentSettleResponse(getHeader) { | ||
| const paymentResponse = getHeader("PAYMENT-RESPONSE"); | ||
| if (paymentResponse) { | ||
| return decodePaymentResponseHeader(paymentResponse); | ||
| } | ||
| const xPaymentResponse = getHeader("X-PAYMENT-RESPONSE"); | ||
| if (xPaymentResponse) { | ||
| return decodePaymentResponseHeader(xPaymentResponse); | ||
| } | ||
| throw new Error("Payment response header not found"); | ||
| } | ||
| /** | ||
| * Creates a payment payload for the given payment requirements. | ||
| * Delegates to the underlying x402Client. | ||
| * | ||
| * @param paymentRequired - The payment required response from the server | ||
| * @returns Promise resolving to the payment payload | ||
| */ | ||
| async createPaymentPayload(paymentRequired) { | ||
| return this.client.createPaymentPayload(paymentRequired); | ||
| } | ||
| /** | ||
| * Parses response headers into protocol types, fires payment response hooks (v2 only), | ||
| * and returns whether a hook signaled recovery. | ||
| * | ||
| * Called by transport wrappers (fetch, axios) after the paid request completes. | ||
| * | ||
| * @param paymentPayload - The payload that was sent with the request | ||
| * @param getHeader - Function to retrieve a response header by name | ||
| * @param status - The HTTP status code of the response | ||
| * @returns Whether a hook recovered and the parsed settle response (if any) | ||
| */ | ||
| async processPaymentResult(paymentPayload, getHeader, status) { | ||
| let settleResponse; | ||
| try { | ||
| settleResponse = this.getPaymentSettleResponse(getHeader); | ||
| } catch { | ||
| } | ||
| if (paymentPayload.x402Version === 1) { | ||
| return { recovered: false, settleResponse }; | ||
| } | ||
| let paymentRequired; | ||
| if (!settleResponse && status === 402) { | ||
| try { | ||
| paymentRequired = this.getPaymentRequiredResponse(getHeader); | ||
| } catch { | ||
| } | ||
| } | ||
| const requirements = paymentPayload.accepted; | ||
| if (!requirements) { | ||
| throw new Error("Invalid x402 v2 payment payload: missing `accepted`"); | ||
| } | ||
| const ctx = { | ||
| paymentPayload, | ||
| requirements, | ||
| ...settleResponse ? { settleResponse } : {}, | ||
| ...paymentRequired ? { paymentRequired } : {} | ||
| }; | ||
| const result = await this.client.handlePaymentResponse(ctx); | ||
| return { recovered: result?.recovered === true, settleResponse }; | ||
| } | ||
| /** | ||
| * Parses a fetch Response into a discriminated `x402PaymentResult` for app-level convenience. | ||
| * | ||
| * @param response - The fetch Response to process | ||
| * @returns A discriminated union describing the payment outcome | ||
| */ | ||
| async processResponse(response) { | ||
| const getHeader = (name) => response.headers.get(name); | ||
| let settleResponse; | ||
| try { | ||
| settleResponse = this.getPaymentSettleResponse(getHeader); | ||
| } catch { | ||
| } | ||
| const contentType = response.headers.get("content-type") ?? ""; | ||
| const body = contentType.includes("application/json") ? await response.json() : await response.text(); | ||
| if (settleResponse && settleResponse.success) { | ||
| return { kind: "success", response, body, settleResponse }; | ||
| } | ||
| if (settleResponse && !settleResponse.success) { | ||
| return { kind: "settle_failed", response, body, settleResponse }; | ||
| } | ||
| if (response.status === 402) { | ||
| try { | ||
| const paymentRequired = this.getPaymentRequiredResponse(getHeader, body); | ||
| return { kind: "payment_required", response, paymentRequired }; | ||
| } catch { | ||
| } | ||
| } | ||
| if (response.ok) { | ||
| return { kind: "passthrough", response, body }; | ||
| } | ||
| return { kind: "error", response, status: response.status, body }; | ||
| } | ||
| /** | ||
| * Manual HTTP hooks run before extension hooks scoped to the 402 response. | ||
| * | ||
| * @param paymentRequired - The payment required response from the server | ||
| * @returns Hooks in invocation order | ||
| */ | ||
| getPaymentRequiredHooks(paymentRequired) { | ||
| const hooks = [...this.paymentRequiredHooks]; | ||
| const declaredExtensions = paymentRequired.extensions; | ||
| if (!declaredExtensions) return hooks; | ||
| for (const extension of this.client.getExtensions()) { | ||
| const httpExtension = extension; | ||
| const hook = httpExtension.transportHooks?.http?.onPaymentRequired; | ||
| if (!hook || !(extension.key in declaredExtensions)) continue; | ||
| hooks.push((context) => hook(declaredExtensions[extension.key], context)); | ||
| } | ||
| return hooks; | ||
| } | ||
| }; | ||
| // src/http/index.ts | ||
| function encodePaymentSignatureHeader(paymentPayload) { | ||
| return safeBase64Encode(JSON.stringify(paymentPayload)); | ||
| } | ||
| function decodePaymentSignatureHeader(paymentSignatureHeader) { | ||
| if (!Base64EncodedRegex.test(paymentSignatureHeader)) { | ||
| throw new Error("Invalid payment signature header"); | ||
| } | ||
| return JSON.parse(safeBase64Decode(paymentSignatureHeader)); | ||
| } | ||
| function encodePaymentRequiredHeader(paymentRequired) { | ||
| return safeBase64Encode(JSON.stringify(paymentRequired)); | ||
| } | ||
| function decodePaymentRequiredHeader(paymentRequiredHeader) { | ||
| if (!Base64EncodedRegex.test(paymentRequiredHeader)) { | ||
| throw new Error("Invalid payment required header"); | ||
| } | ||
| return JSON.parse(safeBase64Decode(paymentRequiredHeader)); | ||
| } | ||
| function encodePaymentResponseHeader(paymentResponse) { | ||
| return safeBase64Encode(JSON.stringify(paymentResponse)); | ||
| } | ||
| function decodePaymentResponseHeader(paymentResponseHeader) { | ||
| if (!Base64EncodedRegex.test(paymentResponseHeader)) { | ||
| throw new Error("Invalid payment response header"); | ||
| } | ||
| return JSON.parse(safeBase64Decode(paymentResponseHeader)); | ||
| } | ||
| export { | ||
| SETTLEMENT_OVERRIDES_HEADER, | ||
| checkIfBazaarNeeded, | ||
| RouteConfigurationError, | ||
| x402HTTPResourceServer, | ||
| HTTPFacilitatorClient, | ||
| encodePaymentSignatureHeader, | ||
| decodePaymentSignatureHeader, | ||
| encodePaymentRequiredHeader, | ||
| decodePaymentRequiredHeader, | ||
| encodePaymentResponseHeader, | ||
| decodePaymentResponseHeader, | ||
| x402HTTPClient | ||
| }; | ||
| //# sourceMappingURL=chunk-W4OPBTK7.mjs.map |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1420884
3.36%13140
2.73%