@dexterai/x402 1.7.1 → 1.8.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 +47 -5
- package/dist/adapters/index.cjs +408 -412
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +7 -6
- package/dist/adapters/index.d.ts +7 -6
- package/dist/adapters/index.js +385 -415
- package/dist/adapters/index.js.map +1 -1
- package/dist/client/index.cjs +583 -548
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +8 -8
- package/dist/client/index.d.ts +8 -8
- package/dist/client/index.js +594 -555
- package/dist/client/index.js.map +1 -1
- package/dist/react/index.cjs +413 -398
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +4 -4
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +409 -396
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +211 -81
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +88 -38
- package/dist/server/index.d.ts +88 -38
- package/dist/server/index.js +211 -81
- package/dist/server/index.js.map +1 -1
- package/dist/{solana-CnW6P4lJ.d.cts → solana-BeGAqPta.d.cts} +17 -5
- package/dist/{solana-CJdhHls8.d.ts → solana-CQD9yMju.d.ts} +17 -5
- package/dist/{types-ClEZ34n4.d.ts → types-B477nBpg.d.cts} +8 -3
- package/dist/{types-BB-2vowq.d.cts → types-BWnUAPvD.d.ts} +8 -3
- package/dist/{types-C6ty4U6C.d.ts → types-DYLi7SuF.d.cts} +2 -0
- package/dist/{types-C6ty4U6C.d.cts → types-DYLi7SuF.d.ts} +2 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/{x402-client-B9ECWy7k.d.ts → x402-client-D9b3PHai.d.ts} +24 -3
- package/dist/{x402-client-BhLOoqwa.d.cts → x402-client-Dk9q2QQF.d.cts} +24 -3
- package/package.json +9 -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 || {});
|
|
@@ -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,29 @@ 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
|
+
servers.set(net, createX402Server({
|
|
474
|
+
payTo: netPayTo,
|
|
475
|
+
network: net,
|
|
476
|
+
asset,
|
|
477
|
+
facilitatorUrl,
|
|
478
|
+
defaultTimeoutSeconds: timeoutSeconds
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
const primaryServer = servers.get(configuredNetworks[0]);
|
|
393
482
|
return async (req, res, next) => {
|
|
394
483
|
try {
|
|
395
484
|
const paymentSignature = req.headers["payment-signature"];
|
|
@@ -407,8 +496,23 @@ function x402Middleware(config) {
|
|
|
407
496
|
mimeType,
|
|
408
497
|
timeoutSeconds
|
|
409
498
|
};
|
|
410
|
-
const
|
|
411
|
-
|
|
499
|
+
const allAccepts = [];
|
|
500
|
+
let requirements = null;
|
|
501
|
+
for (const [, srv] of servers) {
|
|
502
|
+
try {
|
|
503
|
+
const reqs = await srv.buildRequirements(requirementsOptions);
|
|
504
|
+
allAccepts.push(...reqs.accepts);
|
|
505
|
+
if (!requirements) requirements = reqs;
|
|
506
|
+
} catch (e) {
|
|
507
|
+
log("Failed to build requirements for a network:", e);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (!requirements || allAccepts.length === 0) {
|
|
511
|
+
res.status(500).json({ error: "Failed to build payment requirements" });
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
requirements = { ...requirements, accepts: allAccepts };
|
|
515
|
+
const encoded = primaryServer.encodeRequirements(requirements);
|
|
412
516
|
res.setHeader("PAYMENT-REQUIRED", encoded);
|
|
413
517
|
res.status(402).json({
|
|
414
518
|
error: "Payment required",
|
|
@@ -418,7 +522,16 @@ function x402Middleware(config) {
|
|
|
418
522
|
return;
|
|
419
523
|
}
|
|
420
524
|
log("Payment signature received, verifying...");
|
|
421
|
-
|
|
525
|
+
let targetServer = primaryServer;
|
|
526
|
+
try {
|
|
527
|
+
const decoded = JSON.parse(Buffer.from(paymentSignature, "base64").toString());
|
|
528
|
+
const paymentNetwork = decoded?.accepted?.network;
|
|
529
|
+
if (paymentNetwork && servers.has(paymentNetwork)) {
|
|
530
|
+
targetServer = servers.get(paymentNetwork);
|
|
531
|
+
}
|
|
532
|
+
} catch {
|
|
533
|
+
}
|
|
534
|
+
const verifyResult = await targetServer.verifyPayment(paymentSignature);
|
|
422
535
|
if (!verifyResult.isValid) {
|
|
423
536
|
log("Payment verification failed:", verifyResult.invalidReason);
|
|
424
537
|
res.status(402).json({
|
|
@@ -428,7 +541,7 @@ function x402Middleware(config) {
|
|
|
428
541
|
return;
|
|
429
542
|
}
|
|
430
543
|
log("Payment verified, settling...");
|
|
431
|
-
const settleResult = await
|
|
544
|
+
const settleResult = await targetServer.settlePayment(paymentSignature);
|
|
432
545
|
if (!settleResult.success) {
|
|
433
546
|
log("Payment settlement failed:", settleResult.errorReason);
|
|
434
547
|
res.status(402).json({
|
|
@@ -438,18 +551,39 @@ function x402Middleware(config) {
|
|
|
438
551
|
return;
|
|
439
552
|
}
|
|
440
553
|
log("Payment settled:", settleResult.transaction);
|
|
554
|
+
const settledNetwork = settleResult.network || configuredNetworks[0];
|
|
441
555
|
req.x402 = {
|
|
442
556
|
transaction: settleResult.transaction,
|
|
443
557
|
payer: verifyResult.payer ?? "",
|
|
444
|
-
network
|
|
558
|
+
network: settledNetwork
|
|
445
559
|
};
|
|
446
560
|
const paymentResponseData = {
|
|
447
561
|
success: true,
|
|
448
562
|
transaction: settleResult.transaction,
|
|
449
|
-
network,
|
|
563
|
+
network: settledNetwork,
|
|
450
564
|
payer: verifyResult.payer ?? ""
|
|
451
565
|
};
|
|
566
|
+
if (settleResult.extensions) {
|
|
567
|
+
paymentResponseData.extensions = settleResult.extensions;
|
|
568
|
+
}
|
|
452
569
|
res.setHeader("PAYMENT-RESPONSE", encodeBase64Json(paymentResponseData));
|
|
570
|
+
if (config.sponsoredAccess && settleResult.extensions?.["sponsored-access"]) {
|
|
571
|
+
const extData = settleResult.extensions["sponsored-access"];
|
|
572
|
+
const recs = extData?.info?.recommendations;
|
|
573
|
+
if (recs && recs.length > 0) {
|
|
574
|
+
log("Injecting sponsored-access recommendations into response");
|
|
575
|
+
const originalJson = res.json.bind(res);
|
|
576
|
+
res.json = function patchedJson(body) {
|
|
577
|
+
if (typeof config.sponsoredAccess === "object" && config.sponsoredAccess.inject) {
|
|
578
|
+
return originalJson(config.sponsoredAccess.inject(body, recs));
|
|
579
|
+
}
|
|
580
|
+
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
581
|
+
return originalJson({ _x402_sponsored: recs, ...body });
|
|
582
|
+
}
|
|
583
|
+
return originalJson(body);
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
453
587
|
next();
|
|
454
588
|
} catch (error) {
|
|
455
589
|
log("Middleware error:", error);
|
|
@@ -1883,13 +2017,9 @@ function stripePayTo(secretKeyOrConfig) {
|
|
|
1883
2017
|
amount: amountInCents,
|
|
1884
2018
|
currency: "usd",
|
|
1885
2019
|
payment_method_types: ["crypto"],
|
|
1886
|
-
payment_method_data: {
|
|
1887
|
-
type: "crypto"
|
|
1888
|
-
},
|
|
2020
|
+
payment_method_data: { type: "crypto" },
|
|
1889
2021
|
payment_method_options: {
|
|
1890
|
-
crypto: {
|
|
1891
|
-
mode: "custom"
|
|
1892
|
-
}
|
|
2022
|
+
crypto: { mode: "custom" }
|
|
1893
2023
|
},
|
|
1894
2024
|
confirm: true
|
|
1895
2025
|
});
|