@payai/x402
Version:
PayAI-distributed wrapper for @x402/core v2
260 lines (259 loc) • 9.47 kB
JavaScript
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