UNPKG

@payai/x402

Version:

PayAI-distributed wrapper for @x402/core v2

423 lines (420 loc) 14.5 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/facilitator/index.ts var facilitator_exports = {}; __export(facilitator_exports, { x402Facilitator: () => x402Facilitator }); module.exports = __toCommonJS(facilitator_exports); // src/index.ts var x402Version = 2; // src/facilitator/x402Facilitator.ts var x402Facilitator = class { constructor() { this.registeredFacilitatorSchemes = /* @__PURE__ */ new Map(); this.extensions = /* @__PURE__ */ new Map(); this.beforeVerifyHooks = []; this.afterVerifyHooks = []; this.onVerifyFailureHooks = []; this.beforeSettleHooks = []; this.afterSettleHooks = []; this.onSettleFailureHooks = []; } /** * Registers a scheme facilitator for the current x402 version. * Networks are stored and used for getSupported() - no need to specify them later. * * @param networks - Single network or array of networks this facilitator supports * @param facilitator - The scheme network facilitator to register * @returns The x402Facilitator instance for chaining */ register(networks, facilitator) { const networksArray = Array.isArray(networks) ? networks : [networks]; return this._registerScheme(x402Version, networksArray, facilitator); } /** * Registers a scheme facilitator for x402 version 1. * Networks are stored and used for getSupported() - no need to specify them later. * * @param networks - Single network or array of networks this facilitator supports * @param facilitator - The scheme network facilitator to register * @returns The x402Facilitator instance for chaining */ registerV1(networks, facilitator) { const networksArray = Array.isArray(networks) ? networks : [networks]; return this._registerScheme(1, networksArray, facilitator); } /** * Registers a protocol extension. * * @param extension - The extension object to register * @returns The x402Facilitator instance for chaining */ registerExtension(extension) { this.extensions.set(extension.key, extension); return this; } /** * Gets the list of registered extension keys. * * @returns Array of extension key strings */ getExtensions() { return Array.from(this.extensions.keys()); } /** * Gets a registered extension by key. * * @param key - The extension key to look up * @returns The extension object, or undefined if not registered */ getExtension(key) { return this.extensions.get(key); } /** * Register a hook to execute before facilitator payment verification. * Can abort verification by returning { abort: true, reason: string } * * @param hook - The hook function to register * @returns The x402Facilitator instance for chaining */ onBeforeVerify(hook) { this.beforeVerifyHooks.push(hook); return this; } /** * Register a hook to execute after successful facilitator payment verification (isValid: true). * This hook is NOT called when verification fails (isValid: false) - use onVerifyFailure for that. * * @param hook - The hook function to register * @returns The x402Facilitator instance for chaining */ onAfterVerify(hook) { this.afterVerifyHooks.push(hook); return this; } /** * Register a hook to execute when facilitator payment verification fails. * Called when: verification returns isValid: false, or an exception is thrown during verification. * Can recover from failure by returning { recovered: true, result: VerifyResponse } * * @param hook - The hook function to register * @returns The x402Facilitator instance for chaining */ onVerifyFailure(hook) { this.onVerifyFailureHooks.push(hook); return this; } /** * Register a hook to execute before facilitator payment settlement. * Can abort settlement by returning { abort: true, reason: string } * * @param hook - The hook function to register * @returns The x402Facilitator instance for chaining */ onBeforeSettle(hook) { this.beforeSettleHooks.push(hook); return this; } /** * Register a hook to execute after successful facilitator payment settlement. * * @param hook - The hook function to register * @returns The x402Facilitator instance for chaining */ onAfterSettle(hook) { this.afterSettleHooks.push(hook); return this; } /** * Register a hook to execute when facilitator payment settlement fails. * Can recover from failure by returning { recovered: true, result: SettleResponse } * * @param hook - The hook function to register * @returns The x402Facilitator instance for chaining */ onSettleFailure(hook) { this.onSettleFailureHooks.push(hook); return this; } /** * Gets supported payment kinds, extensions, and signers. * Uses networks registered during register() calls - no parameters needed. * Returns flat array format for backward compatibility with V1 clients. * * @returns Supported response with kinds as array (with version in each element), extensions, and signers */ getSupported() { const kinds = []; const signersByFamily = {}; for (const [version, schemeDataArray] of this.registeredFacilitatorSchemes) { for (const schemeData of schemeDataArray) { const { facilitator, networks } = schemeData; const scheme = facilitator.scheme; for (const network of networks) { const extra = facilitator.getExtra(network); kinds.push({ x402Version: version, scheme, network, ...extra && { extra } }); const family = facilitator.caipFamily; if (!signersByFamily[family]) { signersByFamily[family] = /* @__PURE__ */ new Set(); } facilitator.getSigners(network).forEach((signer) => signersByFamily[family].add(signer)); } } } const signers = {}; for (const [family, signerSet] of Object.entries(signersByFamily)) { signers[family] = Array.from(signerSet); } return { kinds, extensions: this.getExtensions(), signers }; } /** * Verifies a payment payload against requirements. * * @param paymentPayload - The payment payload to verify * @param paymentRequirements - The payment requirements to verify against * @returns Promise resolving to the verification response */ async verify(paymentPayload, paymentRequirements) { const context = { paymentPayload, requirements: paymentRequirements }; for (const hook of this.beforeVerifyHooks) { const result = await hook(context); if (result && "abort" in result && result.abort) { return { isValid: false, invalidReason: result.reason }; } } try { const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version); if (!schemeDataArray) { throw new Error( `No facilitator registered for x402 version: ${paymentPayload.x402Version}` ); } let schemeNetworkFacilitator; for (const schemeData of schemeDataArray) { if (schemeData.facilitator.scheme === paymentRequirements.scheme) { if (schemeData.networks.has(paymentRequirements.network)) { schemeNetworkFacilitator = schemeData.facilitator; break; } const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); if (patternRegex.test(paymentRequirements.network)) { schemeNetworkFacilitator = schemeData.facilitator; break; } } } if (!schemeNetworkFacilitator) { throw new Error( `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}` ); } const facilitatorContext = this.buildFacilitatorContext(); const verifyResult = await schemeNetworkFacilitator.verify( paymentPayload, paymentRequirements, facilitatorContext ); if (!verifyResult.isValid) { const failureContext = { ...context, error: new Error(verifyResult.invalidReason || "Verification failed") }; for (const hook of this.onVerifyFailureHooks) { const result = await hook(failureContext); if (result && "recovered" in result && result.recovered) { const recoveredContext = { ...context, result: result.result }; for (const hook2 of this.afterVerifyHooks) { await hook2(recoveredContext); } return result.result; } } return verifyResult; } const resultContext = { ...context, result: verifyResult }; for (const hook of this.afterVerifyHooks) { await hook(resultContext); } return verifyResult; } catch (error) { const failureContext = { ...context, error }; for (const hook of this.onVerifyFailureHooks) { const result = await hook(failureContext); if (result && "recovered" in result && result.recovered) { return result.result; } } throw error; } } /** * Settles a payment based on the payload and requirements. * * @param paymentPayload - The payment payload to settle * @param paymentRequirements - The payment requirements for settlement * @returns Promise resolving to the settlement response */ async settle(paymentPayload, paymentRequirements) { const context = { paymentPayload, requirements: paymentRequirements }; for (const hook of this.beforeSettleHooks) { const result = await hook(context); if (result && "abort" in result && result.abort) { throw new Error(`Settlement aborted: ${result.reason}`); } } try { const schemeDataArray = this.registeredFacilitatorSchemes.get(paymentPayload.x402Version); if (!schemeDataArray) { throw new Error( `No facilitator registered for x402 version: ${paymentPayload.x402Version}` ); } let schemeNetworkFacilitator; for (const schemeData of schemeDataArray) { if (schemeData.facilitator.scheme === paymentRequirements.scheme) { if (schemeData.networks.has(paymentRequirements.network)) { schemeNetworkFacilitator = schemeData.facilitator; break; } const patternRegex = new RegExp("^" + schemeData.pattern.replace("*", ".*") + "$"); if (patternRegex.test(paymentRequirements.network)) { schemeNetworkFacilitator = schemeData.facilitator; break; } } } if (!schemeNetworkFacilitator) { throw new Error( `No facilitator registered for scheme: ${paymentRequirements.scheme} and network: ${paymentRequirements.network}` ); } const facilitatorContext = this.buildFacilitatorContext(); const settleResult = await schemeNetworkFacilitator.settle( paymentPayload, paymentRequirements, facilitatorContext ); const resultContext = { ...context, result: settleResult }; for (const hook of this.afterSettleHooks) { await hook(resultContext); } return settleResult; } catch (error) { const failureContext = { ...context, error }; for (const hook of this.onSettleFailureHooks) { const result = await hook(failureContext); if (result && "recovered" in result && result.recovered) { return result.result; } } throw error; } } /** * Builds a FacilitatorContext from the registered extensions map. * Passed to mechanism verify/settle so they can access extension capabilities. * * @returns A FacilitatorContext backed by this facilitator's registered extensions */ buildFacilitatorContext() { const extensionsMap = this.extensions; return { getExtension(key) { return extensionsMap.get(key); } }; } /** * Internal method to register a scheme facilitator. * * @param x402Version - The x402 protocol version * @param networks - Array of concrete networks this facilitator supports * @param facilitator - The scheme network facilitator to register * @returns The x402Facilitator instance for chaining */ _registerScheme(x402Version2, networks, facilitator) { if (!this.registeredFacilitatorSchemes.has(x402Version2)) { this.registeredFacilitatorSchemes.set(x402Version2, []); } const schemeDataArray = this.registeredFacilitatorSchemes.get(x402Version2); schemeDataArray.push({ facilitator, networks: new Set(networks), pattern: this.derivePattern(networks) }); return this; } /** * Derives a wildcard pattern from an array of networks. * If all networks share the same namespace, returns wildcard pattern. * Otherwise returns the first network for exact matching. * * @param networks - Array of networks * @returns Derived pattern for matching */ derivePattern(networks) { if (networks.length === 0) return ""; if (networks.length === 1) return networks[0]; const namespaces = networks.map((n) => n.split(":")[0]); const uniqueNamespaces = new Set(namespaces); if (uniqueNamespaces.size === 1) { return `${namespaces[0]}:*`; } return networks[0]; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { x402Facilitator }); //# sourceMappingURL=index.js.map