@dexterai/x402 1.7.2 → 1.8.1
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 +73 -6
- package/dist/adapters/index.cjs +419 -412
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +11 -5
- package/dist/adapters/index.d.ts +11 -5
- package/dist/adapters/index.js +395 -415
- package/dist/adapters/index.js.map +1 -1
- package/dist/client/index.cjs +585 -566
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +6 -6
- package/dist/client/index.d.ts +6 -6
- package/dist/client/index.js +595 -572
- package/dist/client/index.js.map +1 -1
- package/dist/react/index.cjs +415 -416
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +410 -413
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +220 -94
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +53 -36
- package/dist/server/index.d.ts +53 -36
- package/dist/server/index.js +220 -94
- package/dist/server/index.js.map +1 -1
- package/dist/{solana-BWNn6iue.d.cts → solana-BeGAqPta.d.cts} +16 -4
- package/dist/{solana-q6Na2BF-.d.ts → solana-CQD9yMju.d.ts} +16 -4
- package/dist/{types-BzL-q-AE.d.cts → types-B477nBpg.d.cts} +7 -2
- package/dist/{types-By5Hhdmr.d.ts → types-BWnUAPvD.d.ts} +7 -2
- package/dist/{x402-client-rG7xFxSR.d.ts → x402-client-D9b3PHai.d.ts} +23 -2
- package/dist/{x402-client-BvQfgGwD.d.cts → x402-client-Dk9q2QQF.d.cts} +23 -2
- package/package.json +13 -3
package/dist/server/index.cjs
CHANGED
|
@@ -107,25 +107,77 @@ function decodeBase64Json(encoded) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/server/facilitator-client.ts
|
|
110
|
+
function isRetryable(error) {
|
|
111
|
+
if (error instanceof TypeError) return true;
|
|
112
|
+
if (error && typeof error === "object" && "status" in error) {
|
|
113
|
+
const status = error.status;
|
|
114
|
+
return status >= 500 && status < 600;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
var HttpError = class extends Error {
|
|
119
|
+
status;
|
|
120
|
+
body;
|
|
121
|
+
constructor(status, body) {
|
|
122
|
+
super(`HTTP ${status}`);
|
|
123
|
+
this.status = status;
|
|
124
|
+
this.body = body;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
110
127
|
var FacilitatorClient = class {
|
|
111
128
|
facilitatorUrl;
|
|
112
129
|
cachedSupported = null;
|
|
113
130
|
cacheTime = 0;
|
|
114
131
|
CACHE_TTL_MS = 6e4;
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
timeoutMs;
|
|
133
|
+
maxRetries;
|
|
134
|
+
retryBaseMs;
|
|
135
|
+
constructor(facilitatorUrl = DEXTER_FACILITATOR_URL, config) {
|
|
117
136
|
this.facilitatorUrl = facilitatorUrl.replace(/\/$/, "");
|
|
137
|
+
this.timeoutMs = config?.timeoutMs ?? 1e4;
|
|
138
|
+
this.maxRetries = config?.maxRetries ?? 3;
|
|
139
|
+
this.retryBaseMs = config?.retryBaseMs ?? 500;
|
|
140
|
+
}
|
|
141
|
+
async fetchWithTimeout(url, init) {
|
|
142
|
+
const controller = new AbortController();
|
|
143
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
144
|
+
try {
|
|
145
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
146
|
+
} finally {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async fetchWithRetry(url, init) {
|
|
151
|
+
let lastError;
|
|
152
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
153
|
+
try {
|
|
154
|
+
const response = await this.fetchWithTimeout(url, init);
|
|
155
|
+
if (!response.ok && response.status >= 500) {
|
|
156
|
+
throw new HttpError(response.status, await response.text());
|
|
157
|
+
}
|
|
158
|
+
return response;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
lastError = error;
|
|
161
|
+
if (attempt < this.maxRetries - 1 && isRetryable(error)) {
|
|
162
|
+
const delay = this.retryBaseMs * Math.pow(2, attempt);
|
|
163
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw lastError;
|
|
118
170
|
}
|
|
119
171
|
/**
|
|
120
|
-
* Get supported payment kinds from the facilitator
|
|
121
|
-
* Results are cached for 1 minute to reduce network calls
|
|
172
|
+
* Get supported payment kinds from the facilitator.
|
|
173
|
+
* Results are cached for 1 minute to reduce network calls.
|
|
122
174
|
*/
|
|
123
175
|
async getSupported() {
|
|
124
176
|
const now = Date.now();
|
|
125
177
|
if (this.cachedSupported && now - this.cacheTime < this.CACHE_TTL_MS) {
|
|
126
178
|
return this.cachedSupported;
|
|
127
179
|
}
|
|
128
|
-
const response = await
|
|
180
|
+
const response = await this.fetchWithTimeout(`${this.facilitatorUrl}/supported`);
|
|
129
181
|
if (!response.ok) {
|
|
130
182
|
throw new Error(`Facilitator /supported returned ${response.status}`);
|
|
131
183
|
}
|
|
@@ -135,9 +187,6 @@ var FacilitatorClient = class {
|
|
|
135
187
|
}
|
|
136
188
|
/**
|
|
137
189
|
* Get the fee payer address for a specific network
|
|
138
|
-
*
|
|
139
|
-
* @param network - CAIP-2 network identifier
|
|
140
|
-
* @returns Fee payer address
|
|
141
190
|
*/
|
|
142
191
|
async getFeePayer(network) {
|
|
143
192
|
const supported = await this.getSupported();
|
|
@@ -153,9 +202,6 @@ var FacilitatorClient = class {
|
|
|
153
202
|
}
|
|
154
203
|
/**
|
|
155
204
|
* Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)
|
|
156
|
-
*
|
|
157
|
-
* @param network - CAIP-2 network identifier
|
|
158
|
-
* @returns Extra data from /supported
|
|
159
205
|
*/
|
|
160
206
|
async getNetworkExtra(network) {
|
|
161
207
|
const supported = await this.getSupported();
|
|
@@ -165,30 +211,22 @@ var FacilitatorClient = class {
|
|
|
165
211
|
return kind?.extra;
|
|
166
212
|
}
|
|
167
213
|
/**
|
|
168
|
-
* Verify a payment with the facilitator
|
|
169
|
-
*
|
|
170
|
-
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
171
|
-
* @param requirements - The payment requirements that were sent to the client
|
|
172
|
-
* @returns Verification response
|
|
214
|
+
* Verify a payment with the facilitator.
|
|
215
|
+
* Retries on 5xx and network errors with exponential backoff.
|
|
173
216
|
*/
|
|
174
217
|
async verifyPayment(paymentSignatureHeader, requirements) {
|
|
175
218
|
try {
|
|
176
219
|
const paymentPayload = decodeBase64Json(paymentSignatureHeader);
|
|
177
|
-
const
|
|
178
|
-
x402Version: 2,
|
|
179
|
-
paymentPayload,
|
|
180
|
-
paymentRequirements: requirements
|
|
181
|
-
};
|
|
182
|
-
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
220
|
+
const response = await this.fetchWithRetry(`${this.facilitatorUrl}/verify`, {
|
|
183
221
|
method: "POST",
|
|
184
|
-
headers: {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
222
|
+
headers: { "Content-Type": "application/json" },
|
|
223
|
+
body: JSON.stringify({
|
|
224
|
+
x402Version: 2,
|
|
225
|
+
paymentPayload,
|
|
226
|
+
paymentRequirements: requirements
|
|
227
|
+
})
|
|
188
228
|
});
|
|
189
229
|
if (!response.ok) {
|
|
190
|
-
const errorText = await response.text();
|
|
191
|
-
console.error(`Facilitator /verify returned ${response.status}:`, errorText);
|
|
192
230
|
return {
|
|
193
231
|
isValid: false,
|
|
194
232
|
invalidReason: `facilitator_error_${response.status}`
|
|
@@ -196,38 +234,27 @@ var FacilitatorClient = class {
|
|
|
196
234
|
}
|
|
197
235
|
return await response.json();
|
|
198
236
|
} catch (error) {
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
isValid: false,
|
|
202
|
-
invalidReason: error instanceof Error ? error.message : "unexpected_verify_error"
|
|
203
|
-
};
|
|
237
|
+
const reason = error instanceof HttpError ? `facilitator_error_${error.status}` : error instanceof Error && error.name === "AbortError" ? "facilitator_timeout" : error instanceof Error ? error.message : "unexpected_verify_error";
|
|
238
|
+
return { isValid: false, invalidReason: reason };
|
|
204
239
|
}
|
|
205
240
|
}
|
|
206
241
|
/**
|
|
207
|
-
* Settle a payment with the facilitator
|
|
208
|
-
*
|
|
209
|
-
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
210
|
-
* @param requirements - The payment requirements that were sent to the client
|
|
211
|
-
* @returns Settlement response with transaction signature on success
|
|
242
|
+
* Settle a payment with the facilitator.
|
|
243
|
+
* Retries on 5xx and network errors with exponential backoff.
|
|
212
244
|
*/
|
|
213
245
|
async settlePayment(paymentSignatureHeader, requirements) {
|
|
214
246
|
try {
|
|
215
247
|
const paymentPayload = decodeBase64Json(paymentSignatureHeader);
|
|
216
|
-
const
|
|
217
|
-
x402Version: 2,
|
|
218
|
-
paymentPayload,
|
|
219
|
-
paymentRequirements: requirements
|
|
220
|
-
};
|
|
221
|
-
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
248
|
+
const response = await this.fetchWithRetry(`${this.facilitatorUrl}/settle`, {
|
|
222
249
|
method: "POST",
|
|
223
|
-
headers: {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
250
|
+
headers: { "Content-Type": "application/json" },
|
|
251
|
+
body: JSON.stringify({
|
|
252
|
+
x402Version: 2,
|
|
253
|
+
paymentPayload,
|
|
254
|
+
paymentRequirements: requirements
|
|
255
|
+
})
|
|
227
256
|
});
|
|
228
257
|
if (!response.ok) {
|
|
229
|
-
const errorText = await response.text();
|
|
230
|
-
console.error(`Facilitator /settle returned ${response.status}:`, errorText);
|
|
231
258
|
return {
|
|
232
259
|
success: false,
|
|
233
260
|
network: requirements.network,
|
|
@@ -235,22 +262,27 @@ var FacilitatorClient = class {
|
|
|
235
262
|
};
|
|
236
263
|
}
|
|
237
264
|
const result = await response.json();
|
|
238
|
-
return {
|
|
239
|
-
...result,
|
|
240
|
-
network: requirements.network
|
|
241
|
-
};
|
|
265
|
+
return { ...result, network: requirements.network };
|
|
242
266
|
} catch (error) {
|
|
243
|
-
|
|
267
|
+
const reason = error instanceof HttpError ? `facilitator_error_${error.status}` : error instanceof Error && error.name === "AbortError" ? "facilitator_timeout" : error instanceof Error ? error.message : "unexpected_settle_error";
|
|
244
268
|
return {
|
|
245
269
|
success: false,
|
|
246
270
|
network: requirements.network,
|
|
247
|
-
errorReason:
|
|
271
|
+
errorReason: reason
|
|
248
272
|
};
|
|
249
273
|
}
|
|
250
274
|
}
|
|
251
275
|
};
|
|
252
276
|
|
|
253
277
|
// src/server/x402-server.ts
|
|
278
|
+
function extractAmountFromHeader(paymentSignatureHeader) {
|
|
279
|
+
try {
|
|
280
|
+
const decoded = decodeBase64Json(paymentSignatureHeader);
|
|
281
|
+
return decoded?.accepted?.amount ?? decoded?.accepted?.maxAmountRequired;
|
|
282
|
+
} catch {
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
254
286
|
function createX402Server(config) {
|
|
255
287
|
const {
|
|
256
288
|
payTo,
|
|
@@ -261,6 +293,29 @@ function createX402Server(config) {
|
|
|
261
293
|
} = config;
|
|
262
294
|
const facilitator = new FacilitatorClient(facilitatorUrl);
|
|
263
295
|
let cachedExtra = null;
|
|
296
|
+
const requirementsCache = /* @__PURE__ */ new Map();
|
|
297
|
+
const CACHE_PRUNE_INTERVAL = 3e4;
|
|
298
|
+
let lastPrune = Date.now();
|
|
299
|
+
function cacheRequirements(accept) {
|
|
300
|
+
const ttl = (accept.maxTimeoutSeconds || defaultTimeoutSeconds) * 1e3;
|
|
301
|
+
requirementsCache.set(accept.payTo, { accept, expiresAt: Date.now() + ttl });
|
|
302
|
+
if (Date.now() - lastPrune > CACHE_PRUNE_INTERVAL) {
|
|
303
|
+
const now = Date.now();
|
|
304
|
+
for (const [key, entry] of requirementsCache) {
|
|
305
|
+
if (entry.expiresAt < now) requirementsCache.delete(key);
|
|
306
|
+
}
|
|
307
|
+
lastPrune = now;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function getCachedRequirements(address) {
|
|
311
|
+
const entry = requirementsCache.get(address);
|
|
312
|
+
if (!entry) return void 0;
|
|
313
|
+
if (entry.expiresAt < Date.now()) {
|
|
314
|
+
requirementsCache.delete(address);
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
return entry.accept;
|
|
318
|
+
}
|
|
264
319
|
async function resolvePayTo(context) {
|
|
265
320
|
if (typeof payTo === "string") return payTo;
|
|
266
321
|
return payTo(context || {});
|
|
@@ -269,15 +324,15 @@ function createX402Server(config) {
|
|
|
269
324
|
if (!cachedExtra) {
|
|
270
325
|
cachedExtra = await facilitator.getNetworkExtra(network);
|
|
271
326
|
}
|
|
272
|
-
|
|
327
|
+
const isSvm = network.startsWith("solana:");
|
|
328
|
+
if (isSvm && !cachedExtra?.feePayer) {
|
|
273
329
|
throw new Error(`Facilitator does not provide feePayer for network "${network}"`);
|
|
274
330
|
}
|
|
275
331
|
return {
|
|
276
|
-
feePayer: cachedExtra.feePayer,
|
|
277
|
-
decimals: cachedExtra
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
version: cachedExtra.version
|
|
332
|
+
...cachedExtra?.feePayer ? { feePayer: cachedExtra.feePayer } : {},
|
|
333
|
+
decimals: cachedExtra?.decimals ?? asset.decimals,
|
|
334
|
+
name: cachedExtra?.name,
|
|
335
|
+
version: cachedExtra?.version
|
|
281
336
|
};
|
|
282
337
|
}
|
|
283
338
|
async function buildPaymentAccept(resolvedPayTo, options) {
|
|
@@ -286,7 +341,7 @@ function createX402Server(config) {
|
|
|
286
341
|
timeoutSeconds = defaultTimeoutSeconds
|
|
287
342
|
} = options;
|
|
288
343
|
const extra = await getNetworkExtra();
|
|
289
|
-
|
|
344
|
+
const accept = {
|
|
290
345
|
scheme: "exact",
|
|
291
346
|
network,
|
|
292
347
|
amount: amountAtomic,
|
|
@@ -296,6 +351,8 @@ function createX402Server(config) {
|
|
|
296
351
|
maxTimeoutSeconds: timeoutSeconds,
|
|
297
352
|
extra
|
|
298
353
|
};
|
|
354
|
+
cacheRequirements(accept);
|
|
355
|
+
return accept;
|
|
299
356
|
}
|
|
300
357
|
async function getPaymentAccept(options) {
|
|
301
358
|
const address = await resolvePayTo({
|
|
@@ -338,14 +395,26 @@ function createX402Server(config) {
|
|
|
338
395
|
async function verifyPayment(paymentSignatureHeader, requirements) {
|
|
339
396
|
if (!requirements) {
|
|
340
397
|
const address = await resolvePayTo({ paymentHeader: paymentSignatureHeader });
|
|
341
|
-
requirements =
|
|
398
|
+
requirements = getCachedRequirements(address);
|
|
399
|
+
if (!requirements) {
|
|
400
|
+
requirements = await buildPaymentAccept(address, {
|
|
401
|
+
amountAtomic: extractAmountFromHeader(paymentSignatureHeader) ?? "0",
|
|
402
|
+
resourceUrl: ""
|
|
403
|
+
});
|
|
404
|
+
}
|
|
342
405
|
}
|
|
343
406
|
return facilitator.verifyPayment(paymentSignatureHeader, requirements);
|
|
344
407
|
}
|
|
345
408
|
async function settlePayment(paymentSignatureHeader, requirements) {
|
|
346
409
|
if (!requirements) {
|
|
347
410
|
const address = await resolvePayTo({ paymentHeader: paymentSignatureHeader });
|
|
348
|
-
requirements =
|
|
411
|
+
requirements = getCachedRequirements(address);
|
|
412
|
+
if (!requirements) {
|
|
413
|
+
requirements = await buildPaymentAccept(address, {
|
|
414
|
+
amountAtomic: extractAmountFromHeader(paymentSignatureHeader) ?? "0",
|
|
415
|
+
resourceUrl: ""
|
|
416
|
+
});
|
|
417
|
+
}
|
|
349
418
|
}
|
|
350
419
|
return facilitator.settlePayment(paymentSignatureHeader, requirements);
|
|
351
420
|
}
|
|
@@ -364,6 +433,15 @@ function createX402Server(config) {
|
|
|
364
433
|
// src/server/middleware.ts
|
|
365
434
|
var DEFAULT_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
366
435
|
var USDC_DECIMALS = 6;
|
|
436
|
+
function resolvePayToForNetwork(payTo, network) {
|
|
437
|
+
if (typeof payTo === "string" || typeof payTo === "function") return payTo;
|
|
438
|
+
if (network in payTo) return payTo[network];
|
|
439
|
+
const prefix = network.split(":")[0];
|
|
440
|
+
const globKey = `${prefix}:*`;
|
|
441
|
+
if (globKey in payTo) return payTo[globKey];
|
|
442
|
+
if ("*" in payTo) return payTo["*"];
|
|
443
|
+
throw new Error(`No payTo configured for network "${network}"`);
|
|
444
|
+
}
|
|
367
445
|
function x402Middleware(config) {
|
|
368
446
|
const {
|
|
369
447
|
payTo,
|
|
@@ -378,18 +456,37 @@ function x402Middleware(config) {
|
|
|
378
456
|
getAmount,
|
|
379
457
|
getDescription
|
|
380
458
|
} = config;
|
|
381
|
-
const providerDefaults = typeof payTo !== "string" ? payTo._x402Defaults : void 0;
|
|
382
|
-
const network = config.network ?? providerDefaults?.network ?? DEFAULT_NETWORK;
|
|
383
|
-
const facilitatorUrl = config.facilitatorUrl ?? providerDefaults?.facilitatorUrl;
|
|
384
459
|
const log = verbose ? console.log.bind(console, "[x402:middleware]") : () => {
|
|
385
460
|
};
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
461
|
+
const singleProviderDefaults = typeof payTo === "function" ? payTo._x402Defaults : void 0;
|
|
462
|
+
const facilitatorUrl = config.facilitatorUrl ?? singleProviderDefaults?.facilitatorUrl;
|
|
463
|
+
const configuredNetworks = (() => {
|
|
464
|
+
if (config.network) {
|
|
465
|
+
return Array.isArray(config.network) ? config.network : [config.network];
|
|
466
|
+
}
|
|
467
|
+
if (singleProviderDefaults?.network) return [singleProviderDefaults.network];
|
|
468
|
+
return [DEFAULT_NETWORK];
|
|
469
|
+
})();
|
|
470
|
+
const servers = /* @__PURE__ */ new Map();
|
|
471
|
+
for (const net of configuredNetworks) {
|
|
472
|
+
const netPayTo = resolvePayToForNetwork(payTo, net);
|
|
473
|
+
if (typeof netPayTo === "function" && netPayTo._stripeNetwork) {
|
|
474
|
+
const stripeNet = netPayTo._stripeNetwork;
|
|
475
|
+
if (net !== stripeNet) {
|
|
476
|
+
throw new Error(
|
|
477
|
+
`stripePayTo is configured for "${stripeNet}" but middleware includes network "${net}". Stripe only supports Base deposit addresses. Use a static payTo for other chains.`
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
servers.set(net, createX402Server({
|
|
482
|
+
payTo: netPayTo,
|
|
483
|
+
network: net,
|
|
484
|
+
asset,
|
|
485
|
+
facilitatorUrl,
|
|
486
|
+
defaultTimeoutSeconds: timeoutSeconds
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
489
|
+
const primaryServer = servers.get(configuredNetworks[0]);
|
|
393
490
|
return async (req, res, next) => {
|
|
394
491
|
try {
|
|
395
492
|
const paymentSignature = req.headers["payment-signature"];
|
|
@@ -407,8 +504,23 @@ function x402Middleware(config) {
|
|
|
407
504
|
mimeType,
|
|
408
505
|
timeoutSeconds
|
|
409
506
|
};
|
|
410
|
-
const
|
|
411
|
-
|
|
507
|
+
const allAccepts = [];
|
|
508
|
+
let requirements = null;
|
|
509
|
+
for (const [, srv] of servers) {
|
|
510
|
+
try {
|
|
511
|
+
const reqs = await srv.buildRequirements(requirementsOptions);
|
|
512
|
+
allAccepts.push(...reqs.accepts);
|
|
513
|
+
if (!requirements) requirements = reqs;
|
|
514
|
+
} catch (e) {
|
|
515
|
+
log("Failed to build requirements for a network:", e);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (!requirements || allAccepts.length === 0) {
|
|
519
|
+
res.status(500).json({ error: "Failed to build payment requirements" });
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
requirements = { ...requirements, accepts: allAccepts };
|
|
523
|
+
const encoded = primaryServer.encodeRequirements(requirements);
|
|
412
524
|
res.setHeader("PAYMENT-REQUIRED", encoded);
|
|
413
525
|
res.status(402).json({
|
|
414
526
|
error: "Payment required",
|
|
@@ -418,7 +530,16 @@ function x402Middleware(config) {
|
|
|
418
530
|
return;
|
|
419
531
|
}
|
|
420
532
|
log("Payment signature received, verifying...");
|
|
421
|
-
|
|
533
|
+
let targetServer = primaryServer;
|
|
534
|
+
try {
|
|
535
|
+
const decoded = JSON.parse(Buffer.from(paymentSignature, "base64").toString());
|
|
536
|
+
const paymentNetwork = decoded?.accepted?.network;
|
|
537
|
+
if (paymentNetwork && servers.has(paymentNetwork)) {
|
|
538
|
+
targetServer = servers.get(paymentNetwork);
|
|
539
|
+
}
|
|
540
|
+
} catch {
|
|
541
|
+
}
|
|
542
|
+
const verifyResult = await targetServer.verifyPayment(paymentSignature);
|
|
422
543
|
if (!verifyResult.isValid) {
|
|
423
544
|
log("Payment verification failed:", verifyResult.invalidReason);
|
|
424
545
|
res.status(402).json({
|
|
@@ -428,7 +549,7 @@ function x402Middleware(config) {
|
|
|
428
549
|
return;
|
|
429
550
|
}
|
|
430
551
|
log("Payment verified, settling...");
|
|
431
|
-
const settleResult = await
|
|
552
|
+
const settleResult = await targetServer.settlePayment(paymentSignature);
|
|
432
553
|
if (!settleResult.success) {
|
|
433
554
|
log("Payment settlement failed:", settleResult.errorReason);
|
|
434
555
|
res.status(402).json({
|
|
@@ -438,15 +559,16 @@ function x402Middleware(config) {
|
|
|
438
559
|
return;
|
|
439
560
|
}
|
|
440
561
|
log("Payment settled:", settleResult.transaction);
|
|
562
|
+
const settledNetwork = settleResult.network || configuredNetworks[0];
|
|
441
563
|
req.x402 = {
|
|
442
564
|
transaction: settleResult.transaction,
|
|
443
565
|
payer: verifyResult.payer ?? "",
|
|
444
|
-
network
|
|
566
|
+
network: settledNetwork
|
|
445
567
|
};
|
|
446
568
|
const paymentResponseData = {
|
|
447
569
|
success: true,
|
|
448
570
|
transaction: settleResult.transaction,
|
|
449
|
-
network,
|
|
571
|
+
network: settledNetwork,
|
|
450
572
|
payer: verifyResult.payer ?? ""
|
|
451
573
|
};
|
|
452
574
|
if (settleResult.extensions) {
|
|
@@ -482,6 +604,9 @@ function x402Middleware(config) {
|
|
|
482
604
|
}
|
|
483
605
|
|
|
484
606
|
// src/server/browser-support.ts
|
|
607
|
+
function esc(s) {
|
|
608
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
609
|
+
}
|
|
485
610
|
var USDC_ICON_SVG = `<svg width="18" height="18" viewBox="0 0 2000 2000" xmlns="http://www.w3.org/2000/svg"><path d="M1000 2000c554.17 0 1000-445.83 1000-1000S1554.17 0 1000 0 0 445.83 0 1000s445.83 1000 1000 1000z" fill="#2775ca"/><path d="M1275 1158.33c0-145.83-87.5-195.83-262.5-216.66-125-16.67-150-50-150-108.34s41.67-95.83 125-95.83c75 0 116.67 25 137.5 87.5 4.17 12.5 16.67 20.83 29.17 20.83h66.66c16.67 0 29.17-12.5 29.17-29.16v-4.17c-16.67-91.67-91.67-162.5-187.5-170.83v-100c0-16.67-12.5-29.17-33.33-33.34h-62.5c-16.67 0-29.17 12.5-33.34 33.34v95.83c-125 16.67-204.16 100-204.16 204.17 0 137.5 83.33 191.66 258.33 212.5 116.67 20.83 154.17 45.83 154.17 112.5s-58.34 112.5-137.5 112.5c-108.34 0-145.84-45.84-158.34-108.34-4.16-16.66-16.66-25-29.16-25h-70.84c-16.66 0-29.16 12.5-29.16 29.17v4.17c16.66 104.16 83.33 179.16 220.83 200v100c0 16.66 12.5 29.16 33.33 33.33h62.5c16.67 0 29.17-12.5 33.34-33.33v-100c125-20.84 208.33-108.34 208.33-220.84z" fill="#fff"/><path d="M787.5 1595.83c-325-116.66-491.67-479.16-370.83-800 62.5-175 200-308.33 370.83-370.83 16.67-8.33 25-20.83 25-41.67V325c0-16.67-8.33-29.17-25-33.33-4.17 0-12.5 0-16.67 4.16-395.83 125-612.5 545.84-487.5 941.67 75 233.33 254.17 412.5 487.5 487.5 16.67 8.33 33.34 0 37.5-16.67 4.17-4.16 4.17-8.33 4.17-16.66v-58.34c0-12.5-12.5-29.16-25-37.5zM1229.17 295.83c-16.67-8.33-33.34 0-37.5 16.67-4.17 4.17-4.17 8.33-4.17 16.67v58.33c0 16.67 12.5 33.33 25 41.67 325 116.66 491.67 479.16 370.83 800-62.5 175-200 308.33-370.83 370.83-16.67 8.33-25 20.83-25 41.67V1700c0 16.67 8.33 29.17 25 33.33 4.17 0 12.5 0 16.67-4.16 395.83-125 612.5-545.84 487.5-941.67-75-237.5-258.34-416.67-487.5-491.67z" fill="#fff"/></svg>`;
|
|
486
611
|
var DEXTER_CREST_SVG = `<svg width="36" height="36" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg"><g><path fill="#F2681A" d="m324.93,313.11c-115.5,0-231,0-350,0l350,0z"/><path fill="#FDFAF5" d="m230.43,50.62c1.1.85 2.19 1.7 3.32 2.57 6.02 4.8 11.77 9.88 17.46 15.07.92.84.92.84 1.86 1.69 1.82 1.69 3.59 3.42 5.35 5.16.61.56 1.22 1.13 1.84 1.71 5.66 5.76 6.18 10.43 6.13 18.3.02 1.16.04 2.32.06 3.52.06 3.83.06 7.65.07 11.48.02 2.68.05 5.35.08 8.03.05 5.6.09 11.21.1 16.81.02 7.15.09 14.31.17 21.46.06 5.53.1 11.05.13 16.58.02 2.64.04 5.27.07 7.91.18 17.58.12 32.82-11.24 47.32-7.35 7.27-16.54 12.06-25.42 17.22-1.97 1.16-3.94 2.33-5.91 3.49-7.16 4.24-14.34 8.44-21.53 12.62-4.8 2.79-9.59 5.6-14.38 8.42-1.25.73-2.5 1.47-3.79 2.23-2.32 1.36-4.64 2.73-6.96 4.1-27.47 16.09-27.47 16.09-42.16 12.93-8.06-2.28-14.94-5.82-22.16-10.02-1.17-.67-2.34-1.34-3.54-2.04-24.55-14.25-43.58-27.03-51.9-55.58-1.07-4.58-1.54-8.92-1.52-13.61.28-9.5.28-9.5-3.3-17.97-1.81-1.49-3.68-2.92-5.59-4.28-9.19-7.06-12.7-20.03-14.18-31.06-.54-5.77-.55-11.56-.6-17.35-.03-1.32-.07-2.63-.1-3.99-.01-1.26-.02-2.53-.03-3.83-.02-1.15-.03-2.29-.05-3.47.72-4.02 1.94-5.36 5.21-7.74 2.89-.53 2.89-.53 6.07-.46 1.71.02 1.71.02 3.46.05 1.19.04 2.37.08 3.59.12 1.2.02 2.41.04 3.65.06 2.97.05 5.93.13 8.9.23.14-1.35.29-2.7.43-4.08.63-5 1.78-9.74 3.14-14.58.22-.79.43-1.59.66-2.4.53-1.92 1.06-3.84 1.6-5.76-1.55-.45-1.55-.45-3.13-.9-9.52-3.52-17.1-10.95-21.37-20.1-3.81-9.26-3.87-20.34-.29-29.68 6.49-13.99 16.36-23.23 30.66-29.01 49.81-17.69 115.79 8.35 155.13 38.85z"/><path fill="#F2671A" d="m142.93,22.62c.86.19 1.73.39 2.62.59 36.12 8.21 68.79 24.98 95.38 50.75 1.02.98 2.03 1.97 3.08 2.98 10.84 10.66 10.84 10.66 11.05 14.62-2.06 3.55-5.44 4.18-9.17 5.3-.79.25-1.59.49-2.41.75-28.13 8.43-60.95 6.37-87.13-7.16-.86-.49-1.71-.97-2.6-1.48-7.37-4.05-12.59-3.36-20.59-1.54-22.76 4-48.47 1.53-68.69-9.74-4.88-3.88-8.23-8.29-10.21-14.22-.93-10.38-.67-18.44 5.83-26.83 19.57-23.38 55.99-20.36 82.83-14z"/><path fill="#F16619" d="m44.93,129.12c27.36-.03 54.72-.05 82.08-.06 12.7-.01 25.41-.01 38.11-.03 11.07-.01 22.14-.02 33.2-.02 5.86 0 11.73-.01 17.59-.01 5.51-.01 11.03-.01 16.54-.01 2.03 0 4.06 0 6.09-.01 2.76-.01 5.52 0 8.28 0 .81 0 1.63-.01 2.47-.01 5.51.02 5.51.02 6.81 1.32.22 3.43.22 3.43 0 7-2.75 2.75-3.42 2.66-7.15 2.82-1.41.07-1.41.07-2.85.14-1.47.05-1.47.05-2.98.11-1.49.07-1.49.07-3 .14-2.45.11-4.9.21-7.35.3-.2 1.3-.4 2.59-.6 3.93-2.57 16.08-5.93 29.89-18.89 40.86-10.35 7.28-21.87 8.49-34.17 7.71-13.11-2.33-22.52-9.19-30.33-19.83-4.49-7.64-4.8-17.05-5.83-25.67-4.24.39-8.47.77-12.83 1.17-.28 1.84-.28 1.84-.56 3.71-2.32 14.39-5.63 23.35-16.95 33.11-2.32 1.67-2.32 1.67-4.65 1.67 4 4.67 9.06 6.59 14.87 8.24 3.79 1.09 3.79 1.09 6.12 3.43-.65 5.31-.65 5.31-2.33 7-8.42-.27-15.13-2.29-22.17-7-1.09-1.21-2.17-2.43-3.25-3.65-2.72-2.81-4.45-3.84-8.36-4.16-1.67-.02-3.34-.02-5.01.01-1.77-.04-3.54-.09-5.3-.15-1.27-.04-1.27-.04-2.56-.08-9.26-.54-17.6-4.56-24.51-10.64-9.58-11.11-11.03-22.56-10.72-36.82.02-1.4.03-2.8.05-4.24.04-3.42.1-6.85.17-10.27z"/><path fill="#F26117" d="m172.68,203.08c7.27.09 13.23 1.97 18.87 6.65 2.88 3.07 3.86 5.12 4.25 9.32-.12 1.01-.24 2.02-.36 3.06-2.55.95-2.55.95-5.83 1.17-3.28-2.84-3.28-2.84-5.83-5.83-.36.58-.71 1.16-1.08 1.75-7.6 11.29-20.06 17.74-33.05 21.09-20.36 3.1-36.81-1.66-53.37-13.73-2.33-2.11-2.33-2.11-4.67-5.61.42-3.45.99-4.49 3.5-7 4.07.37 5.95 2.13 8.75 4.96 9.81 8.93 22.53 11.87 35.51 11.69 11.74-1.05 22.38-5.85 31.57-13.15 2.06-2.45 2.06-2.45 3.5-4.67-1.66.07-1.66.07-3.35.15-3.65-.15-3.65-.15-5.98-2.48.75-6.18 1.46-7.19 7.58-7.36z"/></g></svg>`;
|
|
487
612
|
var DEXTER_STYLES = `
|
|
@@ -741,22 +866,22 @@ function generatePaywallHtml(paymentRequiredHeader, requestUrl, method, config,
|
|
|
741
866
|
} catch {
|
|
742
867
|
}
|
|
743
868
|
const chainName = network.includes("solana") ? "Solana" : network.includes("eip155") ? "Base" : "";
|
|
744
|
-
const endpointSection = config.showEndpoint ? `<div class="endpoint"><code>${method} ${requestUrl}</code></div>` : "";
|
|
869
|
+
const endpointSection = config.showEndpoint ? `<div class="endpoint"><code>${esc(method)} ${esc(requestUrl)}</code></div>` : "";
|
|
745
870
|
return `<!DOCTYPE html>
|
|
746
871
|
<html lang="en">
|
|
747
872
|
<head>
|
|
748
873
|
<meta charset="utf-8">
|
|
749
874
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
750
|
-
<title>${config.title} \u2014 ${price} USDC</title>
|
|
875
|
+
<title>${esc(config.title)} \u2014 ${esc(price)} USDC</title>
|
|
751
876
|
<style>${DEXTER_STYLES}${PAY_BUTTON_STYLES}</style>
|
|
752
877
|
</head>
|
|
753
878
|
<body>
|
|
754
879
|
<div class="card">
|
|
755
880
|
<div class="crest">${DEXTER_CREST_SVG}</div>
|
|
756
|
-
<h1>${config.title}</h1>
|
|
757
|
-
<p class="desc">${description}</p>
|
|
758
|
-
<div class="price">${USDC_ICON_SVG}<span id="price-value">${price}</span></div>
|
|
759
|
-
<div class="chain">${chainName}${chainName ? " network" : ""}</div>
|
|
881
|
+
<h1>${esc(config.title)}</h1>
|
|
882
|
+
<p class="desc">${esc(description)}</p>
|
|
883
|
+
<div class="price">${USDC_ICON_SVG}<span id="price-value">${esc(price)}</span></div>
|
|
884
|
+
<div class="chain">${esc(chainName)}${chainName ? " network" : ""}</div>
|
|
760
885
|
${endpointSection}
|
|
761
886
|
|
|
762
887
|
<div id="pay-section" class="pay-section" style="display:none">
|
|
@@ -890,11 +1015,15 @@ function x402AccessPass(config) {
|
|
|
890
1015
|
facilitatorUrl,
|
|
891
1016
|
tiers: tierPrices,
|
|
892
1017
|
ratePerHour,
|
|
893
|
-
secret
|
|
1018
|
+
secret: explicitSecret,
|
|
894
1019
|
issuer = "x402-access-pass",
|
|
895
1020
|
verbose = false,
|
|
896
1021
|
description
|
|
897
1022
|
} = config;
|
|
1023
|
+
const secret = explicitSecret ?? import_crypto.default.randomBytes(32);
|
|
1024
|
+
if (!explicitSecret) {
|
|
1025
|
+
console.warn("[x402:access-pass] No secret provided \u2014 access passes will be invalidated on server restart. Set `secret` for production use.");
|
|
1026
|
+
}
|
|
898
1027
|
if (!tierPrices && !ratePerHour) {
|
|
899
1028
|
throw new Error("x402AccessPass: at least one of `tiers` or `ratePerHour` is required");
|
|
900
1029
|
}
|
|
@@ -1903,13 +2032,9 @@ function stripePayTo(secretKeyOrConfig) {
|
|
|
1903
2032
|
amount: amountInCents,
|
|
1904
2033
|
currency: "usd",
|
|
1905
2034
|
payment_method_types: ["crypto"],
|
|
1906
|
-
payment_method_data: {
|
|
1907
|
-
type: "crypto"
|
|
1908
|
-
},
|
|
2035
|
+
payment_method_data: { type: "crypto" },
|
|
1909
2036
|
payment_method_options: {
|
|
1910
|
-
crypto: {
|
|
1911
|
-
mode: "custom"
|
|
1912
|
-
}
|
|
2037
|
+
crypto: { mode: "custom" }
|
|
1913
2038
|
},
|
|
1914
2039
|
confirm: true
|
|
1915
2040
|
});
|
|
@@ -1932,6 +2057,7 @@ function stripePayTo(secretKeyOrConfig) {
|
|
|
1932
2057
|
network: caip2Network,
|
|
1933
2058
|
facilitatorUrl: "https://x402.dexter.cash"
|
|
1934
2059
|
};
|
|
2060
|
+
provider._stripeNetwork = caip2Network;
|
|
1935
2061
|
return provider;
|
|
1936
2062
|
}
|
|
1937
2063
|
// Annotate the CommonJS export names for ESM import in node:
|