@parallel-protocol/x402 0.7.1 → 0.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 +53 -20
- package/dist/{chunk-JPMK5YBU.cjs → chunk-KZZY5LS6.cjs} +13 -2
- package/dist/{chunk-POU7EH3E.cjs → chunk-NA7245L5.cjs} +201 -84
- package/dist/{chunk-AFRDHGDT.js → chunk-QAECRSWF.js} +13 -2
- package/dist/{chunk-AENXD4OC.js → chunk-SA5ULSG2.js} +179 -64
- package/dist/express.cjs +6 -6
- package/dist/express.d.cts +1 -1
- package/dist/express.d.ts +1 -1
- package/dist/express.js +2 -2
- package/dist/fastify.cjs +20 -29
- package/dist/fastify.d.cts +1 -1
- package/dist/fastify.d.ts +1 -1
- package/dist/fastify.js +13 -22
- package/dist/hono.cjs +7 -7
- package/dist/hono.d.cts +1 -1
- package/dist/hono.d.ts +1 -1
- package/dist/hono.js +2 -2
- package/dist/index.cjs +27 -19
- package/dist/index.d.cts +50 -6
- package/dist/index.d.ts +50 -6
- package/dist/index.js +2 -2
- package/dist/llm.cjs +13 -13
- package/dist/llm.d.cts +1 -1
- package/dist/llm.d.ts +1 -1
- package/dist/llm.js +1 -1
- package/dist/next.cjs +28 -44
- package/dist/next.d.cts +1 -1
- package/dist/next.d.ts +1 -1
- package/dist/next.js +20 -36
- package/dist/{types-q_gu5wjz.d.cts → types-BkGQ2lPA.d.cts} +30 -1
- package/dist/{types-q_gu5wjz.d.ts → types-BkGQ2lPA.d.ts} +30 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkKZZY5LS6_cjs = require('./chunk-KZZY5LS6.cjs');
|
|
4
4
|
var chains = require('@parallel-protocol/chains');
|
|
5
5
|
var zod = require('zod');
|
|
6
6
|
|
|
@@ -172,26 +172,32 @@ var facilitatorResponseSchema = zod.z.discriminatedUnion("success", [
|
|
|
172
172
|
chain: zod.z.string(),
|
|
173
173
|
gasSponsored: zod.z.boolean(),
|
|
174
174
|
networkId: zod.z.string(),
|
|
175
|
-
amountSettled: zod.z.string().optional()
|
|
175
|
+
amountSettled: zod.z.string().optional(),
|
|
176
|
+
alreadySettled: zod.z.boolean().optional()
|
|
176
177
|
}),
|
|
177
178
|
zod.z.object({
|
|
178
179
|
success: zod.z.literal(false),
|
|
179
180
|
error: zod.z.object({
|
|
180
181
|
code: zod.z.string(),
|
|
181
182
|
message: zod.z.string(),
|
|
182
|
-
hint: zod.z.string().optional()
|
|
183
|
+
hint: zod.z.string().optional(),
|
|
184
|
+
status: zod.z.number().optional(),
|
|
185
|
+
retryAfter: zod.z.string().optional()
|
|
183
186
|
})
|
|
184
187
|
})
|
|
185
188
|
]);
|
|
186
189
|
var verifyResponseSchema = zod.z.object({
|
|
187
|
-
isValid: zod.z.literal(true)
|
|
190
|
+
isValid: zod.z.literal(true),
|
|
191
|
+
settleToken: zod.z.string().min(1)
|
|
188
192
|
});
|
|
189
193
|
var settleSuccessSchema = zod.z.object({
|
|
190
194
|
txHash: txHashSchema,
|
|
191
195
|
route: zod.z.string(),
|
|
192
196
|
gasSponsored: zod.z.boolean(),
|
|
193
197
|
// Metered routes: the amount actually charged (smallest unit).
|
|
194
|
-
amountSettled: zod.z.string().optional()
|
|
198
|
+
amountSettled: zod.z.string().optional(),
|
|
199
|
+
// The facilitator answered from a recorded outcome, nothing was broadcast.
|
|
200
|
+
alreadySettled: zod.z.boolean().optional()
|
|
195
201
|
});
|
|
196
202
|
var settleErrorSchema = zod.z.object({
|
|
197
203
|
error: zod.z.string(),
|
|
@@ -205,11 +211,25 @@ var settleResponseSchema = zod.z.union([
|
|
|
205
211
|
// src/client.ts
|
|
206
212
|
var FACILITATOR_TIMEOUT_MS = 1e4;
|
|
207
213
|
var SETTLE_TIMEOUT_MS = 6e4;
|
|
214
|
+
var RETRY_AFTER_SHAPE = /^(?:\d{1,10}|[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT)$/;
|
|
215
|
+
async function readErrorBody(response) {
|
|
216
|
+
const json = await response.json().catch(() => null);
|
|
217
|
+
if (typeof json !== "object" || json === null) return {};
|
|
218
|
+
const { error, message } = json;
|
|
219
|
+
return {
|
|
220
|
+
...typeof error === "string" && { error },
|
|
221
|
+
...typeof message === "string" && { message }
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function retryAfterOf(response) {
|
|
225
|
+
const value = response.headers.get("retry-after");
|
|
226
|
+
return value !== null && RETRY_AFTER_SHAPE.test(value) ? value : void 0;
|
|
227
|
+
}
|
|
208
228
|
function decodePaymentHeader(paymentHeader) {
|
|
209
229
|
try {
|
|
210
230
|
return decodeBase64(paymentHeader);
|
|
211
231
|
} catch {
|
|
212
|
-
throw new
|
|
232
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.INVALID_PAYMENT);
|
|
213
233
|
}
|
|
214
234
|
}
|
|
215
235
|
var FacilitatorClient = class {
|
|
@@ -228,19 +248,26 @@ var FacilitatorClient = class {
|
|
|
228
248
|
signal: AbortSignal.timeout(FACILITATOR_TIMEOUT_MS)
|
|
229
249
|
});
|
|
230
250
|
if (!response.ok) {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
251
|
+
const body = await readErrorBody(response);
|
|
252
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(
|
|
253
|
+
body.error ?? chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE,
|
|
254
|
+
{
|
|
255
|
+
detail: body.message,
|
|
256
|
+
status: response.status,
|
|
257
|
+
retryAfter: retryAfterOf(response)
|
|
258
|
+
}
|
|
259
|
+
);
|
|
234
260
|
}
|
|
235
261
|
json = await response.json();
|
|
236
262
|
} catch (e) {
|
|
237
|
-
if (e instanceof
|
|
238
|
-
throw new
|
|
263
|
+
if (e instanceof chunkKZZY5LS6_cjs.X402RuntimeError) throw e;
|
|
264
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
239
265
|
}
|
|
240
266
|
try {
|
|
241
|
-
verifyResponseSchema.parse(json);
|
|
267
|
+
const { settleToken } = verifyResponseSchema.parse(json);
|
|
268
|
+
return { settleToken };
|
|
242
269
|
} catch {
|
|
243
|
-
throw new
|
|
270
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE);
|
|
244
271
|
}
|
|
245
272
|
}
|
|
246
273
|
async settle(paymentHeader, paymentRequirements, options) {
|
|
@@ -250,26 +277,43 @@ var FacilitatorClient = class {
|
|
|
250
277
|
const response = await fetch(`${this.config.url}/x402/settle`, {
|
|
251
278
|
method: "POST",
|
|
252
279
|
headers: this.buildHeaders(),
|
|
253
|
-
// settleAmount only travels for metered routes
|
|
280
|
+
// settleAmount only travels for metered routes; the settle token
|
|
281
|
+
// travels whenever verify issued one.
|
|
254
282
|
body: JSON.stringify({
|
|
255
283
|
paymentPayload,
|
|
256
284
|
paymentRequirements,
|
|
257
285
|
...options?.amount !== void 0 && {
|
|
258
286
|
settleAmount: options.amount
|
|
287
|
+
},
|
|
288
|
+
...options?.settleToken !== void 0 && {
|
|
289
|
+
settleToken: options.settleToken
|
|
259
290
|
}
|
|
260
291
|
}),
|
|
261
292
|
signal: AbortSignal.timeout(SETTLE_TIMEOUT_MS)
|
|
262
293
|
});
|
|
263
294
|
if (!response.ok) {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
295
|
+
const body = await readErrorBody(response);
|
|
296
|
+
if (body.error === void 0 && response.status >= 500) {
|
|
297
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
298
|
+
}
|
|
299
|
+
const retryAfter = retryAfterOf(response);
|
|
300
|
+
return {
|
|
301
|
+
success: false,
|
|
302
|
+
error: {
|
|
303
|
+
code: body.error ?? "SUBMISSION_FAILED",
|
|
304
|
+
message: body.message ?? "Settlement failed",
|
|
305
|
+
status: response.status,
|
|
306
|
+
...retryAfter !== void 0 && { retryAfter }
|
|
307
|
+
}
|
|
308
|
+
};
|
|
268
309
|
}
|
|
269
310
|
json = await response.json();
|
|
270
311
|
} catch (e) {
|
|
271
|
-
if (e instanceof
|
|
272
|
-
|
|
312
|
+
if (e instanceof chunkKZZY5LS6_cjs.X402RuntimeError) throw e;
|
|
313
|
+
if (e instanceof Error && e.name === "TimeoutError") {
|
|
314
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.SETTLE_TIMEOUT);
|
|
315
|
+
}
|
|
316
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
273
317
|
}
|
|
274
318
|
try {
|
|
275
319
|
const parsed = settleResponseSchema.parse(json);
|
|
@@ -283,7 +327,8 @@ var FacilitatorClient = class {
|
|
|
283
327
|
networkId: toEip155Network(paymentRequirements.network),
|
|
284
328
|
...parsed.amountSettled !== void 0 && {
|
|
285
329
|
amountSettled: parsed.amountSettled
|
|
286
|
-
}
|
|
330
|
+
},
|
|
331
|
+
...parsed.alreadySettled && { alreadySettled: true }
|
|
287
332
|
};
|
|
288
333
|
}
|
|
289
334
|
return {
|
|
@@ -291,7 +336,7 @@ var FacilitatorClient = class {
|
|
|
291
336
|
error: { code: parsed.error, message: parsed.message }
|
|
292
337
|
};
|
|
293
338
|
} catch {
|
|
294
|
-
throw new
|
|
339
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE);
|
|
295
340
|
}
|
|
296
341
|
}
|
|
297
342
|
async pay(paymentHeader) {
|
|
@@ -305,17 +350,17 @@ var FacilitatorClient = class {
|
|
|
305
350
|
signal: AbortSignal.timeout(FACILITATOR_TIMEOUT_MS)
|
|
306
351
|
});
|
|
307
352
|
if (!response.ok) {
|
|
308
|
-
throw new
|
|
353
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
309
354
|
}
|
|
310
355
|
json = await response.json();
|
|
311
356
|
} catch (e) {
|
|
312
|
-
if (e instanceof
|
|
313
|
-
throw new
|
|
357
|
+
if (e instanceof chunkKZZY5LS6_cjs.X402RuntimeError) throw e;
|
|
358
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
314
359
|
}
|
|
315
360
|
try {
|
|
316
361
|
return facilitatorResponseSchema.parse(json);
|
|
317
362
|
} catch {
|
|
318
|
-
throw new
|
|
363
|
+
throw new chunkKZZY5LS6_cjs.X402RuntimeError(chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE);
|
|
319
364
|
}
|
|
320
365
|
}
|
|
321
366
|
buildHeaders() {
|
|
@@ -358,20 +403,20 @@ async function requestContext(adapter) {
|
|
|
358
403
|
}
|
|
359
404
|
async function resolvePrice(config, ctx) {
|
|
360
405
|
if (typeof config.price !== "function") return config.price;
|
|
361
|
-
if (!ctx) throw new
|
|
406
|
+
if (!ctx) throw new chunkKZZY5LS6_cjs.X402ConfigError("A price function needs the request");
|
|
362
407
|
let price;
|
|
363
408
|
try {
|
|
364
409
|
price = await config.price(ctx);
|
|
365
410
|
} catch (cause) {
|
|
366
|
-
if (
|
|
367
|
-
throw new
|
|
411
|
+
if (chunkKZZY5LS6_cjs.isPriceRejection(cause)) throw cause;
|
|
412
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
368
413
|
`The price function of route "${config.network}${ctx.path}" threw`,
|
|
369
414
|
cause
|
|
370
415
|
);
|
|
371
416
|
}
|
|
372
417
|
const valid = typeof price === "bigint" ? price > 0n : typeof price === "string" && PRICE_FORMAT.test(price) && Number(price) > 0;
|
|
373
418
|
if (!valid) {
|
|
374
|
-
throw new
|
|
419
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
375
420
|
`The price function of route "${config.network}${ctx.path}" returned ${JSON.stringify(
|
|
376
421
|
typeof price === "bigint" ? price.toString() : price
|
|
377
422
|
)} \u2014 expected a positive amount like "0.01" or a bigint`
|
|
@@ -381,10 +426,21 @@ async function resolvePrice(config, ctx) {
|
|
|
381
426
|
}
|
|
382
427
|
var SETTLE_AMOUNT_HEADER = "payment-settle-amount";
|
|
383
428
|
var DEFAULT_MAX_TIMEOUT_SECONDS = 300;
|
|
429
|
+
var SETTLEMENT_RESERVE_SECONDS = 120;
|
|
384
430
|
function handlerTimeoutFor(config) {
|
|
385
431
|
if ((config.scheme ?? "exact") !== "upto") return HANDLER_TIMEOUT_MS;
|
|
386
432
|
const seconds = config.maxTimeoutSeconds ?? DEFAULT_MAX_TIMEOUT_SECONDS;
|
|
387
|
-
return Math.max(
|
|
433
|
+
return Math.max(
|
|
434
|
+
HANDLER_TIMEOUT_MS,
|
|
435
|
+
(seconds - SETTLEMENT_RESERVE_SECONDS) * 1e3
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
function jsonResult(status, body, headers = {}) {
|
|
439
|
+
return {
|
|
440
|
+
status,
|
|
441
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
442
|
+
body
|
|
443
|
+
};
|
|
388
444
|
}
|
|
389
445
|
function resolveSettleAmount(raw, maxAmount, decimals) {
|
|
390
446
|
if (raw === void 0)
|
|
@@ -412,13 +468,16 @@ var INVALID_METER_RESPONSE = {
|
|
|
412
468
|
message: "The route's meter function failed or returned something that is not an amount; the response was discarded and nothing was charged."
|
|
413
469
|
};
|
|
414
470
|
function invalidSettleAmountResponse(reason) {
|
|
415
|
-
return
|
|
471
|
+
return jsonResult(
|
|
472
|
+
402,
|
|
473
|
+
reason === "meter" ? INVALID_METER_RESPONSE : INVALID_SETTLE_AMOUNT_RESPONSE
|
|
474
|
+
);
|
|
416
475
|
}
|
|
417
476
|
var AMOUNT_FORMAT = /^\d+(?:\.\d+)?$/;
|
|
418
477
|
async function resolveCharge(verified, settleHeader, response) {
|
|
419
478
|
if (verified.meter) {
|
|
420
479
|
if (!verified.request) {
|
|
421
|
-
throw new
|
|
480
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
422
481
|
"A metered payment carries a meter but no request context"
|
|
423
482
|
);
|
|
424
483
|
}
|
|
@@ -469,14 +528,14 @@ function resolveAcceptedTokens(config) {
|
|
|
469
528
|
}
|
|
470
529
|
const defaults = config.scheme === "upto" ? getMeteredAcceptedTokens(config.network) : getDefaultAcceptedTokens(config.network);
|
|
471
530
|
if (defaults) return defaults;
|
|
472
|
-
throw new
|
|
531
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
473
532
|
`acceptedTokens is required for network "${config.network}" \u2014 no defaults available for this chain`
|
|
474
533
|
);
|
|
475
534
|
}
|
|
476
535
|
function resolveDecimals(config, asset) {
|
|
477
536
|
const decimals = config.decimals ?? getTokenDecimals(config.network, asset);
|
|
478
537
|
if (decimals === void 0) {
|
|
479
|
-
throw new
|
|
538
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
480
539
|
`Unknown decimals for token ${asset} on "${config.network}" \u2014 add it to the @parallel-protocol/chains catalog or set the route's \`decimals\` explicitly`
|
|
481
540
|
);
|
|
482
541
|
}
|
|
@@ -485,7 +544,7 @@ function resolveDecimals(config, asset) {
|
|
|
485
544
|
function priceInUnits(price, decimals, asset) {
|
|
486
545
|
const units = parsePrice(price, decimals);
|
|
487
546
|
if (units === 0n) {
|
|
488
|
-
throw new
|
|
547
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
489
548
|
`Price ${typeof price === "bigint" ? price.toString() : `"${price}"`} is below the precision of token ${asset} (${decimals} decimals)`
|
|
490
549
|
);
|
|
491
550
|
}
|
|
@@ -560,29 +619,43 @@ function buildFacilitatorRequirements(config, asset, price) {
|
|
|
560
619
|
}
|
|
561
620
|
};
|
|
562
621
|
}
|
|
563
|
-
|
|
622
|
+
var CODE_SHAPE = /^[A-Z][A-Z0-9_]{2,63}$/;
|
|
623
|
+
var MESSAGE_MAX_LENGTH = 300;
|
|
624
|
+
function shapedCode(code) {
|
|
625
|
+
return typeof code === "string" && CODE_SHAPE.test(code) ? code : void 0;
|
|
626
|
+
}
|
|
627
|
+
function boundedMessage(message) {
|
|
628
|
+
if (typeof message !== "string") return void 0;
|
|
629
|
+
const oneLine = message.replace(/[\p{Cc}\p{Cf}]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
630
|
+
if (!oneLine) return void 0;
|
|
631
|
+
const chars = Array.from(oneLine);
|
|
632
|
+
return chars.length > MESSAGE_MAX_LENGTH ? `${chars.slice(0, MESSAGE_MAX_LENGTH).join("")}\u2026` : oneLine;
|
|
633
|
+
}
|
|
634
|
+
function paymentRequiredResponse(url, config, resolvedTokens, price, refusal) {
|
|
564
635
|
const paymentRequired = buildPaymentRequired(
|
|
565
636
|
url,
|
|
566
637
|
config,
|
|
567
638
|
resolvedTokens,
|
|
568
639
|
price
|
|
569
640
|
);
|
|
570
|
-
if (
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
641
|
+
if (refusal) {
|
|
642
|
+
paymentRequired.error = shapedCode(refusal.code) ?? chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE;
|
|
643
|
+
const message = boundedMessage(refusal.detail);
|
|
644
|
+
if (message) paymentRequired.message = message;
|
|
645
|
+
}
|
|
646
|
+
return jsonResult(402, paymentRequired, {
|
|
647
|
+
"payment-required": encodeBase64(paymentRequired),
|
|
648
|
+
"access-control-expose-headers": "payment-required",
|
|
649
|
+
...refusal?.retryAfter !== void 0 && {
|
|
650
|
+
"Retry-After": refusal.retryAfter
|
|
651
|
+
}
|
|
652
|
+
});
|
|
580
653
|
}
|
|
581
654
|
function createPaymentGate(config) {
|
|
582
655
|
try {
|
|
583
656
|
paymentMiddlewareConfigSchema.parse(config);
|
|
584
657
|
} catch (cause) {
|
|
585
|
-
throw new
|
|
658
|
+
throw new chunkKZZY5LS6_cjs.X402ConfigError(
|
|
586
659
|
"Invalid paymentMiddleware config \u2014 check your routes, facilitator URL and payTo addresses",
|
|
587
660
|
cause
|
|
588
661
|
);
|
|
@@ -599,7 +672,7 @@ function createPaymentGate(config) {
|
|
|
599
672
|
try {
|
|
600
673
|
price = await resolvePrice(routeConfig, request);
|
|
601
674
|
} catch (err) {
|
|
602
|
-
if (!
|
|
675
|
+
if (!chunkKZZY5LS6_cjs.isPriceRejection(err)) throw err;
|
|
603
676
|
const status = Number.isInteger(err.status) && err.status >= 400 && err.status <= 599 ? err.status : 400;
|
|
604
677
|
return {
|
|
605
678
|
type: "error",
|
|
@@ -632,10 +705,11 @@ function createPaymentGate(config) {
|
|
|
632
705
|
asset,
|
|
633
706
|
price
|
|
634
707
|
);
|
|
708
|
+
let settleToken;
|
|
635
709
|
try {
|
|
636
|
-
await client.verify(paymentHeader, requirements);
|
|
710
|
+
({ settleToken } = await client.verify(paymentHeader, requirements));
|
|
637
711
|
} catch (e) {
|
|
638
|
-
const
|
|
712
|
+
const refusal = e instanceof chunkKZZY5LS6_cjs.X402RuntimeError ? { code: e.code, detail: e.detail, retryAfter: e.retryAfter } : { code: chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE };
|
|
639
713
|
return {
|
|
640
714
|
type: "error",
|
|
641
715
|
result: paymentRequiredResponse(
|
|
@@ -643,7 +717,7 @@ function createPaymentGate(config) {
|
|
|
643
717
|
routeConfig,
|
|
644
718
|
resolvedTokens,
|
|
645
719
|
price,
|
|
646
|
-
|
|
720
|
+
refusal
|
|
647
721
|
)
|
|
648
722
|
};
|
|
649
723
|
}
|
|
@@ -654,12 +728,71 @@ function createPaymentGate(config) {
|
|
|
654
728
|
maxAmount: BigInt(requirements.maxAmountRequired),
|
|
655
729
|
decimals: resolveDecimals(routeConfig, asset),
|
|
656
730
|
handlerTimeoutMs: handlerTimeoutFor(routeConfig),
|
|
657
|
-
|
|
658
|
-
settle: (options) => options ? client.settle(paymentHeader, requirements, options) : client.settle(paymentHeader, requirements),
|
|
731
|
+
settle: (options) => client.settle(paymentHeader, requirements, { ...options, settleToken }),
|
|
659
732
|
...routeConfig.meter && { meter: routeConfig.meter, request }
|
|
660
733
|
};
|
|
661
734
|
};
|
|
662
735
|
}
|
|
736
|
+
var UNKNOWN_SETTLEMENT_MESSAGE = "The payment could not be settled.";
|
|
737
|
+
var MERCHANT_SETTLEMENT_MESSAGE = "The merchant could not complete the settlement of this payment. The payment was not taken: do not sign another payment for this request before checking with the merchant.";
|
|
738
|
+
var AGENT_MESSAGES = {
|
|
739
|
+
[chunkKZZY5LS6_cjs.X402_ERROR_CODES.SETTLE_TIMEOUT]: "The facilitator did not confirm the settlement in time. The payment may still settle: do not sign another payment for this request before checking with the merchant.",
|
|
740
|
+
[chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE]: "The facilitator could not be reached to settle the payment. It may still have settled: do not sign another payment for this request before checking with the merchant.",
|
|
741
|
+
[chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE]: "The facilitator answered the settlement with an unexpected response. The payment may have settled: do not sign another payment for this request before checking with the merchant.",
|
|
742
|
+
SETTLE_IN_PROGRESS: "The facilitator is still settling this payment. Do not sign another payment for this request before checking with the merchant.",
|
|
743
|
+
RECEIPT_UNAVAILABLE: "The facilitator sent the transaction but could not read its confirmation. The payment may have settled: do not sign another payment for this request before checking with the merchant.",
|
|
744
|
+
SETTLE_TOKEN_INVALID: MERCHANT_SETTLEMENT_MESSAGE,
|
|
745
|
+
SETTLE_TOKEN_REQUIRED: MERCHANT_SETTLEMENT_MESSAGE,
|
|
746
|
+
AMOUNT_MISMATCH: "The facilitator settled a different amount than this request asked for. The response was not served: do not sign another payment for this request before checking with the merchant.",
|
|
747
|
+
UNKNOWN: UNKNOWN_SETTLEMENT_MESSAGE
|
|
748
|
+
};
|
|
749
|
+
function settlementFailure(code, failure) {
|
|
750
|
+
const reason = shapedCode(code) ?? "UNKNOWN";
|
|
751
|
+
return {
|
|
752
|
+
settled: false,
|
|
753
|
+
reason,
|
|
754
|
+
message: AGENT_MESSAGES[reason] ?? boundedMessage(failure?.message) ?? UNKNOWN_SETTLEMENT_MESSAGE,
|
|
755
|
+
...failure?.retryAfter !== void 0 && {
|
|
756
|
+
retryAfter: failure.retryAfter
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
var RETRY_ONCE_CODES = /* @__PURE__ */ new Set([
|
|
761
|
+
chunkKZZY5LS6_cjs.X402_ERROR_CODES.SETTLE_TIMEOUT,
|
|
762
|
+
chunkKZZY5LS6_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE
|
|
763
|
+
]);
|
|
764
|
+
async function settleVerified(verified, options) {
|
|
765
|
+
let response;
|
|
766
|
+
for (let attempt = 0; response === void 0; attempt++) {
|
|
767
|
+
try {
|
|
768
|
+
response = await verified.settle(options);
|
|
769
|
+
} catch (e) {
|
|
770
|
+
const code = e instanceof chunkKZZY5LS6_cjs.X402RuntimeError ? e.code : "UNKNOWN";
|
|
771
|
+
if (attempt > 0 || !RETRY_ONCE_CODES.has(code)) {
|
|
772
|
+
return settlementFailure(code);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (!response.success) {
|
|
777
|
+
return settlementFailure(response.error.code, response.error);
|
|
778
|
+
}
|
|
779
|
+
if (options?.amount !== void 0 && response.amountSettled !== options.amount) {
|
|
780
|
+
return settlementFailure("AMOUNT_MISMATCH");
|
|
781
|
+
}
|
|
782
|
+
return { settled: true, confirmation: response };
|
|
783
|
+
}
|
|
784
|
+
function settlementFailureResponse(failure) {
|
|
785
|
+
const body = {
|
|
786
|
+
error: "PAYMENT_SETTLEMENT_FAILED",
|
|
787
|
+
reason: failure.reason,
|
|
788
|
+
message: failure.message
|
|
789
|
+
};
|
|
790
|
+
return jsonResult(402, body, {
|
|
791
|
+
...failure.retryAfter !== void 0 && {
|
|
792
|
+
"Retry-After": failure.retryAfter
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
}
|
|
663
796
|
function createPaymentMiddleware(config) {
|
|
664
797
|
const gate = createPaymentGate(config);
|
|
665
798
|
return async (adapter) => {
|
|
@@ -689,11 +822,7 @@ function createPaymentMiddleware(config) {
|
|
|
689
822
|
});
|
|
690
823
|
if (resolved.kind === "invalid") {
|
|
691
824
|
handlerResult.discardResponse();
|
|
692
|
-
return
|
|
693
|
-
status: 402,
|
|
694
|
-
headers: { "Content-Type": "application/json" },
|
|
695
|
-
body: invalidSettleAmountResponse(resolved.reason)
|
|
696
|
-
};
|
|
825
|
+
return invalidSettleAmountResponse(resolved.reason);
|
|
697
826
|
}
|
|
698
827
|
if (resolved.kind === "skip") {
|
|
699
828
|
handlerResult.sendResponse();
|
|
@@ -701,33 +830,19 @@ function createPaymentMiddleware(config) {
|
|
|
701
830
|
}
|
|
702
831
|
settleOptions = { amount: resolved.amount };
|
|
703
832
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
const settleResponse = settleOptions ? await gateResult.settle(settleOptions) : await gateResult.settle();
|
|
707
|
-
if (settleResponse.success) {
|
|
708
|
-
handlerResult.setResponseHeader(
|
|
709
|
-
"payment-response",
|
|
710
|
-
encodeBase64(settleResponse)
|
|
711
|
-
);
|
|
712
|
-
handlerResult.setResponseHeader(
|
|
713
|
-
"access-control-expose-headers",
|
|
714
|
-
"payment-response"
|
|
715
|
-
);
|
|
716
|
-
settled = true;
|
|
717
|
-
}
|
|
718
|
-
} catch {
|
|
719
|
-
}
|
|
720
|
-
if (!settled) {
|
|
833
|
+
const outcome = await settleVerified(gateResult, settleOptions);
|
|
834
|
+
if (!outcome.settled) {
|
|
721
835
|
handlerResult.discardResponse();
|
|
722
|
-
return
|
|
723
|
-
status: 402,
|
|
724
|
-
headers: { "Content-Type": "application/json" },
|
|
725
|
-
body: {
|
|
726
|
-
error: "PAYMENT_SETTLEMENT_FAILED",
|
|
727
|
-
message: "Payment could not be confirmed after multiple attempts. Please retry."
|
|
728
|
-
}
|
|
729
|
-
};
|
|
836
|
+
return settlementFailureResponse(outcome);
|
|
730
837
|
}
|
|
838
|
+
handlerResult.setResponseHeader(
|
|
839
|
+
"payment-response",
|
|
840
|
+
encodeBase64(outcome.confirmation)
|
|
841
|
+
);
|
|
842
|
+
handlerResult.setResponseHeader(
|
|
843
|
+
"access-control-expose-headers",
|
|
844
|
+
"payment-response"
|
|
845
|
+
);
|
|
731
846
|
}
|
|
732
847
|
handlerResult.sendResponse();
|
|
733
848
|
return { pass: true, handlerRan: true };
|
|
@@ -751,5 +866,7 @@ exports.parsePrice = parsePrice;
|
|
|
751
866
|
exports.raceTimeout = raceTimeout;
|
|
752
867
|
exports.resolveCharge = resolveCharge;
|
|
753
868
|
exports.resolveSettleAmount = resolveSettleAmount;
|
|
869
|
+
exports.settleVerified = settleVerified;
|
|
870
|
+
exports.settlementFailureResponse = settlementFailureResponse;
|
|
754
871
|
exports.toEip155Network = toEip155Network;
|
|
755
872
|
exports.validateAddress = validateAddress;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
var X402_ERROR_CODES = {
|
|
3
3
|
FACILITATOR_UNAVAILABLE: "FACILITATOR_UNAVAILABLE",
|
|
4
4
|
FACILITATOR_INVALID_RESPONSE: "FACILITATOR_INVALID_RESPONSE",
|
|
5
|
-
INVALID_PAYMENT: "INVALID_PAYMENT"
|
|
5
|
+
INVALID_PAYMENT: "INVALID_PAYMENT",
|
|
6
|
+
/** The facilitator did not answer a settle within the client's timeout: the payment may still settle. */
|
|
7
|
+
SETTLE_TIMEOUT: "SETTLE_TIMEOUT"
|
|
6
8
|
};
|
|
7
9
|
var X402ConfigError = class extends Error {
|
|
8
10
|
constructor(message, cause) {
|
|
@@ -12,12 +14,21 @@ var X402ConfigError = class extends Error {
|
|
|
12
14
|
}
|
|
13
15
|
};
|
|
14
16
|
var X402RuntimeError = class extends Error {
|
|
15
|
-
constructor(code) {
|
|
17
|
+
constructor(code, extra) {
|
|
16
18
|
super(code);
|
|
17
19
|
this.code = code;
|
|
18
20
|
this.name = "X402RuntimeError";
|
|
21
|
+
if (extra?.detail !== void 0) this.detail = extra.detail;
|
|
22
|
+
if (extra?.status !== void 0) this.status = extra.status;
|
|
23
|
+
if (extra?.retryAfter !== void 0) this.retryAfter = extra.retryAfter;
|
|
19
24
|
}
|
|
20
25
|
code;
|
|
26
|
+
/** The facilitator's message for this code, when it answered with one. */
|
|
27
|
+
detail;
|
|
28
|
+
/** The facilitator's HTTP status, when the error comes from its answer. */
|
|
29
|
+
status;
|
|
30
|
+
/** The facilitator's `Retry-After` header, when it sent one. */
|
|
31
|
+
retryAfter;
|
|
21
32
|
};
|
|
22
33
|
var X402PriceRejectedError = class extends Error {
|
|
23
34
|
constructor(code, message, status = 400) {
|