@bankofai/x402-core 2.6.0-beta.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 +293 -0
- package/dist/cjs/assetRegistry-CL0zA4s0.d.ts +831 -0
- package/dist/cjs/assetRegistry-CRVM0KEs.d.ts +831 -0
- package/dist/cjs/client/index.d.ts +329 -0
- package/dist/cjs/client/index.js +712 -0
- package/dist/cjs/client/index.js.map +1 -0
- package/dist/cjs/facilitator/index.d.ts +206 -0
- package/dist/cjs/facilitator/index.js +625 -0
- package/dist/cjs/facilitator/index.js.map +1 -0
- package/dist/cjs/http/index.d.ts +51 -0
- package/dist/cjs/http/index.js +1178 -0
- package/dist/cjs/http/index.js.map +1 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.js +250 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/mechanisms-q7I6xfUE.d.ts +726 -0
- package/dist/cjs/schemas/index.d.ts +825 -0
- package/dist/cjs/schemas/index.js +212 -0
- package/dist/cjs/schemas/index.js.map +1 -0
- package/dist/cjs/server/index.d.ts +2 -0
- package/dist/cjs/server/index.js +1782 -0
- package/dist/cjs/server/index.js.map +1 -0
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/cjs/types/index.js +72 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/types/v1/index.d.ts +1 -0
- package/dist/cjs/types/v1/index.js +19 -0
- package/dist/cjs/types/v1/index.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +48 -0
- package/dist/cjs/utils/index.js +116 -0
- package/dist/cjs/utils/index.js.map +1 -0
- package/dist/cjs/x402HTTPResourceServer-BFVo1_74.d.ts +433 -0
- package/dist/cjs/x402HTTPResourceServer-DaU2yFzy.d.ts +434 -0
- package/dist/cjs/x402HTTPResourceServer-DswI2hZQ.d.ts +434 -0
- package/dist/esm/assetRegistry-CRVM0KEs.d.mts +831 -0
- package/dist/esm/chunk-BJTO5JO5.mjs +11 -0
- package/dist/esm/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/esm/chunk-DACUCTGT.mjs +891 -0
- package/dist/esm/chunk-DACUCTGT.mjs.map +1 -0
- package/dist/esm/chunk-DFUINDLZ.mjs +221 -0
- package/dist/esm/chunk-DFUINDLZ.mjs.map +1 -0
- package/dist/esm/chunk-HRQUGJ3Y.mjs +45 -0
- package/dist/esm/chunk-HRQUGJ3Y.mjs.map +1 -0
- package/dist/esm/chunk-TDLQZ6MP.mjs +86 -0
- package/dist/esm/chunk-TDLQZ6MP.mjs.map +1 -0
- package/dist/esm/client/index.d.mts +329 -0
- package/dist/esm/client/index.mjs +330 -0
- package/dist/esm/client/index.mjs.map +1 -0
- package/dist/esm/facilitator/index.d.mts +206 -0
- package/dist/esm/facilitator/index.mjs +398 -0
- package/dist/esm/facilitator/index.mjs.map +1 -0
- package/dist/esm/http/index.d.mts +51 -0
- package/dist/esm/http/index.mjs +29 -0
- package/dist/esm/http/index.mjs.map +1 -0
- package/dist/esm/index.d.mts +13 -0
- package/dist/esm/index.mjs +14 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/schemas/index.d.mts +825 -0
- package/dist/esm/schemas/index.mjs +158 -0
- package/dist/esm/schemas/index.mjs.map +1 -0
- package/dist/esm/server/index.d.mts +2 -0
- package/dist/esm/server/index.mjs +712 -0
- package/dist/esm/server/index.mjs.map +1 -0
- package/dist/esm/types/index.d.mts +1 -0
- package/dist/esm/types/index.mjs +10 -0
- package/dist/esm/types/index.mjs.map +1 -0
- package/dist/esm/types/v1/index.d.mts +1 -0
- package/dist/esm/types/v1/index.mjs +1 -0
- package/dist/esm/types/v1/index.mjs.map +1 -0
- package/dist/esm/utils/index.d.mts +48 -0
- package/dist/esm/utils/index.mjs +20 -0
- package/dist/esm/utils/index.mjs.map +1 -0
- package/dist/esm/x402HTTPResourceServer-B6uf_UDm.d.mts +434 -0
- package/package.json +139 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HTTPFacilitatorClient,
|
|
3
|
+
RouteConfigurationError,
|
|
4
|
+
x402HTTPResourceServer
|
|
5
|
+
} from "../chunk-DACUCTGT.mjs";
|
|
6
|
+
import {
|
|
7
|
+
convertMoney,
|
|
8
|
+
globalAssetRegistry,
|
|
9
|
+
x402Version
|
|
10
|
+
} from "../chunk-DFUINDLZ.mjs";
|
|
11
|
+
import {
|
|
12
|
+
SettleError,
|
|
13
|
+
VerifyError
|
|
14
|
+
} from "../chunk-HRQUGJ3Y.mjs";
|
|
15
|
+
import {
|
|
16
|
+
deepEqual,
|
|
17
|
+
findByNetworkAndScheme
|
|
18
|
+
} from "../chunk-TDLQZ6MP.mjs";
|
|
19
|
+
import "../chunk-BJTO5JO5.mjs";
|
|
20
|
+
|
|
21
|
+
// src/server/x402ResourceServer.ts
|
|
22
|
+
var x402ResourceServer = class {
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new x402ResourceServer instance.
|
|
25
|
+
*
|
|
26
|
+
* @param facilitatorClients - Optional facilitator client(s) for payment processing
|
|
27
|
+
* @param assetRegistry - Optional asset registry for symbol-based asset resolution
|
|
28
|
+
*/
|
|
29
|
+
constructor(facilitatorClients, assetRegistry) {
|
|
30
|
+
this.registeredServerSchemes = /* @__PURE__ */ new Map();
|
|
31
|
+
this.supportedResponsesMap = /* @__PURE__ */ new Map();
|
|
32
|
+
this.facilitatorClientsMap = /* @__PURE__ */ new Map();
|
|
33
|
+
this.registeredExtensions = /* @__PURE__ */ new Map();
|
|
34
|
+
this.beforeVerifyHooks = [];
|
|
35
|
+
this.afterVerifyHooks = [];
|
|
36
|
+
this.onVerifyFailureHooks = [];
|
|
37
|
+
this.beforeSettleHooks = [];
|
|
38
|
+
this.afterSettleHooks = [];
|
|
39
|
+
this.onSettleFailureHooks = [];
|
|
40
|
+
this.assetRegistry = assetRegistry ?? globalAssetRegistry;
|
|
41
|
+
if (!facilitatorClients) {
|
|
42
|
+
this.facilitatorClients = [new HTTPFacilitatorClient()];
|
|
43
|
+
} else if (Array.isArray(facilitatorClients)) {
|
|
44
|
+
this.facilitatorClients = facilitatorClients.length > 0 ? facilitatorClients : [new HTTPFacilitatorClient()];
|
|
45
|
+
} else {
|
|
46
|
+
this.facilitatorClients = [facilitatorClients];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Register a scheme/network server implementation.
|
|
51
|
+
*
|
|
52
|
+
* @param network - The network identifier
|
|
53
|
+
* @param server - The scheme/network server implementation
|
|
54
|
+
* @returns The x402ResourceServer instance for chaining
|
|
55
|
+
*/
|
|
56
|
+
register(network, server) {
|
|
57
|
+
if (!this.registeredServerSchemes.has(network)) {
|
|
58
|
+
this.registeredServerSchemes.set(network, /* @__PURE__ */ new Map());
|
|
59
|
+
}
|
|
60
|
+
const serverByScheme = this.registeredServerSchemes.get(network);
|
|
61
|
+
if (!serverByScheme.has(server.scheme)) {
|
|
62
|
+
serverByScheme.set(server.scheme, server);
|
|
63
|
+
}
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a scheme is registered for a given network.
|
|
68
|
+
*
|
|
69
|
+
* @param network - The network identifier
|
|
70
|
+
* @param scheme - The payment scheme name
|
|
71
|
+
* @returns True if the scheme is registered for the network, false otherwise
|
|
72
|
+
*/
|
|
73
|
+
hasRegisteredScheme(network, scheme) {
|
|
74
|
+
return !!findByNetworkAndScheme(this.registeredServerSchemes, scheme, network);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Registers a resource service extension that can enrich extension declarations.
|
|
78
|
+
*
|
|
79
|
+
* @param extension - The extension to register
|
|
80
|
+
* @returns The x402ResourceServer instance for chaining
|
|
81
|
+
*/
|
|
82
|
+
registerExtension(extension) {
|
|
83
|
+
this.registeredExtensions.set(extension.key, extension);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if an extension is registered.
|
|
88
|
+
*
|
|
89
|
+
* @param key - The extension key
|
|
90
|
+
* @returns True if the extension is registered
|
|
91
|
+
*/
|
|
92
|
+
hasExtension(key) {
|
|
93
|
+
return this.registeredExtensions.has(key);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get all registered extensions.
|
|
97
|
+
*
|
|
98
|
+
* @returns Array of registered extensions
|
|
99
|
+
*/
|
|
100
|
+
getExtensions() {
|
|
101
|
+
return Array.from(this.registeredExtensions.values());
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Enriches declared extensions using registered extension hooks.
|
|
105
|
+
*
|
|
106
|
+
* @param declaredExtensions - Extensions declared on the route
|
|
107
|
+
* @param transportContext - Transport-specific context (HTTP, A2A, MCP, etc.)
|
|
108
|
+
* @returns Enriched extensions map
|
|
109
|
+
*/
|
|
110
|
+
enrichExtensions(declaredExtensions, transportContext) {
|
|
111
|
+
const enriched = {};
|
|
112
|
+
for (const [key, declaration] of Object.entries(declaredExtensions)) {
|
|
113
|
+
const extension = this.registeredExtensions.get(key);
|
|
114
|
+
if (extension?.enrichDeclaration) {
|
|
115
|
+
enriched[key] = extension.enrichDeclaration(declaration, transportContext);
|
|
116
|
+
} else {
|
|
117
|
+
enriched[key] = declaration;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return enriched;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Register a hook to execute before payment verification.
|
|
124
|
+
* Can abort verification by returning { abort: true, reason: string }
|
|
125
|
+
*
|
|
126
|
+
* @param hook - The hook function to register
|
|
127
|
+
* @returns The x402ResourceServer instance for chaining
|
|
128
|
+
*/
|
|
129
|
+
onBeforeVerify(hook) {
|
|
130
|
+
this.beforeVerifyHooks.push(hook);
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Register a hook to execute after successful payment verification.
|
|
135
|
+
*
|
|
136
|
+
* @param hook - The hook function to register
|
|
137
|
+
* @returns The x402ResourceServer instance for chaining
|
|
138
|
+
*/
|
|
139
|
+
onAfterVerify(hook) {
|
|
140
|
+
this.afterVerifyHooks.push(hook);
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Register a hook to execute when payment verification fails.
|
|
145
|
+
* Can recover from failure by returning { recovered: true, result: VerifyResponse }
|
|
146
|
+
*
|
|
147
|
+
* @param hook - The hook function to register
|
|
148
|
+
* @returns The x402ResourceServer instance for chaining
|
|
149
|
+
*/
|
|
150
|
+
onVerifyFailure(hook) {
|
|
151
|
+
this.onVerifyFailureHooks.push(hook);
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Register a hook to execute before payment settlement.
|
|
156
|
+
* Can abort settlement by returning { abort: true, reason: string }
|
|
157
|
+
*
|
|
158
|
+
* @param hook - The hook function to register
|
|
159
|
+
* @returns The x402ResourceServer instance for chaining
|
|
160
|
+
*/
|
|
161
|
+
onBeforeSettle(hook) {
|
|
162
|
+
this.beforeSettleHooks.push(hook);
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Register a hook to execute after successful payment settlement.
|
|
167
|
+
*
|
|
168
|
+
* @param hook - The hook function to register
|
|
169
|
+
* @returns The x402ResourceServer instance for chaining
|
|
170
|
+
*/
|
|
171
|
+
onAfterSettle(hook) {
|
|
172
|
+
this.afterSettleHooks.push(hook);
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Register a hook to execute when payment settlement fails.
|
|
177
|
+
* Can recover from failure by returning { recovered: true, result: SettleResponse }
|
|
178
|
+
*
|
|
179
|
+
* @param hook - The hook function to register
|
|
180
|
+
* @returns The x402ResourceServer instance for chaining
|
|
181
|
+
*/
|
|
182
|
+
onSettleFailure(hook) {
|
|
183
|
+
this.onSettleFailureHooks.push(hook);
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Initialize by fetching supported kinds from all facilitators
|
|
188
|
+
* Creates mappings for supported responses and facilitator clients
|
|
189
|
+
* Earlier facilitators in the array get precedence
|
|
190
|
+
*/
|
|
191
|
+
async initialize() {
|
|
192
|
+
this.supportedResponsesMap.clear();
|
|
193
|
+
this.facilitatorClientsMap.clear();
|
|
194
|
+
for (const facilitatorClient of this.facilitatorClients) {
|
|
195
|
+
try {
|
|
196
|
+
const supported = await facilitatorClient.getSupported();
|
|
197
|
+
for (const kind of supported.kinds) {
|
|
198
|
+
const x402Version2 = kind.x402Version;
|
|
199
|
+
if (!this.supportedResponsesMap.has(x402Version2)) {
|
|
200
|
+
this.supportedResponsesMap.set(x402Version2, /* @__PURE__ */ new Map());
|
|
201
|
+
}
|
|
202
|
+
const responseVersionMap = this.supportedResponsesMap.get(x402Version2);
|
|
203
|
+
if (!this.facilitatorClientsMap.has(x402Version2)) {
|
|
204
|
+
this.facilitatorClientsMap.set(x402Version2, /* @__PURE__ */ new Map());
|
|
205
|
+
}
|
|
206
|
+
const clientVersionMap = this.facilitatorClientsMap.get(x402Version2);
|
|
207
|
+
if (!responseVersionMap.has(kind.network)) {
|
|
208
|
+
responseVersionMap.set(kind.network, /* @__PURE__ */ new Map());
|
|
209
|
+
}
|
|
210
|
+
const responseNetworkMap = responseVersionMap.get(kind.network);
|
|
211
|
+
if (!clientVersionMap.has(kind.network)) {
|
|
212
|
+
clientVersionMap.set(kind.network, /* @__PURE__ */ new Map());
|
|
213
|
+
}
|
|
214
|
+
const clientNetworkMap = clientVersionMap.get(kind.network);
|
|
215
|
+
if (!responseNetworkMap.has(kind.scheme)) {
|
|
216
|
+
responseNetworkMap.set(kind.scheme, supported);
|
|
217
|
+
clientNetworkMap.set(kind.scheme, facilitatorClient);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.warn(`Failed to fetch supported kinds from facilitator: ${error}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (this.supportedResponsesMap.size === 0) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
"Failed to initialize: no supported payment kinds loaded from any facilitator."
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get supported kind for a specific version, network, and scheme
|
|
232
|
+
*
|
|
233
|
+
* @param x402Version - The x402 version
|
|
234
|
+
* @param network - The network identifier
|
|
235
|
+
* @param scheme - The payment scheme
|
|
236
|
+
* @returns The supported kind or undefined if not found
|
|
237
|
+
*/
|
|
238
|
+
getSupportedKind(x402Version2, network, scheme) {
|
|
239
|
+
const versionMap = this.supportedResponsesMap.get(x402Version2);
|
|
240
|
+
if (!versionMap) return void 0;
|
|
241
|
+
const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
|
|
242
|
+
if (!supportedResponse) return void 0;
|
|
243
|
+
return supportedResponse.kinds.find(
|
|
244
|
+
(kind) => kind.x402Version === x402Version2 && kind.network === network && kind.scheme === scheme
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get facilitator extensions for a specific version, network, and scheme
|
|
249
|
+
*
|
|
250
|
+
* @param x402Version - The x402 version
|
|
251
|
+
* @param network - The network identifier
|
|
252
|
+
* @param scheme - The payment scheme
|
|
253
|
+
* @returns The facilitator extensions or empty array if not found
|
|
254
|
+
*/
|
|
255
|
+
getFacilitatorExtensions(x402Version2, network, scheme) {
|
|
256
|
+
const versionMap = this.supportedResponsesMap.get(x402Version2);
|
|
257
|
+
if (!versionMap) return [];
|
|
258
|
+
const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
|
|
259
|
+
return supportedResponse?.extensions || [];
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Build payment requirements for a protected resource
|
|
263
|
+
*
|
|
264
|
+
* @param resourceConfig - Configuration for the protected resource
|
|
265
|
+
* @returns Array of payment requirements
|
|
266
|
+
*/
|
|
267
|
+
async buildPaymentRequirements(resourceConfig) {
|
|
268
|
+
const requirements = [];
|
|
269
|
+
const scheme = resourceConfig.scheme;
|
|
270
|
+
const SchemeNetworkServer = findByNetworkAndScheme(
|
|
271
|
+
this.registeredServerSchemes,
|
|
272
|
+
scheme,
|
|
273
|
+
resourceConfig.network
|
|
274
|
+
);
|
|
275
|
+
if (!SchemeNetworkServer) {
|
|
276
|
+
console.warn(
|
|
277
|
+
`No server implementation registered for scheme: ${scheme}, network: ${resourceConfig.network}`
|
|
278
|
+
);
|
|
279
|
+
return requirements;
|
|
280
|
+
}
|
|
281
|
+
const supportedKind = this.getSupportedKind(
|
|
282
|
+
x402Version,
|
|
283
|
+
resourceConfig.network,
|
|
284
|
+
SchemeNetworkServer.scheme
|
|
285
|
+
);
|
|
286
|
+
if (!supportedKind) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`Facilitator does not support ${SchemeNetworkServer.scheme} on ${resourceConfig.network}. Make sure to call initialize() to fetch supported kinds from facilitators.`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const facilitatorExtensions = this.getFacilitatorExtensions(
|
|
292
|
+
x402Version,
|
|
293
|
+
resourceConfig.network,
|
|
294
|
+
SchemeNetworkServer.scheme
|
|
295
|
+
);
|
|
296
|
+
if (resourceConfig.assets?.length && (typeof resourceConfig.price === "string" || typeof resourceConfig.price === "number")) {
|
|
297
|
+
const results = [];
|
|
298
|
+
for (const symbol of resourceConfig.assets) {
|
|
299
|
+
const assetInfo = this.assetRegistry.resolve(resourceConfig.network, symbol);
|
|
300
|
+
const amount = convertMoney(resourceConfig.price, assetInfo.decimals);
|
|
301
|
+
const { address, decimals, name, version, assetTransferMethod, supportsEip2612, ...rest } = assetInfo;
|
|
302
|
+
const includeEip712Domain = !assetTransferMethod || supportsEip2612;
|
|
303
|
+
const extra = {
|
|
304
|
+
...includeEip712Domain && name && { name },
|
|
305
|
+
...includeEip712Domain && version && { version },
|
|
306
|
+
...assetTransferMethod && { assetTransferMethod },
|
|
307
|
+
...rest,
|
|
308
|
+
...resourceConfig.extra
|
|
309
|
+
};
|
|
310
|
+
const baseReq = {
|
|
311
|
+
scheme: SchemeNetworkServer.scheme,
|
|
312
|
+
network: resourceConfig.network,
|
|
313
|
+
amount,
|
|
314
|
+
asset: address,
|
|
315
|
+
payTo: resourceConfig.payTo,
|
|
316
|
+
maxTimeoutSeconds: resourceConfig.maxTimeoutSeconds || 300,
|
|
317
|
+
extra
|
|
318
|
+
};
|
|
319
|
+
const enhanced = await SchemeNetworkServer.enhancePaymentRequirements(
|
|
320
|
+
baseReq,
|
|
321
|
+
{ ...supportedKind, x402Version },
|
|
322
|
+
facilitatorExtensions
|
|
323
|
+
);
|
|
324
|
+
results.push(enhanced);
|
|
325
|
+
}
|
|
326
|
+
return results;
|
|
327
|
+
}
|
|
328
|
+
const parsedPrice = await SchemeNetworkServer.parsePrice(
|
|
329
|
+
resourceConfig.price,
|
|
330
|
+
resourceConfig.network
|
|
331
|
+
);
|
|
332
|
+
const baseRequirements = {
|
|
333
|
+
scheme: SchemeNetworkServer.scheme,
|
|
334
|
+
network: resourceConfig.network,
|
|
335
|
+
amount: parsedPrice.amount,
|
|
336
|
+
asset: parsedPrice.asset,
|
|
337
|
+
payTo: resourceConfig.payTo,
|
|
338
|
+
maxTimeoutSeconds: resourceConfig.maxTimeoutSeconds || 300,
|
|
339
|
+
// Default 5 minutes
|
|
340
|
+
extra: {
|
|
341
|
+
...parsedPrice.extra,
|
|
342
|
+
...resourceConfig.extra
|
|
343
|
+
// Merge user-provided extra
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
const requirement = await SchemeNetworkServer.enhancePaymentRequirements(
|
|
347
|
+
baseRequirements,
|
|
348
|
+
{
|
|
349
|
+
...supportedKind,
|
|
350
|
+
x402Version
|
|
351
|
+
},
|
|
352
|
+
facilitatorExtensions
|
|
353
|
+
);
|
|
354
|
+
requirements.push(requirement);
|
|
355
|
+
return requirements;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Build payment requirements from multiple payment options
|
|
359
|
+
* This method handles resolving dynamic payTo/price functions and builds requirements for each option
|
|
360
|
+
*
|
|
361
|
+
* @param paymentOptions - Array of payment options to convert
|
|
362
|
+
* @param context - HTTP request context for resolving dynamic functions
|
|
363
|
+
* @returns Array of payment requirements (one per option)
|
|
364
|
+
*/
|
|
365
|
+
async buildPaymentRequirementsFromOptions(paymentOptions, context) {
|
|
366
|
+
const allRequirements = [];
|
|
367
|
+
for (const option of paymentOptions) {
|
|
368
|
+
const resolvedPayTo = typeof option.payTo === "function" ? await option.payTo(context) : option.payTo;
|
|
369
|
+
const resolvedPrice = typeof option.price === "function" ? await option.price(context) : option.price;
|
|
370
|
+
const resourceConfig = {
|
|
371
|
+
scheme: option.scheme,
|
|
372
|
+
payTo: resolvedPayTo,
|
|
373
|
+
price: resolvedPrice,
|
|
374
|
+
network: option.network,
|
|
375
|
+
maxTimeoutSeconds: option.maxTimeoutSeconds,
|
|
376
|
+
extra: option.extra,
|
|
377
|
+
assets: option.assets
|
|
378
|
+
};
|
|
379
|
+
const requirements = await this.buildPaymentRequirements(resourceConfig);
|
|
380
|
+
allRequirements.push(...requirements);
|
|
381
|
+
}
|
|
382
|
+
return allRequirements;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Create a payment required response
|
|
386
|
+
*
|
|
387
|
+
* @param requirements - Payment requirements
|
|
388
|
+
* @param resourceInfo - Resource information
|
|
389
|
+
* @param error - Error message
|
|
390
|
+
* @param extensions - Optional declared extensions (for per-key enrichment)
|
|
391
|
+
* @param transportContext - Optional transport-specific context (e.g., HTTP request, MCP tool context)
|
|
392
|
+
* @returns Payment required response object
|
|
393
|
+
*/
|
|
394
|
+
async createPaymentRequiredResponse(requirements, resourceInfo, error, extensions, transportContext) {
|
|
395
|
+
let response = {
|
|
396
|
+
x402Version: 2,
|
|
397
|
+
error,
|
|
398
|
+
resource: resourceInfo,
|
|
399
|
+
accepts: requirements
|
|
400
|
+
};
|
|
401
|
+
if (extensions && Object.keys(extensions).length > 0) {
|
|
402
|
+
response.extensions = extensions;
|
|
403
|
+
}
|
|
404
|
+
if (extensions) {
|
|
405
|
+
for (const [key, declaration] of Object.entries(extensions)) {
|
|
406
|
+
const extension = this.registeredExtensions.get(key);
|
|
407
|
+
if (extension?.enrichPaymentRequiredResponse) {
|
|
408
|
+
try {
|
|
409
|
+
const context = {
|
|
410
|
+
requirements,
|
|
411
|
+
resourceInfo,
|
|
412
|
+
error,
|
|
413
|
+
paymentRequiredResponse: response,
|
|
414
|
+
transportContext
|
|
415
|
+
};
|
|
416
|
+
const extensionData = await extension.enrichPaymentRequiredResponse(
|
|
417
|
+
declaration,
|
|
418
|
+
context
|
|
419
|
+
);
|
|
420
|
+
if (extensionData !== void 0) {
|
|
421
|
+
if (!response.extensions) {
|
|
422
|
+
response.extensions = {};
|
|
423
|
+
}
|
|
424
|
+
response.extensions[key] = extensionData;
|
|
425
|
+
}
|
|
426
|
+
} catch (error2) {
|
|
427
|
+
console.error(
|
|
428
|
+
`Error in enrichPaymentRequiredResponse hook for extension ${key}:`,
|
|
429
|
+
error2
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return response;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Verify a payment against requirements
|
|
439
|
+
*
|
|
440
|
+
* @param paymentPayload - The payment payload to verify
|
|
441
|
+
* @param requirements - The payment requirements
|
|
442
|
+
* @returns Verification response
|
|
443
|
+
*/
|
|
444
|
+
async verifyPayment(paymentPayload, requirements) {
|
|
445
|
+
const context = {
|
|
446
|
+
paymentPayload,
|
|
447
|
+
requirements
|
|
448
|
+
};
|
|
449
|
+
for (const hook of this.beforeVerifyHooks) {
|
|
450
|
+
try {
|
|
451
|
+
const result = await hook(context);
|
|
452
|
+
if (result && "abort" in result && result.abort) {
|
|
453
|
+
return {
|
|
454
|
+
isValid: false,
|
|
455
|
+
invalidReason: result.reason,
|
|
456
|
+
invalidMessage: result.message
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
} catch (error) {
|
|
460
|
+
throw new VerifyError(400, {
|
|
461
|
+
isValid: false,
|
|
462
|
+
invalidReason: "before_verify_hook_error",
|
|
463
|
+
invalidMessage: error instanceof Error ? error.message : ""
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
const facilitatorClient = this.getFacilitatorClient(
|
|
469
|
+
paymentPayload.x402Version,
|
|
470
|
+
requirements.network,
|
|
471
|
+
requirements.scheme
|
|
472
|
+
);
|
|
473
|
+
let verifyResult;
|
|
474
|
+
if (!facilitatorClient) {
|
|
475
|
+
let lastError;
|
|
476
|
+
for (const client of this.facilitatorClients) {
|
|
477
|
+
try {
|
|
478
|
+
verifyResult = await client.verify(paymentPayload, requirements);
|
|
479
|
+
break;
|
|
480
|
+
} catch (error) {
|
|
481
|
+
lastError = error;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (!verifyResult) {
|
|
485
|
+
throw lastError || new Error(
|
|
486
|
+
`No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
verifyResult = await facilitatorClient.verify(paymentPayload, requirements);
|
|
491
|
+
}
|
|
492
|
+
const resultContext = {
|
|
493
|
+
...context,
|
|
494
|
+
result: verifyResult
|
|
495
|
+
};
|
|
496
|
+
for (const hook of this.afterVerifyHooks) {
|
|
497
|
+
await hook(resultContext);
|
|
498
|
+
}
|
|
499
|
+
return verifyResult;
|
|
500
|
+
} catch (error) {
|
|
501
|
+
const failureContext = {
|
|
502
|
+
...context,
|
|
503
|
+
error
|
|
504
|
+
};
|
|
505
|
+
for (const hook of this.onVerifyFailureHooks) {
|
|
506
|
+
const result = await hook(failureContext);
|
|
507
|
+
if (result && "recovered" in result && result.recovered) {
|
|
508
|
+
return result.result;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
throw error;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Settle a verified payment
|
|
516
|
+
*
|
|
517
|
+
* @param paymentPayload - The payment payload to settle
|
|
518
|
+
* @param requirements - The payment requirements
|
|
519
|
+
* @param declaredExtensions - Optional declared extensions (for per-key enrichment)
|
|
520
|
+
* @param transportContext - Optional transport-specific context (e.g., HTTP request/response, MCP tool context)
|
|
521
|
+
* @returns Settlement response
|
|
522
|
+
*/
|
|
523
|
+
async settlePayment(paymentPayload, requirements, declaredExtensions, transportContext) {
|
|
524
|
+
const context = {
|
|
525
|
+
paymentPayload,
|
|
526
|
+
requirements
|
|
527
|
+
};
|
|
528
|
+
for (const hook of this.beforeSettleHooks) {
|
|
529
|
+
try {
|
|
530
|
+
const result = await hook(context);
|
|
531
|
+
if (result && "abort" in result && result.abort) {
|
|
532
|
+
throw new SettleError(400, {
|
|
533
|
+
success: false,
|
|
534
|
+
errorReason: result.reason,
|
|
535
|
+
errorMessage: result.message,
|
|
536
|
+
transaction: "",
|
|
537
|
+
network: requirements.network
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
} catch (error) {
|
|
541
|
+
if (error instanceof SettleError) {
|
|
542
|
+
throw error;
|
|
543
|
+
}
|
|
544
|
+
throw new SettleError(400, {
|
|
545
|
+
success: false,
|
|
546
|
+
errorReason: "before_settle_hook_error",
|
|
547
|
+
errorMessage: error instanceof Error ? error.message : "",
|
|
548
|
+
transaction: "",
|
|
549
|
+
network: requirements.network
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
const facilitatorClient = this.getFacilitatorClient(
|
|
555
|
+
paymentPayload.x402Version,
|
|
556
|
+
requirements.network,
|
|
557
|
+
requirements.scheme
|
|
558
|
+
);
|
|
559
|
+
let settleResult;
|
|
560
|
+
if (!facilitatorClient) {
|
|
561
|
+
let lastError;
|
|
562
|
+
for (const client of this.facilitatorClients) {
|
|
563
|
+
try {
|
|
564
|
+
settleResult = await client.settle(paymentPayload, requirements);
|
|
565
|
+
break;
|
|
566
|
+
} catch (error) {
|
|
567
|
+
lastError = error;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (!settleResult) {
|
|
571
|
+
throw lastError || new Error(
|
|
572
|
+
`No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
settleResult = await facilitatorClient.settle(paymentPayload, requirements);
|
|
577
|
+
}
|
|
578
|
+
const resultContext = {
|
|
579
|
+
...context,
|
|
580
|
+
result: settleResult,
|
|
581
|
+
transportContext
|
|
582
|
+
};
|
|
583
|
+
for (const hook of this.afterSettleHooks) {
|
|
584
|
+
await hook(resultContext);
|
|
585
|
+
}
|
|
586
|
+
if (declaredExtensions) {
|
|
587
|
+
for (const [key, declaration] of Object.entries(declaredExtensions)) {
|
|
588
|
+
const extension = this.registeredExtensions.get(key);
|
|
589
|
+
if (extension?.enrichSettlementResponse) {
|
|
590
|
+
try {
|
|
591
|
+
const extensionData = await extension.enrichSettlementResponse(
|
|
592
|
+
declaration,
|
|
593
|
+
resultContext
|
|
594
|
+
);
|
|
595
|
+
if (extensionData !== void 0) {
|
|
596
|
+
if (!settleResult.extensions) {
|
|
597
|
+
settleResult.extensions = {};
|
|
598
|
+
}
|
|
599
|
+
settleResult.extensions[key] = extensionData;
|
|
600
|
+
}
|
|
601
|
+
} catch (error) {
|
|
602
|
+
console.error(`Error in enrichSettlementResponse hook for extension ${key}:`, error);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return settleResult;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
const failureContext = {
|
|
610
|
+
...context,
|
|
611
|
+
error
|
|
612
|
+
};
|
|
613
|
+
for (const hook of this.onSettleFailureHooks) {
|
|
614
|
+
const result = await hook(failureContext);
|
|
615
|
+
if (result && "recovered" in result && result.recovered) {
|
|
616
|
+
return result.result;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
throw error;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Find matching payment requirements for a payment
|
|
624
|
+
*
|
|
625
|
+
* @param availableRequirements - Array of available payment requirements
|
|
626
|
+
* @param paymentPayload - The payment payload
|
|
627
|
+
* @returns Matching payment requirements or undefined
|
|
628
|
+
*/
|
|
629
|
+
findMatchingRequirements(availableRequirements, paymentPayload) {
|
|
630
|
+
switch (paymentPayload.x402Version) {
|
|
631
|
+
case 2:
|
|
632
|
+
return availableRequirements.find(
|
|
633
|
+
(paymentRequirements) => deepEqual(paymentRequirements, paymentPayload.accepted)
|
|
634
|
+
);
|
|
635
|
+
case 1:
|
|
636
|
+
return availableRequirements.find(
|
|
637
|
+
(req) => req.scheme === paymentPayload.accepted.scheme && req.network === paymentPayload.accepted.network
|
|
638
|
+
);
|
|
639
|
+
default:
|
|
640
|
+
throw new Error(
|
|
641
|
+
`Unsupported x402 version: ${paymentPayload.x402Version}`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Process a payment request
|
|
647
|
+
*
|
|
648
|
+
* @param paymentPayload - Optional payment payload if provided
|
|
649
|
+
* @param resourceConfig - Configuration for the protected resource
|
|
650
|
+
* @param resourceInfo - Information about the resource being accessed
|
|
651
|
+
* @param extensions - Optional extensions to include in the response
|
|
652
|
+
* @returns Processing result
|
|
653
|
+
*/
|
|
654
|
+
async processPaymentRequest(paymentPayload, resourceConfig, resourceInfo, extensions) {
|
|
655
|
+
const requirements = await this.buildPaymentRequirements(resourceConfig);
|
|
656
|
+
if (!paymentPayload) {
|
|
657
|
+
return {
|
|
658
|
+
success: false,
|
|
659
|
+
requiresPayment: await this.createPaymentRequiredResponse(
|
|
660
|
+
requirements,
|
|
661
|
+
resourceInfo,
|
|
662
|
+
"Payment required",
|
|
663
|
+
extensions
|
|
664
|
+
)
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const matchingRequirements = this.findMatchingRequirements(requirements, paymentPayload);
|
|
668
|
+
if (!matchingRequirements) {
|
|
669
|
+
return {
|
|
670
|
+
success: false,
|
|
671
|
+
requiresPayment: await this.createPaymentRequiredResponse(
|
|
672
|
+
requirements,
|
|
673
|
+
resourceInfo,
|
|
674
|
+
"No matching payment requirements found",
|
|
675
|
+
extensions
|
|
676
|
+
)
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
const verificationResult = await this.verifyPayment(paymentPayload, matchingRequirements);
|
|
680
|
+
if (!verificationResult.isValid) {
|
|
681
|
+
return {
|
|
682
|
+
success: false,
|
|
683
|
+
error: verificationResult.invalidReason,
|
|
684
|
+
verificationResult
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
success: true,
|
|
689
|
+
verificationResult
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get facilitator client for a specific version, network, and scheme
|
|
694
|
+
*
|
|
695
|
+
* @param x402Version - The x402 version
|
|
696
|
+
* @param network - The network identifier
|
|
697
|
+
* @param scheme - The payment scheme
|
|
698
|
+
* @returns The facilitator client or undefined if not found
|
|
699
|
+
*/
|
|
700
|
+
getFacilitatorClient(x402Version2, network, scheme) {
|
|
701
|
+
const versionMap = this.facilitatorClientsMap.get(x402Version2);
|
|
702
|
+
if (!versionMap) return void 0;
|
|
703
|
+
return findByNetworkAndScheme(versionMap, scheme, network);
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
export {
|
|
707
|
+
HTTPFacilitatorClient,
|
|
708
|
+
RouteConfigurationError,
|
|
709
|
+
x402HTTPResourceServer,
|
|
710
|
+
x402ResourceServer
|
|
711
|
+
};
|
|
712
|
+
//# sourceMappingURL=index.mjs.map
|