@openreceive/browser 0.4.10 → 0.4.12
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-FIAYCDZO.js → chunk-XDFN6BT3.js} +250 -219
- package/dist/headless.d.ts +22 -5
- package/dist/headless.js +70 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{status-Cnx1Vv-w.d.ts → status-Q1CJqaYo.d.ts} +3 -4
- package/package.json +3 -3
- package/skills/integrate-openreceive/SKILL.md +4 -3
- package/skills/integrate-openreceive/references/btcpay.md +67 -36
- package/skills/integrate-openreceive/references/django.md +291 -244
- package/skills/integrate-openreceive/references/fastapi.md +206 -155
- package/skills/integrate-openreceive/references/fastify.md +200 -147
- package/skills/integrate-openreceive/references/laravel.md +294 -234
- package/skills/integrate-openreceive/references/next.md +210 -162
- package/skills/integrate-openreceive/references/node.md +188 -138
- package/skills/integrate-openreceive/references/php.md +248 -193
- package/skills/integrate-openreceive/references/rails.md +262 -218
- package/skills/integrate-openreceive/references/woocommerce.md +68 -51
|
@@ -950,8 +950,8 @@ function createSwapDisplayModel(invoice, options = {}) {
|
|
|
950
950
|
const depositAmount = formatDepositAmount(swap.deposit_amount);
|
|
951
951
|
const networkWarningEmphasis = `${depositAmount} ${asset.assetLabel} on the ${asset.networkLabel} network`;
|
|
952
952
|
const depositRisk = swapDepositRisk(swap.pay_in_asset);
|
|
953
|
-
const doubleSpendWarning = `Pay with one method only \u2014 if you already sent ${asset.assetLabel}, do not also pay the Lightning invoice.`;
|
|
954
953
|
const settled = invoice.transaction_state === "settled";
|
|
954
|
+
const instructionsExpired = expiresInSeconds === 0 && swap.provider_state === "awaiting_deposit";
|
|
955
955
|
const feeBreakdown = createSwapFeeBreakdown(swap.fee, swap);
|
|
956
956
|
return {
|
|
957
957
|
provider: swap.provider,
|
|
@@ -962,7 +962,8 @@ function createSwapDisplayModel(invoice, options = {}) {
|
|
|
962
962
|
depositRisk,
|
|
963
963
|
networkWarningTitle: depositRisk === "pinned" ? checkoutLabels.sendExactAmountTitle : checkoutLabels.wrongCurrencyOrNetworkTitle,
|
|
964
964
|
networkWarningEmphasis,
|
|
965
|
-
|
|
965
|
+
// The lost-funds sentence is claimed only where it is reachable.
|
|
966
|
+
networkWarning: depositRisk === "pinned" ? `Send exactly ${networkWarningEmphasis}.` : `Be sure you are sending exactly ${networkWarningEmphasis}. If you send the wrong currency or send on the wrong network, your funds will be lost!`,
|
|
966
967
|
depositAddress: swap.deposit_address,
|
|
967
968
|
...swap.deposit_memo === void 0 ? {} : { depositMemo: swap.deposit_memo },
|
|
968
969
|
depositAmount,
|
|
@@ -987,14 +988,14 @@ function createSwapDisplayModel(invoice, options = {}) {
|
|
|
987
988
|
],
|
|
988
989
|
{ label: checkoutLabels.swapCopyAmount, value: depositAmount, selectable: true }
|
|
989
990
|
],
|
|
990
|
-
providerStateLabel: settled ? "Payment complete" : getSwapProviderStateLabel(swap.provider_state),
|
|
991
|
-
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, {
|
|
992
993
|
refundReason: swap.refund_reason,
|
|
993
994
|
depositAmount: swap.deposit_amount,
|
|
994
995
|
depositReceivedAmount: swap.deposit_received_amount,
|
|
995
996
|
refundAmount: swap.refund_amount
|
|
996
997
|
}),
|
|
997
|
-
state: settled ? "settled" : getSwapPanelState(swap.provider_state),
|
|
998
|
+
state: settled ? "settled" : instructionsExpired ? "progress" : getSwapPanelState(swap.provider_state),
|
|
998
999
|
expiresInSeconds,
|
|
999
1000
|
countdownLabel: formatCountdown(expiresInSeconds),
|
|
1000
1001
|
qrPayload: createSwapQrPayload(swap),
|
|
@@ -1046,10 +1047,13 @@ function overlaySwapRefundStagingIntoSnapshot(snapshot, staged) {
|
|
|
1046
1047
|
function selectCurrentSwapInvoice(snapshot, options = {}) {
|
|
1047
1048
|
const dismissedInvoiceId = options.dismissedInvoiceId ?? null;
|
|
1048
1049
|
const started = options.started ?? null;
|
|
1049
|
-
|
|
1050
|
-
(
|
|
1051
|
-
|
|
1052
|
-
|
|
1050
|
+
if (started === null) {
|
|
1051
|
+
if (dismissedInvoiceId !== null) return void 0;
|
|
1052
|
+
return snapshot?.invoices.find(
|
|
1053
|
+
(invoice) => invoice.rail === "swap" && invoice.swap !== void 0
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
if (started.invoice_id === dismissedInvoiceId) return void 0;
|
|
1053
1057
|
const matched = snapshot?.invoices.find((invoice) => invoice.invoice_id === started.invoice_id) ?? started;
|
|
1054
1058
|
return overlaySwapRefundStaging(matched, started);
|
|
1055
1059
|
}
|
|
@@ -1087,7 +1091,7 @@ function getSwapProviderStateDetail(state, payInAsset, refundContext = {}) {
|
|
|
1087
1091
|
return "Your payment is confirmed and being converted. This usually finishes within a minute.";
|
|
1088
1092
|
}
|
|
1089
1093
|
if (state === "paying_invoice" || state === "completed") {
|
|
1090
|
-
return "
|
|
1094
|
+
return "We are checking whether the wallet received the payment. Provider completion alone does not confirm payment.";
|
|
1091
1095
|
}
|
|
1092
1096
|
if (state === "expired") return "No payment was received before the payment window closed.";
|
|
1093
1097
|
if (state === "refund_required" || state === "refund_pending" || state === "refunded") {
|
|
@@ -1361,6 +1365,127 @@ function isCheckoutStateSource(value) {
|
|
|
1361
1365
|
return "checkout_id" in value && "reference" in value && "invoice_id" in value && "invoice" in value && "transaction_state" in value && "phase" in value;
|
|
1362
1366
|
}
|
|
1363
1367
|
|
|
1368
|
+
// src/internal/console-logger.ts
|
|
1369
|
+
import { isSensitiveLogKey, sanitizeLogValue } from "@openreceive/core";
|
|
1370
|
+
|
|
1371
|
+
// src/internal/log-level.ts
|
|
1372
|
+
var BROWSER_LOG_LEVEL_ORDER = {
|
|
1373
|
+
debug: 10,
|
|
1374
|
+
info: 20,
|
|
1375
|
+
warn: 30,
|
|
1376
|
+
error: 40
|
|
1377
|
+
};
|
|
1378
|
+
var BROWSER_LOG_LEVEL_ALIASES = {
|
|
1379
|
+
debug: "debug",
|
|
1380
|
+
info: "info",
|
|
1381
|
+
warn: "warn",
|
|
1382
|
+
warning: "warn",
|
|
1383
|
+
error: "error"
|
|
1384
|
+
};
|
|
1385
|
+
function browserLogLevelOrder(level) {
|
|
1386
|
+
return BROWSER_LOG_LEVEL_ORDER[level];
|
|
1387
|
+
}
|
|
1388
|
+
function parseBrowserLogLevel(value) {
|
|
1389
|
+
if (value === void 0 || value === null) return void 0;
|
|
1390
|
+
const normalized = value.trim().toLowerCase();
|
|
1391
|
+
if (normalized.length === 0) return void 0;
|
|
1392
|
+
return BROWSER_LOG_LEVEL_ALIASES[normalized];
|
|
1393
|
+
}
|
|
1394
|
+
function resolveBrowserLogLevel(value, fallback = "info") {
|
|
1395
|
+
return parseBrowserLogLevel(value) ?? fallback;
|
|
1396
|
+
}
|
|
1397
|
+
function readBrowserLogLevelFromEnvironment(source) {
|
|
1398
|
+
if (source !== void 0) {
|
|
1399
|
+
return resolveBrowserLogLevel(
|
|
1400
|
+
source.LOG_LEVEL ?? source.VITE_LOG_LEVEL ?? source.NEXT_PUBLIC_LOG_LEVEL
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
const fromGlobal = parseBrowserLogLevel(readGlobalLogLevel());
|
|
1404
|
+
if (fromGlobal !== void 0) return fromGlobal;
|
|
1405
|
+
const metaEnv = readImportMetaEnv();
|
|
1406
|
+
const parsedMeta = parseBrowserLogLevel(
|
|
1407
|
+
metaEnv?.LOG_LEVEL ?? metaEnv?.VITE_LOG_LEVEL ?? metaEnv?.NEXT_PUBLIC_LOG_LEVEL
|
|
1408
|
+
);
|
|
1409
|
+
if (parsedMeta !== void 0) return parsedMeta;
|
|
1410
|
+
const processEnv = typeof process !== "undefined" ? process.env : void 0;
|
|
1411
|
+
return resolveBrowserLogLevel(processEnv?.LOG_LEVEL);
|
|
1412
|
+
}
|
|
1413
|
+
function readGlobalLogLevel() {
|
|
1414
|
+
try {
|
|
1415
|
+
const value = globalThis.__OPENRECEIVE_LOG_LEVEL__;
|
|
1416
|
+
return typeof value === "string" ? value : void 0;
|
|
1417
|
+
} catch {
|
|
1418
|
+
return void 0;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function readImportMetaEnv() {
|
|
1422
|
+
try {
|
|
1423
|
+
return import.meta.env;
|
|
1424
|
+
} catch {
|
|
1425
|
+
return void 0;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
// src/internal/console-logger.ts
|
|
1430
|
+
function createConsoleWriter(options) {
|
|
1431
|
+
const target = options.console ?? console;
|
|
1432
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1433
|
+
return (level, prefix, event, message, fields) => {
|
|
1434
|
+
const minLevel = options.minLevel === void 0 ? readBrowserLogLevelFromEnvironment() : resolveBrowserLogLevel(options.minLevel);
|
|
1435
|
+
if (browserLogLevelOrder(level) < browserLogLevelOrder(minLevel)) {
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
const method = level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "info";
|
|
1439
|
+
const sink = target[method] ?? target.log;
|
|
1440
|
+
const {
|
|
1441
|
+
level: _level,
|
|
1442
|
+
event: _event,
|
|
1443
|
+
message: cleanMessage,
|
|
1444
|
+
...cleanFields
|
|
1445
|
+
} = sanitizeBrowserLogEntry({ level, event, message, ...fields });
|
|
1446
|
+
sink.call(
|
|
1447
|
+
target,
|
|
1448
|
+
`[${now().toISOString()}] ${level.toUpperCase()} [${prefix}] ${event}: ${cleanMessage}`,
|
|
1449
|
+
cleanFields
|
|
1450
|
+
);
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
function sanitizeBrowserLogEntry(entry) {
|
|
1454
|
+
const clean = {};
|
|
1455
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
1456
|
+
if (isSensitiveLogKey(key)) {
|
|
1457
|
+
clean[key] = "[REDACTED]";
|
|
1458
|
+
} else {
|
|
1459
|
+
clean[key] = sanitizeLogValue(value);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return clean;
|
|
1463
|
+
}
|
|
1464
|
+
function createBrowserConsoleLogger(options = {}) {
|
|
1465
|
+
const prefix = options.prefix ?? "openreceive";
|
|
1466
|
+
const write = createConsoleWriter(options);
|
|
1467
|
+
return (entry) => {
|
|
1468
|
+
const { level, event, message, ...fields } = entry;
|
|
1469
|
+
write(level, prefix, event, message, fields);
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
function createAppBrowserConsoleLogger(options) {
|
|
1473
|
+
const write = createConsoleWriter(options);
|
|
1474
|
+
return (event, message, fields = {}, level = "info") => {
|
|
1475
|
+
write(level, options.prefix, event, message, fields);
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
var defaultBrowserConsoleLogger;
|
|
1479
|
+
function getDefaultBrowserConsoleLogger() {
|
|
1480
|
+
defaultBrowserConsoleLogger ??= createBrowserConsoleLogger();
|
|
1481
|
+
return defaultBrowserConsoleLogger;
|
|
1482
|
+
}
|
|
1483
|
+
function resolveBrowserLogger(logger) {
|
|
1484
|
+
if (logger === false) return void 0;
|
|
1485
|
+
if (logger !== void 0) return logger;
|
|
1486
|
+
return getDefaultBrowserConsoleLogger();
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1364
1489
|
// src/internal/wizard.ts
|
|
1365
1490
|
import { getSwapRefundAddressError } from "@openreceive/core/swap-address";
|
|
1366
1491
|
import {
|
|
@@ -1956,176 +2081,6 @@ function createPaymentWizardController(options = {}) {
|
|
|
1956
2081
|
return new BrowserPaymentWizardController(options);
|
|
1957
2082
|
}
|
|
1958
2083
|
|
|
1959
|
-
// src/internal/log-level.ts
|
|
1960
|
-
var BROWSER_LOG_LEVEL_ORDER = {
|
|
1961
|
-
debug: 10,
|
|
1962
|
-
info: 20,
|
|
1963
|
-
warn: 30,
|
|
1964
|
-
error: 40
|
|
1965
|
-
};
|
|
1966
|
-
var BROWSER_LOG_LEVEL_ALIASES = {
|
|
1967
|
-
debug: "debug",
|
|
1968
|
-
info: "info",
|
|
1969
|
-
warn: "warn",
|
|
1970
|
-
warning: "warn",
|
|
1971
|
-
error: "error"
|
|
1972
|
-
};
|
|
1973
|
-
function browserLogLevelOrder(level) {
|
|
1974
|
-
return BROWSER_LOG_LEVEL_ORDER[level];
|
|
1975
|
-
}
|
|
1976
|
-
function parseBrowserLogLevel(value) {
|
|
1977
|
-
if (value === void 0 || value === null) return void 0;
|
|
1978
|
-
const normalized = value.trim().toLowerCase();
|
|
1979
|
-
if (normalized.length === 0) return void 0;
|
|
1980
|
-
return BROWSER_LOG_LEVEL_ALIASES[normalized];
|
|
1981
|
-
}
|
|
1982
|
-
function resolveBrowserLogLevel(value, fallback = "info") {
|
|
1983
|
-
return parseBrowserLogLevel(value) ?? fallback;
|
|
1984
|
-
}
|
|
1985
|
-
function readBrowserLogLevelFromEnvironment(source) {
|
|
1986
|
-
if (source !== void 0) {
|
|
1987
|
-
return resolveBrowserLogLevel(
|
|
1988
|
-
source.LOG_LEVEL ?? source.VITE_LOG_LEVEL ?? source.NEXT_PUBLIC_LOG_LEVEL
|
|
1989
|
-
);
|
|
1990
|
-
}
|
|
1991
|
-
const fromGlobal = parseBrowserLogLevel(readGlobalLogLevel());
|
|
1992
|
-
if (fromGlobal !== void 0) return fromGlobal;
|
|
1993
|
-
const metaEnv = readImportMetaEnv();
|
|
1994
|
-
const parsedMeta = parseBrowserLogLevel(
|
|
1995
|
-
metaEnv?.LOG_LEVEL ?? metaEnv?.VITE_LOG_LEVEL ?? metaEnv?.NEXT_PUBLIC_LOG_LEVEL
|
|
1996
|
-
);
|
|
1997
|
-
if (parsedMeta !== void 0) return parsedMeta;
|
|
1998
|
-
const processEnv = typeof process !== "undefined" ? process.env : void 0;
|
|
1999
|
-
return resolveBrowserLogLevel(processEnv?.LOG_LEVEL);
|
|
2000
|
-
}
|
|
2001
|
-
function readGlobalLogLevel() {
|
|
2002
|
-
try {
|
|
2003
|
-
const value = globalThis.__OPENRECEIVE_LOG_LEVEL__;
|
|
2004
|
-
return typeof value === "string" ? value : void 0;
|
|
2005
|
-
} catch {
|
|
2006
|
-
return void 0;
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
function readImportMetaEnv() {
|
|
2010
|
-
try {
|
|
2011
|
-
return import.meta.env;
|
|
2012
|
-
} catch {
|
|
2013
|
-
return void 0;
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
// src/internal/console-logger.ts
|
|
2018
|
-
function createConsoleWriter(options) {
|
|
2019
|
-
const target = options.console ?? console;
|
|
2020
|
-
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
2021
|
-
return (level, prefix, event, message, fields) => {
|
|
2022
|
-
const minLevel = options.minLevel === void 0 ? readBrowserLogLevelFromEnvironment() : resolveBrowserLogLevel(options.minLevel);
|
|
2023
|
-
if (browserLogLevelOrder(level) < browserLogLevelOrder(minLevel)) {
|
|
2024
|
-
return;
|
|
2025
|
-
}
|
|
2026
|
-
const method = level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "info";
|
|
2027
|
-
const sink = target[method] ?? target.log;
|
|
2028
|
-
const {
|
|
2029
|
-
level: _level,
|
|
2030
|
-
event: _event,
|
|
2031
|
-
message: cleanMessage,
|
|
2032
|
-
...cleanFields
|
|
2033
|
-
} = sanitizeBrowserLogEntry({ level, event, message, ...fields });
|
|
2034
|
-
sink.call(
|
|
2035
|
-
target,
|
|
2036
|
-
`[${now().toISOString()}] ${level.toUpperCase()} [${prefix}] ${event}: ${cleanMessage}`,
|
|
2037
|
-
cleanFields
|
|
2038
|
-
);
|
|
2039
|
-
};
|
|
2040
|
-
}
|
|
2041
|
-
function sanitizeBrowserLogEntry(entry) {
|
|
2042
|
-
const clean = {};
|
|
2043
|
-
for (const [key, value] of Object.entries(entry)) {
|
|
2044
|
-
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
2045
|
-
clean[key] = "[REDACTED]";
|
|
2046
|
-
} else {
|
|
2047
|
-
clean[key] = sanitizeBrowserLogValue(value);
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
|
-
return clean;
|
|
2051
|
-
}
|
|
2052
|
-
function sanitizeBrowserLogValue(value) {
|
|
2053
|
-
if (typeof value === "string") return redactBrowserSecrets(value);
|
|
2054
|
-
if (Array.isArray(value)) return value.map(sanitizeBrowserLogValue);
|
|
2055
|
-
if (typeof value !== "object" || value === null) return value;
|
|
2056
|
-
const clean = {};
|
|
2057
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
2058
|
-
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
2059
|
-
clean[key] = "[REDACTED]";
|
|
2060
|
-
} else {
|
|
2061
|
-
clean[key] = sanitizeBrowserLogValue(nested);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
return clean;
|
|
2065
|
-
}
|
|
2066
|
-
function redactBrowserSecrets(value) {
|
|
2067
|
-
return value.replace(/nostr\+walletconnect:\/\/[^\s"'`<>]+/g, "[REDACTED_NWC]").replace(/([?&](?:_or_evt|token|secret)=)[^&\s"'`<>]+/gi, "$1[REDACTED]");
|
|
2068
|
-
}
|
|
2069
|
-
function createBrowserConsoleLogger(options = {}) {
|
|
2070
|
-
const prefix = options.prefix ?? "openreceive";
|
|
2071
|
-
const write = createConsoleWriter(options);
|
|
2072
|
-
return (entry) => {
|
|
2073
|
-
const { level, event, message, ...fields } = entry;
|
|
2074
|
-
write(level, prefix, event, message, fields);
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
function createAppBrowserConsoleLogger(options) {
|
|
2078
|
-
const write = createConsoleWriter(options);
|
|
2079
|
-
return (event, message, fields = {}, level = "info") => {
|
|
2080
|
-
write(level, options.prefix, event, message, fields);
|
|
2081
|
-
};
|
|
2082
|
-
}
|
|
2083
|
-
var defaultBrowserConsoleLogger;
|
|
2084
|
-
function getDefaultBrowserConsoleLogger() {
|
|
2085
|
-
defaultBrowserConsoleLogger ??= createBrowserConsoleLogger();
|
|
2086
|
-
return defaultBrowserConsoleLogger;
|
|
2087
|
-
}
|
|
2088
|
-
function resolveBrowserLogger(logger) {
|
|
2089
|
-
if (logger === false) return void 0;
|
|
2090
|
-
if (logger !== void 0) return logger;
|
|
2091
|
-
return getDefaultBrowserConsoleLogger();
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
// src/internal/checkout-read.ts
|
|
2095
|
-
import { nonEmptyString } from "@openreceive/core";
|
|
2096
|
-
function optionalRecord(value) {
|
|
2097
|
-
if (value === void 0) return void 0;
|
|
2098
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2099
|
-
throw new Error("OpenReceive metadata must be an object.");
|
|
2100
|
-
}
|
|
2101
|
-
return value;
|
|
2102
|
-
}
|
|
2103
|
-
function requiredInvoiceRail(value) {
|
|
2104
|
-
if (value === "lightning" || value === "swap" || value === "checkout_lock") return value;
|
|
2105
|
-
throw new TypeError("OpenReceive invoice rail must be lightning, swap, or checkout_lock.");
|
|
2106
|
-
}
|
|
2107
|
-
function requiredString(value, fieldName) {
|
|
2108
|
-
const text = nonEmptyString(value);
|
|
2109
|
-
if (text === void 0) {
|
|
2110
|
-
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2111
|
-
}
|
|
2112
|
-
return text;
|
|
2113
|
-
}
|
|
2114
|
-
function optionalSafeInteger(value, fieldName = "integer") {
|
|
2115
|
-
if (value === void 0 || value === null) return void 0;
|
|
2116
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
2117
|
-
throw new TypeError(`OpenReceive checkout response ${fieldName} must be a safe integer.`);
|
|
2118
|
-
}
|
|
2119
|
-
return value;
|
|
2120
|
-
}
|
|
2121
|
-
function requiredSafeInteger(value, fieldName) {
|
|
2122
|
-
const integer = optionalSafeInteger(value, fieldName);
|
|
2123
|
-
if (integer === void 0) {
|
|
2124
|
-
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2125
|
-
}
|
|
2126
|
-
return integer;
|
|
2127
|
-
}
|
|
2128
|
-
|
|
2129
2084
|
// src/internal/checkout-log.ts
|
|
2130
2085
|
import { compact } from "@openreceive/core";
|
|
2131
2086
|
function checkoutLogFields(state) {
|
|
@@ -2206,6 +2161,41 @@ function emitBrowserLog(logger, level, event, message, fields = {}) {
|
|
|
2206
2161
|
}
|
|
2207
2162
|
}
|
|
2208
2163
|
|
|
2164
|
+
// src/internal/checkout-read.ts
|
|
2165
|
+
import { nonEmptyString } from "@openreceive/core";
|
|
2166
|
+
function optionalRecord(value) {
|
|
2167
|
+
if (value === void 0) return void 0;
|
|
2168
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2169
|
+
throw new Error("OpenReceive metadata must be an object.");
|
|
2170
|
+
}
|
|
2171
|
+
return value;
|
|
2172
|
+
}
|
|
2173
|
+
function requiredInvoiceRail(value) {
|
|
2174
|
+
if (value === "lightning" || value === "swap" || value === "checkout_lock") return value;
|
|
2175
|
+
throw new TypeError("OpenReceive invoice rail must be lightning, swap, or checkout_lock.");
|
|
2176
|
+
}
|
|
2177
|
+
function requiredString(value, fieldName) {
|
|
2178
|
+
const text = nonEmptyString(value);
|
|
2179
|
+
if (text === void 0) {
|
|
2180
|
+
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2181
|
+
}
|
|
2182
|
+
return text;
|
|
2183
|
+
}
|
|
2184
|
+
function optionalSafeInteger(value, fieldName = "integer") {
|
|
2185
|
+
if (value === void 0 || value === null) return void 0;
|
|
2186
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
2187
|
+
throw new TypeError(`OpenReceive checkout response ${fieldName} must be a safe integer.`);
|
|
2188
|
+
}
|
|
2189
|
+
return value;
|
|
2190
|
+
}
|
|
2191
|
+
function requiredSafeInteger(value, fieldName) {
|
|
2192
|
+
const integer = optionalSafeInteger(value, fieldName);
|
|
2193
|
+
if (integer === void 0) {
|
|
2194
|
+
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
2195
|
+
}
|
|
2196
|
+
return integer;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2209
2199
|
// src/internal/checkout-state.ts
|
|
2210
2200
|
function selectCheckoutDisplayInvoice(snapshot) {
|
|
2211
2201
|
if (snapshot.active !== void 0 && snapshot.active.rail !== "checkout_lock") {
|
|
@@ -2380,15 +2370,14 @@ function normalizeCheckoutState(state, now) {
|
|
|
2380
2370
|
const expiresInSeconds = base.expires_at === void 0 || now === void 0 ? void 0 : Math.max(0, base.expires_at - now);
|
|
2381
2371
|
const phase = statePhase === "invoice_created" || statePhase === "verifying" ? expiresInSeconds === 0 ? "expired" : statePhase : statePhase;
|
|
2382
2372
|
const settled = base.paid || base.transaction_state === "settled";
|
|
2373
|
+
const monitoring = checkoutMonitoring(base);
|
|
2383
2374
|
return {
|
|
2384
2375
|
...base,
|
|
2385
2376
|
...deriveCheckoutStateLabels(base),
|
|
2386
2377
|
phase,
|
|
2387
2378
|
settled,
|
|
2388
|
-
// A
|
|
2389
|
-
|
|
2390
|
-
// both /payments/check and the provider instead of asking forever.
|
|
2391
|
-
terminal: isTerminalPhase(phase) || !settled && isTerminalSwapProviderState(base.swap?.provider_state),
|
|
2379
|
+
// A local instruction deadline says nothing about wallet or refund finality.
|
|
2380
|
+
terminal: !monitoring.payment && !monitoring.provider,
|
|
2392
2381
|
...expiresInSeconds === void 0 ? {} : { expires_in_seconds: expiresInSeconds }
|
|
2393
2382
|
};
|
|
2394
2383
|
}
|
|
@@ -2409,8 +2398,13 @@ function getCheckoutPhase(transactionState, workflowState) {
|
|
|
2409
2398
|
}
|
|
2410
2399
|
return "invoice_created";
|
|
2411
2400
|
}
|
|
2412
|
-
function
|
|
2413
|
-
|
|
2401
|
+
function checkoutMonitoring(state) {
|
|
2402
|
+
if (state.workflow_state === "cancelled") return { payment: false, provider: false };
|
|
2403
|
+
const payment = !state.paid && !["settled", "expired", "failed", "attention"].includes(state.transaction_state ?? "pending") && !["expired", "failed", "attention"].includes(state.workflow_state ?? "invoice_created");
|
|
2404
|
+
const providerState = state.swap?.provider_state;
|
|
2405
|
+
const recovery = providerState === "refund_required" || providerState === "refund_pending";
|
|
2406
|
+
const provider = providerState !== void 0 && !isTerminalSwapProviderState(providerState) && providerState !== "completed" && (recovery || !state.paid && state.transaction_state !== "settled");
|
|
2407
|
+
return { payment, provider };
|
|
2414
2408
|
}
|
|
2415
2409
|
function snapshotFromCheckoutState(state) {
|
|
2416
2410
|
const invoice = {
|
|
@@ -2570,6 +2564,7 @@ function normalizeRequestCheckoutOptions(options) {
|
|
|
2570
2564
|
const reference = nonEmptyString2(options.reference);
|
|
2571
2565
|
const metadata = optionalRecord(options.metadata);
|
|
2572
2566
|
return {
|
|
2567
|
+
signal: options.signal,
|
|
2573
2568
|
routes: checkoutRoutes(options.prefix),
|
|
2574
2569
|
reference: reference ?? "",
|
|
2575
2570
|
fetch: options.fetch,
|
|
@@ -2599,6 +2594,7 @@ async function requestCheckout(options) {
|
|
|
2599
2594
|
const headers = request.headers === void 0 ? {} : request.headers;
|
|
2600
2595
|
const response = await fetcher(request.routes.checkouts, {
|
|
2601
2596
|
method: "POST",
|
|
2597
|
+
signal: request.signal,
|
|
2602
2598
|
headers: requestHeaders(headers, request.csrfHeader),
|
|
2603
2599
|
body: JSON.stringify(requestBody)
|
|
2604
2600
|
});
|
|
@@ -2622,6 +2618,7 @@ async function prepareCheckout(options) {
|
|
|
2622
2618
|
const headers = request.headers === void 0 ? {} : request.headers;
|
|
2623
2619
|
const response = await fetcher(request.routes.checkoutsPrepare, {
|
|
2624
2620
|
method: "POST",
|
|
2621
|
+
signal: request.signal,
|
|
2625
2622
|
headers: requestHeaders(headers, request.csrfHeader),
|
|
2626
2623
|
body: JSON.stringify({ reference: request.reference })
|
|
2627
2624
|
});
|
|
@@ -2645,50 +2642,57 @@ function createStatusFetcher(options) {
|
|
|
2645
2642
|
if (activePaymentHash === void 0 || !/^[0-9a-f]{64}$/i.test(activePaymentHash)) {
|
|
2646
2643
|
return snapshot;
|
|
2647
2644
|
}
|
|
2648
|
-
const response = await fetcher(routes.paymentsCheck, {
|
|
2649
|
-
method: "POST",
|
|
2650
|
-
headers: requestHeaders(headers, options.csrfHeader),
|
|
2651
|
-
body: JSON.stringify({
|
|
2652
|
-
reference,
|
|
2653
|
-
payment_hash: activePaymentHash
|
|
2654
|
-
})
|
|
2655
|
-
});
|
|
2656
|
-
const body = await readJsonResponse(response, "Could not refresh invoice status.");
|
|
2657
|
-
const payment = recordOrEmpty(body);
|
|
2658
2645
|
const next = structuredClone(snapshot);
|
|
2659
2646
|
if (next.active === void 0) return next;
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
let
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2647
|
+
let active = next.active;
|
|
2648
|
+
let payment = {};
|
|
2649
|
+
let paymentError;
|
|
2650
|
+
if (checkoutMonitoring(active).payment) {
|
|
2651
|
+
try {
|
|
2652
|
+
const response = await fetcher(routes.paymentsCheck, {
|
|
2653
|
+
method: "POST",
|
|
2654
|
+
headers: requestHeaders(headers, options.csrfHeader),
|
|
2655
|
+
body: JSON.stringify({ reference, payment_hash: activePaymentHash })
|
|
2656
|
+
});
|
|
2657
|
+
payment = recordOrEmpty(
|
|
2658
|
+
await readJsonResponse(response, "Could not refresh invoice status.")
|
|
2659
|
+
);
|
|
2660
|
+
const state2 = nonEmptyString2(payment.status) ?? "pending";
|
|
2661
|
+
const paidAt = optionalSafeInteger(payment.paid_at, "paid_at");
|
|
2662
|
+
active = {
|
|
2663
|
+
...active,
|
|
2664
|
+
transaction_state: state2 === "not_found" ? "pending" : state2,
|
|
2665
|
+
...paidAt === void 0 ? {} : { settled_at: paidAt }
|
|
2666
|
+
};
|
|
2667
|
+
} catch (error) {
|
|
2668
|
+
paymentError = error;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
if (active.rail === "swap" && checkoutMonitoring(active).provider) {
|
|
2669
2672
|
try {
|
|
2670
2673
|
const swapResponse = await fetcher(routes.swapsStatus, {
|
|
2671
2674
|
method: "POST",
|
|
2672
2675
|
headers: requestHeaders(headers, options.csrfHeader),
|
|
2673
2676
|
body: JSON.stringify({ reference, payment_hash: activePaymentHash })
|
|
2674
2677
|
});
|
|
2675
|
-
|
|
2676
|
-
|
|
2678
|
+
active = mergeSwapStatusIntoInvoice(
|
|
2679
|
+
active,
|
|
2680
|
+
recordOrEmpty(await readJsonResponse(swapResponse, "Could not refresh swap status."))
|
|
2677
2681
|
);
|
|
2678
|
-
active = mergeSwapStatusIntoInvoice(active, swapBody);
|
|
2679
2682
|
swapStatusFailures = 0;
|
|
2680
2683
|
} catch (error) {
|
|
2681
2684
|
swapStatusFailures += 1;
|
|
2682
2685
|
const definitive = error instanceof BrowserRequestError && error.retryable === false;
|
|
2683
|
-
|
|
2684
|
-
if (swap !== void 0 && (definitive || swapStatusFailures >= SWAP_STATUS_FAILURE_LIMIT)) {
|
|
2686
|
+
if (active.swap !== void 0 && (definitive || swapStatusFailures >= SWAP_STATUS_FAILURE_LIMIT)) {
|
|
2685
2687
|
active = {
|
|
2686
2688
|
...active,
|
|
2687
|
-
swap: { ...swap, attention: true, attention_reason: "swap_status_unavailable" }
|
|
2689
|
+
swap: { ...active.swap, attention: true, attention_reason: "swap_status_unavailable" }
|
|
2688
2690
|
};
|
|
2689
2691
|
}
|
|
2692
|
+
if (paymentError === void 0 && !checkoutMonitoring(active).payment) paymentError = error;
|
|
2690
2693
|
}
|
|
2691
2694
|
}
|
|
2695
|
+
const state = active.transaction_state;
|
|
2692
2696
|
const paymentMethods2 = normalizePaymentMethods(payment.payment_methods);
|
|
2693
2697
|
const others = next.invoices.filter(
|
|
2694
2698
|
(entry) => entry.invoice_id !== active.invoice_id && entry.rail !== "checkout_lock"
|
|
@@ -2700,6 +2704,7 @@ function createStatusFetcher(options) {
|
|
|
2700
2704
|
status: state === "settled" ? "paid" : state === "expired" || state === "failed" ? "expired" : "open",
|
|
2701
2705
|
...paymentMethods2 === void 0 ? {} : { payment_methods: paymentMethods2 }
|
|
2702
2706
|
};
|
|
2707
|
+
if (paymentError !== void 0 && active.swap === void 0) throw paymentError;
|
|
2703
2708
|
return snapshot;
|
|
2704
2709
|
};
|
|
2705
2710
|
}
|
|
@@ -3095,6 +3100,7 @@ async function requestJson(options, url, body) {
|
|
|
3095
3100
|
const fetcher = options.fetch;
|
|
3096
3101
|
const response = await fetcher(url, {
|
|
3097
3102
|
method: "POST",
|
|
3103
|
+
signal: options.signal,
|
|
3098
3104
|
headers: requestHeaders(options.headers, options.csrfHeader),
|
|
3099
3105
|
body: JSON.stringify(body)
|
|
3100
3106
|
});
|
|
@@ -3176,6 +3182,8 @@ var CheckoutWatcher = class {
|
|
|
3176
3182
|
countdownTimer;
|
|
3177
3183
|
pollTimer;
|
|
3178
3184
|
running = false;
|
|
3185
|
+
generation = 0;
|
|
3186
|
+
pendingRefresh;
|
|
3179
3187
|
pollInFlight = false;
|
|
3180
3188
|
pollFailureCount = 0;
|
|
3181
3189
|
pollBackoffUntil;
|
|
@@ -3242,6 +3250,9 @@ var CheckoutWatcher = class {
|
|
|
3242
3250
|
}
|
|
3243
3251
|
stop() {
|
|
3244
3252
|
this.running = false;
|
|
3253
|
+
this.generation += 1;
|
|
3254
|
+
this.pollInFlight = false;
|
|
3255
|
+
this.pendingRefresh = void 0;
|
|
3245
3256
|
this.stopCountdown();
|
|
3246
3257
|
this.stopPolling();
|
|
3247
3258
|
}
|
|
@@ -3280,8 +3291,10 @@ var CheckoutWatcher = class {
|
|
|
3280
3291
|
if (refreshStatus === void 0 || current.reference.length === 0) {
|
|
3281
3292
|
return current;
|
|
3282
3293
|
}
|
|
3294
|
+
const generation = this.generation;
|
|
3283
3295
|
try {
|
|
3284
|
-
const fetched = await
|
|
3296
|
+
const fetched = await this.fetchSnapshot(current.reference);
|
|
3297
|
+
if (generation !== this.generation) return this.state ?? current;
|
|
3285
3298
|
if (fetched === null) return current;
|
|
3286
3299
|
const next = this.publishSnapshot(fetched);
|
|
3287
3300
|
const nextState = createCheckoutState(next, {
|
|
@@ -3297,10 +3310,20 @@ var CheckoutWatcher = class {
|
|
|
3297
3310
|
}
|
|
3298
3311
|
return nextState;
|
|
3299
3312
|
} catch (error) {
|
|
3313
|
+
if (generation !== this.generation) return this.state ?? current;
|
|
3300
3314
|
this.options.onError?.(error);
|
|
3301
3315
|
throw error;
|
|
3302
3316
|
}
|
|
3303
3317
|
}
|
|
3318
|
+
fetchSnapshot(reference) {
|
|
3319
|
+
if (this.pendingRefresh !== void 0) return this.pendingRefresh;
|
|
3320
|
+
const pending = Promise.resolve(this.options.refreshStatus?.(reference) ?? null);
|
|
3321
|
+
this.pendingRefresh = pending;
|
|
3322
|
+
void pending.finally(() => {
|
|
3323
|
+
if (this.pendingRefresh === pending) this.pendingRefresh = void 0;
|
|
3324
|
+
}).catch(() => void 0);
|
|
3325
|
+
return pending;
|
|
3326
|
+
}
|
|
3304
3327
|
applyState(state) {
|
|
3305
3328
|
if (!this.running) return;
|
|
3306
3329
|
this.state = state;
|
|
@@ -3314,7 +3337,7 @@ var CheckoutWatcher = class {
|
|
|
3314
3337
|
this.stop();
|
|
3315
3338
|
return;
|
|
3316
3339
|
}
|
|
3317
|
-
if (state.settled || state.expires_at === void 0) {
|
|
3340
|
+
if (state.settled || state.expires_at === void 0 || state.expires_in_seconds === 0) {
|
|
3318
3341
|
this.stopCountdown();
|
|
3319
3342
|
} else if (this.countdownTimer === void 0) {
|
|
3320
3343
|
this.countdownTimer = this.setInterval()(() => {
|
|
@@ -3330,7 +3353,7 @@ var CheckoutWatcher = class {
|
|
|
3330
3353
|
}, 1e3);
|
|
3331
3354
|
}
|
|
3332
3355
|
const canPollAttempt = state.rail !== "checkout_lock" && typeof state.payment_hash === "string" && /^[0-9a-f]{64}$/i.test(state.payment_hash);
|
|
3333
|
-
if (
|
|
3356
|
+
if (this.options.refreshStatus === void 0 || state.reference.length === 0 || !canPollAttempt) {
|
|
3334
3357
|
this.stopPolling();
|
|
3335
3358
|
} else if (this.pollTimer === void 0) {
|
|
3336
3359
|
this.pollTimer = this.setInterval()(() => {
|
|
@@ -3342,20 +3365,22 @@ var CheckoutWatcher = class {
|
|
|
3342
3365
|
const refreshStatus = this.options.refreshStatus;
|
|
3343
3366
|
const current = this.state;
|
|
3344
3367
|
if (!this.running || refreshStatus === void 0 || current === void 0) return;
|
|
3345
|
-
if (current.terminal
|
|
3368
|
+
if (current.terminal) {
|
|
3346
3369
|
this.stopPolling();
|
|
3347
3370
|
return;
|
|
3348
3371
|
}
|
|
3349
3372
|
if (this.pollInFlight) return;
|
|
3350
3373
|
if (this.pollBackoffUntil !== void 0 && this.now() < this.pollBackoffUntil) return;
|
|
3351
3374
|
this.pollInFlight = true;
|
|
3375
|
+
const generation = this.generation;
|
|
3352
3376
|
try {
|
|
3353
|
-
const fetched = await
|
|
3377
|
+
const fetched = await this.fetchSnapshot(current.reference);
|
|
3378
|
+
if (generation !== this.generation) return;
|
|
3354
3379
|
this.pollFailureCount = 0;
|
|
3355
3380
|
this.pollBackoffUntil = void 0;
|
|
3356
3381
|
if (fetched === null) return;
|
|
3357
3382
|
if (!this.running || this.state === void 0) return;
|
|
3358
|
-
if (this.state.
|
|
3383
|
+
if (this.state.terminal) return;
|
|
3359
3384
|
const next = this.publishSnapshot(fetched);
|
|
3360
3385
|
this.applyState(
|
|
3361
3386
|
createCheckoutState(next, {
|
|
@@ -3366,13 +3391,14 @@ var CheckoutWatcher = class {
|
|
|
3366
3391
|
})
|
|
3367
3392
|
);
|
|
3368
3393
|
} catch (error) {
|
|
3394
|
+
if (generation !== this.generation) return;
|
|
3369
3395
|
this.pollFailureCount += 1;
|
|
3370
3396
|
const retryAfterSeconds = error instanceof BrowserRequestError ? error.retryAfterSeconds : void 0;
|
|
3371
3397
|
const backoffSeconds = retryAfterSeconds ?? Math.min(60, 2 ** Math.min(this.pollFailureCount, 6));
|
|
3372
3398
|
this.pollBackoffUntil = this.now() + backoffSeconds;
|
|
3373
3399
|
this.options.onError?.(error);
|
|
3374
3400
|
} finally {
|
|
3375
|
-
this.pollInFlight = false;
|
|
3401
|
+
if (generation === this.generation) this.pollInFlight = false;
|
|
3376
3402
|
}
|
|
3377
3403
|
}
|
|
3378
3404
|
stopCountdown() {
|
|
@@ -3399,6 +3425,7 @@ var BrowserCheckoutController = class {
|
|
|
3399
3425
|
options;
|
|
3400
3426
|
watcher;
|
|
3401
3427
|
state;
|
|
3428
|
+
generation = 0;
|
|
3402
3429
|
constructor(options) {
|
|
3403
3430
|
this.options = options;
|
|
3404
3431
|
this.watcher = this.createWatcher(options);
|
|
@@ -3408,6 +3435,7 @@ var BrowserCheckoutController = class {
|
|
|
3408
3435
|
return this.state;
|
|
3409
3436
|
}
|
|
3410
3437
|
stop() {
|
|
3438
|
+
this.generation += 1;
|
|
3411
3439
|
this.watcher.stop();
|
|
3412
3440
|
}
|
|
3413
3441
|
getState() {
|
|
@@ -3469,6 +3497,7 @@ var BrowserCheckoutController = class {
|
|
|
3469
3497
|
if (paymentHash === void 0) {
|
|
3470
3498
|
throw new Error(`No swap attempt ${options.attemptId} in this checkout.`);
|
|
3471
3499
|
}
|
|
3500
|
+
const generation = this.generation;
|
|
3472
3501
|
const invoice = await requestSwapRefund({
|
|
3473
3502
|
fetch: this.options.fetch ?? globalThis.fetch,
|
|
3474
3503
|
prefix,
|
|
@@ -3480,10 +3509,12 @@ var BrowserCheckoutController = class {
|
|
|
3480
3509
|
refundAddress: options.refundAddress,
|
|
3481
3510
|
confirm
|
|
3482
3511
|
});
|
|
3512
|
+
if (generation !== this.generation) throw new DOMException("Checkout changed.", "AbortError");
|
|
3483
3513
|
this.watcher.stageSwapInvoice(invoice);
|
|
3484
3514
|
return invoice;
|
|
3485
3515
|
}
|
|
3486
3516
|
cancel() {
|
|
3517
|
+
this.generation += 1;
|
|
3487
3518
|
this.state = this.watcher.cancel();
|
|
3488
3519
|
emitBrowserLog(
|
|
3489
3520
|
this.options.logger,
|
|
@@ -3759,6 +3790,7 @@ export {
|
|
|
3759
3790
|
createTransactionDetails,
|
|
3760
3791
|
createTransactionDetailsFromState,
|
|
3761
3792
|
resolveTransactionDetailRows,
|
|
3793
|
+
createAppBrowserConsoleLogger,
|
|
3762
3794
|
getWizardEmptyMessage,
|
|
3763
3795
|
getRouteNetworkLabel,
|
|
3764
3796
|
createWizardRouteAssetDisplays,
|
|
@@ -3790,7 +3822,6 @@ export {
|
|
|
3790
3822
|
createPaymentWizardModel,
|
|
3791
3823
|
updatePaymentWizardSelection,
|
|
3792
3824
|
createPaymentWizardController,
|
|
3793
|
-
createAppBrowserConsoleLogger,
|
|
3794
3825
|
selectCheckoutDisplayInvoice,
|
|
3795
3826
|
isPaidCheckoutSnapshot,
|
|
3796
3827
|
isReusableLightningInvoice,
|