@openreceive/browser 0.4.9 → 0.4.11
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/dist/{chunk-CPVYEWVM.js → chunk-5F7F2ZAM.js} +291 -238
- package/dist/headless.d.ts +14 -3
- package/dist/headless.js +70 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{status-Doh8Wz4D.d.ts → status-hEzap3jk.d.ts} +8 -4
- package/package.json +3 -3
- package/skills/debug-openreceive-payment/SKILL.md +8 -0
- package/skills/integrate-openreceive/SKILL.md +11 -0
- package/skills/integrate-openreceive/references/btcpay.md +9 -7
- package/skills/integrate-openreceive/references/django.md +12 -3
- package/skills/integrate-openreceive/references/fastapi.md +13 -4
- package/skills/integrate-openreceive/references/fastify.md +13 -4
- package/skills/integrate-openreceive/references/laravel.md +12 -3
- package/skills/integrate-openreceive/references/next.md +13 -4
- package/skills/integrate-openreceive/references/node.md +13 -4
- package/skills/integrate-openreceive/references/php.md +12 -3
- package/skills/integrate-openreceive/references/rails.md +12 -3
- package/skills/integrate-openreceive/references/woocommerce.md +3 -1
|
@@ -864,7 +864,8 @@ import {
|
|
|
864
864
|
formatDecimal as formatDecimal2,
|
|
865
865
|
parseDecimal as parseDecimal2,
|
|
866
866
|
payInAssetNetwork,
|
|
867
|
-
swapAddressNetworkForPayInAsset
|
|
867
|
+
swapAddressNetworkForPayInAsset,
|
|
868
|
+
swapPayInAssetPeggedTo
|
|
868
869
|
} from "@openreceive/core";
|
|
869
870
|
|
|
870
871
|
// src/internal/unix-seconds.ts
|
|
@@ -881,34 +882,56 @@ function resolveNow(now) {
|
|
|
881
882
|
}
|
|
882
883
|
|
|
883
884
|
// src/internal/checkout-swap-view.ts
|
|
884
|
-
function
|
|
885
|
+
function swapFeeInTokenUnits(fee, payInAsset) {
|
|
886
|
+
return swapPayInAssetPeggedTo(payInAsset) === fee.currency;
|
|
887
|
+
}
|
|
888
|
+
function createSwapFeeBreakdown(fee, swap) {
|
|
885
889
|
if (fee === void 0) return void 0;
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
payIn = parseDecimal2(fee.pay_in_fiat);
|
|
890
|
-
payout = parseDecimal2(fee.payout_fiat);
|
|
891
|
-
} catch {
|
|
892
|
-
return void 0;
|
|
893
|
-
}
|
|
894
|
-
if (payout.units <= 0n) return void 0;
|
|
895
|
-
const scale = Math.max(payIn.scale, payout.scale);
|
|
896
|
-
const payInUnits = payIn.units * 10n ** BigInt(scale - payIn.scale);
|
|
897
|
-
const payoutUnits = payout.units * 10n ** BigInt(scale - payout.scale);
|
|
898
|
-
const feeUnits = payInUnits > payoutUnits ? payInUnits - payoutUnits : 0n;
|
|
899
|
-
const format = (units) => {
|
|
890
|
+
const payout = parseFeeDecimal(fee.payout_fiat);
|
|
891
|
+
if (payout === void 0 || payout.units <= 0n) return void 0;
|
|
892
|
+
const fiat = (units, scale) => {
|
|
900
893
|
const value = formatDecimal2(rescaleHalfUp(units, scale, 2), 2);
|
|
901
894
|
return formatFiatAmount({ currency: fee.currency, value }) ?? `${value} ${fee.currency}`;
|
|
902
895
|
};
|
|
903
|
-
|
|
904
|
-
|
|
896
|
+
if (swap !== void 0 && swapFeeInTokenUnits(fee, swap.pay_in_asset)) {
|
|
897
|
+
const deposit = parseFeeDecimal(swap.deposit_amount);
|
|
898
|
+
if (deposit === void 0) return void 0;
|
|
899
|
+
const { assetLabel } = getSwapAssetDisplay(swap.pay_in_asset);
|
|
900
|
+
const gap2 = decimalGap(deposit, payout);
|
|
901
|
+
return {
|
|
902
|
+
cartTotal: fiat(gap2.base, gap2.scale),
|
|
903
|
+
youSend: `${formatDepositAmount(swap.deposit_amount)} ${assetLabel}`,
|
|
904
|
+
fee: `${formatDecimal2(rescaleHalfUp(gap2.fee, gap2.scale, 2), 2)} ${assetLabel}`,
|
|
905
|
+
feePercent: percentOf(gap2.fee, gap2.base)
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
const payIn = parseFeeDecimal(fee.pay_in_fiat);
|
|
909
|
+
if (payIn === void 0) return void 0;
|
|
910
|
+
const gap = decimalGap(payIn, payout);
|
|
905
911
|
return {
|
|
906
|
-
cartTotal:
|
|
907
|
-
youSend:
|
|
908
|
-
fee:
|
|
909
|
-
feePercent
|
|
912
|
+
cartTotal: fiat(gap.base, gap.scale),
|
|
913
|
+
youSend: fiat(gap.base + gap.fee, gap.scale),
|
|
914
|
+
fee: fiat(gap.fee, gap.scale),
|
|
915
|
+
feePercent: percentOf(gap.fee, gap.base)
|
|
910
916
|
};
|
|
911
917
|
}
|
|
918
|
+
function parseFeeDecimal(value) {
|
|
919
|
+
try {
|
|
920
|
+
return parseDecimal2(value);
|
|
921
|
+
} catch {
|
|
922
|
+
return void 0;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function decimalGap(paid, base) {
|
|
926
|
+
const scale = Math.max(paid.scale, base.scale);
|
|
927
|
+
const paidUnits = paid.units * 10n ** BigInt(scale - paid.scale);
|
|
928
|
+
const baseUnits = base.units * 10n ** BigInt(scale - base.scale);
|
|
929
|
+
return { fee: paidUnits > baseUnits ? paidUnits - baseUnits : 0n, base: baseUnits, scale };
|
|
930
|
+
}
|
|
931
|
+
function percentOf(fee, base) {
|
|
932
|
+
const tenths = roundedDiv(fee * 1000n, base);
|
|
933
|
+
return `${(tenths / 10n).toString()}.${(tenths % 10n).toString()}%`;
|
|
934
|
+
}
|
|
912
935
|
function swapDepositRisk(payInAsset) {
|
|
913
936
|
const network = swapAddressNetworkForPayInAsset(payInAsset);
|
|
914
937
|
if (network === void 0 || network === "ETH") return "chain_ambiguous";
|
|
@@ -929,7 +952,8 @@ function createSwapDisplayModel(invoice, options = {}) {
|
|
|
929
952
|
const depositRisk = swapDepositRisk(swap.pay_in_asset);
|
|
930
953
|
const doubleSpendWarning = `Pay with one method only \u2014 if you already sent ${asset.assetLabel}, do not also pay the Lightning invoice.`;
|
|
931
954
|
const settled = invoice.transaction_state === "settled";
|
|
932
|
-
const
|
|
955
|
+
const instructionsExpired = expiresInSeconds === 0 && swap.provider_state === "awaiting_deposit";
|
|
956
|
+
const feeBreakdown = createSwapFeeBreakdown(swap.fee, swap);
|
|
933
957
|
return {
|
|
934
958
|
provider: swap.provider,
|
|
935
959
|
attemptId: swap.attempt_id ?? invoice.invoice_id,
|
|
@@ -964,14 +988,14 @@ function createSwapDisplayModel(invoice, options = {}) {
|
|
|
964
988
|
],
|
|
965
989
|
{ label: checkoutLabels.swapCopyAmount, value: depositAmount, selectable: true }
|
|
966
990
|
],
|
|
967
|
-
providerStateLabel: settled ? "Payment complete" : getSwapProviderStateLabel(swap.provider_state),
|
|
968
|
-
providerStateDetail: settled ? "Your payment is confirmed and your order is complete." : getSwapProviderStateDetail(swap.provider_state, swap.pay_in_asset, {
|
|
991
|
+
providerStateLabel: instructionsExpired ? "Payment window closed" : settled ? "Payment complete" : getSwapProviderStateLabel(swap.provider_state),
|
|
992
|
+
providerStateDetail: instructionsExpired ? "Do not send another payment. We are checking for an existing deposit and any recovery options." : settled ? "Your payment is confirmed and your order is complete." : getSwapProviderStateDetail(swap.provider_state, swap.pay_in_asset, {
|
|
969
993
|
refundReason: swap.refund_reason,
|
|
970
994
|
depositAmount: swap.deposit_amount,
|
|
971
995
|
depositReceivedAmount: swap.deposit_received_amount,
|
|
972
996
|
refundAmount: swap.refund_amount
|
|
973
997
|
}),
|
|
974
|
-
state: settled ? "settled" : getSwapPanelState(swap.provider_state),
|
|
998
|
+
state: settled ? "settled" : instructionsExpired ? "progress" : getSwapPanelState(swap.provider_state),
|
|
975
999
|
expiresInSeconds,
|
|
976
1000
|
countdownLabel: formatCountdown(expiresInSeconds),
|
|
977
1001
|
qrPayload: createSwapQrPayload(swap),
|
|
@@ -1064,7 +1088,7 @@ function getSwapProviderStateDetail(state, payInAsset, refundContext = {}) {
|
|
|
1064
1088
|
return "Your payment is confirmed and being converted. This usually finishes within a minute.";
|
|
1065
1089
|
}
|
|
1066
1090
|
if (state === "paying_invoice" || state === "completed") {
|
|
1067
|
-
return "
|
|
1091
|
+
return "We are checking whether the wallet received the payment. Provider completion alone does not confirm payment.";
|
|
1068
1092
|
}
|
|
1069
1093
|
if (state === "expired") return "No payment was received before the payment window closed.";
|
|
1070
1094
|
if (state === "refund_required" || state === "refund_pending" || state === "refunded") {
|
|
@@ -1291,7 +1315,7 @@ function createTransactionDetails(input) {
|
|
|
1291
1315
|
push("Lightning payout", swap.payout_tx_id);
|
|
1292
1316
|
push("Refund address", swap.refund_address, void 0, "address");
|
|
1293
1317
|
push("Refund transaction", swap.refund_tx_id, void 0, "tx");
|
|
1294
|
-
const feeBreakdown = createSwapFeeBreakdown(swap.fee);
|
|
1318
|
+
const feeBreakdown = createSwapFeeBreakdown(swap.fee, swap);
|
|
1295
1319
|
if (feeBreakdown !== void 0) {
|
|
1296
1320
|
push("Cart total", feeBreakdown.cartTotal);
|
|
1297
1321
|
push("You send", feeBreakdown.youSend);
|
|
@@ -1301,7 +1325,9 @@ function createTransactionDetails(input) {
|
|
|
1301
1325
|
);
|
|
1302
1326
|
} else if (swap.fee !== void 0) {
|
|
1303
1327
|
push("Fee currency", swap.fee.currency);
|
|
1304
|
-
|
|
1328
|
+
if (!swapFeeInTokenUnits(swap.fee, swap.pay_in_asset)) {
|
|
1329
|
+
push("Pay-in fiat", swap.fee.pay_in_fiat);
|
|
1330
|
+
}
|
|
1305
1331
|
push("Payout fiat", swap.fee.payout_fiat);
|
|
1306
1332
|
}
|
|
1307
1333
|
}
|
|
@@ -1336,6 +1362,127 @@ function isCheckoutStateSource(value) {
|
|
|
1336
1362
|
return "checkout_id" in value && "reference" in value && "invoice_id" in value && "invoice" in value && "transaction_state" in value && "phase" in value;
|
|
1337
1363
|
}
|
|
1338
1364
|
|
|
1365
|
+
// src/internal/console-logger.ts
|
|
1366
|
+
import { isSensitiveLogKey, sanitizeLogValue } from "@openreceive/core";
|
|
1367
|
+
|
|
1368
|
+
// src/internal/log-level.ts
|
|
1369
|
+
var BROWSER_LOG_LEVEL_ORDER = {
|
|
1370
|
+
debug: 10,
|
|
1371
|
+
info: 20,
|
|
1372
|
+
warn: 30,
|
|
1373
|
+
error: 40
|
|
1374
|
+
};
|
|
1375
|
+
var BROWSER_LOG_LEVEL_ALIASES = {
|
|
1376
|
+
debug: "debug",
|
|
1377
|
+
info: "info",
|
|
1378
|
+
warn: "warn",
|
|
1379
|
+
warning: "warn",
|
|
1380
|
+
error: "error"
|
|
1381
|
+
};
|
|
1382
|
+
function browserLogLevelOrder(level) {
|
|
1383
|
+
return BROWSER_LOG_LEVEL_ORDER[level];
|
|
1384
|
+
}
|
|
1385
|
+
function parseBrowserLogLevel(value) {
|
|
1386
|
+
if (value === void 0 || value === null) return void 0;
|
|
1387
|
+
const normalized = value.trim().toLowerCase();
|
|
1388
|
+
if (normalized.length === 0) return void 0;
|
|
1389
|
+
return BROWSER_LOG_LEVEL_ALIASES[normalized];
|
|
1390
|
+
}
|
|
1391
|
+
function resolveBrowserLogLevel(value, fallback = "info") {
|
|
1392
|
+
return parseBrowserLogLevel(value) ?? fallback;
|
|
1393
|
+
}
|
|
1394
|
+
function readBrowserLogLevelFromEnvironment(source) {
|
|
1395
|
+
if (source !== void 0) {
|
|
1396
|
+
return resolveBrowserLogLevel(
|
|
1397
|
+
source.LOG_LEVEL ?? source.VITE_LOG_LEVEL ?? source.NEXT_PUBLIC_LOG_LEVEL
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
const fromGlobal = parseBrowserLogLevel(readGlobalLogLevel());
|
|
1401
|
+
if (fromGlobal !== void 0) return fromGlobal;
|
|
1402
|
+
const metaEnv = readImportMetaEnv();
|
|
1403
|
+
const parsedMeta = parseBrowserLogLevel(
|
|
1404
|
+
metaEnv?.LOG_LEVEL ?? metaEnv?.VITE_LOG_LEVEL ?? metaEnv?.NEXT_PUBLIC_LOG_LEVEL
|
|
1405
|
+
);
|
|
1406
|
+
if (parsedMeta !== void 0) return parsedMeta;
|
|
1407
|
+
const processEnv = typeof process !== "undefined" ? process.env : void 0;
|
|
1408
|
+
return resolveBrowserLogLevel(processEnv?.LOG_LEVEL);
|
|
1409
|
+
}
|
|
1410
|
+
function readGlobalLogLevel() {
|
|
1411
|
+
try {
|
|
1412
|
+
const value = globalThis.__OPENRECEIVE_LOG_LEVEL__;
|
|
1413
|
+
return typeof value === "string" ? value : void 0;
|
|
1414
|
+
} catch {
|
|
1415
|
+
return void 0;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
function readImportMetaEnv() {
|
|
1419
|
+
try {
|
|
1420
|
+
return import.meta.env;
|
|
1421
|
+
} catch {
|
|
1422
|
+
return void 0;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// src/internal/console-logger.ts
|
|
1427
|
+
function createConsoleWriter(options) {
|
|
1428
|
+
const target = options.console ?? console;
|
|
1429
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1430
|
+
return (level, prefix, event, message, fields) => {
|
|
1431
|
+
const minLevel = options.minLevel === void 0 ? readBrowserLogLevelFromEnvironment() : resolveBrowserLogLevel(options.minLevel);
|
|
1432
|
+
if (browserLogLevelOrder(level) < browserLogLevelOrder(minLevel)) {
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
const method = level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "info";
|
|
1436
|
+
const sink = target[method] ?? target.log;
|
|
1437
|
+
const {
|
|
1438
|
+
level: _level,
|
|
1439
|
+
event: _event,
|
|
1440
|
+
message: cleanMessage,
|
|
1441
|
+
...cleanFields
|
|
1442
|
+
} = sanitizeBrowserLogEntry({ level, event, message, ...fields });
|
|
1443
|
+
sink.call(
|
|
1444
|
+
target,
|
|
1445
|
+
`[${now().toISOString()}] ${level.toUpperCase()} [${prefix}] ${event}: ${cleanMessage}`,
|
|
1446
|
+
cleanFields
|
|
1447
|
+
);
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
function sanitizeBrowserLogEntry(entry) {
|
|
1451
|
+
const clean = {};
|
|
1452
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
1453
|
+
if (isSensitiveLogKey(key)) {
|
|
1454
|
+
clean[key] = "[REDACTED]";
|
|
1455
|
+
} else {
|
|
1456
|
+
clean[key] = sanitizeLogValue(value);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
return clean;
|
|
1460
|
+
}
|
|
1461
|
+
function createBrowserConsoleLogger(options = {}) {
|
|
1462
|
+
const prefix = options.prefix ?? "openreceive";
|
|
1463
|
+
const write = createConsoleWriter(options);
|
|
1464
|
+
return (entry) => {
|
|
1465
|
+
const { level, event, message, ...fields } = entry;
|
|
1466
|
+
write(level, prefix, event, message, fields);
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
function createAppBrowserConsoleLogger(options) {
|
|
1470
|
+
const write = createConsoleWriter(options);
|
|
1471
|
+
return (event, message, fields = {}, level = "info") => {
|
|
1472
|
+
write(level, options.prefix, event, message, fields);
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
var defaultBrowserConsoleLogger;
|
|
1476
|
+
function getDefaultBrowserConsoleLogger() {
|
|
1477
|
+
defaultBrowserConsoleLogger ??= createBrowserConsoleLogger();
|
|
1478
|
+
return defaultBrowserConsoleLogger;
|
|
1479
|
+
}
|
|
1480
|
+
function resolveBrowserLogger(logger) {
|
|
1481
|
+
if (logger === false) return void 0;
|
|
1482
|
+
if (logger !== void 0) return logger;
|
|
1483
|
+
return getDefaultBrowserConsoleLogger();
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1339
1486
|
// src/internal/wizard.ts
|
|
1340
1487
|
import { getSwapRefundAddressError } from "@openreceive/core/swap-address";
|
|
1341
1488
|
import {
|
|
@@ -1931,176 +2078,6 @@ function createPaymentWizardController(options = {}) {
|
|
|
1931
2078
|
return new BrowserPaymentWizardController(options);
|
|
1932
2079
|
}
|
|
1933
2080
|
|
|
1934
|
-
// src/internal/log-level.ts
|
|
1935
|
-
var BROWSER_LOG_LEVEL_ORDER = {
|
|
1936
|
-
debug: 10,
|
|
1937
|
-
info: 20,
|
|
1938
|
-
warn: 30,
|
|
1939
|
-
error: 40
|
|
1940
|
-
};
|
|
1941
|
-
var BROWSER_LOG_LEVEL_ALIASES = {
|
|
1942
|
-
debug: "debug",
|
|
1943
|
-
info: "info",
|
|
1944
|
-
warn: "warn",
|
|
1945
|
-
warning: "warn",
|
|
1946
|
-
error: "error"
|
|
1947
|
-
};
|
|
1948
|
-
function browserLogLevelOrder(level) {
|
|
1949
|
-
return BROWSER_LOG_LEVEL_ORDER[level];
|
|
1950
|
-
}
|
|
1951
|
-
function parseBrowserLogLevel(value) {
|
|
1952
|
-
if (value === void 0 || value === null) return void 0;
|
|
1953
|
-
const normalized = value.trim().toLowerCase();
|
|
1954
|
-
if (normalized.length === 0) return void 0;
|
|
1955
|
-
return BROWSER_LOG_LEVEL_ALIASES[normalized];
|
|
1956
|
-
}
|
|
1957
|
-
function resolveBrowserLogLevel(value, fallback = "info") {
|
|
1958
|
-
return parseBrowserLogLevel(value) ?? fallback;
|
|
1959
|
-
}
|
|
1960
|
-
function readBrowserLogLevelFromEnvironment(source) {
|
|
1961
|
-
if (source !== void 0) {
|
|
1962
|
-
return resolveBrowserLogLevel(
|
|
1963
|
-
source.LOG_LEVEL ?? source.VITE_LOG_LEVEL ?? source.NEXT_PUBLIC_LOG_LEVEL
|
|
1964
|
-
);
|
|
1965
|
-
}
|
|
1966
|
-
const fromGlobal = parseBrowserLogLevel(readGlobalLogLevel());
|
|
1967
|
-
if (fromGlobal !== void 0) return fromGlobal;
|
|
1968
|
-
const metaEnv = readImportMetaEnv();
|
|
1969
|
-
const parsedMeta = parseBrowserLogLevel(
|
|
1970
|
-
metaEnv?.LOG_LEVEL ?? metaEnv?.VITE_LOG_LEVEL ?? metaEnv?.NEXT_PUBLIC_LOG_LEVEL
|
|
1971
|
-
);
|
|
1972
|
-
if (parsedMeta !== void 0) return parsedMeta;
|
|
1973
|
-
const processEnv = typeof process !== "undefined" ? process.env : void 0;
|
|
1974
|
-
return resolveBrowserLogLevel(processEnv?.LOG_LEVEL);
|
|
1975
|
-
}
|
|
1976
|
-
function readGlobalLogLevel() {
|
|
1977
|
-
try {
|
|
1978
|
-
const value = globalThis.__OPENRECEIVE_LOG_LEVEL__;
|
|
1979
|
-
return typeof value === "string" ? value : void 0;
|
|
1980
|
-
} catch {
|
|
1981
|
-
return void 0;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
function readImportMetaEnv() {
|
|
1985
|
-
try {
|
|
1986
|
-
return import.meta.env;
|
|
1987
|
-
} catch {
|
|
1988
|
-
return void 0;
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
// src/internal/console-logger.ts
|
|
1993
|
-
function createConsoleWriter(options) {
|
|
1994
|
-
const target = options.console ?? console;
|
|
1995
|
-
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1996
|
-
return (level, prefix, event, message, fields) => {
|
|
1997
|
-
const minLevel = options.minLevel === void 0 ? readBrowserLogLevelFromEnvironment() : resolveBrowserLogLevel(options.minLevel);
|
|
1998
|
-
if (browserLogLevelOrder(level) < browserLogLevelOrder(minLevel)) {
|
|
1999
|
-
return;
|
|
2000
|
-
}
|
|
2001
|
-
const method = level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "info";
|
|
2002
|
-
const sink = target[method] ?? target.log;
|
|
2003
|
-
const {
|
|
2004
|
-
level: _level,
|
|
2005
|
-
event: _event,
|
|
2006
|
-
message: cleanMessage,
|
|
2007
|
-
...cleanFields
|
|
2008
|
-
} = sanitizeBrowserLogEntry({ level, event, message, ...fields });
|
|
2009
|
-
sink.call(
|
|
2010
|
-
target,
|
|
2011
|
-
`[${now().toISOString()}] ${level.toUpperCase()} [${prefix}] ${event}: ${cleanMessage}`,
|
|
2012
|
-
cleanFields
|
|
2013
|
-
);
|
|
2014
|
-
};
|
|
2015
|
-
}
|
|
2016
|
-
function sanitizeBrowserLogEntry(entry) {
|
|
2017
|
-
const clean = {};
|
|
2018
|
-
for (const [key, value] of Object.entries(entry)) {
|
|
2019
|
-
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
2020
|
-
clean[key] = "[REDACTED]";
|
|
2021
|
-
} else {
|
|
2022
|
-
clean[key] = sanitizeBrowserLogValue(value);
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
return clean;
|
|
2026
|
-
}
|
|
2027
|
-
function sanitizeBrowserLogValue(value) {
|
|
2028
|
-
if (typeof value === "string") return redactBrowserSecrets(value);
|
|
2029
|
-
if (Array.isArray(value)) return value.map(sanitizeBrowserLogValue);
|
|
2030
|
-
if (typeof value !== "object" || value === null) return value;
|
|
2031
|
-
const clean = {};
|
|
2032
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
2033
|
-
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
2034
|
-
clean[key] = "[REDACTED]";
|
|
2035
|
-
} else {
|
|
2036
|
-
clean[key] = sanitizeBrowserLogValue(nested);
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
return clean;
|
|
2040
|
-
}
|
|
2041
|
-
function redactBrowserSecrets(value) {
|
|
2042
|
-
return value.replace(/nostr\+walletconnect:\/\/[^\s"'`<>]+/g, "[REDACTED_NWC]").replace(/([?&](?:_or_evt|token|secret)=)[^&\s"'`<>]+/gi, "$1[REDACTED]");
|
|
2043
|
-
}
|
|
2044
|
-
function createBrowserConsoleLogger(options = {}) {
|
|
2045
|
-
const prefix = options.prefix ?? "openreceive";
|
|
2046
|
-
const write = createConsoleWriter(options);
|
|
2047
|
-
return (entry) => {
|
|
2048
|
-
const { level, event, message, ...fields } = entry;
|
|
2049
|
-
write(level, prefix, event, message, fields);
|
|
2050
|
-
};
|
|
2051
|
-
}
|
|
2052
|
-
function createAppBrowserConsoleLogger(options) {
|
|
2053
|
-
const write = createConsoleWriter(options);
|
|
2054
|
-
return (event, message, fields = {}, level = "info") => {
|
|
2055
|
-
write(level, options.prefix, event, message, fields);
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2058
|
-
var defaultBrowserConsoleLogger;
|
|
2059
|
-
function getDefaultBrowserConsoleLogger() {
|
|
2060
|
-
defaultBrowserConsoleLogger ??= createBrowserConsoleLogger();
|
|
2061
|
-
return defaultBrowserConsoleLogger;
|
|
2062
|
-
}
|
|
2063
|
-
function resolveBrowserLogger(logger) {
|
|
2064
|
-
if (logger === false) return void 0;
|
|
2065
|
-
if (logger !== void 0) return logger;
|
|
2066
|
-
return getDefaultBrowserConsoleLogger();
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
// src/internal/checkout-read.ts
|
|
2070
|
-
import { nonEmptyString } from "@openreceive/core";
|
|
2071
|
-
function optionalRecord(value) {
|
|
2072
|
-
if (value === void 0) return void 0;
|
|
2073
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2074
|
-
throw new Error("OpenReceive metadata must be an object.");
|
|
2075
|
-
}
|
|
2076
|
-
return value;
|
|
2077
|
-
}
|
|
2078
|
-
function requiredInvoiceRail(value) {
|
|
2079
|
-
if (value === "lightning" || value === "swap" || value === "checkout_lock") return value;
|
|
2080
|
-
throw new TypeError("OpenReceive invoice rail must be lightning, swap, or checkout_lock.");
|
|
2081
|
-
}
|
|
2082
|
-
function requiredString(value, fieldName) {
|
|
2083
|
-
const text = nonEmptyString(value);
|
|
2084
|
-
if (text === void 0) {
|
|
2085
|
-
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2086
|
-
}
|
|
2087
|
-
return text;
|
|
2088
|
-
}
|
|
2089
|
-
function optionalSafeInteger(value, fieldName = "integer") {
|
|
2090
|
-
if (value === void 0 || value === null) return void 0;
|
|
2091
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
2092
|
-
throw new TypeError(`OpenReceive checkout response ${fieldName} must be a safe integer.`);
|
|
2093
|
-
}
|
|
2094
|
-
return value;
|
|
2095
|
-
}
|
|
2096
|
-
function requiredSafeInteger(value, fieldName) {
|
|
2097
|
-
const integer = optionalSafeInteger(value, fieldName);
|
|
2098
|
-
if (integer === void 0) {
|
|
2099
|
-
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2100
|
-
}
|
|
2101
|
-
return integer;
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
2081
|
// src/internal/checkout-log.ts
|
|
2105
2082
|
import { compact } from "@openreceive/core";
|
|
2106
2083
|
function checkoutLogFields(state) {
|
|
@@ -2181,6 +2158,41 @@ function emitBrowserLog(logger, level, event, message, fields = {}) {
|
|
|
2181
2158
|
}
|
|
2182
2159
|
}
|
|
2183
2160
|
|
|
2161
|
+
// src/internal/checkout-read.ts
|
|
2162
|
+
import { nonEmptyString } from "@openreceive/core";
|
|
2163
|
+
function optionalRecord(value) {
|
|
2164
|
+
if (value === void 0) return void 0;
|
|
2165
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2166
|
+
throw new Error("OpenReceive metadata must be an object.");
|
|
2167
|
+
}
|
|
2168
|
+
return value;
|
|
2169
|
+
}
|
|
2170
|
+
function requiredInvoiceRail(value) {
|
|
2171
|
+
if (value === "lightning" || value === "swap" || value === "checkout_lock") return value;
|
|
2172
|
+
throw new TypeError("OpenReceive invoice rail must be lightning, swap, or checkout_lock.");
|
|
2173
|
+
}
|
|
2174
|
+
function requiredString(value, fieldName) {
|
|
2175
|
+
const text = nonEmptyString(value);
|
|
2176
|
+
if (text === void 0) {
|
|
2177
|
+
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2178
|
+
}
|
|
2179
|
+
return text;
|
|
2180
|
+
}
|
|
2181
|
+
function optionalSafeInteger(value, fieldName = "integer") {
|
|
2182
|
+
if (value === void 0 || value === null) return void 0;
|
|
2183
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
2184
|
+
throw new TypeError(`OpenReceive checkout response ${fieldName} must be a safe integer.`);
|
|
2185
|
+
}
|
|
2186
|
+
return value;
|
|
2187
|
+
}
|
|
2188
|
+
function requiredSafeInteger(value, fieldName) {
|
|
2189
|
+
const integer = optionalSafeInteger(value, fieldName);
|
|
2190
|
+
if (integer === void 0) {
|
|
2191
|
+
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2192
|
+
}
|
|
2193
|
+
return integer;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2184
2196
|
// src/internal/checkout-state.ts
|
|
2185
2197
|
function selectCheckoutDisplayInvoice(snapshot) {
|
|
2186
2198
|
if (snapshot.active !== void 0 && snapshot.active.rail !== "checkout_lock") {
|
|
@@ -2355,15 +2367,14 @@ function normalizeCheckoutState(state, now) {
|
|
|
2355
2367
|
const expiresInSeconds = base.expires_at === void 0 || now === void 0 ? void 0 : Math.max(0, base.expires_at - now);
|
|
2356
2368
|
const phase = statePhase === "invoice_created" || statePhase === "verifying" ? expiresInSeconds === 0 ? "expired" : statePhase : statePhase;
|
|
2357
2369
|
const settled = base.paid || base.transaction_state === "settled";
|
|
2370
|
+
const monitoring = checkoutMonitoring(base);
|
|
2358
2371
|
return {
|
|
2359
2372
|
...base,
|
|
2360
2373
|
...deriveCheckoutStateLabels(base),
|
|
2361
2374
|
phase,
|
|
2362
2375
|
settled,
|
|
2363
|
-
// A
|
|
2364
|
-
|
|
2365
|
-
// both /payments/check and the provider instead of asking forever.
|
|
2366
|
-
terminal: isTerminalPhase(phase) || !settled && isTerminalSwapProviderState(base.swap?.provider_state),
|
|
2376
|
+
// A local instruction deadline says nothing about wallet or refund finality.
|
|
2377
|
+
terminal: !monitoring.payment && !monitoring.provider,
|
|
2367
2378
|
...expiresInSeconds === void 0 ? {} : { expires_in_seconds: expiresInSeconds }
|
|
2368
2379
|
};
|
|
2369
2380
|
}
|
|
@@ -2384,8 +2395,13 @@ function getCheckoutPhase(transactionState, workflowState) {
|
|
|
2384
2395
|
}
|
|
2385
2396
|
return "invoice_created";
|
|
2386
2397
|
}
|
|
2387
|
-
function
|
|
2388
|
-
|
|
2398
|
+
function checkoutMonitoring(state) {
|
|
2399
|
+
if (state.workflow_state === "cancelled") return { payment: false, provider: false };
|
|
2400
|
+
const payment = !state.paid && !["settled", "expired", "failed", "attention"].includes(state.transaction_state ?? "pending") && !["expired", "failed", "attention"].includes(state.workflow_state ?? "invoice_created");
|
|
2401
|
+
const providerState = state.swap?.provider_state;
|
|
2402
|
+
const recovery = providerState === "refund_required" || providerState === "refund_pending";
|
|
2403
|
+
const provider = providerState !== void 0 && !isTerminalSwapProviderState(providerState) && providerState !== "completed" && (recovery || !state.paid && state.transaction_state !== "settled");
|
|
2404
|
+
return { payment, provider };
|
|
2389
2405
|
}
|
|
2390
2406
|
function snapshotFromCheckoutState(state) {
|
|
2391
2407
|
const invoice = {
|
|
@@ -2545,6 +2561,7 @@ function normalizeRequestCheckoutOptions(options) {
|
|
|
2545
2561
|
const reference = nonEmptyString2(options.reference);
|
|
2546
2562
|
const metadata = optionalRecord(options.metadata);
|
|
2547
2563
|
return {
|
|
2564
|
+
signal: options.signal,
|
|
2548
2565
|
routes: checkoutRoutes(options.prefix),
|
|
2549
2566
|
reference: reference ?? "",
|
|
2550
2567
|
fetch: options.fetch,
|
|
@@ -2574,6 +2591,7 @@ async function requestCheckout(options) {
|
|
|
2574
2591
|
const headers = request.headers === void 0 ? {} : request.headers;
|
|
2575
2592
|
const response = await fetcher(request.routes.checkouts, {
|
|
2576
2593
|
method: "POST",
|
|
2594
|
+
signal: request.signal,
|
|
2577
2595
|
headers: requestHeaders(headers, request.csrfHeader),
|
|
2578
2596
|
body: JSON.stringify(requestBody)
|
|
2579
2597
|
});
|
|
@@ -2597,6 +2615,7 @@ async function prepareCheckout(options) {
|
|
|
2597
2615
|
const headers = request.headers === void 0 ? {} : request.headers;
|
|
2598
2616
|
const response = await fetcher(request.routes.checkoutsPrepare, {
|
|
2599
2617
|
method: "POST",
|
|
2618
|
+
signal: request.signal,
|
|
2600
2619
|
headers: requestHeaders(headers, request.csrfHeader),
|
|
2601
2620
|
body: JSON.stringify({ reference: request.reference })
|
|
2602
2621
|
});
|
|
@@ -2620,50 +2639,57 @@ function createStatusFetcher(options) {
|
|
|
2620
2639
|
if (activePaymentHash === void 0 || !/^[0-9a-f]{64}$/i.test(activePaymentHash)) {
|
|
2621
2640
|
return snapshot;
|
|
2622
2641
|
}
|
|
2623
|
-
const response = await fetcher(routes.paymentsCheck, {
|
|
2624
|
-
method: "POST",
|
|
2625
|
-
headers: requestHeaders(headers, options.csrfHeader),
|
|
2626
|
-
body: JSON.stringify({
|
|
2627
|
-
reference,
|
|
2628
|
-
payment_hash: activePaymentHash
|
|
2629
|
-
})
|
|
2630
|
-
});
|
|
2631
|
-
const body = await readJsonResponse(response, "Could not refresh invoice status.");
|
|
2632
|
-
const payment = recordOrEmpty(body);
|
|
2633
2642
|
const next = structuredClone(snapshot);
|
|
2634
2643
|
if (next.active === void 0) return next;
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
let
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
+
let active = next.active;
|
|
2645
|
+
let payment = {};
|
|
2646
|
+
let paymentError;
|
|
2647
|
+
if (checkoutMonitoring(active).payment) {
|
|
2648
|
+
try {
|
|
2649
|
+
const response = await fetcher(routes.paymentsCheck, {
|
|
2650
|
+
method: "POST",
|
|
2651
|
+
headers: requestHeaders(headers, options.csrfHeader),
|
|
2652
|
+
body: JSON.stringify({ reference, payment_hash: activePaymentHash })
|
|
2653
|
+
});
|
|
2654
|
+
payment = recordOrEmpty(
|
|
2655
|
+
await readJsonResponse(response, "Could not refresh invoice status.")
|
|
2656
|
+
);
|
|
2657
|
+
const state2 = nonEmptyString2(payment.status) ?? "pending";
|
|
2658
|
+
const paidAt = optionalSafeInteger(payment.paid_at, "paid_at");
|
|
2659
|
+
active = {
|
|
2660
|
+
...active,
|
|
2661
|
+
transaction_state: state2 === "not_found" ? "pending" : state2,
|
|
2662
|
+
...paidAt === void 0 ? {} : { settled_at: paidAt }
|
|
2663
|
+
};
|
|
2664
|
+
} catch (error) {
|
|
2665
|
+
paymentError = error;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
if (active.rail === "swap" && checkoutMonitoring(active).provider) {
|
|
2644
2669
|
try {
|
|
2645
2670
|
const swapResponse = await fetcher(routes.swapsStatus, {
|
|
2646
2671
|
method: "POST",
|
|
2647
2672
|
headers: requestHeaders(headers, options.csrfHeader),
|
|
2648
2673
|
body: JSON.stringify({ reference, payment_hash: activePaymentHash })
|
|
2649
2674
|
});
|
|
2650
|
-
|
|
2651
|
-
|
|
2675
|
+
active = mergeSwapStatusIntoInvoice(
|
|
2676
|
+
active,
|
|
2677
|
+
recordOrEmpty(await readJsonResponse(swapResponse, "Could not refresh swap status."))
|
|
2652
2678
|
);
|
|
2653
|
-
active = mergeSwapStatusIntoInvoice(active, swapBody);
|
|
2654
2679
|
swapStatusFailures = 0;
|
|
2655
2680
|
} catch (error) {
|
|
2656
2681
|
swapStatusFailures += 1;
|
|
2657
2682
|
const definitive = error instanceof BrowserRequestError && error.retryable === false;
|
|
2658
|
-
|
|
2659
|
-
if (swap !== void 0 && (definitive || swapStatusFailures >= SWAP_STATUS_FAILURE_LIMIT)) {
|
|
2683
|
+
if (active.swap !== void 0 && (definitive || swapStatusFailures >= SWAP_STATUS_FAILURE_LIMIT)) {
|
|
2660
2684
|
active = {
|
|
2661
2685
|
...active,
|
|
2662
|
-
swap: { ...swap, attention: true, attention_reason: "swap_status_unavailable" }
|
|
2686
|
+
swap: { ...active.swap, attention: true, attention_reason: "swap_status_unavailable" }
|
|
2663
2687
|
};
|
|
2664
2688
|
}
|
|
2689
|
+
if (paymentError === void 0 && !checkoutMonitoring(active).payment) paymentError = error;
|
|
2665
2690
|
}
|
|
2666
2691
|
}
|
|
2692
|
+
const state = active.transaction_state;
|
|
2667
2693
|
const paymentMethods2 = normalizePaymentMethods(payment.payment_methods);
|
|
2668
2694
|
const others = next.invoices.filter(
|
|
2669
2695
|
(entry) => entry.invoice_id !== active.invoice_id && entry.rail !== "checkout_lock"
|
|
@@ -2675,6 +2701,7 @@ function createStatusFetcher(options) {
|
|
|
2675
2701
|
status: state === "settled" ? "paid" : state === "expired" || state === "failed" ? "expired" : "open",
|
|
2676
2702
|
...paymentMethods2 === void 0 ? {} : { payment_methods: paymentMethods2 }
|
|
2677
2703
|
};
|
|
2704
|
+
if (paymentError !== void 0 && active.swap === void 0) throw paymentError;
|
|
2678
2705
|
return snapshot;
|
|
2679
2706
|
};
|
|
2680
2707
|
}
|
|
@@ -3070,6 +3097,7 @@ async function requestJson(options, url, body) {
|
|
|
3070
3097
|
const fetcher = options.fetch;
|
|
3071
3098
|
const response = await fetcher(url, {
|
|
3072
3099
|
method: "POST",
|
|
3100
|
+
signal: options.signal,
|
|
3073
3101
|
headers: requestHeaders(options.headers, options.csrfHeader),
|
|
3074
3102
|
body: JSON.stringify(body)
|
|
3075
3103
|
});
|
|
@@ -3151,6 +3179,8 @@ var CheckoutWatcher = class {
|
|
|
3151
3179
|
countdownTimer;
|
|
3152
3180
|
pollTimer;
|
|
3153
3181
|
running = false;
|
|
3182
|
+
generation = 0;
|
|
3183
|
+
pendingRefresh;
|
|
3154
3184
|
pollInFlight = false;
|
|
3155
3185
|
pollFailureCount = 0;
|
|
3156
3186
|
pollBackoffUntil;
|
|
@@ -3217,6 +3247,9 @@ var CheckoutWatcher = class {
|
|
|
3217
3247
|
}
|
|
3218
3248
|
stop() {
|
|
3219
3249
|
this.running = false;
|
|
3250
|
+
this.generation += 1;
|
|
3251
|
+
this.pollInFlight = false;
|
|
3252
|
+
this.pendingRefresh = void 0;
|
|
3220
3253
|
this.stopCountdown();
|
|
3221
3254
|
this.stopPolling();
|
|
3222
3255
|
}
|
|
@@ -3255,8 +3288,10 @@ var CheckoutWatcher = class {
|
|
|
3255
3288
|
if (refreshStatus === void 0 || current.reference.length === 0) {
|
|
3256
3289
|
return current;
|
|
3257
3290
|
}
|
|
3291
|
+
const generation = this.generation;
|
|
3258
3292
|
try {
|
|
3259
|
-
const fetched = await
|
|
3293
|
+
const fetched = await this.fetchSnapshot(current.reference);
|
|
3294
|
+
if (generation !== this.generation) return this.state ?? current;
|
|
3260
3295
|
if (fetched === null) return current;
|
|
3261
3296
|
const next = this.publishSnapshot(fetched);
|
|
3262
3297
|
const nextState = createCheckoutState(next, {
|
|
@@ -3272,10 +3307,20 @@ var CheckoutWatcher = class {
|
|
|
3272
3307
|
}
|
|
3273
3308
|
return nextState;
|
|
3274
3309
|
} catch (error) {
|
|
3310
|
+
if (generation !== this.generation) return this.state ?? current;
|
|
3275
3311
|
this.options.onError?.(error);
|
|
3276
3312
|
throw error;
|
|
3277
3313
|
}
|
|
3278
3314
|
}
|
|
3315
|
+
fetchSnapshot(reference) {
|
|
3316
|
+
if (this.pendingRefresh !== void 0) return this.pendingRefresh;
|
|
3317
|
+
const pending = Promise.resolve(this.options.refreshStatus?.(reference) ?? null);
|
|
3318
|
+
this.pendingRefresh = pending;
|
|
3319
|
+
void pending.finally(() => {
|
|
3320
|
+
if (this.pendingRefresh === pending) this.pendingRefresh = void 0;
|
|
3321
|
+
}).catch(() => void 0);
|
|
3322
|
+
return pending;
|
|
3323
|
+
}
|
|
3279
3324
|
applyState(state) {
|
|
3280
3325
|
if (!this.running) return;
|
|
3281
3326
|
this.state = state;
|
|
@@ -3289,7 +3334,7 @@ var CheckoutWatcher = class {
|
|
|
3289
3334
|
this.stop();
|
|
3290
3335
|
return;
|
|
3291
3336
|
}
|
|
3292
|
-
if (state.settled || state.expires_at === void 0) {
|
|
3337
|
+
if (state.settled || state.expires_at === void 0 || state.expires_in_seconds === 0) {
|
|
3293
3338
|
this.stopCountdown();
|
|
3294
3339
|
} else if (this.countdownTimer === void 0) {
|
|
3295
3340
|
this.countdownTimer = this.setInterval()(() => {
|
|
@@ -3305,7 +3350,7 @@ var CheckoutWatcher = class {
|
|
|
3305
3350
|
}, 1e3);
|
|
3306
3351
|
}
|
|
3307
3352
|
const canPollAttempt = state.rail !== "checkout_lock" && typeof state.payment_hash === "string" && /^[0-9a-f]{64}$/i.test(state.payment_hash);
|
|
3308
|
-
if (
|
|
3353
|
+
if (this.options.refreshStatus === void 0 || state.reference.length === 0 || !canPollAttempt) {
|
|
3309
3354
|
this.stopPolling();
|
|
3310
3355
|
} else if (this.pollTimer === void 0) {
|
|
3311
3356
|
this.pollTimer = this.setInterval()(() => {
|
|
@@ -3317,20 +3362,22 @@ var CheckoutWatcher = class {
|
|
|
3317
3362
|
const refreshStatus = this.options.refreshStatus;
|
|
3318
3363
|
const current = this.state;
|
|
3319
3364
|
if (!this.running || refreshStatus === void 0 || current === void 0) return;
|
|
3320
|
-
if (current.terminal
|
|
3365
|
+
if (current.terminal) {
|
|
3321
3366
|
this.stopPolling();
|
|
3322
3367
|
return;
|
|
3323
3368
|
}
|
|
3324
3369
|
if (this.pollInFlight) return;
|
|
3325
3370
|
if (this.pollBackoffUntil !== void 0 && this.now() < this.pollBackoffUntil) return;
|
|
3326
3371
|
this.pollInFlight = true;
|
|
3372
|
+
const generation = this.generation;
|
|
3327
3373
|
try {
|
|
3328
|
-
const fetched = await
|
|
3374
|
+
const fetched = await this.fetchSnapshot(current.reference);
|
|
3375
|
+
if (generation !== this.generation) return;
|
|
3329
3376
|
this.pollFailureCount = 0;
|
|
3330
3377
|
this.pollBackoffUntil = void 0;
|
|
3331
3378
|
if (fetched === null) return;
|
|
3332
3379
|
if (!this.running || this.state === void 0) return;
|
|
3333
|
-
if (this.state.
|
|
3380
|
+
if (this.state.terminal) return;
|
|
3334
3381
|
const next = this.publishSnapshot(fetched);
|
|
3335
3382
|
this.applyState(
|
|
3336
3383
|
createCheckoutState(next, {
|
|
@@ -3341,13 +3388,14 @@ var CheckoutWatcher = class {
|
|
|
3341
3388
|
})
|
|
3342
3389
|
);
|
|
3343
3390
|
} catch (error) {
|
|
3391
|
+
if (generation !== this.generation) return;
|
|
3344
3392
|
this.pollFailureCount += 1;
|
|
3345
3393
|
const retryAfterSeconds = error instanceof BrowserRequestError ? error.retryAfterSeconds : void 0;
|
|
3346
3394
|
const backoffSeconds = retryAfterSeconds ?? Math.min(60, 2 ** Math.min(this.pollFailureCount, 6));
|
|
3347
3395
|
this.pollBackoffUntil = this.now() + backoffSeconds;
|
|
3348
3396
|
this.options.onError?.(error);
|
|
3349
3397
|
} finally {
|
|
3350
|
-
this.pollInFlight = false;
|
|
3398
|
+
if (generation === this.generation) this.pollInFlight = false;
|
|
3351
3399
|
}
|
|
3352
3400
|
}
|
|
3353
3401
|
stopCountdown() {
|
|
@@ -3374,6 +3422,7 @@ var BrowserCheckoutController = class {
|
|
|
3374
3422
|
options;
|
|
3375
3423
|
watcher;
|
|
3376
3424
|
state;
|
|
3425
|
+
generation = 0;
|
|
3377
3426
|
constructor(options) {
|
|
3378
3427
|
this.options = options;
|
|
3379
3428
|
this.watcher = this.createWatcher(options);
|
|
@@ -3383,6 +3432,7 @@ var BrowserCheckoutController = class {
|
|
|
3383
3432
|
return this.state;
|
|
3384
3433
|
}
|
|
3385
3434
|
stop() {
|
|
3435
|
+
this.generation += 1;
|
|
3386
3436
|
this.watcher.stop();
|
|
3387
3437
|
}
|
|
3388
3438
|
getState() {
|
|
@@ -3444,6 +3494,7 @@ var BrowserCheckoutController = class {
|
|
|
3444
3494
|
if (paymentHash === void 0) {
|
|
3445
3495
|
throw new Error(`No swap attempt ${options.attemptId} in this checkout.`);
|
|
3446
3496
|
}
|
|
3497
|
+
const generation = this.generation;
|
|
3447
3498
|
const invoice = await requestSwapRefund({
|
|
3448
3499
|
fetch: this.options.fetch ?? globalThis.fetch,
|
|
3449
3500
|
prefix,
|
|
@@ -3455,10 +3506,12 @@ var BrowserCheckoutController = class {
|
|
|
3455
3506
|
refundAddress: options.refundAddress,
|
|
3456
3507
|
confirm
|
|
3457
3508
|
});
|
|
3509
|
+
if (generation !== this.generation) throw new DOMException("Checkout changed.", "AbortError");
|
|
3458
3510
|
this.watcher.stageSwapInvoice(invoice);
|
|
3459
3511
|
return invoice;
|
|
3460
3512
|
}
|
|
3461
3513
|
cancel() {
|
|
3514
|
+
this.generation += 1;
|
|
3462
3515
|
this.state = this.watcher.cancel();
|
|
3463
3516
|
emitBrowserLog(
|
|
3464
3517
|
this.options.logger,
|
|
@@ -3734,6 +3787,7 @@ export {
|
|
|
3734
3787
|
createTransactionDetails,
|
|
3735
3788
|
createTransactionDetailsFromState,
|
|
3736
3789
|
resolveTransactionDetailRows,
|
|
3790
|
+
createAppBrowserConsoleLogger,
|
|
3737
3791
|
getWizardEmptyMessage,
|
|
3738
3792
|
getRouteNetworkLabel,
|
|
3739
3793
|
createWizardRouteAssetDisplays,
|
|
@@ -3765,7 +3819,6 @@ export {
|
|
|
3765
3819
|
createPaymentWizardModel,
|
|
3766
3820
|
updatePaymentWizardSelection,
|
|
3767
3821
|
createPaymentWizardController,
|
|
3768
|
-
createAppBrowserConsoleLogger,
|
|
3769
3822
|
selectCheckoutDisplayInvoice,
|
|
3770
3823
|
isPaidCheckoutSnapshot,
|
|
3771
3824
|
isReusableLightningInvoice,
|