UNPKG

@payai/x402

Version:

PayAI-distributed wrapper for @x402/core v2

260 lines (259 loc) 9.47 kB
import { x402HTTPClient } from "../chunk-CWIHZ5RE.mjs"; import { x402Version } from "../chunk-VE37GDG2.mjs"; import "../chunk-L2RJI3BI.mjs"; import { findByNetworkAndScheme, findSchemesByNetwork } from "../chunk-TDLQZ6MP.mjs"; import "../chunk-BJTO5JO5.mjs"; // src/client/x402Client.ts var x402Client = class _x402Client { /** * Creates a new x402Client instance. * * @param paymentRequirementsSelector - Function to select payment requirements from available options */ constructor(paymentRequirementsSelector) { this.registeredClientSchemes = /* @__PURE__ */ new Map(); this.policies = []; this.beforePaymentCreationHooks = []; this.afterPaymentCreationHooks = []; this.onPaymentCreationFailureHooks = []; this.paymentRequirementsSelector = paymentRequirementsSelector || ((x402Version2, accepts) => accepts[0]); } /** * Creates a new x402Client instance from a configuration object. * * @param config - The client configuration including schemes, policies, and payment requirements selector * @returns A configured x402Client instance */ static fromConfig(config) { const client = new _x402Client(config.paymentRequirementsSelector); config.schemes.forEach((scheme) => { if (scheme.x402Version === 1) { client.registerV1(scheme.network, scheme.client); } else { client.register(scheme.network, scheme.client); } }); config.policies?.forEach((policy) => { client.registerPolicy(policy); }); return client; } /** * Registers a scheme client for the current x402 version. * * @param network - The network to register the client for * @param client - The scheme network client to register * @returns The x402Client instance for chaining */ register(network, client) { return this._registerScheme(x402Version, network, client); } /** * Registers a scheme client for x402 version 1. * * @param network - The v1 network identifier (e.g., 'base-sepolia', 'solana-devnet') * @param client - The scheme network client to register * @returns The x402Client instance for chaining */ registerV1(network, client) { return this._registerScheme(1, network, client); } /** * Registers a policy to filter or transform payment requirements. * * Policies are applied in order after filtering by registered schemes * and before the selector chooses the final payment requirement. * * @param policy - Function to filter/transform payment requirements * @returns The x402Client instance for chaining * * @example * ```typescript * // Prefer cheaper options * client.registerPolicy((version, reqs) => * reqs.filter(r => BigInt(r.value) < BigInt('1000000')) * ); * * // Prefer specific networks * client.registerPolicy((version, reqs) => * reqs.filter(r => r.network.startsWith('eip155:')) * ); * ``` */ registerPolicy(policy) { this.policies.push(policy); return this; } /** * Register a hook to execute before payment payload creation. * Can abort creation by returning { abort: true, reason: string } * * @param hook - The hook function to register * @returns The x402Client instance for chaining */ onBeforePaymentCreation(hook) { this.beforePaymentCreationHooks.push(hook); return this; } /** * Register a hook to execute after successful payment payload creation. * * @param hook - The hook function to register * @returns The x402Client instance for chaining */ onAfterPaymentCreation(hook) { this.afterPaymentCreationHooks.push(hook); return this; } /** * Register a hook to execute when payment payload creation fails. * Can recover from failure by returning { recovered: true, payload: PaymentPayload } * * @param hook - The hook function to register * @returns The x402Client instance for chaining */ onPaymentCreationFailure(hook) { this.onPaymentCreationFailureHooks.push(hook); return this; } /** * Creates a payment payload based on a PaymentRequired response. * * Automatically extracts x402Version, resource, and extensions from the PaymentRequired * response and constructs a complete PaymentPayload with the accepted requirements. * * @param paymentRequired - The PaymentRequired response from the server * @returns Promise resolving to the complete payment payload */ async createPaymentPayload(paymentRequired) { const clientSchemesByNetwork = this.registeredClientSchemes.get(paymentRequired.x402Version); if (!clientSchemesByNetwork) { throw new Error(`No client registered for x402 version: ${paymentRequired.x402Version}`); } const requirements = this.selectPaymentRequirements(paymentRequired.x402Version, paymentRequired.accepts); const context = { paymentRequired, selectedRequirements: requirements }; for (const hook of this.beforePaymentCreationHooks) { const result = await hook(context); if (result && "abort" in result && result.abort) { throw new Error(`Payment creation aborted: ${result.reason}`); } } try { const schemeNetworkClient = findByNetworkAndScheme(clientSchemesByNetwork, requirements.scheme, requirements.network); if (!schemeNetworkClient) { throw new Error(`No client registered for scheme: ${requirements.scheme} and network: ${requirements.network}`); } const partialPayload = await schemeNetworkClient.createPaymentPayload(paymentRequired.x402Version, requirements); let paymentPayload; if (partialPayload.x402Version == 1) { paymentPayload = partialPayload; } else { paymentPayload = { ...partialPayload, extensions: paymentRequired.extensions, resource: paymentRequired.resource, accepted: requirements }; } const createdContext = { ...context, paymentPayload }; for (const hook of this.afterPaymentCreationHooks) { await hook(createdContext); } return paymentPayload; } catch (error) { const failureContext = { ...context, error }; for (const hook of this.onPaymentCreationFailureHooks) { const result = await hook(failureContext); if (result && "recovered" in result && result.recovered) { return result.payload; } } throw error; } } /** * Selects appropriate payment requirements based on registered clients and policies. * * Selection process: * 1. Filter by registered schemes (network + scheme support) * 2. Apply all registered policies in order * 3. Use selector to choose final requirement * * @param x402Version - The x402 protocol version * @param paymentRequirements - Array of available payment requirements * @returns The selected payment requirements */ selectPaymentRequirements(x402Version2, paymentRequirements) { const clientSchemesByNetwork = this.registeredClientSchemes.get(x402Version2); if (!clientSchemesByNetwork) { throw new Error(`No client registered for x402 version: ${x402Version2}`); } const supportedPaymentRequirements = paymentRequirements.filter((requirement) => { let clientSchemes = findSchemesByNetwork(clientSchemesByNetwork, requirement.network); if (!clientSchemes) { return false; } return clientSchemes.has(requirement.scheme); }); if (supportedPaymentRequirements.length === 0) { throw new Error(`No network/scheme registered for x402 version: ${x402Version2} which comply with the payment requirements. ${JSON.stringify({ x402Version: x402Version2, paymentRequirements, x402Versions: Array.from(this.registeredClientSchemes.keys()), networks: Array.from(clientSchemesByNetwork.keys()), schemes: Array.from(clientSchemesByNetwork.values()).map((schemes) => Array.from(schemes.keys())).flat() })}`); } let filteredRequirements = supportedPaymentRequirements; for (const policy of this.policies) { filteredRequirements = policy(x402Version2, filteredRequirements); if (filteredRequirements.length === 0) { throw new Error(`All payment requirements were filtered out by policies for x402 version: ${x402Version2}`); } } return this.paymentRequirementsSelector(x402Version2, filteredRequirements); } /** * Internal method to register a scheme client. * * @param x402Version - The x402 protocol version * @param network - The network to register the client for * @param client - The scheme network client to register * @returns The x402Client instance for chaining */ _registerScheme(x402Version2, network, client) { if (!this.registeredClientSchemes.has(x402Version2)) { this.registeredClientSchemes.set(x402Version2, /* @__PURE__ */ new Map()); } const clientSchemesByNetwork = this.registeredClientSchemes.get(x402Version2); if (!clientSchemesByNetwork.has(network)) { clientSchemesByNetwork.set(network, /* @__PURE__ */ new Map()); } const clientByScheme = clientSchemesByNetwork.get(network); if (!clientByScheme.has(client.scheme)) { clientByScheme.set(client.scheme, client); } return this; } }; export { x402Client, x402HTTPClient }; //# sourceMappingURL=index.mjs.map