moltspay 0.8.14 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -504
- package/dist/cli/index.js +553 -171
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +555 -173
- package/dist/cli/index.mjs.map +1 -1
- package/dist/facilitators/index.d.mts +61 -0
- package/dist/facilitators/index.d.ts +61 -0
- package/dist/facilitators/index.js +502 -0
- package/dist/facilitators/index.js.map +1 -0
- package/dist/facilitators/index.mjs +461 -0
- package/dist/facilitators/index.mjs.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +583 -190
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +580 -192
- package/dist/index.mjs.map +1 -1
- package/dist/registry-OsEO2dOu.d.mts +233 -0
- package/dist/registry-OsEO2dOu.d.ts +233 -0
- package/dist/server/index.d.mts +19 -15
- package/dist/server/index.d.ts +19 -15
- package/dist/server/index.js +514 -145
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +514 -145
- package/dist/server/index.mjs.map +1 -1
- package/package.json +7 -2
- package/schemas/moltspay.services.schema.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { B as BaseFacilitator, a as FacilitatorConfig, H as HealthCheckResult, X as X402PaymentPayload, f as X402PaymentRequirements, V as VerifyResult, e as SettleResult, b as FacilitatorFee } from '../registry-OsEO2dOu.js';
|
|
2
|
+
export { F as Facilitator, c as FacilitatorRegistry, d as FacilitatorSelection, S as SelectionStrategy, g as createRegistry, h as getDefaultRegistry } from '../registry-OsEO2dOu.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CDP Facilitator
|
|
6
|
+
*
|
|
7
|
+
* Coinbase Developer Platform x402 facilitator implementation.
|
|
8
|
+
* Supports both mainnet (Base) and testnet (Base Sepolia).
|
|
9
|
+
*
|
|
10
|
+
* @see https://docs.cdp.coinbase.com/x402/core-concepts/facilitator
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface CDPFacilitatorConfig extends FacilitatorConfig {
|
|
14
|
+
/** Use mainnet (true) or testnet (false, default) */
|
|
15
|
+
useMainnet?: boolean;
|
|
16
|
+
/** CDP API Key ID (required for mainnet) */
|
|
17
|
+
apiKeyId?: string;
|
|
18
|
+
/** CDP API Key Secret (required for mainnet) */
|
|
19
|
+
apiKeySecret?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* CDP (Coinbase Developer Platform) Facilitator
|
|
23
|
+
*
|
|
24
|
+
* Handles payment verification and settlement via Coinbase's x402 facilitator.
|
|
25
|
+
*/
|
|
26
|
+
declare class CDPFacilitator extends BaseFacilitator {
|
|
27
|
+
readonly name = "cdp";
|
|
28
|
+
readonly displayName = "Coinbase CDP";
|
|
29
|
+
readonly supportedNetworks: string[];
|
|
30
|
+
private endpoint;
|
|
31
|
+
private useMainnet;
|
|
32
|
+
private apiKeyId?;
|
|
33
|
+
private apiKeySecret?;
|
|
34
|
+
constructor(config?: CDPFacilitatorConfig);
|
|
35
|
+
/**
|
|
36
|
+
* Get auth headers for CDP API requests
|
|
37
|
+
*/
|
|
38
|
+
private getAuthHeaders;
|
|
39
|
+
/**
|
|
40
|
+
* Health check - verify facilitator is reachable
|
|
41
|
+
*/
|
|
42
|
+
healthCheck(): Promise<HealthCheckResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Verify payment signature with facilitator
|
|
45
|
+
*/
|
|
46
|
+
verify(paymentPayload: X402PaymentPayload, requirements: X402PaymentRequirements): Promise<VerifyResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Settle payment on-chain via facilitator
|
|
49
|
+
*/
|
|
50
|
+
settle(paymentPayload: X402PaymentPayload, requirements: X402PaymentRequirements): Promise<SettleResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Get CDP fee information
|
|
53
|
+
*/
|
|
54
|
+
getFee(): Promise<FacilitatorFee>;
|
|
55
|
+
/**
|
|
56
|
+
* Get configuration summary (for logging)
|
|
57
|
+
*/
|
|
58
|
+
getConfigSummary(): string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { BaseFacilitator, CDPFacilitator, type CDPFacilitatorConfig, FacilitatorConfig, FacilitatorFee, HealthCheckResult, SettleResult, VerifyResult, X402PaymentPayload, X402PaymentRequirements };
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/facilitators/index.ts
|
|
31
|
+
var facilitators_exports = {};
|
|
32
|
+
__export(facilitators_exports, {
|
|
33
|
+
BaseFacilitator: () => BaseFacilitator,
|
|
34
|
+
CDPFacilitator: () => CDPFacilitator,
|
|
35
|
+
FacilitatorRegistry: () => FacilitatorRegistry,
|
|
36
|
+
createRegistry: () => createRegistry,
|
|
37
|
+
getDefaultRegistry: () => getDefaultRegistry
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(facilitators_exports);
|
|
40
|
+
|
|
41
|
+
// src/facilitators/interface.ts
|
|
42
|
+
var BaseFacilitator = class {
|
|
43
|
+
supportsNetwork(network) {
|
|
44
|
+
return this.supportedNetworks.includes(network);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/facilitators/cdp.ts
|
|
49
|
+
var import_fs = require("fs");
|
|
50
|
+
var path = __toESM(require("path"));
|
|
51
|
+
var X402_VERSION = 2;
|
|
52
|
+
var CDP_MAINNET_URL = "https://api.cdp.coinbase.com/platform/v2/x402";
|
|
53
|
+
var CDP_TESTNET_URL = "https://www.x402.org/facilitator";
|
|
54
|
+
function loadEnvFile() {
|
|
55
|
+
const envPaths = [
|
|
56
|
+
path.join(process.cwd(), ".env"),
|
|
57
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
58
|
+
];
|
|
59
|
+
for (const envPath of envPaths) {
|
|
60
|
+
if ((0, import_fs.existsSync)(envPath)) {
|
|
61
|
+
try {
|
|
62
|
+
const content = (0, import_fs.readFileSync)(envPath, "utf-8");
|
|
63
|
+
for (const line of content.split("\n")) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
66
|
+
const eqIndex = trimmed.indexOf("=");
|
|
67
|
+
if (eqIndex === -1) continue;
|
|
68
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
69
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
70
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
71
|
+
value = value.slice(1, -1);
|
|
72
|
+
}
|
|
73
|
+
if (!process.env[key]) {
|
|
74
|
+
process.env[key] = value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var CDPFacilitator = class extends BaseFacilitator {
|
|
84
|
+
name = "cdp";
|
|
85
|
+
displayName = "Coinbase CDP";
|
|
86
|
+
supportedNetworks;
|
|
87
|
+
endpoint;
|
|
88
|
+
useMainnet;
|
|
89
|
+
apiKeyId;
|
|
90
|
+
apiKeySecret;
|
|
91
|
+
constructor(config = {}) {
|
|
92
|
+
super();
|
|
93
|
+
loadEnvFile();
|
|
94
|
+
this.useMainnet = config.useMainnet ?? process.env.USE_MAINNET?.toLowerCase() === "true";
|
|
95
|
+
this.apiKeyId = config.apiKeyId || process.env.CDP_API_KEY_ID;
|
|
96
|
+
this.apiKeySecret = config.apiKeySecret || process.env.CDP_API_KEY_SECRET;
|
|
97
|
+
this.endpoint = this.useMainnet ? CDP_MAINNET_URL : CDP_TESTNET_URL;
|
|
98
|
+
this.supportedNetworks = this.useMainnet ? ["eip155:8453"] : ["eip155:8453", "eip155:84532"];
|
|
99
|
+
if (this.useMainnet && (!this.apiKeyId || !this.apiKeySecret)) {
|
|
100
|
+
console.warn("[CDPFacilitator] WARNING: Mainnet mode but missing CDP credentials!");
|
|
101
|
+
console.warn("[CDPFacilitator] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get auth headers for CDP API requests
|
|
106
|
+
*/
|
|
107
|
+
async getAuthHeaders(method, urlPath, body) {
|
|
108
|
+
if (!this.useMainnet) {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
if (!this.apiKeyId || !this.apiKeySecret) {
|
|
112
|
+
throw new Error("CDP credentials required for mainnet");
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
116
|
+
return await getAuthHeaders({
|
|
117
|
+
apiKeyId: this.apiKeyId,
|
|
118
|
+
apiKeySecret: this.apiKeySecret,
|
|
119
|
+
requestMethod: method,
|
|
120
|
+
requestHost: "api.cdp.coinbase.com",
|
|
121
|
+
requestPath: urlPath,
|
|
122
|
+
requestBody: body
|
|
123
|
+
});
|
|
124
|
+
} catch (err) {
|
|
125
|
+
throw new Error(`Failed to generate CDP auth: ${err.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Health check - verify facilitator is reachable
|
|
130
|
+
*/
|
|
131
|
+
async healthCheck() {
|
|
132
|
+
const start = Date.now();
|
|
133
|
+
try {
|
|
134
|
+
const controller = new AbortController();
|
|
135
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
136
|
+
const response = await fetch(this.endpoint.replace("/x402", ""), {
|
|
137
|
+
method: "HEAD",
|
|
138
|
+
signal: controller.signal
|
|
139
|
+
}).catch(() => null);
|
|
140
|
+
clearTimeout(timeout);
|
|
141
|
+
const latencyMs = Date.now() - start;
|
|
142
|
+
return {
|
|
143
|
+
healthy: response !== null,
|
|
144
|
+
latencyMs
|
|
145
|
+
};
|
|
146
|
+
} catch (err) {
|
|
147
|
+
return {
|
|
148
|
+
healthy: false,
|
|
149
|
+
error: err.message,
|
|
150
|
+
latencyMs: Date.now() - start
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Verify payment signature with facilitator
|
|
156
|
+
*/
|
|
157
|
+
async verify(paymentPayload, requirements) {
|
|
158
|
+
try {
|
|
159
|
+
const requestBody = {
|
|
160
|
+
x402Version: X402_VERSION,
|
|
161
|
+
paymentPayload,
|
|
162
|
+
paymentRequirements: requirements
|
|
163
|
+
};
|
|
164
|
+
const headers = {
|
|
165
|
+
"Content-Type": "application/json"
|
|
166
|
+
};
|
|
167
|
+
if (this.useMainnet) {
|
|
168
|
+
const authHeaders = await this.getAuthHeaders(
|
|
169
|
+
"POST",
|
|
170
|
+
"/platform/v2/x402/verify",
|
|
171
|
+
requestBody
|
|
172
|
+
);
|
|
173
|
+
Object.assign(headers, authHeaders);
|
|
174
|
+
}
|
|
175
|
+
const response = await fetch(`${this.endpoint}/verify`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers,
|
|
178
|
+
body: JSON.stringify(requestBody)
|
|
179
|
+
});
|
|
180
|
+
const result = await response.json();
|
|
181
|
+
if (!response.ok || !result.isValid) {
|
|
182
|
+
return {
|
|
183
|
+
valid: false,
|
|
184
|
+
error: result.invalidReason || result.error || "Verification failed",
|
|
185
|
+
details: result
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return { valid: true, details: result };
|
|
189
|
+
} catch (err) {
|
|
190
|
+
return {
|
|
191
|
+
valid: false,
|
|
192
|
+
error: `Facilitator error: ${err.message}`
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Settle payment on-chain via facilitator
|
|
198
|
+
*/
|
|
199
|
+
async settle(paymentPayload, requirements) {
|
|
200
|
+
try {
|
|
201
|
+
const requestBody = {
|
|
202
|
+
x402Version: X402_VERSION,
|
|
203
|
+
paymentPayload,
|
|
204
|
+
paymentRequirements: requirements
|
|
205
|
+
};
|
|
206
|
+
const headers = {
|
|
207
|
+
"Content-Type": "application/json"
|
|
208
|
+
};
|
|
209
|
+
if (this.useMainnet) {
|
|
210
|
+
const authHeaders = await this.getAuthHeaders(
|
|
211
|
+
"POST",
|
|
212
|
+
"/platform/v2/x402/settle",
|
|
213
|
+
requestBody
|
|
214
|
+
);
|
|
215
|
+
Object.assign(headers, authHeaders);
|
|
216
|
+
}
|
|
217
|
+
const response = await fetch(`${this.endpoint}/settle`, {
|
|
218
|
+
method: "POST",
|
|
219
|
+
headers,
|
|
220
|
+
body: JSON.stringify(requestBody)
|
|
221
|
+
});
|
|
222
|
+
const result = await response.json();
|
|
223
|
+
if (!response.ok || !result.success) {
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
error: result.error || result.errorReason || "Settlement failed"
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
success: true,
|
|
231
|
+
transaction: result.transaction,
|
|
232
|
+
status: result.status || "settled"
|
|
233
|
+
};
|
|
234
|
+
} catch (err) {
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
error: `Settlement error: ${err.message}`
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get CDP fee information
|
|
243
|
+
*/
|
|
244
|
+
async getFee() {
|
|
245
|
+
return {
|
|
246
|
+
perTx: 1e-3,
|
|
247
|
+
currency: "USD",
|
|
248
|
+
freeQuota: 1e3
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Get configuration summary (for logging)
|
|
253
|
+
*/
|
|
254
|
+
getConfigSummary() {
|
|
255
|
+
const mode = this.useMainnet ? "mainnet" : "testnet";
|
|
256
|
+
const hasCredentials = !!(this.apiKeyId && this.apiKeySecret);
|
|
257
|
+
return `CDP Facilitator (${mode}, credentials: ${hasCredentials ? "yes" : "no"})`;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/facilitators/registry.ts
|
|
262
|
+
var FacilitatorRegistry = class {
|
|
263
|
+
factories = /* @__PURE__ */ new Map();
|
|
264
|
+
instances = /* @__PURE__ */ new Map();
|
|
265
|
+
selection;
|
|
266
|
+
roundRobinIndex = 0;
|
|
267
|
+
constructor(selection) {
|
|
268
|
+
this.registerFactory("cdp", (config) => new CDPFacilitator(config));
|
|
269
|
+
this.selection = selection || { primary: "cdp", strategy: "failover" };
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Register a new facilitator factory
|
|
273
|
+
*/
|
|
274
|
+
registerFactory(name, factory) {
|
|
275
|
+
this.factories.set(name, factory);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get or create a facilitator instance
|
|
279
|
+
*/
|
|
280
|
+
get(name, config) {
|
|
281
|
+
if (this.instances.has(name)) {
|
|
282
|
+
return this.instances.get(name);
|
|
283
|
+
}
|
|
284
|
+
const factory = this.factories.get(name);
|
|
285
|
+
if (!factory) {
|
|
286
|
+
throw new Error(`Unknown facilitator: ${name}. Available: ${Array.from(this.factories.keys()).join(", ")}`);
|
|
287
|
+
}
|
|
288
|
+
const mergedConfig = {
|
|
289
|
+
...this.selection.config?.[name],
|
|
290
|
+
...config
|
|
291
|
+
};
|
|
292
|
+
const instance = factory(mergedConfig);
|
|
293
|
+
this.instances.set(name, instance);
|
|
294
|
+
return instance;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get all configured facilitator names
|
|
298
|
+
*/
|
|
299
|
+
getConfiguredNames() {
|
|
300
|
+
const names = [this.selection.primary];
|
|
301
|
+
if (this.selection.fallback) {
|
|
302
|
+
names.push(...this.selection.fallback);
|
|
303
|
+
}
|
|
304
|
+
return names;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Get list of facilitators based on selection strategy
|
|
308
|
+
*/
|
|
309
|
+
async getOrderedFacilitators(network) {
|
|
310
|
+
const names = this.getConfiguredNames();
|
|
311
|
+
const facilitators = [];
|
|
312
|
+
for (const name of names) {
|
|
313
|
+
try {
|
|
314
|
+
const f = this.get(name);
|
|
315
|
+
if (f.supportsNetwork(network)) {
|
|
316
|
+
facilitators.push(f);
|
|
317
|
+
}
|
|
318
|
+
} catch (err) {
|
|
319
|
+
console.warn(`[Registry] Failed to get facilitator ${name}:`, err);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (facilitators.length === 0) {
|
|
323
|
+
throw new Error(`No facilitators available for network: ${network}`);
|
|
324
|
+
}
|
|
325
|
+
switch (this.selection.strategy) {
|
|
326
|
+
case "random":
|
|
327
|
+
return this.shuffle(facilitators);
|
|
328
|
+
case "roundrobin":
|
|
329
|
+
this.roundRobinIndex = (this.roundRobinIndex + 1) % facilitators.length;
|
|
330
|
+
return [
|
|
331
|
+
...facilitators.slice(this.roundRobinIndex),
|
|
332
|
+
...facilitators.slice(0, this.roundRobinIndex)
|
|
333
|
+
];
|
|
334
|
+
case "cheapest":
|
|
335
|
+
return this.sortByCheapest(facilitators);
|
|
336
|
+
case "fastest":
|
|
337
|
+
return this.sortByFastest(facilitators);
|
|
338
|
+
case "failover":
|
|
339
|
+
default:
|
|
340
|
+
return facilitators;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
shuffle(array) {
|
|
344
|
+
const result = [...array];
|
|
345
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
346
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
347
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
async sortByCheapest(facilitators) {
|
|
352
|
+
const withFees = await Promise.all(
|
|
353
|
+
facilitators.map(async (f) => {
|
|
354
|
+
try {
|
|
355
|
+
const fee = await f.getFee?.();
|
|
356
|
+
return { facilitator: f, perTx: fee?.perTx ?? Infinity };
|
|
357
|
+
} catch {
|
|
358
|
+
return { facilitator: f, perTx: Infinity };
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
withFees.sort((a, b) => a.perTx - b.perTx);
|
|
363
|
+
return withFees.map((w) => w.facilitator);
|
|
364
|
+
}
|
|
365
|
+
async sortByFastest(facilitators) {
|
|
366
|
+
const withLatency = await Promise.all(
|
|
367
|
+
facilitators.map(async (f) => {
|
|
368
|
+
try {
|
|
369
|
+
const health = await f.healthCheck();
|
|
370
|
+
return { facilitator: f, latency: health.latencyMs ?? Infinity };
|
|
371
|
+
} catch {
|
|
372
|
+
return { facilitator: f, latency: Infinity };
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
withLatency.sort((a, b) => a.latency - b.latency);
|
|
377
|
+
return withLatency.map((w) => w.facilitator);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Verify payment using configured facilitators
|
|
381
|
+
*/
|
|
382
|
+
async verify(paymentPayload, requirements) {
|
|
383
|
+
const network = paymentPayload.accepted?.network || paymentPayload.network || requirements.network;
|
|
384
|
+
const facilitators = await this.getOrderedFacilitators(network);
|
|
385
|
+
let lastError;
|
|
386
|
+
for (const f of facilitators) {
|
|
387
|
+
try {
|
|
388
|
+
console.log(`[Registry] Trying ${f.name} for verify...`);
|
|
389
|
+
const result = await f.verify(paymentPayload, requirements);
|
|
390
|
+
if (result.valid) {
|
|
391
|
+
console.log(`[Registry] ${f.name} verify succeeded`);
|
|
392
|
+
return { ...result, facilitator: f.name };
|
|
393
|
+
}
|
|
394
|
+
lastError = result.error;
|
|
395
|
+
console.log(`[Registry] ${f.name} verify failed: ${result.error}`);
|
|
396
|
+
if (this.selection.strategy === "failover" && !this.isTransientError(result.error)) {
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
} catch (err) {
|
|
400
|
+
lastError = err.message;
|
|
401
|
+
console.error(`[Registry] ${f.name} error:`, err.message);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
valid: false,
|
|
406
|
+
error: lastError || "All facilitators failed",
|
|
407
|
+
facilitator: "none"
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Settle payment using configured facilitators
|
|
412
|
+
*/
|
|
413
|
+
async settle(paymentPayload, requirements) {
|
|
414
|
+
const network = paymentPayload.accepted?.network || paymentPayload.network || requirements.network;
|
|
415
|
+
const facilitators = await this.getOrderedFacilitators(network);
|
|
416
|
+
let lastError;
|
|
417
|
+
for (const f of facilitators) {
|
|
418
|
+
try {
|
|
419
|
+
console.log(`[Registry] Trying ${f.name} for settle...`);
|
|
420
|
+
const result = await f.settle(paymentPayload, requirements);
|
|
421
|
+
if (result.success) {
|
|
422
|
+
console.log(`[Registry] ${f.name} settle succeeded: ${result.transaction}`);
|
|
423
|
+
return { ...result, facilitator: f.name };
|
|
424
|
+
}
|
|
425
|
+
lastError = result.error;
|
|
426
|
+
console.log(`[Registry] ${f.name} settle failed: ${result.error}`);
|
|
427
|
+
} catch (err) {
|
|
428
|
+
lastError = err.message;
|
|
429
|
+
console.error(`[Registry] ${f.name} error:`, err.message);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
success: false,
|
|
434
|
+
error: lastError || "All facilitators failed",
|
|
435
|
+
facilitator: "none"
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Check health of all configured facilitators
|
|
440
|
+
*/
|
|
441
|
+
async healthCheckAll() {
|
|
442
|
+
const results = {};
|
|
443
|
+
for (const name of this.getConfiguredNames()) {
|
|
444
|
+
try {
|
|
445
|
+
const f = this.get(name);
|
|
446
|
+
results[name] = await f.healthCheck();
|
|
447
|
+
} catch (err) {
|
|
448
|
+
results[name] = { healthy: false, error: err.message };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return results;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Check if an error is transient (network/server issue) vs permanent (bad request)
|
|
455
|
+
*/
|
|
456
|
+
isTransientError(error) {
|
|
457
|
+
if (!error) return true;
|
|
458
|
+
const transientPatterns = [
|
|
459
|
+
/timeout/i,
|
|
460
|
+
/network/i,
|
|
461
|
+
/connection/i,
|
|
462
|
+
/ECONNREFUSED/i,
|
|
463
|
+
/ETIMEDOUT/i,
|
|
464
|
+
/503/,
|
|
465
|
+
/502/,
|
|
466
|
+
/500/
|
|
467
|
+
];
|
|
468
|
+
return transientPatterns.some((p) => p.test(error));
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Update selection configuration
|
|
472
|
+
*/
|
|
473
|
+
setSelection(selection) {
|
|
474
|
+
this.selection = selection;
|
|
475
|
+
this.instances.clear();
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Get current selection configuration
|
|
479
|
+
*/
|
|
480
|
+
getSelection() {
|
|
481
|
+
return { ...this.selection };
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
var defaultRegistry = null;
|
|
485
|
+
function getDefaultRegistry() {
|
|
486
|
+
if (!defaultRegistry) {
|
|
487
|
+
defaultRegistry = new FacilitatorRegistry();
|
|
488
|
+
}
|
|
489
|
+
return defaultRegistry;
|
|
490
|
+
}
|
|
491
|
+
function createRegistry(selection) {
|
|
492
|
+
return new FacilitatorRegistry(selection);
|
|
493
|
+
}
|
|
494
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
495
|
+
0 && (module.exports = {
|
|
496
|
+
BaseFacilitator,
|
|
497
|
+
CDPFacilitator,
|
|
498
|
+
FacilitatorRegistry,
|
|
499
|
+
createRegistry,
|
|
500
|
+
getDefaultRegistry
|
|
501
|
+
});
|
|
502
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/facilitators/index.ts","../../src/facilitators/interface.ts","../../src/facilitators/cdp.ts","../../src/facilitators/registry.ts"],"sourcesContent":["/**\n * Facilitator Module\n * \n * Provides pluggable payment facilitator support for MoltsPay.\n * \n * @example\n * ```typescript\n * import { FacilitatorRegistry, CDPFacilitator } from 'moltspay/facilitators';\n * \n * // Use default CDP facilitator\n * const registry = new FacilitatorRegistry();\n * const result = await registry.verify(paymentPayload, requirements);\n * \n * // Or with custom config\n * const registry = new FacilitatorRegistry({\n * primary: 'cdp',\n * fallback: ['chaoschain'], // Coming in v0.9.0\n * strategy: 'failover',\n * config: {\n * cdp: { useMainnet: true }\n * }\n * });\n * ```\n */\n\n// Interface & types\nexport {\n Facilitator,\n BaseFacilitator,\n FacilitatorConfig,\n X402PaymentPayload,\n X402PaymentRequirements,\n VerifyResult,\n SettleResult,\n HealthCheckResult,\n FacilitatorFee,\n} from './interface.js';\n\n// CDP Facilitator\nexport {\n CDPFacilitator,\n CDPFacilitatorConfig,\n} from './cdp.js';\n\n// Registry\nexport {\n FacilitatorRegistry,\n FacilitatorSelection,\n SelectionStrategy,\n getDefaultRegistry,\n createRegistry,\n} from './registry.js';\n","/**\n * Facilitator Interface\n * \n * A facilitator is a service that handles x402 payment verification and settlement.\n * This abstraction allows MoltsPay to support multiple facilitators.\n * \n * @see https://www.x402.org/ecosystem?category=facilitators\n */\n\n/**\n * x402 Payment Payload (from client)\n */\nexport interface X402PaymentPayload {\n x402Version: number;\n scheme?: string;\n network?: string;\n accepted?: {\n scheme: string;\n network: string;\n asset: string;\n amount: string;\n payTo: string;\n maxTimeoutSeconds: number;\n extra?: Record<string, unknown>;\n };\n payload: unknown;\n}\n\n/**\n * x402 Payment Requirements (server specifies what it accepts)\n */\nexport interface X402PaymentRequirements {\n scheme: string;\n network: string;\n asset: string;\n amount: string;\n payTo: string;\n maxTimeoutSeconds: number;\n extra?: Record<string, unknown>;\n}\n\n/**\n * Result of payment verification\n */\nexport interface VerifyResult {\n valid: boolean;\n error?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Result of payment settlement\n */\nexport interface SettleResult {\n success: boolean;\n transaction?: string;\n error?: string;\n status?: string;\n}\n\n/**\n * Facilitator health check result\n */\nexport interface HealthCheckResult {\n healthy: boolean;\n latencyMs?: number;\n error?: string;\n}\n\n/**\n * Facilitator fee information (for selection strategies)\n */\nexport interface FacilitatorFee {\n perTx: number;\n currency: string;\n freeQuota?: number;\n}\n\n/**\n * Facilitator configuration\n */\nexport interface FacilitatorConfig {\n /** Facilitator endpoint URL */\n endpoint?: string;\n /** API key (if required) */\n apiKey?: string;\n /** API secret (if required) */\n apiSecret?: string;\n /** Additional config specific to facilitator */\n [key: string]: unknown;\n}\n\n/**\n * Facilitator Interface\n * \n * All facilitators must implement this interface.\n */\nexport interface Facilitator {\n /** Unique identifier for this facilitator */\n readonly name: string;\n \n /** Human-readable display name */\n readonly displayName: string;\n \n /** Supported networks (e.g., [\"eip155:8453\", \"eip155:84532\"]) */\n readonly supportedNetworks: string[];\n \n /**\n * Check if facilitator is available and responsive\n */\n healthCheck(): Promise<HealthCheckResult>;\n \n /**\n * Verify a payment signature without executing it\n * \n * @param paymentPayload - The x402 payment payload from client\n * @param requirements - The payment requirements from server\n */\n verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult>;\n \n /**\n * Settle a payment on-chain\n * \n * @param paymentPayload - The x402 payment payload from client\n * @param requirements - The payment requirements from server\n */\n settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult>;\n \n /**\n * Get current fee information (optional, for selection strategies)\n */\n getFee?(): Promise<FacilitatorFee>;\n \n /**\n * Check if this facilitator supports a given network\n */\n supportsNetwork(network: string): boolean;\n}\n\n/**\n * Base class with common functionality\n */\nexport abstract class BaseFacilitator implements Facilitator {\n abstract readonly name: string;\n abstract readonly displayName: string;\n abstract readonly supportedNetworks: string[];\n \n abstract healthCheck(): Promise<HealthCheckResult>;\n abstract verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult>;\n abstract settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult>;\n \n supportsNetwork(network: string): boolean {\n return this.supportedNetworks.includes(network);\n }\n}\n","/**\n * CDP Facilitator\n * \n * Coinbase Developer Platform x402 facilitator implementation.\n * Supports both mainnet (Base) and testnet (Base Sepolia).\n * \n * @see https://docs.cdp.coinbase.com/x402/core-concepts/facilitator\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport * as path from 'path';\nimport {\n BaseFacilitator,\n X402PaymentPayload,\n X402PaymentRequirements,\n VerifyResult,\n SettleResult,\n HealthCheckResult,\n FacilitatorFee,\n FacilitatorConfig,\n} from './interface.js';\n\n// x402 protocol version\nconst X402_VERSION = 2;\n\n// CDP Facilitator URLs\nconst CDP_MAINNET_URL = 'https://api.cdp.coinbase.com/platform/v2/x402';\nconst CDP_TESTNET_URL = 'https://www.x402.org/facilitator';\n\nexport interface CDPFacilitatorConfig extends FacilitatorConfig {\n /** Use mainnet (true) or testnet (false, default) */\n useMainnet?: boolean;\n /** CDP API Key ID (required for mainnet) */\n apiKeyId?: string;\n /** CDP API Key Secret (required for mainnet) */\n apiKeySecret?: string;\n}\n\n/**\n * Load environment from .env files\n */\nfunction loadEnvFile(): void {\n const envPaths = [\n path.join(process.cwd(), '.env'),\n path.join(process.env.HOME || '', '.moltspay', '.env'),\n ];\n \n for (const envPath of envPaths) {\n if (existsSync(envPath)) {\n try {\n const content = readFileSync(envPath, 'utf-8');\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIndex = trimmed.indexOf('=');\n if (eqIndex === -1) continue;\n const key = trimmed.slice(0, eqIndex).trim();\n let value = trimmed.slice(eqIndex + 1).trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n break;\n } catch {\n // Ignore errors\n }\n }\n }\n}\n\n/**\n * CDP (Coinbase Developer Platform) Facilitator\n * \n * Handles payment verification and settlement via Coinbase's x402 facilitator.\n */\nexport class CDPFacilitator extends BaseFacilitator {\n readonly name = 'cdp';\n readonly displayName = 'Coinbase CDP';\n readonly supportedNetworks: string[];\n \n private endpoint: string;\n private useMainnet: boolean;\n private apiKeyId?: string;\n private apiKeySecret?: string;\n \n constructor(config: CDPFacilitatorConfig = {}) {\n super();\n \n // Load env files for credentials\n loadEnvFile();\n \n // Determine mainnet vs testnet\n this.useMainnet = config.useMainnet ?? \n (process.env.USE_MAINNET?.toLowerCase() === 'true');\n \n // Get credentials\n this.apiKeyId = config.apiKeyId || process.env.CDP_API_KEY_ID;\n this.apiKeySecret = config.apiKeySecret || process.env.CDP_API_KEY_SECRET;\n \n // Set endpoint\n this.endpoint = this.useMainnet ? CDP_MAINNET_URL : CDP_TESTNET_URL;\n \n // Set supported networks\n this.supportedNetworks = this.useMainnet \n ? ['eip155:8453'] // Base mainnet only\n : ['eip155:8453', 'eip155:84532']; // Both mainnet and testnet via x402.org\n \n // Warn if mainnet without credentials\n if (this.useMainnet && (!this.apiKeyId || !this.apiKeySecret)) {\n console.warn('[CDPFacilitator] WARNING: Mainnet mode but missing CDP credentials!');\n console.warn('[CDPFacilitator] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET');\n }\n }\n \n /**\n * Get auth headers for CDP API requests\n */\n private async getAuthHeaders(\n method: string,\n urlPath: string,\n body?: unknown\n ): Promise<Record<string, string>> {\n if (!this.useMainnet) {\n // Testnet (x402.org) doesn't require auth\n return {};\n }\n \n if (!this.apiKeyId || !this.apiKeySecret) {\n throw new Error('CDP credentials required for mainnet');\n }\n \n try {\n const { getAuthHeaders } = await import('@coinbase/cdp-sdk/auth');\n \n return await getAuthHeaders({\n apiKeyId: this.apiKeyId,\n apiKeySecret: this.apiKeySecret,\n requestMethod: method,\n requestHost: 'api.cdp.coinbase.com',\n requestPath: urlPath,\n requestBody: body,\n });\n } catch (err: any) {\n throw new Error(`Failed to generate CDP auth: ${err.message}`);\n }\n }\n \n /**\n * Health check - verify facilitator is reachable\n */\n async healthCheck(): Promise<HealthCheckResult> {\n const start = Date.now();\n \n try {\n // For testnet, just check if x402.org responds\n // For mainnet, we could hit a health endpoint or just check DNS\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n \n const response = await fetch(this.endpoint.replace('/x402', ''), {\n method: 'HEAD',\n signal: controller.signal,\n }).catch(() => null);\n \n clearTimeout(timeout);\n \n const latencyMs = Date.now() - start;\n \n return {\n healthy: response !== null,\n latencyMs,\n };\n } catch (err: any) {\n return {\n healthy: false,\n error: err.message,\n latencyMs: Date.now() - start,\n };\n }\n }\n \n /**\n * Verify payment signature with facilitator\n */\n async verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult> {\n try {\n const requestBody = {\n x402Version: X402_VERSION,\n paymentPayload,\n paymentRequirements: requirements,\n };\n \n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n \n if (this.useMainnet) {\n const authHeaders = await this.getAuthHeaders(\n 'POST',\n '/platform/v2/x402/verify',\n requestBody\n );\n Object.assign(headers, authHeaders);\n }\n \n const response = await fetch(`${this.endpoint}/verify`, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n });\n \n const result = await response.json() as any;\n \n if (!response.ok || !result.isValid) {\n return {\n valid: false,\n error: result.invalidReason || result.error || 'Verification failed',\n details: result,\n };\n }\n \n return { valid: true, details: result };\n } catch (err: any) {\n return {\n valid: false,\n error: `Facilitator error: ${err.message}`,\n };\n }\n }\n \n /**\n * Settle payment on-chain via facilitator\n */\n async settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult> {\n try {\n const requestBody = {\n x402Version: X402_VERSION,\n paymentPayload,\n paymentRequirements: requirements,\n };\n \n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n \n if (this.useMainnet) {\n const authHeaders = await this.getAuthHeaders(\n 'POST',\n '/platform/v2/x402/settle',\n requestBody\n );\n Object.assign(headers, authHeaders);\n }\n \n const response = await fetch(`${this.endpoint}/settle`, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n });\n \n const result = await response.json() as any;\n \n if (!response.ok || !result.success) {\n return {\n success: false,\n error: result.error || result.errorReason || 'Settlement failed',\n };\n }\n \n return {\n success: true,\n transaction: result.transaction,\n status: result.status || 'settled',\n };\n } catch (err: any) {\n return {\n success: false,\n error: `Settlement error: ${err.message}`,\n };\n }\n }\n \n /**\n * Get CDP fee information\n */\n async getFee(): Promise<FacilitatorFee> {\n // CDP pricing: 1000 free/month, then $0.001/tx\n return {\n perTx: 0.001,\n currency: 'USD',\n freeQuota: 1000,\n };\n }\n \n /**\n * Get configuration summary (for logging)\n */\n getConfigSummary(): string {\n const mode = this.useMainnet ? 'mainnet' : 'testnet';\n const hasCredentials = !!(this.apiKeyId && this.apiKeySecret);\n return `CDP Facilitator (${mode}, credentials: ${hasCredentials ? 'yes' : 'no'})`;\n }\n}\n","/**\n * Facilitator Registry\n * \n * Central registry for all available facilitators.\n * Supports selection strategies for failover, load balancing, etc.\n */\n\nimport {\n Facilitator,\n FacilitatorConfig,\n X402PaymentPayload,\n X402PaymentRequirements,\n VerifyResult,\n SettleResult,\n HealthCheckResult,\n} from './interface.js';\nimport { CDPFacilitator, CDPFacilitatorConfig } from './cdp.js';\n\n/**\n * Selection strategy for choosing facilitators\n */\nexport type SelectionStrategy = \n | 'failover' // Use primary, switch to fallback on failure\n | 'cheapest' // Use facilitator with lowest fees\n | 'fastest' // Use first responder\n | 'random' // Random selection (load balancing)\n | 'roundrobin'; // Rotate through facilitators\n\n/**\n * Facilitator selection configuration\n */\nexport interface FacilitatorSelection {\n /** Primary facilitator to use */\n primary: string;\n /** Fallback facilitators (in order of preference) */\n fallback?: string[];\n /** Selection strategy */\n strategy?: SelectionStrategy;\n /** Per-facilitator config overrides */\n config?: Record<string, FacilitatorConfig>;\n}\n\n/**\n * Factory function type for creating facilitators\n */\ntype FacilitatorFactory = (config?: FacilitatorConfig) => Facilitator;\n\n/**\n * Facilitator Registry\n * \n * Manages available facilitators and provides selection logic.\n */\nexport class FacilitatorRegistry {\n private factories: Map<string, FacilitatorFactory> = new Map();\n private instances: Map<string, Facilitator> = new Map();\n private selection: FacilitatorSelection;\n private roundRobinIndex = 0;\n \n constructor(selection?: FacilitatorSelection) {\n // Register built-in facilitators\n this.registerFactory('cdp', (config) => new CDPFacilitator(config as CDPFacilitatorConfig));\n \n // Default selection\n this.selection = selection || { primary: 'cdp', strategy: 'failover' };\n }\n \n /**\n * Register a new facilitator factory\n */\n registerFactory(name: string, factory: FacilitatorFactory): void {\n this.factories.set(name, factory);\n }\n \n /**\n * Get or create a facilitator instance\n */\n get(name: string, config?: FacilitatorConfig): Facilitator {\n // Check cache first\n if (this.instances.has(name)) {\n return this.instances.get(name)!;\n }\n \n // Look up factory\n const factory = this.factories.get(name);\n if (!factory) {\n throw new Error(`Unknown facilitator: ${name}. Available: ${Array.from(this.factories.keys()).join(', ')}`);\n }\n \n // Merge config from selection\n const mergedConfig = {\n ...this.selection.config?.[name],\n ...config,\n };\n \n // Create and cache instance\n const instance = factory(mergedConfig);\n this.instances.set(name, instance);\n return instance;\n }\n \n /**\n * Get all configured facilitator names\n */\n getConfiguredNames(): string[] {\n const names = [this.selection.primary];\n if (this.selection.fallback) {\n names.push(...this.selection.fallback);\n }\n return names;\n }\n \n /**\n * Get list of facilitators based on selection strategy\n */\n private async getOrderedFacilitators(network: string): Promise<Facilitator[]> {\n const names = this.getConfiguredNames();\n const facilitators: Facilitator[] = [];\n \n for (const name of names) {\n try {\n const f = this.get(name);\n if (f.supportsNetwork(network)) {\n facilitators.push(f);\n }\n } catch (err) {\n console.warn(`[Registry] Failed to get facilitator ${name}:`, err);\n }\n }\n \n if (facilitators.length === 0) {\n throw new Error(`No facilitators available for network: ${network}`);\n }\n \n // Apply strategy\n switch (this.selection.strategy) {\n case 'random':\n return this.shuffle(facilitators);\n \n case 'roundrobin':\n this.roundRobinIndex = (this.roundRobinIndex + 1) % facilitators.length;\n return [\n ...facilitators.slice(this.roundRobinIndex),\n ...facilitators.slice(0, this.roundRobinIndex),\n ];\n \n case 'cheapest':\n return this.sortByCheapest(facilitators);\n \n case 'fastest':\n return this.sortByFastest(facilitators);\n \n case 'failover':\n default:\n return facilitators;\n }\n }\n \n private shuffle<T>(array: T[]): T[] {\n const result = [...array];\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n return result;\n }\n \n private async sortByCheapest(facilitators: Facilitator[]): Promise<Facilitator[]> {\n const withFees = await Promise.all(\n facilitators.map(async (f) => {\n try {\n const fee = await f.getFee?.();\n return { facilitator: f, perTx: fee?.perTx ?? Infinity };\n } catch {\n return { facilitator: f, perTx: Infinity };\n }\n })\n );\n withFees.sort((a, b) => a.perTx - b.perTx);\n return withFees.map(w => w.facilitator);\n }\n \n private async sortByFastest(facilitators: Facilitator[]): Promise<Facilitator[]> {\n const withLatency = await Promise.all(\n facilitators.map(async (f) => {\n try {\n const health = await f.healthCheck();\n return { facilitator: f, latency: health.latencyMs ?? Infinity };\n } catch {\n return { facilitator: f, latency: Infinity };\n }\n })\n );\n withLatency.sort((a, b) => a.latency - b.latency);\n return withLatency.map(w => w.facilitator);\n }\n \n /**\n * Verify payment using configured facilitators\n */\n async verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult & { facilitator: string }> {\n const network = paymentPayload.accepted?.network || paymentPayload.network || requirements.network;\n const facilitators = await this.getOrderedFacilitators(network);\n \n let lastError: string | undefined;\n \n for (const f of facilitators) {\n try {\n console.log(`[Registry] Trying ${f.name} for verify...`);\n const result = await f.verify(paymentPayload, requirements);\n \n if (result.valid) {\n console.log(`[Registry] ${f.name} verify succeeded`);\n return { ...result, facilitator: f.name };\n }\n \n lastError = result.error;\n console.log(`[Registry] ${f.name} verify failed: ${result.error}`);\n \n // For failover strategy, only try next if it's a network/server error\n if (this.selection.strategy === 'failover' && !this.isTransientError(result.error)) {\n // Permanent error (e.g., invalid signature) - don't try others\n break;\n }\n } catch (err: any) {\n lastError = err.message;\n console.error(`[Registry] ${f.name} error:`, err.message);\n }\n }\n \n return {\n valid: false,\n error: lastError || 'All facilitators failed',\n facilitator: 'none',\n };\n }\n \n /**\n * Settle payment using configured facilitators\n */\n async settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult & { facilitator: string }> {\n const network = paymentPayload.accepted?.network || paymentPayload.network || requirements.network;\n const facilitators = await this.getOrderedFacilitators(network);\n \n let lastError: string | undefined;\n \n for (const f of facilitators) {\n try {\n console.log(`[Registry] Trying ${f.name} for settle...`);\n const result = await f.settle(paymentPayload, requirements);\n \n if (result.success) {\n console.log(`[Registry] ${f.name} settle succeeded: ${result.transaction}`);\n return { ...result, facilitator: f.name };\n }\n \n lastError = result.error;\n console.log(`[Registry] ${f.name} settle failed: ${result.error}`);\n } catch (err: any) {\n lastError = err.message;\n console.error(`[Registry] ${f.name} error:`, err.message);\n }\n }\n \n return {\n success: false,\n error: lastError || 'All facilitators failed',\n facilitator: 'none',\n };\n }\n \n /**\n * Check health of all configured facilitators\n */\n async healthCheckAll(): Promise<Record<string, HealthCheckResult>> {\n const results: Record<string, HealthCheckResult> = {};\n \n for (const name of this.getConfiguredNames()) {\n try {\n const f = this.get(name);\n results[name] = await f.healthCheck();\n } catch (err: any) {\n results[name] = { healthy: false, error: err.message };\n }\n }\n \n return results;\n }\n \n /**\n * Check if an error is transient (network/server issue) vs permanent (bad request)\n */\n private isTransientError(error?: string): boolean {\n if (!error) return true;\n const transientPatterns = [\n /timeout/i,\n /network/i,\n /connection/i,\n /ECONNREFUSED/i,\n /ETIMEDOUT/i,\n /503/,\n /502/,\n /500/,\n ];\n return transientPatterns.some(p => p.test(error));\n }\n \n /**\n * Update selection configuration\n */\n setSelection(selection: FacilitatorSelection): void {\n this.selection = selection;\n // Clear cached instances to pick up new config\n this.instances.clear();\n }\n \n /**\n * Get current selection configuration\n */\n getSelection(): FacilitatorSelection {\n return { ...this.selection };\n }\n}\n\n// Default registry instance\nlet defaultRegistry: FacilitatorRegistry | null = null;\n\n/**\n * Get the default facilitator registry\n */\nexport function getDefaultRegistry(): FacilitatorRegistry {\n if (!defaultRegistry) {\n defaultRegistry = new FacilitatorRegistry();\n }\n return defaultRegistry;\n}\n\n/**\n * Create a new registry with custom selection\n */\nexport function createRegistry(selection?: FacilitatorSelection): FacilitatorRegistry {\n return new FacilitatorRegistry(selection);\n}\n"],"mappings":"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","names":[]}
|