@openreceive/browser 0.2.1 → 0.2.3
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-V7C55QB2.js → chunk-JFJBHGR4.js} +1221 -1162
- package/dist/headless.d.ts +11 -6
- package/dist/headless.js +5 -46
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{status-CLCX5lfx.d.ts → status-B1vDOhvh.d.ts} +44 -6
- package/package.json +3 -3
|
@@ -671,12 +671,22 @@ function assertInvoice(invoice) {
|
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
// src/internal/checkout-swap-view.ts
|
|
674
|
-
import {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
674
|
+
import { formatDecimal as formatDecimal2, parseDecimal as parseDecimal2, payInAssetNetwork } from "@openreceive/core";
|
|
675
|
+
|
|
676
|
+
// src/internal/unix-seconds.ts
|
|
677
|
+
import { unixSeconds as unixSeconds2 } from "@openreceive/core";
|
|
678
|
+
var MILLISECONDS_THRESHOLD = 1e12;
|
|
679
|
+
function resolveNow(now) {
|
|
680
|
+
if (now === void 0) return unixSeconds2();
|
|
681
|
+
if (now > MILLISECONDS_THRESHOLD) {
|
|
682
|
+
throw new RangeError(
|
|
683
|
+
`OpenReceive \`now\` is a unix timestamp in SECONDS; received ${now}, which is milliseconds. Pass Math.floor(Date.now() / 1000).`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
return now;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// src/internal/checkout-swap-view.ts
|
|
680
690
|
function createSwapFeeBreakdown(fee) {
|
|
681
691
|
if (fee === void 0) return void 0;
|
|
682
692
|
let payIn;
|
|
@@ -712,7 +722,7 @@ function createSwapDisplayModel(invoice, options = {}) {
|
|
|
712
722
|
swap.provider_expires_at,
|
|
713
723
|
invoice.expires_at ?? swap.provider_expires_at
|
|
714
724
|
);
|
|
715
|
-
const expiresInSeconds = Math.max(0, expiresAt - (options.now
|
|
725
|
+
const expiresInSeconds = Math.max(0, expiresAt - resolveNow(options.now));
|
|
716
726
|
const asset = getSwapAssetDisplay(swap.pay_in_asset);
|
|
717
727
|
const depositAmount = formatDepositAmount(swap.deposit_amount);
|
|
718
728
|
const networkWarningEmphasis = `${depositAmount} ${asset.assetLabel} on the ${asset.networkLabel} network`;
|
|
@@ -1099,1274 +1109,1320 @@ function createPaymentDataEntries(source) {
|
|
|
1099
1109
|
return entries;
|
|
1100
1110
|
}
|
|
1101
1111
|
|
|
1102
|
-
// src/internal/
|
|
1103
|
-
import {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
return value;
|
|
1112
|
+
// src/internal/wizard.ts
|
|
1113
|
+
import { getSwapRefundAddressError } from "@openreceive/core/swap-address";
|
|
1114
|
+
import {
|
|
1115
|
+
getPaymentWizardRoutes,
|
|
1116
|
+
listAssets,
|
|
1117
|
+
payTutorialUrls,
|
|
1118
|
+
providerIconUrls
|
|
1119
|
+
} from "@openreceive/provider-data";
|
|
1120
|
+
function getBitcoinAssets() {
|
|
1121
|
+
return listAssets().filter((asset) => asset.symbol === "btc" && asset.route !== void 0);
|
|
1113
1122
|
}
|
|
1114
|
-
function
|
|
1115
|
-
|
|
1116
|
-
|
|
1123
|
+
function getDefaultBitcoinRoute() {
|
|
1124
|
+
const routes = [
|
|
1125
|
+
...new Set(
|
|
1126
|
+
getBitcoinAssets().flatMap((asset) => asset.route === void 0 ? [] : [asset.route])
|
|
1127
|
+
)
|
|
1128
|
+
];
|
|
1129
|
+
return routes.length === 1 ? routes[0] ?? null : null;
|
|
1117
1130
|
}
|
|
1118
|
-
function
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1131
|
+
function getPaymentStatusText(phase) {
|
|
1132
|
+
if (phase === "settled") {
|
|
1133
|
+
return {
|
|
1134
|
+
title: checkoutLabels.paymentStatus.settledTitle,
|
|
1135
|
+
detail: checkoutLabels.paymentStatus.settledDetail
|
|
1136
|
+
};
|
|
1122
1137
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
throw new TypeError(`OpenReceive checkout response ${fieldName} must be a safe integer.`);
|
|
1138
|
+
if (phase === "expired") {
|
|
1139
|
+
return {
|
|
1140
|
+
title: checkoutLabels.paymentStatus.expiredTitle,
|
|
1141
|
+
detail: checkoutLabels.paymentStatus.expiredDetail
|
|
1142
|
+
};
|
|
1129
1143
|
}
|
|
1130
|
-
return
|
|
1144
|
+
return {
|
|
1145
|
+
title: checkoutLabels.paymentStatus.waitingTitle,
|
|
1146
|
+
detail: checkoutLabels.paymentStatus.waitingDetail
|
|
1147
|
+
};
|
|
1131
1148
|
}
|
|
1132
|
-
function
|
|
1133
|
-
|
|
1134
|
-
if (integer === void 0) {
|
|
1135
|
-
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
1136
|
-
}
|
|
1137
|
-
return integer;
|
|
1149
|
+
function getWizardEmptyMessage() {
|
|
1150
|
+
return checkoutLabels.emptyBitcoin;
|
|
1138
1151
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1152
|
+
function getCheckoutProviderOpenLabel() {
|
|
1153
|
+
return checkoutLabels.openProvider;
|
|
1154
|
+
}
|
|
1155
|
+
function getCheckoutProviderIcon(provider) {
|
|
1156
|
+
return providerIconUrls[provider.icon_path] ?? paymentIconUrls.crypto;
|
|
1157
|
+
}
|
|
1158
|
+
function getCheckoutProviderTutorials(provider) {
|
|
1159
|
+
return (provider.tutorials ?? []).map((tutorial) => ({
|
|
1160
|
+
index: tutorial.index,
|
|
1161
|
+
path: tutorial.path,
|
|
1162
|
+
image: payTutorialUrls[tutorial.path] ?? tutorial.path,
|
|
1163
|
+
caption: tutorial.caption
|
|
1164
|
+
}));
|
|
1165
|
+
}
|
|
1166
|
+
function getRouteNetworkLabel(routeId) {
|
|
1167
|
+
return routeId === "lightning" || routeId === "btc-lightning" ? checkoutLabels.lightningNetwork : routeId;
|
|
1168
|
+
}
|
|
1169
|
+
function createWizardRouteAssetDisplays(assets, options = {}) {
|
|
1170
|
+
return assets.map((asset) => {
|
|
1171
|
+
const id = asset.route ?? asset.symbol;
|
|
1172
|
+
return {
|
|
1173
|
+
id,
|
|
1174
|
+
label: asset.label,
|
|
1175
|
+
subtitle: getRouteNetworkLabel(id),
|
|
1176
|
+
icon: getRouteIcon(asset),
|
|
1177
|
+
selected: options.selectedRoute === id
|
|
1178
|
+
};
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
function createWizardRouteDisplays(routes, options = {}) {
|
|
1182
|
+
return routes.map((route) => ({
|
|
1183
|
+
key: getWizardRouteDisplayKey(route),
|
|
1184
|
+
title: getWizardRouteDisplayTitle(route),
|
|
1185
|
+
subtitle: getWizardRouteDisplaySubtitle(route),
|
|
1186
|
+
providers: (options.providerPreviewLimit === void 0 ? route.providers : route.providers.slice(0, options.providerPreviewLimit)).map((entry) => createWizardProviderDisplay(entry))
|
|
1187
|
+
}));
|
|
1188
|
+
}
|
|
1189
|
+
function getWizardRouteDisplayKey(route) {
|
|
1190
|
+
return route.route.id;
|
|
1191
|
+
}
|
|
1192
|
+
function getWizardRouteDisplayTitle(route) {
|
|
1193
|
+
return route.route.label;
|
|
1194
|
+
}
|
|
1195
|
+
function getWizardRouteDisplaySubtitle(route) {
|
|
1196
|
+
return route.route.symbol.toUpperCase();
|
|
1197
|
+
}
|
|
1198
|
+
function createWizardProviderDisplay(entry) {
|
|
1142
1199
|
return {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1200
|
+
id: entry.provider.id,
|
|
1201
|
+
name: entry.provider.name,
|
|
1202
|
+
kind: entry.provider.kind,
|
|
1203
|
+
url: entry.provider.lightning_docs_url ?? entry.provider.url,
|
|
1204
|
+
icon: getCheckoutProviderIcon(entry.provider),
|
|
1205
|
+
tutorials: getCheckoutProviderTutorials(entry.provider),
|
|
1206
|
+
copyLabel: checkoutLabels.copyInvoice,
|
|
1207
|
+
copiedLabel: checkoutLabels.copied,
|
|
1208
|
+
openLabel: getCheckoutProviderOpenLabel()
|
|
1146
1209
|
};
|
|
1147
1210
|
}
|
|
1148
|
-
function
|
|
1149
|
-
|
|
1150
|
-
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
1151
|
-
return token ? { "X-CSRF-Token": token } : {};
|
|
1211
|
+
function getPaymentMethodIcon(method) {
|
|
1212
|
+
return paymentIconUrls[paymentMethodIconIds[method]];
|
|
1152
1213
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1214
|
+
function getAssetIcon(symbol) {
|
|
1215
|
+
return paymentIconUrls[assetIconIds[symbol] ?? "crypto"];
|
|
1216
|
+
}
|
|
1217
|
+
function getNetworkIcon(networkLabel) {
|
|
1218
|
+
const key = networkLabel.trim().toLowerCase();
|
|
1219
|
+
if (key === "tron" || key === "trx") return paymentIconUrls.trx;
|
|
1220
|
+
if (key === "solana" || key === "sol") return paymentIconUrls.sol;
|
|
1221
|
+
if (key === "ethereum" || key === "eth") return paymentIconUrls.eth;
|
|
1222
|
+
return paymentIconUrls.crypto;
|
|
1223
|
+
}
|
|
1224
|
+
function getSwapOptionIcon(option) {
|
|
1225
|
+
return getAssetIcon(option.label.trim().toLowerCase());
|
|
1226
|
+
}
|
|
1227
|
+
function groupSwapOptionsByLabel(options) {
|
|
1228
|
+
const groups = [];
|
|
1229
|
+
const indexByLabel = /* @__PURE__ */ new Map();
|
|
1230
|
+
for (const option of options) {
|
|
1231
|
+
const key = option.label.trim().toUpperCase();
|
|
1232
|
+
const existing = indexByLabel.get(key);
|
|
1233
|
+
if (existing === void 0) {
|
|
1234
|
+
indexByLabel.set(key, groups.length);
|
|
1235
|
+
groups.push({ label: option.label, options: [option] });
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
const group = groups[existing];
|
|
1239
|
+
if (group === void 0) continue;
|
|
1240
|
+
groups[existing] = { label: group.label, options: [...group.options, option] };
|
|
1160
1241
|
}
|
|
1161
|
-
|
|
1162
|
-
return {
|
|
1163
|
-
checkouts: `${base}/checkouts`,
|
|
1164
|
-
checkoutsPrepare: `${base}/checkouts/prepare`,
|
|
1165
|
-
paymentsCheck: `${base}/payments/check`,
|
|
1166
|
-
swaps: `${base}/swaps`,
|
|
1167
|
-
swapsQuote: `${base}/swaps/quote`,
|
|
1168
|
-
swapsStatus: `${base}/swaps/status`,
|
|
1169
|
-
swapsRefunds: `${base}/swaps/refunds`
|
|
1170
|
-
};
|
|
1242
|
+
return groups;
|
|
1171
1243
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
this.status = options.status;
|
|
1184
|
-
if (options.code !== void 0) this.code = options.code;
|
|
1185
|
-
if (options.retryable !== void 0) this.retryable = options.retryable;
|
|
1186
|
-
if (options.retryAfterSeconds !== void 0) this.retryAfterSeconds = options.retryAfterSeconds;
|
|
1244
|
+
var OPENRECEIVE_METHOD_GRID_ORDER = [
|
|
1245
|
+
{ kind: "method", id: "bitcoin" },
|
|
1246
|
+
{ kind: "swap", label: "USDT" },
|
|
1247
|
+
{ kind: "swap", label: "USDC" },
|
|
1248
|
+
{ kind: "swap", label: "SOL" },
|
|
1249
|
+
{ kind: "swap", label: "ETH" }
|
|
1250
|
+
];
|
|
1251
|
+
function buildMethodGridEntries(paymentMethods2, swapOptions) {
|
|
1252
|
+
const swapGroups = groupSwapOptionsByLabel(swapOptions);
|
|
1253
|
+
if (swapGroups.length === 0) {
|
|
1254
|
+
return paymentMethods2.map((method) => ({ kind: "method", method }));
|
|
1187
1255
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1256
|
+
const methodsById = new Map(paymentMethods2.map((method) => [method.id, method]));
|
|
1257
|
+
const groupsByLabel = new Map(
|
|
1258
|
+
swapGroups.map((group) => [group.label.trim().toUpperCase(), group])
|
|
1259
|
+
);
|
|
1260
|
+
const usedMethodIds = /* @__PURE__ */ new Set();
|
|
1261
|
+
const usedSwapLabels = /* @__PURE__ */ new Set();
|
|
1262
|
+
const entries = [];
|
|
1263
|
+
for (const slot of OPENRECEIVE_METHOD_GRID_ORDER) {
|
|
1264
|
+
if (slot.kind === "method") {
|
|
1265
|
+
const method = methodsById.get(slot.id);
|
|
1266
|
+
if (method === void 0) continue;
|
|
1267
|
+
usedMethodIds.add(method.id);
|
|
1268
|
+
entries.push({ kind: "method", method });
|
|
1269
|
+
continue;
|
|
1199
1270
|
}
|
|
1200
|
-
|
|
1271
|
+
const group = groupsByLabel.get(slot.label);
|
|
1272
|
+
if (group === void 0) continue;
|
|
1273
|
+
usedSwapLabels.add(slot.label);
|
|
1274
|
+
entries.push({ kind: "swap", group });
|
|
1201
1275
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
throw new BrowserRequestError(
|
|
1206
|
-
typeof record?.message === "string" ? record.message : fallbackMessage,
|
|
1207
|
-
{
|
|
1208
|
-
status: response.status,
|
|
1209
|
-
...typeof record?.code === "string" ? { code: record.code } : {},
|
|
1210
|
-
...typeof record?.retryable === "boolean" ? { retryable: record.retryable } : {},
|
|
1211
|
-
...Number.isFinite(retryAfterRaw) && retryAfterRaw > 0 ? { retryAfterSeconds: retryAfterRaw } : {}
|
|
1212
|
-
}
|
|
1213
|
-
);
|
|
1276
|
+
for (const method of paymentMethods2) {
|
|
1277
|
+
if (usedMethodIds.has(method.id)) continue;
|
|
1278
|
+
entries.push({ kind: "method", method });
|
|
1214
1279
|
}
|
|
1215
|
-
|
|
1280
|
+
for (const group of swapGroups) {
|
|
1281
|
+
const key = group.label.trim().toUpperCase();
|
|
1282
|
+
if (usedSwapLabels.has(key)) continue;
|
|
1283
|
+
entries.push({ kind: "swap", group });
|
|
1284
|
+
}
|
|
1285
|
+
return entries;
|
|
1216
1286
|
}
|
|
1217
|
-
function
|
|
1218
|
-
|
|
1219
|
-
const metadata = optionalRecord(options.metadata);
|
|
1220
|
-
return {
|
|
1221
|
-
routes: checkoutRoutes(options.prefix),
|
|
1222
|
-
reference: reference ?? "",
|
|
1223
|
-
fetch: options.fetch,
|
|
1224
|
-
headers: options.headers,
|
|
1225
|
-
...options.memo === void 0 ? {} : { memo: options.memo },
|
|
1226
|
-
...metadata === void 0 ? {} : { metadata }
|
|
1227
|
-
};
|
|
1287
|
+
function swapPickerKey(label) {
|
|
1288
|
+
return `swap:${label.trim().toUpperCase()}`;
|
|
1228
1289
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1290
|
+
function parseMethodPickerKey(key) {
|
|
1291
|
+
if (!key.startsWith("method:")) return null;
|
|
1292
|
+
return { kind: "method", methodId: key.slice("method:".length) };
|
|
1293
|
+
}
|
|
1294
|
+
function parseSwapPickerKey(key) {
|
|
1295
|
+
if (!key.startsWith("swap:")) return null;
|
|
1296
|
+
return { kind: "swap", label: key.slice("swap:".length) };
|
|
1297
|
+
}
|
|
1298
|
+
function paymentAccentId(labelOrMethodId) {
|
|
1299
|
+
const key = labelOrMethodId.trim().toLowerCase();
|
|
1300
|
+
if (key === "bitcoin" || key === "btc") return "bitcoin";
|
|
1301
|
+
if (key === "usdt") return "usdt";
|
|
1302
|
+
if (key === "usdc") return "usdc";
|
|
1303
|
+
if (key === "sol" || key === "solana") return "sol";
|
|
1304
|
+
if (key === "eth" || key === "ethereum") return "eth";
|
|
1305
|
+
return "default";
|
|
1306
|
+
}
|
|
1307
|
+
var assetActiveClassByAccent = {
|
|
1308
|
+
bitcoin: orClasses.methodCardActiveBitcoin,
|
|
1309
|
+
usdt: orClasses.methodCardActiveUsdt,
|
|
1310
|
+
usdc: orClasses.methodCardActiveUsdc,
|
|
1311
|
+
sol: orClasses.methodCardActiveSol,
|
|
1312
|
+
eth: orClasses.methodCardActiveEth,
|
|
1313
|
+
default: orClasses.methodCardActiveDefault
|
|
1314
|
+
};
|
|
1315
|
+
var networkActiveClassByAccent = {
|
|
1316
|
+
usdt: orClasses.methodNetworkButtonActiveUsdt,
|
|
1317
|
+
usdc: orClasses.methodNetworkButtonActiveUsdc,
|
|
1318
|
+
default: orClasses.methodNetworkButtonActiveDefault
|
|
1319
|
+
};
|
|
1320
|
+
function assetButtonClasses(options) {
|
|
1321
|
+
const base = options.disabled ? orClasses.methodCardUnavailable : orClasses.methodCardReady;
|
|
1322
|
+
if (!options.selected || options.disabled) return base;
|
|
1323
|
+
return `${base} ${assetActiveClassByAccent[options.accent]}`;
|
|
1324
|
+
}
|
|
1325
|
+
function networkButtonClasses(options) {
|
|
1326
|
+
if (options.disabled) return orClasses.methodNetworkButtonUnavailable;
|
|
1327
|
+
if (!options.selected) return orClasses.methodNetworkButton;
|
|
1328
|
+
const accent = options.accent === "usdt" || options.accent === "usdc" ? options.accent : "default";
|
|
1329
|
+
return `${orClasses.methodNetworkButton} ${networkActiveClassByAccent[accent]}`;
|
|
1330
|
+
}
|
|
1331
|
+
function networkMobileRevealClasses(accent) {
|
|
1332
|
+
if (accent === "usdt") return orClasses.methodNetworkRevealMobileUsdt;
|
|
1333
|
+
if (accent === "usdc") return orClasses.methodNetworkRevealMobileUsdc;
|
|
1334
|
+
return orClasses.methodNetworkRevealMobile;
|
|
1335
|
+
}
|
|
1336
|
+
function networkCheckClasses(accent) {
|
|
1337
|
+
return accent === "usdc" ? orClasses.methodNetworkCheckUsdc : orClasses.methodNetworkCheck;
|
|
1338
|
+
}
|
|
1339
|
+
function networkSummaryIconClasses(accent) {
|
|
1340
|
+
return accent === "usdc" ? orClasses.methodNetworkSummaryIconUsdc : orClasses.methodNetworkSummaryIcon;
|
|
1341
|
+
}
|
|
1342
|
+
function formatNetworkSummary(assetLabel, networkLabel) {
|
|
1343
|
+
return checkoutLabels.networkSummary.replace("{asset}", assetLabel).replace("{network}", networkLabel);
|
|
1344
|
+
}
|
|
1345
|
+
function formatChooseNetworkHeading(assetLabel) {
|
|
1346
|
+
return checkoutLabels.chooseAssetNetwork.replace("{asset}", assetLabel);
|
|
1347
|
+
}
|
|
1348
|
+
function resolvePreservedNetworkSelection(options) {
|
|
1349
|
+
const nextKey = options.nextGroup.label.trim().toUpperCase();
|
|
1350
|
+
const current = options.selectedNetworks[nextKey];
|
|
1351
|
+
if (current !== void 0 && options.nextGroup.options.some(
|
|
1352
|
+
(option) => option.pay_in_asset === current && option.available !== false
|
|
1353
|
+
)) {
|
|
1354
|
+
return current;
|
|
1233
1355
|
}
|
|
1234
|
-
const
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
1356
|
+
const previous = options.previousGroup;
|
|
1357
|
+
if (previous === void 0) return void 0;
|
|
1358
|
+
const previousKey = previous.label.trim().toUpperCase();
|
|
1359
|
+
const previousAsset = options.selectedNetworks[previousKey];
|
|
1360
|
+
if (previousAsset === void 0) return void 0;
|
|
1361
|
+
const previousOption = previous.options.find((option) => option.pay_in_asset === previousAsset);
|
|
1362
|
+
if (previousOption === void 0) return void 0;
|
|
1363
|
+
const match = options.nextGroup.options.find(
|
|
1364
|
+
(option) => option.network_label === previousOption.network_label && option.available !== false
|
|
1365
|
+
);
|
|
1366
|
+
return match?.pay_in_asset;
|
|
1367
|
+
}
|
|
1368
|
+
function findSwapGridGroup(entries, pickerKey) {
|
|
1369
|
+
if (pickerKey === null || pickerKey === void 0) return void 0;
|
|
1370
|
+
const parsed = parseSwapPickerKey(pickerKey);
|
|
1371
|
+
if (parsed === null) return void 0;
|
|
1372
|
+
const entry = entries.find(
|
|
1373
|
+
(entry2) => entry2.kind === "swap" && entry2.group.label.trim().toUpperCase() === parsed.label
|
|
1374
|
+
);
|
|
1375
|
+
return entry?.kind === "swap" ? entry.group : void 0;
|
|
1376
|
+
}
|
|
1377
|
+
function updateSelectedSwapNetworks(options) {
|
|
1378
|
+
const nextGroup = findSwapGridGroup(options.entries, options.nextKey);
|
|
1379
|
+
if (nextGroup === void 0 || nextGroup.options.length <= 1) {
|
|
1380
|
+
return options.selectedNetworks;
|
|
1237
1381
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1382
|
+
const previousGroup = findSwapGridGroup(options.entries, options.previousKey);
|
|
1383
|
+
const preserved = resolvePreservedNetworkSelection({
|
|
1384
|
+
previousGroup,
|
|
1385
|
+
nextGroup,
|
|
1386
|
+
selectedNetworks: options.selectedNetworks
|
|
1387
|
+
});
|
|
1388
|
+
const groupKey = nextGroup.label.trim().toUpperCase();
|
|
1389
|
+
if (preserved === void 0) {
|
|
1390
|
+
const { [groupKey]: _removed, ...rest } = options.selectedNetworks;
|
|
1391
|
+
return rest;
|
|
1240
1392
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1393
|
+
return { ...options.selectedNetworks, [groupKey]: preserved };
|
|
1394
|
+
}
|
|
1395
|
+
function wizardNetworkGroupIds(groupKey) {
|
|
1396
|
+
const slug = groupKey.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
1397
|
+
return { panelId: `network-panel-${slug}`, headingId: `network-heading-${slug}` };
|
|
1398
|
+
}
|
|
1399
|
+
function createSwapUnavailableModel(option, checkout) {
|
|
1400
|
+
const detail = swapOptionLimitMessage(option, checkout) ?? option.unavailable_message ?? checkoutLabels.swapUnavailableFallback.replace("{asset}", option.label);
|
|
1401
|
+
const range = option.minimum_pay_amount === void 0 ? void 0 : option.maximum_pay_amount === void 0 ? checkoutLabels.swapUnavailableMinimumOnly.replace("{minimum}", option.minimum_pay_amount).replace("{asset}", option.label) : checkoutLabels.swapUnavailableRange.replace("{minimum}", option.minimum_pay_amount).replace("{maximum}", option.maximum_pay_amount).replace("{asset}", option.label);
|
|
1402
|
+
return {
|
|
1403
|
+
title: checkoutLabels.swapUnavailableTitle.replace("{asset}", option.label),
|
|
1404
|
+
detail,
|
|
1405
|
+
...range === void 0 ? {} : { range },
|
|
1406
|
+
hint: checkoutLabels.swapUnavailableHint
|
|
1245
1407
|
};
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1408
|
+
}
|
|
1409
|
+
function swapOptionLimitMessage(option, checkout) {
|
|
1410
|
+
if (option.available !== false) return void 0;
|
|
1411
|
+
if (option.unavailable_reason === "amount_too_small") {
|
|
1412
|
+
const fiat = checkout === void 0 ? void 0 : formatSwapLimit(checkout, option.minimum_invoice_amount_msats, "ceil");
|
|
1413
|
+
if (fiat !== void 0) return `Minimum amount ${fiat}`;
|
|
1414
|
+
if (option.minimum_pay_amount !== void 0) {
|
|
1415
|
+
return `Minimum ${option.minimum_pay_amount} ${option.label}`;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (option.unavailable_reason === "amount_too_large") {
|
|
1419
|
+
const fiat = checkout === void 0 ? void 0 : formatSwapLimit(checkout, option.maximum_invoice_amount_msats, "floor");
|
|
1420
|
+
if (fiat !== void 0) return `Maximum amount ${fiat}`;
|
|
1421
|
+
if (option.maximum_pay_amount !== void 0) {
|
|
1422
|
+
return `Maximum ${option.maximum_pay_amount} ${option.label}`;
|
|
1423
|
+
}
|
|
1257
1424
|
}
|
|
1258
|
-
return
|
|
1425
|
+
return option.unavailable_message;
|
|
1259
1426
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1427
|
+
function swapGroupLimitOption(options) {
|
|
1428
|
+
if (options.length === 0) return void 0;
|
|
1429
|
+
const unavailable = options.filter((option) => option.available === false);
|
|
1430
|
+
const tooSmall = unavailable.filter((option) => option.unavailable_reason === "amount_too_small");
|
|
1431
|
+
const candidates = tooSmall.length > 0 ? tooSmall : unavailable.length > 0 ? unavailable : options;
|
|
1432
|
+
let best = candidates[0];
|
|
1433
|
+
for (const option of candidates) {
|
|
1434
|
+
if (best === void 0) {
|
|
1435
|
+
best = option;
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
const bestMin = best.minimum_invoice_amount_msats;
|
|
1439
|
+
const optionMin = option.minimum_invoice_amount_msats;
|
|
1440
|
+
if (optionMin === void 0) continue;
|
|
1441
|
+
if (bestMin === void 0 || optionMin < bestMin) best = option;
|
|
1264
1442
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1443
|
+
return best;
|
|
1444
|
+
}
|
|
1445
|
+
function getSwapRefundFormError(payInAsset, address, networkLabel) {
|
|
1446
|
+
if (address.length === 0) return "Enter a refund address.";
|
|
1447
|
+
return getSwapRefundAddressError(payInAsset, address, networkLabel);
|
|
1448
|
+
}
|
|
1449
|
+
function getRouteIcon(asset) {
|
|
1450
|
+
const routeId = asset.route ?? asset.symbol;
|
|
1451
|
+
if (asset.symbol === "btc" && routeId.includes("lightning")) {
|
|
1452
|
+
return paymentIconUrls.lightning;
|
|
1268
1453
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1454
|
+
return getAssetIcon(asset.symbol);
|
|
1455
|
+
}
|
|
1456
|
+
function createPaymentWizardState(request) {
|
|
1457
|
+
const selectedRouteId = request.selectedMethod === "bitcoin" ? request.selectedBitcoinRoute ?? getDefaultBitcoinRoute() : null;
|
|
1458
|
+
const routes = selectedRouteId === null ? [] : getPaymentWizardRoutes({ route: selectedRouteId });
|
|
1459
|
+
return {
|
|
1460
|
+
selectedRouteId,
|
|
1461
|
+
routes
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
function createPaymentWizardSelection() {
|
|
1465
|
+
return {
|
|
1466
|
+
selectedMethod: null,
|
|
1467
|
+
selectedBitcoinRoute: null
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
function createPaymentWizardModel(selection) {
|
|
1471
|
+
const wizard = createPaymentWizardState({
|
|
1472
|
+
selectedMethod: selection.selectedMethod,
|
|
1473
|
+
selectedBitcoinRoute: selection.selectedBitcoinRoute
|
|
1274
1474
|
});
|
|
1275
|
-
const
|
|
1276
|
-
|
|
1475
|
+
const routeAssets = selection.selectedMethod === "bitcoin" ? getBitcoinAssets() : [];
|
|
1476
|
+
const selectedRoute = wizard.selectedRouteId;
|
|
1477
|
+
return {
|
|
1478
|
+
selection,
|
|
1479
|
+
wizard,
|
|
1480
|
+
routeAssets,
|
|
1481
|
+
selectedRoute
|
|
1482
|
+
};
|
|
1277
1483
|
}
|
|
1278
|
-
function
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
const fetcher = options.fetch ?? globalThis.fetch;
|
|
1287
|
-
if (fetcher === void 0) {
|
|
1288
|
-
throw new Error("OpenReceive status refresh requires fetch.");
|
|
1289
|
-
}
|
|
1290
|
-
const headers = options.headers === void 0 ? {} : options.headers;
|
|
1291
|
-
const activePaymentHash = nonEmptyString2(snapshot.active?.payment_hash) ?? (snapshot.active?.rail === "checkout_lock" ? void 0 : nonEmptyString2(snapshot.active?.invoice_id));
|
|
1292
|
-
if (activePaymentHash === void 0 || !/^[0-9a-f]{64}$/i.test(activePaymentHash)) {
|
|
1293
|
-
return snapshot;
|
|
1484
|
+
function updatePaymentWizardSelection(selection, action) {
|
|
1485
|
+
switch (action.type) {
|
|
1486
|
+
case "select_method": {
|
|
1487
|
+
return {
|
|
1488
|
+
...selection,
|
|
1489
|
+
selectedMethod: action.method,
|
|
1490
|
+
selectedBitcoinRoute: action.method === "bitcoin" ? selection.selectedBitcoinRoute ?? getDefaultBitcoinRoute() : selection.selectedBitcoinRoute
|
|
1491
|
+
};
|
|
1294
1492
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
const paidAt = optionalSafeInteger(payment.paid_at, "paid_at");
|
|
1309
|
-
let active = {
|
|
1310
|
-
...next.active,
|
|
1311
|
-
transaction_state: state === "not_found" ? "pending" : state,
|
|
1312
|
-
...paidAt === void 0 ? {} : { settled_at: paidAt }
|
|
1313
|
-
};
|
|
1314
|
-
if (active.rail === "swap" && active.swap !== void 0 && state !== "settled" && // A terminal provider state is final; re-reading it only adds provider load.
|
|
1315
|
-
!isTerminalSwapProviderState(nonEmptyString2(active.swap.provider_state))) {
|
|
1316
|
-
try {
|
|
1317
|
-
const swapResponse = await fetcher(routes.swapsStatus, {
|
|
1318
|
-
method: "POST",
|
|
1319
|
-
headers: requestHeaders(headers),
|
|
1320
|
-
body: JSON.stringify({ reference, payment_hash: activePaymentHash })
|
|
1321
|
-
});
|
|
1322
|
-
const swapBody = recordOrEmpty(
|
|
1323
|
-
await readJsonResponse(swapResponse, "Could not refresh swap status.")
|
|
1324
|
-
);
|
|
1325
|
-
active = mergeSwapStatusIntoInvoice(active, swapBody);
|
|
1326
|
-
swapStatusFailures = 0;
|
|
1327
|
-
} catch (error) {
|
|
1328
|
-
swapStatusFailures += 1;
|
|
1329
|
-
const definitive = error instanceof BrowserRequestError && error.retryable === false;
|
|
1330
|
-
const swap = active.swap;
|
|
1331
|
-
if (swap !== void 0 && (definitive || swapStatusFailures >= SWAP_STATUS_FAILURE_LIMIT)) {
|
|
1332
|
-
active = {
|
|
1333
|
-
...active,
|
|
1334
|
-
swap: { ...swap, attention: true, attention_reason: "swap_status_unavailable" }
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1493
|
+
case "change_method": {
|
|
1494
|
+
return {
|
|
1495
|
+
...selection,
|
|
1496
|
+
selectedMethod: null,
|
|
1497
|
+
selectedBitcoinRoute: null
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
case "change_route": {
|
|
1501
|
+
if (selection.selectedMethod === "bitcoin") {
|
|
1502
|
+
return {
|
|
1503
|
+
...selection,
|
|
1504
|
+
selectedBitcoinRoute: null
|
|
1505
|
+
};
|
|
1337
1506
|
}
|
|
1507
|
+
return selection;
|
|
1508
|
+
}
|
|
1509
|
+
case "select_route": {
|
|
1510
|
+
if (selection.selectedMethod === "bitcoin") {
|
|
1511
|
+
return {
|
|
1512
|
+
...selection,
|
|
1513
|
+
selectedBitcoinRoute: action.route
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
return selection;
|
|
1338
1517
|
}
|
|
1339
|
-
const paymentMethods2 = normalizePaymentMethods(payment.payment_methods);
|
|
1340
|
-
const others = next.invoices.filter(
|
|
1341
|
-
(entry) => entry.invoice_id !== active.invoice_id && entry.rail !== "checkout_lock"
|
|
1342
|
-
);
|
|
1343
|
-
snapshot = {
|
|
1344
|
-
...next,
|
|
1345
|
-
active,
|
|
1346
|
-
invoices: [active, ...others],
|
|
1347
|
-
status: state === "settled" ? "paid" : state === "expired" || state === "failed" ? "expired" : "open",
|
|
1348
|
-
...paymentMethods2 === void 0 ? {} : { payment_methods: paymentMethods2 }
|
|
1349
|
-
};
|
|
1350
|
-
return snapshot;
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
|
-
function mergeSwapStatusIntoInvoice(invoice, status) {
|
|
1354
|
-
const swap = invoice.swap;
|
|
1355
|
-
if (swap === void 0) return invoice;
|
|
1356
|
-
const merged = { ...swap };
|
|
1357
|
-
const providerState = nonEmptyString2(status.provider_state);
|
|
1358
|
-
if (providerState !== void 0) merged.provider_state = providerState;
|
|
1359
|
-
const providerExpiresAt = optionalSafeInteger(status.provider_expires_at, "provider_expires_at");
|
|
1360
|
-
if (providerExpiresAt !== void 0) merged.provider_expires_at = providerExpiresAt;
|
|
1361
|
-
for (const key of [
|
|
1362
|
-
"deposit_tx_id",
|
|
1363
|
-
"payout_tx_id",
|
|
1364
|
-
"refund_tx_id",
|
|
1365
|
-
"refund_reason",
|
|
1366
|
-
"refund_amount",
|
|
1367
|
-
"deposit_received_amount",
|
|
1368
|
-
"refund_address",
|
|
1369
|
-
"attention_reason"
|
|
1370
|
-
]) {
|
|
1371
|
-
if (typeof status[key] === "string") merged[key] = status[key];
|
|
1372
1518
|
}
|
|
1373
|
-
if (typeof status.attention === "boolean") merged.attention = status.attention;
|
|
1374
|
-
return {
|
|
1375
|
-
...invoice,
|
|
1376
|
-
swap: merged,
|
|
1377
|
-
...providerExpiresAt === void 0 ? {} : { expires_at: providerExpiresAt }
|
|
1378
|
-
};
|
|
1379
1519
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1520
|
+
var BrowserPaymentWizardController = class {
|
|
1521
|
+
options;
|
|
1522
|
+
selection;
|
|
1523
|
+
constructor(options = {}) {
|
|
1524
|
+
this.options = options;
|
|
1525
|
+
this.selection = options.selection ?? createPaymentWizardSelection();
|
|
1526
|
+
}
|
|
1527
|
+
getSelection() {
|
|
1528
|
+
return this.selection;
|
|
1529
|
+
}
|
|
1530
|
+
getModel() {
|
|
1531
|
+
return createPaymentWizardModel(this.selection);
|
|
1532
|
+
}
|
|
1533
|
+
update(action) {
|
|
1534
|
+
const next = updatePaymentWizardSelection(this.selection, action);
|
|
1535
|
+
this.selection = next;
|
|
1536
|
+
this.options.onSelection?.(next);
|
|
1537
|
+
return next;
|
|
1538
|
+
}
|
|
1539
|
+
selectMethod(method) {
|
|
1540
|
+
return this.update({
|
|
1541
|
+
type: "select_method",
|
|
1542
|
+
method
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
changeMethod() {
|
|
1546
|
+
return this.update({
|
|
1547
|
+
type: "change_method"
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
selectRoute(route) {
|
|
1551
|
+
return this.update({
|
|
1552
|
+
type: "select_route",
|
|
1553
|
+
route
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
function createPaymentWizardController(options = {}) {
|
|
1558
|
+
return new BrowserPaymentWizardController(options);
|
|
1408
1559
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1560
|
+
|
|
1561
|
+
// src/internal/log-level.ts
|
|
1562
|
+
var BROWSER_LOG_LEVEL_ORDER = {
|
|
1563
|
+
debug: 10,
|
|
1564
|
+
info: 20,
|
|
1565
|
+
warn: 30,
|
|
1566
|
+
error: 40
|
|
1567
|
+
};
|
|
1568
|
+
var BROWSER_LOG_LEVEL_ALIASES = {
|
|
1569
|
+
debug: "debug",
|
|
1570
|
+
info: "info",
|
|
1571
|
+
warn: "warn",
|
|
1572
|
+
warning: "warn",
|
|
1573
|
+
error: "error"
|
|
1574
|
+
};
|
|
1575
|
+
function browserLogLevelOrder(level) {
|
|
1576
|
+
return BROWSER_LOG_LEVEL_ORDER[level];
|
|
1413
1577
|
}
|
|
1414
|
-
function
|
|
1415
|
-
|
|
1416
|
-
const
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
invoice_id: paymentHash,
|
|
1420
|
-
rail: "lightning",
|
|
1421
|
-
invoice: requiredString(checkout.bolt11, "bolt11"),
|
|
1422
|
-
payment_hash: paymentHash,
|
|
1423
|
-
amount_msats: amountMsats,
|
|
1424
|
-
transaction_state: "pending",
|
|
1425
|
-
workflow_state: "invoice_created",
|
|
1426
|
-
expires_at: requiredSafeInteger(checkout.expires_at, "expires_at"),
|
|
1427
|
-
...isRecord(checkout.fiat_quote) || checkout.fiat_quote === null ? { fiat_quote: checkout.fiat_quote } : {}
|
|
1428
|
-
};
|
|
1429
|
-
return {
|
|
1430
|
-
checkout_id: paymentHash,
|
|
1431
|
-
reference,
|
|
1432
|
-
status: "open",
|
|
1433
|
-
amount_msats: amountMsats,
|
|
1434
|
-
active: invoice,
|
|
1435
|
-
invoices: [invoice]
|
|
1436
|
-
};
|
|
1578
|
+
function parseBrowserLogLevel(value) {
|
|
1579
|
+
if (value === void 0 || value === null) return void 0;
|
|
1580
|
+
const normalized = value.trim().toLowerCase();
|
|
1581
|
+
if (normalized.length === 0) return void 0;
|
|
1582
|
+
return BROWSER_LOG_LEVEL_ALIASES[normalized];
|
|
1437
1583
|
}
|
|
1438
|
-
function
|
|
1439
|
-
|
|
1440
|
-
return value.map(normalizePaymentMethod);
|
|
1584
|
+
function resolveBrowserLogLevel(value, fallback = "info") {
|
|
1585
|
+
return parseBrowserLogLevel(value) ?? fallback;
|
|
1441
1586
|
}
|
|
1442
|
-
function
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1587
|
+
function readBrowserLogLevelFromEnvironment(source) {
|
|
1588
|
+
if (source !== void 0) {
|
|
1589
|
+
return resolveBrowserLogLevel(
|
|
1590
|
+
source.LOG_LEVEL ?? source.VITE_LOG_LEVEL ?? source.NEXT_PUBLIC_LOG_LEVEL
|
|
1591
|
+
);
|
|
1447
1592
|
}
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
const
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
const minimumInvoiceAmountMsats = optionalSafeInteger(
|
|
1454
|
-
record.minimum_invoice_amount_msats,
|
|
1455
|
-
"minimum_invoice_amount_msats"
|
|
1456
|
-
);
|
|
1457
|
-
const maximumInvoiceAmountMsats = optionalSafeInteger(
|
|
1458
|
-
record.maximum_invoice_amount_msats,
|
|
1459
|
-
"maximum_invoice_amount_msats"
|
|
1593
|
+
const fromGlobal = parseBrowserLogLevel(readGlobalLogLevel());
|
|
1594
|
+
if (fromGlobal !== void 0) return fromGlobal;
|
|
1595
|
+
const metaEnv = readImportMetaEnv();
|
|
1596
|
+
const parsedMeta = parseBrowserLogLevel(
|
|
1597
|
+
metaEnv?.LOG_LEVEL ?? metaEnv?.VITE_LOG_LEVEL ?? metaEnv?.NEXT_PUBLIC_LOG_LEVEL
|
|
1460
1598
|
);
|
|
1461
|
-
return
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
network_label: requiredString(record.network_label, "network_label"),
|
|
1465
|
-
provider,
|
|
1466
|
-
available: record.available === true,
|
|
1467
|
-
...unavailableReason === void 0 ? {} : { unavailable_reason: unavailableReason },
|
|
1468
|
-
...unavailableMessage === void 0 ? {} : { unavailable_message: unavailableMessage },
|
|
1469
|
-
...payAmount === void 0 ? {} : { pay_amount: payAmount },
|
|
1470
|
-
...minimumPayAmount === void 0 ? {} : { minimum_pay_amount: minimumPayAmount },
|
|
1471
|
-
...maximumPayAmount === void 0 ? {} : { maximum_pay_amount: maximumPayAmount },
|
|
1472
|
-
...minimumInvoiceAmountMsats === void 0 ? {} : { minimum_invoice_amount_msats: minimumInvoiceAmountMsats },
|
|
1473
|
-
...maximumInvoiceAmountMsats === void 0 ? {} : { maximum_invoice_amount_msats: maximumInvoiceAmountMsats }
|
|
1474
|
-
};
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
// src/internal/wizard.ts
|
|
1478
|
-
import { getSwapRefundAddressError } from "@openreceive/core/swap-address";
|
|
1479
|
-
import {
|
|
1480
|
-
getPaymentWizardRoutes,
|
|
1481
|
-
listAssets,
|
|
1482
|
-
payTutorialUrls,
|
|
1483
|
-
providerIconUrls
|
|
1484
|
-
} from "@openreceive/provider-data";
|
|
1485
|
-
function getBitcoinAssets() {
|
|
1486
|
-
return listAssets().filter((asset) => asset.symbol === "btc" && asset.route !== void 0);
|
|
1487
|
-
}
|
|
1488
|
-
function getDefaultBitcoinRoute() {
|
|
1489
|
-
const routes = [
|
|
1490
|
-
...new Set(
|
|
1491
|
-
getBitcoinAssets().flatMap((asset) => asset.route === void 0 ? [] : [asset.route])
|
|
1492
|
-
)
|
|
1493
|
-
];
|
|
1494
|
-
return routes.length === 1 ? routes[0] ?? null : null;
|
|
1599
|
+
if (parsedMeta !== void 0) return parsedMeta;
|
|
1600
|
+
const processEnv = typeof process !== "undefined" ? process.env : void 0;
|
|
1601
|
+
return resolveBrowserLogLevel(processEnv?.LOG_LEVEL);
|
|
1495
1602
|
}
|
|
1496
|
-
function
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
}
|
|
1503
|
-
if (phase === "expired") {
|
|
1504
|
-
return {
|
|
1505
|
-
title: checkoutLabels.paymentStatus.expiredTitle,
|
|
1506
|
-
detail: checkoutLabels.paymentStatus.expiredDetail
|
|
1507
|
-
};
|
|
1603
|
+
function readGlobalLogLevel() {
|
|
1604
|
+
try {
|
|
1605
|
+
const value = globalThis.__OPENRECEIVE_LOG_LEVEL__;
|
|
1606
|
+
return typeof value === "string" ? value : void 0;
|
|
1607
|
+
} catch {
|
|
1608
|
+
return void 0;
|
|
1508
1609
|
}
|
|
1509
|
-
return {
|
|
1510
|
-
title: checkoutLabels.paymentStatus.waitingTitle,
|
|
1511
|
-
detail: checkoutLabels.paymentStatus.waitingDetail
|
|
1512
|
-
};
|
|
1513
1610
|
}
|
|
1514
|
-
function
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
}
|
|
1520
|
-
function getCheckoutProviderIcon(provider) {
|
|
1521
|
-
return providerIconUrls[provider.icon_path] ?? paymentIconUrls.crypto;
|
|
1522
|
-
}
|
|
1523
|
-
function getCheckoutProviderTutorials(provider) {
|
|
1524
|
-
return (provider.tutorials ?? []).map((tutorial) => ({
|
|
1525
|
-
index: tutorial.index,
|
|
1526
|
-
path: tutorial.path,
|
|
1527
|
-
image: payTutorialUrls[tutorial.path] ?? tutorial.path,
|
|
1528
|
-
caption: tutorial.caption
|
|
1529
|
-
}));
|
|
1530
|
-
}
|
|
1531
|
-
function getRouteNetworkLabel(routeId) {
|
|
1532
|
-
return routeId === "lightning" || routeId === "btc-lightning" ? checkoutLabels.lightningNetwork : routeId;
|
|
1611
|
+
function readImportMetaEnv() {
|
|
1612
|
+
try {
|
|
1613
|
+
return import.meta.env;
|
|
1614
|
+
} catch {
|
|
1615
|
+
return void 0;
|
|
1616
|
+
}
|
|
1533
1617
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1618
|
+
|
|
1619
|
+
// src/internal/console-logger.ts
|
|
1620
|
+
function createConsoleWriter(options) {
|
|
1621
|
+
const target = options.console ?? console;
|
|
1622
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1623
|
+
return (level, prefix, event, message, fields) => {
|
|
1624
|
+
const minLevel = options.minLevel === void 0 ? readBrowserLogLevelFromEnvironment() : resolveBrowserLogLevel(options.minLevel);
|
|
1625
|
+
if (browserLogLevelOrder(level) < browserLogLevelOrder(minLevel)) {
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
const method = level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "info";
|
|
1629
|
+
const sink = target[method] ?? target.log;
|
|
1630
|
+
const {
|
|
1631
|
+
level: _level,
|
|
1632
|
+
event: _event,
|
|
1633
|
+
message: cleanMessage,
|
|
1634
|
+
...cleanFields
|
|
1635
|
+
} = sanitizeBrowserLogEntry({ level, event, message, ...fields });
|
|
1636
|
+
sink.call(
|
|
1637
|
+
target,
|
|
1638
|
+
`[${now().toISOString()}] ${level.toUpperCase()} [${prefix}] ${event}: ${cleanMessage}`,
|
|
1639
|
+
cleanFields
|
|
1640
|
+
);
|
|
1641
|
+
};
|
|
1545
1642
|
}
|
|
1546
|
-
function
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1643
|
+
function sanitizeBrowserLogEntry(entry) {
|
|
1644
|
+
const clean = {};
|
|
1645
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
1646
|
+
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
1647
|
+
clean[key] = "[REDACTED]";
|
|
1648
|
+
} else {
|
|
1649
|
+
clean[key] = sanitizeBrowserLogValue(value);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
return clean;
|
|
1553
1653
|
}
|
|
1554
|
-
function
|
|
1555
|
-
return
|
|
1654
|
+
function sanitizeBrowserLogValue(value) {
|
|
1655
|
+
if (typeof value === "string") return redactBrowserSecrets(value);
|
|
1656
|
+
if (Array.isArray(value)) return value.map(sanitizeBrowserLogValue);
|
|
1657
|
+
if (typeof value !== "object" || value === null) return value;
|
|
1658
|
+
const clean = {};
|
|
1659
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
1660
|
+
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
1661
|
+
clean[key] = "[REDACTED]";
|
|
1662
|
+
} else {
|
|
1663
|
+
clean[key] = sanitizeBrowserLogValue(nested);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
return clean;
|
|
1556
1667
|
}
|
|
1557
|
-
function
|
|
1558
|
-
return
|
|
1668
|
+
function redactBrowserSecrets(value) {
|
|
1669
|
+
return value.replace(/nostr\+walletconnect:\/\/[^\s"'`<>]+/g, "[REDACTED_NWC]").replace(/([?&](?:_or_evt|token|secret)=)[^&\s"'`<>]+/gi, "$1[REDACTED]");
|
|
1559
1670
|
}
|
|
1560
|
-
function
|
|
1561
|
-
|
|
1671
|
+
function createBrowserConsoleLogger(options = {}) {
|
|
1672
|
+
const prefix = options.prefix ?? "openreceive";
|
|
1673
|
+
const write = createConsoleWriter(options);
|
|
1674
|
+
return (entry) => {
|
|
1675
|
+
const { level, event, message, ...fields } = entry;
|
|
1676
|
+
write(level, prefix, event, message, fields);
|
|
1677
|
+
};
|
|
1562
1678
|
}
|
|
1563
|
-
function
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
kind: entry.provider.kind,
|
|
1568
|
-
url: entry.provider.lightning_docs_url ?? entry.provider.url,
|
|
1569
|
-
icon: getCheckoutProviderIcon(entry.provider),
|
|
1570
|
-
tutorials: getCheckoutProviderTutorials(entry.provider),
|
|
1571
|
-
copyLabel: checkoutLabels.copyInvoice,
|
|
1572
|
-
copiedLabel: checkoutLabels.copied,
|
|
1573
|
-
openLabel: getCheckoutProviderOpenLabel()
|
|
1679
|
+
function createAppBrowserConsoleLogger(options) {
|
|
1680
|
+
const write = createConsoleWriter(options);
|
|
1681
|
+
return (event, message, fields = {}, level = "info") => {
|
|
1682
|
+
write(level, options.prefix, event, message, fields);
|
|
1574
1683
|
};
|
|
1575
1684
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1685
|
+
var defaultBrowserConsoleLogger;
|
|
1686
|
+
function getDefaultBrowserConsoleLogger() {
|
|
1687
|
+
defaultBrowserConsoleLogger ??= createBrowserConsoleLogger();
|
|
1688
|
+
return defaultBrowserConsoleLogger;
|
|
1578
1689
|
}
|
|
1579
|
-
function
|
|
1580
|
-
return
|
|
1690
|
+
function resolveBrowserLogger(logger) {
|
|
1691
|
+
if (logger === false) return void 0;
|
|
1692
|
+
if (logger !== void 0) return logger;
|
|
1693
|
+
return getDefaultBrowserConsoleLogger();
|
|
1581
1694
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
if (
|
|
1587
|
-
|
|
1695
|
+
|
|
1696
|
+
// src/internal/checkout-read.ts
|
|
1697
|
+
import { nonEmptyString } from "@openreceive/core";
|
|
1698
|
+
function optionalRecord(value) {
|
|
1699
|
+
if (value === void 0) return void 0;
|
|
1700
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1701
|
+
throw new Error("OpenReceive metadata must be an object.");
|
|
1702
|
+
}
|
|
1703
|
+
return value;
|
|
1588
1704
|
}
|
|
1589
|
-
function
|
|
1590
|
-
|
|
1705
|
+
function requiredInvoiceRail(value) {
|
|
1706
|
+
if (value === "lightning" || value === "swap" || value === "checkout_lock") return value;
|
|
1707
|
+
throw new TypeError("OpenReceive invoice rail must be lightning, swap, or checkout_lock.");
|
|
1591
1708
|
}
|
|
1592
|
-
function
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
const key = option.label.trim().toUpperCase();
|
|
1597
|
-
const existing = indexByLabel.get(key);
|
|
1598
|
-
if (existing === void 0) {
|
|
1599
|
-
indexByLabel.set(key, groups.length);
|
|
1600
|
-
groups.push({ label: option.label, options: [option] });
|
|
1601
|
-
continue;
|
|
1602
|
-
}
|
|
1603
|
-
const group = groups[existing];
|
|
1604
|
-
if (group === void 0) continue;
|
|
1605
|
-
groups[existing] = { label: group.label, options: [...group.options, option] };
|
|
1709
|
+
function requiredString(value, fieldName) {
|
|
1710
|
+
const text = nonEmptyString(value);
|
|
1711
|
+
if (text === void 0) {
|
|
1712
|
+
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
1606
1713
|
}
|
|
1607
|
-
return
|
|
1714
|
+
return text;
|
|
1608
1715
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
{ kind: "swap", label: "SOL" },
|
|
1614
|
-
{ kind: "swap", label: "ETH" }
|
|
1615
|
-
];
|
|
1616
|
-
function buildMethodGridEntries(paymentMethods2, swapOptions) {
|
|
1617
|
-
const swapGroups = groupSwapOptionsByLabel(swapOptions);
|
|
1618
|
-
if (swapGroups.length === 0) {
|
|
1619
|
-
return paymentMethods2.map((method) => ({ kind: "method", method }));
|
|
1716
|
+
function optionalSafeInteger(value, fieldName = "integer") {
|
|
1717
|
+
if (value === void 0 || value === null) return void 0;
|
|
1718
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
1719
|
+
throw new TypeError(`OpenReceive checkout response ${fieldName} must be a safe integer.`);
|
|
1620
1720
|
}
|
|
1621
|
-
|
|
1622
|
-
const groupsByLabel = new Map(
|
|
1623
|
-
swapGroups.map((group) => [group.label.trim().toUpperCase(), group])
|
|
1624
|
-
);
|
|
1625
|
-
const usedMethodIds = /* @__PURE__ */ new Set();
|
|
1626
|
-
const usedSwapLabels = /* @__PURE__ */ new Set();
|
|
1627
|
-
const entries = [];
|
|
1628
|
-
for (const slot of OPENRECEIVE_METHOD_GRID_ORDER) {
|
|
1629
|
-
if (slot.kind === "method") {
|
|
1630
|
-
const method = methodsById.get(slot.id);
|
|
1631
|
-
if (method === void 0) continue;
|
|
1632
|
-
usedMethodIds.add(method.id);
|
|
1633
|
-
entries.push({ kind: "method", method });
|
|
1634
|
-
continue;
|
|
1635
|
-
}
|
|
1636
|
-
const group = groupsByLabel.get(slot.label);
|
|
1637
|
-
if (group === void 0) continue;
|
|
1638
|
-
usedSwapLabels.add(slot.label);
|
|
1639
|
-
entries.push({ kind: "swap", group });
|
|
1640
|
-
}
|
|
1641
|
-
for (const method of paymentMethods2) {
|
|
1642
|
-
if (usedMethodIds.has(method.id)) continue;
|
|
1643
|
-
entries.push({ kind: "method", method });
|
|
1644
|
-
}
|
|
1645
|
-
for (const group of swapGroups) {
|
|
1646
|
-
const key = group.label.trim().toUpperCase();
|
|
1647
|
-
if (usedSwapLabels.has(key)) continue;
|
|
1648
|
-
entries.push({ kind: "swap", group });
|
|
1649
|
-
}
|
|
1650
|
-
return entries;
|
|
1651
|
-
}
|
|
1652
|
-
function swapPickerKey(label) {
|
|
1653
|
-
return `swap:${label.trim().toUpperCase()}`;
|
|
1654
|
-
}
|
|
1655
|
-
function parseMethodPickerKey(key) {
|
|
1656
|
-
if (!key.startsWith("method:")) return null;
|
|
1657
|
-
return { kind: "method", methodId: key.slice("method:".length) };
|
|
1658
|
-
}
|
|
1659
|
-
function parseSwapPickerKey(key) {
|
|
1660
|
-
if (!key.startsWith("swap:")) return null;
|
|
1661
|
-
return { kind: "swap", label: key.slice("swap:".length) };
|
|
1662
|
-
}
|
|
1663
|
-
function paymentAccentId(labelOrMethodId) {
|
|
1664
|
-
const key = labelOrMethodId.trim().toLowerCase();
|
|
1665
|
-
if (key === "bitcoin" || key === "btc") return "bitcoin";
|
|
1666
|
-
if (key === "usdt") return "usdt";
|
|
1667
|
-
if (key === "usdc") return "usdc";
|
|
1668
|
-
if (key === "sol" || key === "solana") return "sol";
|
|
1669
|
-
if (key === "eth" || key === "ethereum") return "eth";
|
|
1670
|
-
return "default";
|
|
1671
|
-
}
|
|
1672
|
-
var assetActiveClassByAccent = {
|
|
1673
|
-
bitcoin: orClasses.methodCardActiveBitcoin,
|
|
1674
|
-
usdt: orClasses.methodCardActiveUsdt,
|
|
1675
|
-
usdc: orClasses.methodCardActiveUsdc,
|
|
1676
|
-
sol: orClasses.methodCardActiveSol,
|
|
1677
|
-
eth: orClasses.methodCardActiveEth,
|
|
1678
|
-
default: orClasses.methodCardActiveDefault
|
|
1679
|
-
};
|
|
1680
|
-
var networkActiveClassByAccent = {
|
|
1681
|
-
usdt: orClasses.methodNetworkButtonActiveUsdt,
|
|
1682
|
-
usdc: orClasses.methodNetworkButtonActiveUsdc,
|
|
1683
|
-
default: orClasses.methodNetworkButtonActiveDefault
|
|
1684
|
-
};
|
|
1685
|
-
function assetButtonClasses(options) {
|
|
1686
|
-
const base = options.disabled ? orClasses.methodCardUnavailable : orClasses.methodCardReady;
|
|
1687
|
-
if (!options.selected || options.disabled) return base;
|
|
1688
|
-
return `${base} ${assetActiveClassByAccent[options.accent]}`;
|
|
1689
|
-
}
|
|
1690
|
-
function networkButtonClasses(options) {
|
|
1691
|
-
if (options.disabled) return orClasses.methodNetworkButtonUnavailable;
|
|
1692
|
-
if (!options.selected) return orClasses.methodNetworkButton;
|
|
1693
|
-
const accent = options.accent === "usdt" || options.accent === "usdc" ? options.accent : "default";
|
|
1694
|
-
return `${orClasses.methodNetworkButton} ${networkActiveClassByAccent[accent]}`;
|
|
1695
|
-
}
|
|
1696
|
-
function networkMobileRevealClasses(accent) {
|
|
1697
|
-
if (accent === "usdt") return orClasses.methodNetworkRevealMobileUsdt;
|
|
1698
|
-
if (accent === "usdc") return orClasses.methodNetworkRevealMobileUsdc;
|
|
1699
|
-
return orClasses.methodNetworkRevealMobile;
|
|
1700
|
-
}
|
|
1701
|
-
function networkCheckClasses(accent) {
|
|
1702
|
-
return accent === "usdc" ? orClasses.methodNetworkCheckUsdc : orClasses.methodNetworkCheck;
|
|
1703
|
-
}
|
|
1704
|
-
function networkSummaryIconClasses(accent) {
|
|
1705
|
-
return accent === "usdc" ? orClasses.methodNetworkSummaryIconUsdc : orClasses.methodNetworkSummaryIcon;
|
|
1721
|
+
return value;
|
|
1706
1722
|
}
|
|
1707
|
-
function
|
|
1708
|
-
|
|
1723
|
+
function requiredSafeInteger(value, fieldName) {
|
|
1724
|
+
const integer = optionalSafeInteger(value, fieldName);
|
|
1725
|
+
if (integer === void 0) {
|
|
1726
|
+
throw new TypeError(`OpenReceive checkout response requires ${fieldName}.`);
|
|
1727
|
+
}
|
|
1728
|
+
return integer;
|
|
1709
1729
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1730
|
+
|
|
1731
|
+
// src/internal/checkout-log.ts
|
|
1732
|
+
import { compact } from "@openreceive/core";
|
|
1733
|
+
function checkoutLogFields(state) {
|
|
1734
|
+
return compact({
|
|
1735
|
+
checkout_id: state.checkout_id,
|
|
1736
|
+
reference: state.reference,
|
|
1737
|
+
invoice_id: state.invoice_id,
|
|
1738
|
+
payment_hash: state.payment_hash,
|
|
1739
|
+
amount_msats: state.amount_msats,
|
|
1740
|
+
transaction_state: state.transaction_state,
|
|
1741
|
+
workflow_state: state.workflow_state,
|
|
1742
|
+
phase: state.phase,
|
|
1743
|
+
expires_in_seconds: state.expires_in_seconds,
|
|
1744
|
+
settled: state.settled,
|
|
1745
|
+
paid: state.paid,
|
|
1746
|
+
rail: state.rail,
|
|
1747
|
+
...swapAuditLogFields(state.swap)
|
|
1748
|
+
});
|
|
1712
1749
|
}
|
|
1713
|
-
function
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
const match = options.nextGroup.options.find(
|
|
1729
|
-
(option) => option.network_label === previousOption.network_label && option.available !== false
|
|
1730
|
-
);
|
|
1731
|
-
return match?.pay_in_asset;
|
|
1750
|
+
function swapAuditLogFields(swap) {
|
|
1751
|
+
if (swap === void 0) return {};
|
|
1752
|
+
return compact({
|
|
1753
|
+
attempt_id: swap.attempt_id,
|
|
1754
|
+
provider: swap.provider,
|
|
1755
|
+
provider_order_id: swap.provider_order_id,
|
|
1756
|
+
pay_in_asset: swap.pay_in_asset,
|
|
1757
|
+
provider_state: swap.provider_state,
|
|
1758
|
+
attention: swap.attention,
|
|
1759
|
+
attention_reason: swap.attention_reason,
|
|
1760
|
+
refund_reason: swap.refund_reason,
|
|
1761
|
+
refund_tx_id: swap.refund_tx_id,
|
|
1762
|
+
deposit_tx_id: swap.deposit_tx_id,
|
|
1763
|
+
payout_tx_id: swap.payout_tx_id
|
|
1764
|
+
});
|
|
1732
1765
|
}
|
|
1733
|
-
function
|
|
1734
|
-
|
|
1735
|
-
const
|
|
1736
|
-
if (
|
|
1737
|
-
const
|
|
1738
|
-
|
|
1766
|
+
function emitBrowserSwapTransition(logger, previous, next) {
|
|
1767
|
+
const previousSwap = previous?.swap;
|
|
1768
|
+
const nextSwap = next.swap;
|
|
1769
|
+
if (nextSwap === void 0) return;
|
|
1770
|
+
const previousState = previousSwap?.provider_state;
|
|
1771
|
+
const nextState = nextSwap.provider_state;
|
|
1772
|
+
const previousAttention = previousSwap?.attention_reason;
|
|
1773
|
+
const nextAttention = nextSwap.attention_reason;
|
|
1774
|
+
const previousSettled = previous?.settled === true || previous?.paid === true;
|
|
1775
|
+
const nextSettled = next.settled === true || next.paid === true;
|
|
1776
|
+
const stateChanged = previousState !== nextState;
|
|
1777
|
+
const attentionChanged = previousAttention !== nextAttention;
|
|
1778
|
+
const settlementChanged = previousSettled !== nextSettled;
|
|
1779
|
+
if (!stateChanged && !attentionChanged && !settlementChanged) return;
|
|
1780
|
+
const level = nextState === "attention" || nextSwap.attention === true ? "warn" : nextState === "refund_required" || nextState === "refund_pending" || nextState === "refunded" || nextState === "failed" || nextState === "expired" || settlementChanged ? "info" : "debug";
|
|
1781
|
+
emitBrowserLog(
|
|
1782
|
+
logger,
|
|
1783
|
+
level,
|
|
1784
|
+
"swap.state.changed",
|
|
1785
|
+
"Swap attempt state changed in checkout UI.",
|
|
1786
|
+
{
|
|
1787
|
+
...checkoutLogFields(next),
|
|
1788
|
+
previous_provider_state: previousState,
|
|
1789
|
+
previous_settled: previousSettled,
|
|
1790
|
+
wallet_settled: nextSettled,
|
|
1791
|
+
ui_label: nextSettled ? "Payment complete" : getSwapProviderStateLabel(nextState)
|
|
1792
|
+
}
|
|
1739
1793
|
);
|
|
1740
|
-
return entry?.kind === "swap" ? entry.group : void 0;
|
|
1741
1794
|
}
|
|
1742
|
-
function
|
|
1743
|
-
const
|
|
1744
|
-
if (
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
const { [groupKey]: _removed, ...rest } = options.selectedNetworks;
|
|
1756
|
-
return rest;
|
|
1795
|
+
function emitBrowserLog(logger, level, event, message, fields = {}) {
|
|
1796
|
+
const sink = resolveBrowserLogger(logger);
|
|
1797
|
+
if (sink === void 0) return;
|
|
1798
|
+
try {
|
|
1799
|
+
sink(
|
|
1800
|
+
sanitizeBrowserLogEntry({
|
|
1801
|
+
level,
|
|
1802
|
+
event,
|
|
1803
|
+
message,
|
|
1804
|
+
...fields
|
|
1805
|
+
})
|
|
1806
|
+
);
|
|
1807
|
+
} catch {
|
|
1757
1808
|
}
|
|
1758
|
-
return { ...options.selectedNetworks, [groupKey]: preserved };
|
|
1759
1809
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1810
|
+
|
|
1811
|
+
// src/internal/checkout-state.ts
|
|
1812
|
+
function selectCheckoutDisplayInvoice(snapshot) {
|
|
1813
|
+
if (snapshot.active !== void 0 && snapshot.active.rail !== "checkout_lock") {
|
|
1814
|
+
return snapshot.active;
|
|
1815
|
+
}
|
|
1816
|
+
const nonLock = snapshot.invoices.filter((invoice) => invoice.rail !== "checkout_lock");
|
|
1817
|
+
const withBolt11 = nonLock.filter(
|
|
1818
|
+
(invoice) => typeof invoice.invoice === "string" && invoice.invoice.length > 0
|
|
1819
|
+
);
|
|
1820
|
+
const settledBolt11 = withBolt11.find((invoice) => invoice.transaction_state === "settled");
|
|
1821
|
+
if (settledBolt11 !== void 0) return settledBolt11;
|
|
1822
|
+
if (withBolt11[0] !== void 0) return withBolt11[0];
|
|
1823
|
+
const settledSwap = nonLock.find((invoice) => invoice.transaction_state === "settled");
|
|
1824
|
+
return settledSwap ?? nonLock[0];
|
|
1763
1825
|
}
|
|
1764
|
-
function
|
|
1765
|
-
|
|
1766
|
-
const range = option.minimum_pay_amount === void 0 ? void 0 : option.maximum_pay_amount === void 0 ? checkoutLabels.swapUnavailableMinimumOnly.replace("{minimum}", option.minimum_pay_amount).replace("{asset}", option.label) : checkoutLabels.swapUnavailableRange.replace("{minimum}", option.minimum_pay_amount).replace("{maximum}", option.maximum_pay_amount).replace("{asset}", option.label);
|
|
1767
|
-
return {
|
|
1768
|
-
title: checkoutLabels.swapUnavailableTitle.replace("{asset}", option.label),
|
|
1769
|
-
detail,
|
|
1770
|
-
...range === void 0 ? {} : { range },
|
|
1771
|
-
hint: checkoutLabels.swapUnavailableHint
|
|
1772
|
-
};
|
|
1826
|
+
function isPaidCheckoutSnapshot(snapshot) {
|
|
1827
|
+
return snapshot.status === "paid";
|
|
1773
1828
|
}
|
|
1774
|
-
function
|
|
1775
|
-
|
|
1776
|
-
if (option.unavailable_reason === "amount_too_small") {
|
|
1777
|
-
const fiat = checkout === void 0 ? void 0 : formatSwapLimit(checkout, option.minimum_invoice_amount_msats, "ceil");
|
|
1778
|
-
if (fiat !== void 0) return `Minimum amount ${fiat}`;
|
|
1779
|
-
if (option.minimum_pay_amount !== void 0) {
|
|
1780
|
-
return `Minimum ${option.minimum_pay_amount} ${option.label}`;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
if (option.unavailable_reason === "amount_too_large") {
|
|
1784
|
-
const fiat = checkout === void 0 ? void 0 : formatSwapLimit(checkout, option.maximum_invoice_amount_msats, "floor");
|
|
1785
|
-
if (fiat !== void 0) return `Maximum amount ${fiat}`;
|
|
1786
|
-
if (option.maximum_pay_amount !== void 0) {
|
|
1787
|
-
return `Maximum ${option.maximum_pay_amount} ${option.label}`;
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
return option.unavailable_message;
|
|
1829
|
+
function isReusableLightningInvoice(expiresAt, now) {
|
|
1830
|
+
return expiresAt - resolveNow(now) > OPENRECEIVE_LIGHTNING_REUSE_BUFFER_SECONDS;
|
|
1791
1831
|
}
|
|
1792
|
-
function
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1832
|
+
function createCheckoutState(snapshot, options = {}) {
|
|
1833
|
+
const invoiceRecord = selectCheckoutDisplayInvoice(snapshot);
|
|
1834
|
+
if (invoiceRecord === void 0) {
|
|
1835
|
+
return normalizeCheckoutState(
|
|
1836
|
+
{
|
|
1837
|
+
checkout_id: snapshot.checkout_id,
|
|
1838
|
+
reference: snapshot.reference,
|
|
1839
|
+
invoice_id: "",
|
|
1840
|
+
invoice: "",
|
|
1841
|
+
rail: "checkout_lock",
|
|
1842
|
+
lightning_uri: "",
|
|
1843
|
+
...snapshot.amount_msats === void 0 ? {} : { amount_msats: snapshot.amount_msats },
|
|
1844
|
+
...snapshot.fiat !== void 0 ? { fiat_quote: { fiat: snapshot.fiat } } : {},
|
|
1845
|
+
transaction_state: "pending",
|
|
1846
|
+
workflow_state: "invoice_created",
|
|
1847
|
+
paid: false
|
|
1848
|
+
},
|
|
1849
|
+
resolveNow(options.now)
|
|
1850
|
+
);
|
|
1807
1851
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
const
|
|
1816
|
-
|
|
1817
|
-
|
|
1852
|
+
const invoice = invoiceRecord;
|
|
1853
|
+
const bolt11 = typeof invoice.invoice === "string" && invoice.invoice.length > 0 ? invoice.invoice : invoice.rail === "swap" || invoice.rail === "checkout_lock" ? "" : missingDisplayInvoice();
|
|
1854
|
+
const paid = isPaidCheckoutSnapshot(snapshot);
|
|
1855
|
+
const settledAt = snapshot.paid_at ?? invoice.settled_at;
|
|
1856
|
+
const transactionState = paid ? "settled" : invoice.transaction_state ?? "pending";
|
|
1857
|
+
const workflowState = paid ? "paid" : invoice.workflow_state ?? "invoice_created";
|
|
1858
|
+
const fiatQuote = invoice.fiat_quote === null && snapshot.fiat !== void 0 ? { fiat: snapshot.fiat } : invoice.fiat_quote ?? (snapshot.fiat === void 0 ? void 0 : { fiat: snapshot.fiat });
|
|
1859
|
+
const state = normalizeCheckoutState(
|
|
1860
|
+
{
|
|
1861
|
+
checkout_id: snapshot.checkout_id,
|
|
1862
|
+
reference: snapshot.reference,
|
|
1863
|
+
invoice_id: invoice.invoice_id,
|
|
1864
|
+
invoice: bolt11,
|
|
1865
|
+
rail: invoice.rail,
|
|
1866
|
+
// G4 divergence (d): the swap rail gets NO `lightning:` URI even when the
|
|
1867
|
+
// attempt happens to carry a bolt11. A swap is paid at the deposit
|
|
1868
|
+
// address, and a wallet-jump link on that screen sends the payer down the
|
|
1869
|
+
// wrong rail. `invoice` above still holds the bolt11 for anything that
|
|
1870
|
+
// legitimately needs it.
|
|
1871
|
+
lightning_uri: invoice.rail === "checkout_lock" || invoice.rail === "swap" || bolt11 === "" ? "" : createLightningUri(bolt11),
|
|
1872
|
+
...invoice.payment_hash === void 0 ? {} : { payment_hash: invoice.payment_hash },
|
|
1873
|
+
// G4 divergence (b): the checkout's amount is what is owed, so an attempt
|
|
1874
|
+
// that omits its own amount still shows one rather than no amount at all.
|
|
1875
|
+
amount_msats: invoice.amount_msats ?? snapshot.amount_msats,
|
|
1876
|
+
...fiatQuote === void 0 ? {} : { fiat_quote: fiatQuote },
|
|
1877
|
+
transaction_state: transactionState,
|
|
1878
|
+
workflow_state: workflowState,
|
|
1879
|
+
...invoice.expires_at === void 0 ? {} : { expires_at: invoice.expires_at },
|
|
1880
|
+
...settledAt === void 0 ? {} : { settled_at: settledAt },
|
|
1881
|
+
...invoice.swap === void 0 ? {} : { swap: invoice.swap },
|
|
1882
|
+
paid
|
|
1883
|
+
},
|
|
1884
|
+
resolveNow(options.now)
|
|
1885
|
+
);
|
|
1886
|
+
const source = options.source ?? "create";
|
|
1887
|
+
if (source === "create") {
|
|
1888
|
+
emitBrowserLog(
|
|
1889
|
+
options.logger,
|
|
1890
|
+
"info",
|
|
1891
|
+
"checkout.state.created",
|
|
1892
|
+
"Created checkout state from order snapshot.",
|
|
1893
|
+
checkoutLogFields(state)
|
|
1894
|
+
);
|
|
1895
|
+
} else if (source === "refresh") {
|
|
1896
|
+
emitBrowserLog(
|
|
1897
|
+
options.logger,
|
|
1898
|
+
"debug",
|
|
1899
|
+
"checkout.state.refreshed",
|
|
1900
|
+
"Refreshed checkout state from order status.",
|
|
1901
|
+
checkoutLogFields(state)
|
|
1902
|
+
);
|
|
1903
|
+
emitBrowserSwapTransition(options.logger, options.previousState, state);
|
|
1818
1904
|
}
|
|
1819
|
-
return
|
|
1905
|
+
return state;
|
|
1820
1906
|
}
|
|
1821
|
-
function
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1907
|
+
function createCheckoutSnapshotFromInvoice(attempt, identity = {}) {
|
|
1908
|
+
const rail = requiredInvoiceRail(attempt.rail);
|
|
1909
|
+
const invoiceId = requiredString(attempt.invoice_id, "invoice_id");
|
|
1910
|
+
const invoice = {
|
|
1911
|
+
invoice_id: invoiceId,
|
|
1912
|
+
invoice: attempt.invoice,
|
|
1913
|
+
rail,
|
|
1914
|
+
...attempt.payment_hash === void 0 ? {} : { payment_hash: attempt.payment_hash },
|
|
1915
|
+
...attempt.amount_msats === void 0 ? {} : { amount_msats: attempt.amount_msats },
|
|
1916
|
+
...attempt.fiat_quote === void 0 ? {} : { fiat_quote: attempt.fiat_quote },
|
|
1917
|
+
...attempt.transaction_state === void 0 ? {} : { transaction_state: attempt.transaction_state },
|
|
1918
|
+
...attempt.workflow_state === void 0 ? {} : { workflow_state: attempt.workflow_state },
|
|
1919
|
+
...attempt.expires_at === void 0 ? {} : { expires_at: attempt.expires_at },
|
|
1920
|
+
...attempt.settled_at === void 0 ? {} : { settled_at: attempt.settled_at },
|
|
1921
|
+
...attempt.swap === void 0 ? {} : { swap: attempt.swap }
|
|
1922
|
+
};
|
|
1923
|
+
const paid = attempt.transaction_state === "settled";
|
|
1824
1924
|
return {
|
|
1825
|
-
|
|
1826
|
-
|
|
1925
|
+
checkout_id: identity.checkout_id ?? invoiceId,
|
|
1926
|
+
reference: identity.reference ?? invoiceId,
|
|
1927
|
+
status: paid ? "paid" : "open",
|
|
1928
|
+
...attempt.settled_at === void 0 ? {} : { paid_at: attempt.settled_at },
|
|
1929
|
+
amount_msats: attempt.amount_msats ?? 0,
|
|
1930
|
+
active: paid ? void 0 : invoice,
|
|
1931
|
+
invoices: [invoice]
|
|
1827
1932
|
};
|
|
1828
1933
|
}
|
|
1829
|
-
function
|
|
1934
|
+
function refreshCheckoutState(state, options = {}) {
|
|
1935
|
+
return createCheckoutState(snapshotFromCheckoutState(state), {
|
|
1936
|
+
...options,
|
|
1937
|
+
source: options.source ?? "countdown",
|
|
1938
|
+
previousState: options.previousState ?? state
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
function shouldCheckoutShowWaiting(state, options = {}) {
|
|
1942
|
+
if (state.terminal || state.settled) return false;
|
|
1943
|
+
if (state.expires_at === void 0) return true;
|
|
1944
|
+
return state.expires_at > resolveNow(options.now);
|
|
1945
|
+
}
|
|
1946
|
+
function createCheckoutStatusModel(source, options = {}) {
|
|
1947
|
+
const isCheckoutState = source !== void 0 && "invoice_id" in source && "invoice" in source;
|
|
1948
|
+
const phase = source?.phase ?? "invoice_created";
|
|
1949
|
+
const expiresInSeconds = source?.expires_in_seconds;
|
|
1950
|
+
const displayPhase = phase !== "settled" && phase !== "failed" && phase !== "cancelled" && expiresInSeconds === 0 ? "expired" : phase;
|
|
1951
|
+
const statusText = getPaymentStatusText(displayPhase);
|
|
1830
1952
|
return {
|
|
1831
|
-
|
|
1832
|
-
|
|
1953
|
+
phase: displayPhase,
|
|
1954
|
+
waiting: displayPhase === "expired" ? false : source === void 0 ? false : isCheckoutState ? shouldCheckoutShowWaiting(source, options) : source.waiting ?? false,
|
|
1955
|
+
title: statusText.title,
|
|
1956
|
+
detail: statusText.detail,
|
|
1957
|
+
countdownPrefix: checkoutLabels.countdownPrefix,
|
|
1958
|
+
// No countdown once settled — the invoice's remaining lifetime is meaningless
|
|
1959
|
+
// to a payer whose payment already landed.
|
|
1960
|
+
...expiresInSeconds === void 0 || displayPhase === "expired" || displayPhase === "settled" ? {} : {
|
|
1961
|
+
expires_in_seconds: expiresInSeconds,
|
|
1962
|
+
countdownLabel: formatCountdown(expiresInSeconds)
|
|
1963
|
+
}
|
|
1833
1964
|
};
|
|
1834
1965
|
}
|
|
1835
|
-
function
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1966
|
+
function normalizeCheckoutState(state, now) {
|
|
1967
|
+
const {
|
|
1968
|
+
phase: _phase,
|
|
1969
|
+
settled: _settled,
|
|
1970
|
+
terminal: _terminal,
|
|
1971
|
+
expires_in_seconds: _expiresInSeconds,
|
|
1972
|
+
// Labels are derived, never carried: a caller handing back a previous state
|
|
1973
|
+
// with one field changed must not keep that state's stale labels.
|
|
1974
|
+
amountLabel: _amountLabel,
|
|
1975
|
+
fiatLabel: _fiatLabel,
|
|
1976
|
+
paymentHashLabel: _paymentHashLabel,
|
|
1977
|
+
...base
|
|
1978
|
+
} = state;
|
|
1979
|
+
const statePhase = getCheckoutPhase(state.transaction_state, state.workflow_state);
|
|
1980
|
+
const expiresInSeconds = base.expires_at === void 0 || now === void 0 ? void 0 : Math.max(0, base.expires_at - now);
|
|
1981
|
+
const phase = statePhase === "invoice_created" || statePhase === "verifying" ? expiresInSeconds === 0 ? "expired" : statePhase : statePhase;
|
|
1982
|
+
const settled = base.paid || base.transaction_state === "settled";
|
|
1842
1983
|
return {
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1984
|
+
...base,
|
|
1985
|
+
...deriveCheckoutStateLabels(base),
|
|
1986
|
+
phase,
|
|
1987
|
+
settled,
|
|
1988
|
+
// A swap that expired, refunded, failed, or needs support review is over:
|
|
1989
|
+
// the shadow invoice behind it will not be paid, so watchers stop polling
|
|
1990
|
+
// both /payments/check and the provider instead of asking forever.
|
|
1991
|
+
terminal: isTerminalPhase(phase) || !settled && isTerminalSwapProviderState(base.swap?.provider_state),
|
|
1992
|
+
...expiresInSeconds === void 0 ? {} : { expires_in_seconds: expiresInSeconds }
|
|
1847
1993
|
};
|
|
1848
1994
|
}
|
|
1849
|
-
function
|
|
1850
|
-
|
|
1851
|
-
case "select_method": {
|
|
1852
|
-
return {
|
|
1853
|
-
...selection,
|
|
1854
|
-
selectedMethod: action.method,
|
|
1855
|
-
selectedBitcoinRoute: action.method === "bitcoin" ? selection.selectedBitcoinRoute ?? getDefaultBitcoinRoute() : selection.selectedBitcoinRoute
|
|
1856
|
-
};
|
|
1857
|
-
}
|
|
1858
|
-
case "change_method": {
|
|
1859
|
-
return {
|
|
1860
|
-
...selection,
|
|
1861
|
-
selectedMethod: null,
|
|
1862
|
-
selectedBitcoinRoute: null
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
case "change_route": {
|
|
1866
|
-
if (selection.selectedMethod === "bitcoin") {
|
|
1867
|
-
return {
|
|
1868
|
-
...selection,
|
|
1869
|
-
selectedBitcoinRoute: null
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
return selection;
|
|
1873
|
-
}
|
|
1874
|
-
case "select_route": {
|
|
1875
|
-
if (selection.selectedMethod === "bitcoin") {
|
|
1876
|
-
return {
|
|
1877
|
-
...selection,
|
|
1878
|
-
selectedBitcoinRoute: action.route
|
|
1879
|
-
};
|
|
1880
|
-
}
|
|
1881
|
-
return selection;
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1995
|
+
function missingDisplayInvoice() {
|
|
1996
|
+
throw new TypeError("OpenReceive checkout requires a display Lightning invoice.");
|
|
1884
1997
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
this.selection = options.selection ?? createPaymentWizardSelection();
|
|
1891
|
-
}
|
|
1892
|
-
getSelection() {
|
|
1893
|
-
return this.selection;
|
|
1998
|
+
function getCheckoutPhase(transactionState, workflowState) {
|
|
1999
|
+
if (workflowState === "cancelled") return "cancelled";
|
|
2000
|
+
if (transactionState === "settled") return "settled";
|
|
2001
|
+
if (transactionState === "expired" || workflowState === "expired") {
|
|
2002
|
+
return "expired";
|
|
1894
2003
|
}
|
|
1895
|
-
|
|
1896
|
-
return
|
|
2004
|
+
if (transactionState === "failed" || workflowState === "failed") {
|
|
2005
|
+
return "failed";
|
|
1897
2006
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
this.selection = next;
|
|
1901
|
-
this.options.onSelection?.(next);
|
|
1902
|
-
return next;
|
|
2007
|
+
if (workflowState === "verifying") {
|
|
2008
|
+
return "verifying";
|
|
1903
2009
|
}
|
|
1904
|
-
|
|
1905
|
-
return this.update({
|
|
1906
|
-
type: "select_method",
|
|
1907
|
-
method
|
|
1908
|
-
});
|
|
1909
|
-
}
|
|
1910
|
-
changeMethod() {
|
|
1911
|
-
return this.update({
|
|
1912
|
-
type: "change_method"
|
|
1913
|
-
});
|
|
1914
|
-
}
|
|
1915
|
-
selectRoute(route) {
|
|
1916
|
-
return this.update({
|
|
1917
|
-
type: "select_route",
|
|
1918
|
-
route
|
|
1919
|
-
});
|
|
1920
|
-
}
|
|
1921
|
-
};
|
|
1922
|
-
function createPaymentWizardController(options = {}) {
|
|
1923
|
-
return new BrowserPaymentWizardController(options);
|
|
2010
|
+
return "invoice_created";
|
|
1924
2011
|
}
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
var BROWSER_LOG_LEVEL_ORDER = {
|
|
1928
|
-
debug: 10,
|
|
1929
|
-
info: 20,
|
|
1930
|
-
warn: 30,
|
|
1931
|
-
error: 40
|
|
1932
|
-
};
|
|
1933
|
-
var BROWSER_LOG_LEVEL_ALIASES = {
|
|
1934
|
-
debug: "debug",
|
|
1935
|
-
info: "info",
|
|
1936
|
-
warn: "warn",
|
|
1937
|
-
warning: "warn",
|
|
1938
|
-
error: "error"
|
|
1939
|
-
};
|
|
1940
|
-
function browserLogLevelOrder(level) {
|
|
1941
|
-
return BROWSER_LOG_LEVEL_ORDER[level];
|
|
2012
|
+
function isTerminalPhase(phase) {
|
|
2013
|
+
return phase === "expired" || phase === "failed" || phase === "cancelled";
|
|
1942
2014
|
}
|
|
1943
|
-
function
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2015
|
+
function snapshotFromCheckoutState(state) {
|
|
2016
|
+
const invoice = {
|
|
2017
|
+
invoice_id: state.invoice_id,
|
|
2018
|
+
invoice: state.invoice,
|
|
2019
|
+
rail: state.rail,
|
|
2020
|
+
...state.payment_hash === void 0 ? {} : { payment_hash: state.payment_hash },
|
|
2021
|
+
...state.amount_msats === void 0 ? {} : { amount_msats: state.amount_msats },
|
|
2022
|
+
...state.fiat_quote === void 0 ? {} : { fiat_quote: state.fiat_quote },
|
|
2023
|
+
transaction_state: state.transaction_state,
|
|
2024
|
+
workflow_state: state.workflow_state,
|
|
2025
|
+
...state.expires_at === void 0 ? {} : { expires_at: state.expires_at },
|
|
2026
|
+
...state.settled_at === void 0 ? {} : { settled_at: state.settled_at },
|
|
2027
|
+
...state.swap === void 0 ? {} : { swap: state.swap }
|
|
2028
|
+
};
|
|
2029
|
+
return {
|
|
2030
|
+
checkout_id: state.checkout_id,
|
|
2031
|
+
reference: state.reference,
|
|
2032
|
+
status: state.paid ? "paid" : state.terminal ? "expired" : "open",
|
|
2033
|
+
...state.settled_at === void 0 ? {} : { paid_at: state.settled_at },
|
|
2034
|
+
amount_msats: state.amount_msats ?? 0,
|
|
2035
|
+
active: state.paid ? void 0 : invoice,
|
|
2036
|
+
invoices: [invoice]
|
|
2037
|
+
};
|
|
1948
2038
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
2039
|
+
|
|
2040
|
+
// src/internal/checkout-merge.ts
|
|
2041
|
+
function findReusableLightningInvoice(snapshot, now) {
|
|
2042
|
+
return snapshot.invoices.find(
|
|
2043
|
+
(invoice) => invoice.rail === "lightning" && typeof invoice.invoice === "string" && invoice.invoice.length > 0 && invoice.expires_at !== void 0 && isReusableLightningInvoice(invoice.expires_at, now)
|
|
2044
|
+
);
|
|
1951
2045
|
}
|
|
1952
|
-
function
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
source.LOG_LEVEL ?? source.VITE_LOG_LEVEL ?? source.NEXT_PUBLIC_LOG_LEVEL
|
|
1956
|
-
);
|
|
1957
|
-
}
|
|
1958
|
-
const fromGlobal = parseBrowserLogLevel(readGlobalLogLevel());
|
|
1959
|
-
if (fromGlobal !== void 0) return fromGlobal;
|
|
1960
|
-
const metaEnv = readImportMetaEnv();
|
|
1961
|
-
const parsedMeta = parseBrowserLogLevel(
|
|
1962
|
-
metaEnv?.LOG_LEVEL ?? metaEnv?.VITE_LOG_LEVEL ?? metaEnv?.NEXT_PUBLIC_LOG_LEVEL
|
|
2046
|
+
function mergeAttemptIntoSnapshot(invoice, base) {
|
|
2047
|
+
const withoutSame = base.invoices.filter(
|
|
2048
|
+
(entry) => entry.invoice_id !== invoice.invoice_id && entry.rail !== "checkout_lock"
|
|
1963
2049
|
);
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2050
|
+
return {
|
|
2051
|
+
...base,
|
|
2052
|
+
checkout_id: invoice.invoice_id,
|
|
2053
|
+
active: invoice,
|
|
2054
|
+
invoices: [invoice, ...withoutSame],
|
|
2055
|
+
...invoice.amount_msats === void 0 ? {} : { amount_msats: invoice.amount_msats }
|
|
2056
|
+
};
|
|
1967
2057
|
}
|
|
1968
|
-
function
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2058
|
+
function mergeAttemptIntoCheckout(invoice, previous, reference) {
|
|
2059
|
+
const base = previous ?? {
|
|
2060
|
+
checkout_id: invoice.invoice_id,
|
|
2061
|
+
reference,
|
|
2062
|
+
status: "open",
|
|
2063
|
+
amount_msats: invoice.amount_msats ?? 0,
|
|
2064
|
+
invoices: []
|
|
2065
|
+
};
|
|
2066
|
+
return mergeAttemptIntoSnapshot(invoice, base);
|
|
1975
2067
|
}
|
|
1976
|
-
function
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2068
|
+
function mergeMintedCheckout(checkout, previous) {
|
|
2069
|
+
const minted = selectCheckoutDisplayInvoice(checkout) ?? checkout.active;
|
|
2070
|
+
if (minted === void 0) {
|
|
2071
|
+
return {
|
|
2072
|
+
...checkout,
|
|
2073
|
+
...previous?.payment_methods === void 0 ? {} : { payment_methods: previous.payment_methods }
|
|
2074
|
+
};
|
|
1981
2075
|
}
|
|
2076
|
+
if (previous === void 0) return mergeAttemptIntoSnapshot(minted, checkout);
|
|
2077
|
+
return mergeAttemptIntoSnapshot(minted, {
|
|
2078
|
+
...previous,
|
|
2079
|
+
...checkout,
|
|
2080
|
+
invoices: previous.invoices,
|
|
2081
|
+
payment_methods: previous.payment_methods ?? checkout.payment_methods
|
|
2082
|
+
});
|
|
1982
2083
|
}
|
|
1983
2084
|
|
|
1984
|
-
// src/internal/
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
const method = level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "info";
|
|
1994
|
-
const sink = target[method] ?? target.log;
|
|
1995
|
-
const {
|
|
1996
|
-
level: _level,
|
|
1997
|
-
event: _event,
|
|
1998
|
-
message: cleanMessage,
|
|
1999
|
-
...cleanFields
|
|
2000
|
-
} = sanitizeBrowserLogEntry({ level, event, message, ...fields });
|
|
2001
|
-
sink.call(
|
|
2002
|
-
target,
|
|
2003
|
-
`[${now().toISOString()}] ${level.toUpperCase()} [${prefix}] ${event}: ${cleanMessage}`,
|
|
2004
|
-
cleanFields
|
|
2005
|
-
);
|
|
2085
|
+
// src/internal/checkout-transport.ts
|
|
2086
|
+
import { isRecord, nonEmptyString as nonEmptyString2, recordOrEmpty } from "@openreceive/core";
|
|
2087
|
+
|
|
2088
|
+
// src/internal/request-headers.ts
|
|
2089
|
+
function requestHeaders(host) {
|
|
2090
|
+
return {
|
|
2091
|
+
"Content-Type": "application/json",
|
|
2092
|
+
...csrfTokenHeader(),
|
|
2093
|
+
...host
|
|
2006
2094
|
};
|
|
2007
2095
|
}
|
|
2008
|
-
function
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2096
|
+
function csrfTokenHeader() {
|
|
2097
|
+
if (typeof document === "undefined") return {};
|
|
2098
|
+
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
2099
|
+
return token ? { "X-CSRF-Token": token } : {};
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// src/internal/routes.ts
|
|
2103
|
+
function checkoutRoutes(prefix) {
|
|
2104
|
+
if (typeof prefix !== "string") {
|
|
2105
|
+
throw new TypeError(
|
|
2106
|
+
`OpenReceive requires \`prefix\`, the base path the shipped router is mounted at (for example "/openreceive"); every checkout, status and swap route is derived from it. Received ${prefix === null ? "null" : typeof prefix}.`
|
|
2107
|
+
);
|
|
2016
2108
|
}
|
|
2017
|
-
|
|
2109
|
+
const base = prefix.replace(/\/+$/, "");
|
|
2110
|
+
return {
|
|
2111
|
+
checkouts: `${base}/checkouts`,
|
|
2112
|
+
checkoutsPrepare: `${base}/checkouts/prepare`,
|
|
2113
|
+
paymentsCheck: `${base}/payments/check`,
|
|
2114
|
+
swaps: `${base}/swaps`,
|
|
2115
|
+
swapsQuote: `${base}/swaps/quote`,
|
|
2116
|
+
swapsStatus: `${base}/swaps/status`,
|
|
2117
|
+
swapsRefunds: `${base}/swaps/refunds`
|
|
2118
|
+
};
|
|
2018
2119
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2120
|
+
|
|
2121
|
+
// src/internal/checkout-transport.ts
|
|
2122
|
+
var SWAP_STATUS_FAILURE_LIMIT = 3;
|
|
2123
|
+
var BrowserRequestError = class extends Error {
|
|
2124
|
+
status;
|
|
2125
|
+
code;
|
|
2126
|
+
retryable;
|
|
2127
|
+
retryAfterSeconds;
|
|
2128
|
+
constructor(message, options) {
|
|
2129
|
+
super(message);
|
|
2130
|
+
this.name = "BrowserRequestError";
|
|
2131
|
+
this.status = options.status;
|
|
2132
|
+
if (options.code !== void 0) this.code = options.code;
|
|
2133
|
+
if (options.retryable !== void 0) this.retryable = options.retryable;
|
|
2134
|
+
if (options.retryAfterSeconds !== void 0) this.retryAfterSeconds = options.retryAfterSeconds;
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
async function readJsonResponse(response, fallbackMessage) {
|
|
2138
|
+
let body;
|
|
2139
|
+
try {
|
|
2140
|
+
body = await response.json();
|
|
2141
|
+
} catch {
|
|
2142
|
+
if (response.ok) {
|
|
2143
|
+
throw new BrowserRequestError(`${fallbackMessage} The server returned a non-JSON body.`, {
|
|
2144
|
+
status: response.status,
|
|
2145
|
+
retryable: false
|
|
2146
|
+
});
|
|
2029
2147
|
}
|
|
2148
|
+
body = void 0;
|
|
2030
2149
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2150
|
+
if (!response.ok) {
|
|
2151
|
+
const record = body !== null && typeof body === "object" ? body : void 0;
|
|
2152
|
+
const retryAfterRaw = Number(response.headers?.get?.("retry-after") ?? Number.NaN);
|
|
2153
|
+
throw new BrowserRequestError(
|
|
2154
|
+
typeof record?.message === "string" ? record.message : fallbackMessage,
|
|
2155
|
+
{
|
|
2156
|
+
status: response.status,
|
|
2157
|
+
...typeof record?.code === "string" ? { code: record.code } : {},
|
|
2158
|
+
...typeof record?.retryable === "boolean" ? { retryable: record.retryable } : {},
|
|
2159
|
+
...Number.isFinite(retryAfterRaw) && retryAfterRaw > 0 ? { retryAfterSeconds: retryAfterRaw } : {}
|
|
2160
|
+
}
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
return body;
|
|
2043
2164
|
}
|
|
2044
|
-
function
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2165
|
+
function normalizeRequestCheckoutOptions(options) {
|
|
2166
|
+
const reference = nonEmptyString2(options.reference);
|
|
2167
|
+
const metadata = optionalRecord(options.metadata);
|
|
2168
|
+
return {
|
|
2169
|
+
routes: checkoutRoutes(options.prefix),
|
|
2170
|
+
reference: reference ?? "",
|
|
2171
|
+
fetch: options.fetch,
|
|
2172
|
+
headers: options.headers,
|
|
2173
|
+
...options.memo === void 0 ? {} : { memo: options.memo },
|
|
2174
|
+
...metadata === void 0 ? {} : { metadata }
|
|
2048
2175
|
};
|
|
2049
2176
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
if (
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
amount_msats: state.amount_msats,
|
|
2073
|
-
transaction_state: state.transaction_state,
|
|
2074
|
-
workflow_state: state.workflow_state,
|
|
2075
|
-
phase: state.phase,
|
|
2076
|
-
expires_in_seconds: state.expires_in_seconds,
|
|
2077
|
-
settled: state.settled,
|
|
2078
|
-
paid: state.paid,
|
|
2079
|
-
rail: state.rail,
|
|
2080
|
-
...swapAuditLogFields(state.swap)
|
|
2177
|
+
async function requestCheckout(options) {
|
|
2178
|
+
const request = normalizeRequestCheckoutOptions(options);
|
|
2179
|
+
if (request.reference.length === 0) {
|
|
2180
|
+
throw new Error("OpenReceive checkout creation requires reference.");
|
|
2181
|
+
}
|
|
2182
|
+
const fetcher = request.fetch ?? globalThis.fetch;
|
|
2183
|
+
if (fetcher === void 0) {
|
|
2184
|
+
throw new Error("OpenReceive checkout creation requires fetch.");
|
|
2185
|
+
}
|
|
2186
|
+
if (request.memo !== void 0 && request.memo.length > 500) {
|
|
2187
|
+
throw new Error("OpenReceive memo must be 500 characters or fewer.");
|
|
2188
|
+
}
|
|
2189
|
+
const requestBody = {
|
|
2190
|
+
reference: request.reference,
|
|
2191
|
+
...request.memo === void 0 ? {} : { memo: request.memo },
|
|
2192
|
+
...request.metadata === void 0 ? {} : { metadata: structuredClone(request.metadata) }
|
|
2193
|
+
};
|
|
2194
|
+
const headers = request.headers === void 0 ? {} : request.headers;
|
|
2195
|
+
const response = await fetcher(request.routes.checkouts, {
|
|
2196
|
+
method: "POST",
|
|
2197
|
+
headers: requestHeaders(headers),
|
|
2198
|
+
body: JSON.stringify(requestBody)
|
|
2081
2199
|
});
|
|
2200
|
+
const body = await readJsonResponse(response, "Could not create checkout.");
|
|
2201
|
+
const snapshot = checkoutSnapshotFromResponseBody(body);
|
|
2202
|
+
const responseInvoice = snapshot.active;
|
|
2203
|
+
if (isRecord(responseInvoice) && typeof responseInvoice.invoice === "string") {
|
|
2204
|
+
assertDisplayInvoice(responseInvoice.invoice);
|
|
2205
|
+
}
|
|
2206
|
+
return options.previous === void 0 ? snapshot : mergeMintedCheckout(snapshot, options.previous);
|
|
2082
2207
|
}
|
|
2083
|
-
function
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2208
|
+
async function prepareCheckout(options) {
|
|
2209
|
+
const request = normalizeRequestCheckoutOptions(options);
|
|
2210
|
+
if (request.reference.length === 0) {
|
|
2211
|
+
throw new Error("OpenReceive checkout prepare requires reference.");
|
|
2212
|
+
}
|
|
2213
|
+
const fetcher = request.fetch ?? globalThis.fetch;
|
|
2214
|
+
if (fetcher === void 0) {
|
|
2215
|
+
throw new Error("OpenReceive checkout prepare requires fetch.");
|
|
2216
|
+
}
|
|
2217
|
+
const headers = request.headers === void 0 ? {} : request.headers;
|
|
2218
|
+
const response = await fetcher(request.routes.checkoutsPrepare, {
|
|
2219
|
+
method: "POST",
|
|
2220
|
+
headers: requestHeaders(headers),
|
|
2221
|
+
body: JSON.stringify({ reference: request.reference })
|
|
2097
2222
|
});
|
|
2223
|
+
const body = await readJsonResponse(response, "Could not prepare checkout.");
|
|
2224
|
+
return checkoutLockSnapshotFromPrepareBody(body, request.reference);
|
|
2098
2225
|
}
|
|
2099
|
-
function
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
const nextAttention = nextSwap.attention_reason;
|
|
2107
|
-
const previousSettled = previous?.settled === true || previous?.paid === true;
|
|
2108
|
-
const nextSettled = next.settled === true || next.paid === true;
|
|
2109
|
-
const stateChanged = previousState !== nextState;
|
|
2110
|
-
const attentionChanged = previousAttention !== nextAttention;
|
|
2111
|
-
const settlementChanged = previousSettled !== nextSettled;
|
|
2112
|
-
if (!stateChanged && !attentionChanged && !settlementChanged) return;
|
|
2113
|
-
const level = nextState === "attention" || nextSwap.attention === true ? "warn" : nextState === "refund_required" || nextState === "refund_pending" || nextState === "refunded" || nextState === "failed" || nextState === "expired" || settlementChanged ? "info" : "debug";
|
|
2114
|
-
emitBrowserLog(
|
|
2115
|
-
logger,
|
|
2116
|
-
level,
|
|
2117
|
-
"swap.state.changed",
|
|
2118
|
-
"Swap attempt state changed in checkout UI.",
|
|
2119
|
-
{
|
|
2120
|
-
...checkoutLogFields(next),
|
|
2121
|
-
previous_provider_state: previousState,
|
|
2122
|
-
previous_settled: previousSettled,
|
|
2123
|
-
wallet_settled: nextSettled,
|
|
2124
|
-
ui_label: nextSettled ? "Payment complete" : getSwapProviderStateLabel(nextState)
|
|
2226
|
+
function createStatusFetcher(options) {
|
|
2227
|
+
let snapshot = options.snapshot;
|
|
2228
|
+
let swapStatusFailures = 0;
|
|
2229
|
+
const routes = checkoutRoutes(options.prefix);
|
|
2230
|
+
return async (reference) => {
|
|
2231
|
+
if (reference.length === 0) {
|
|
2232
|
+
throw new Error("OpenReceive status refresh requires reference.");
|
|
2125
2233
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2234
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
2235
|
+
if (fetcher === void 0) {
|
|
2236
|
+
throw new Error("OpenReceive status refresh requires fetch.");
|
|
2237
|
+
}
|
|
2238
|
+
const headers = options.headers === void 0 ? {} : options.headers;
|
|
2239
|
+
const activePaymentHash = nonEmptyString2(snapshot.active?.payment_hash) ?? (snapshot.active?.rail === "checkout_lock" ? void 0 : nonEmptyString2(snapshot.active?.invoice_id));
|
|
2240
|
+
if (activePaymentHash === void 0 || !/^[0-9a-f]{64}$/i.test(activePaymentHash)) {
|
|
2241
|
+
return snapshot;
|
|
2242
|
+
}
|
|
2243
|
+
const response = await fetcher(routes.paymentsCheck, {
|
|
2244
|
+
method: "POST",
|
|
2245
|
+
headers: requestHeaders(headers),
|
|
2246
|
+
body: JSON.stringify({
|
|
2247
|
+
reference,
|
|
2248
|
+
payment_hash: activePaymentHash
|
|
2138
2249
|
})
|
|
2250
|
+
});
|
|
2251
|
+
const body = await readJsonResponse(response, "Could not refresh invoice status.");
|
|
2252
|
+
const payment = recordOrEmpty(body);
|
|
2253
|
+
const next = structuredClone(snapshot);
|
|
2254
|
+
if (next.active === void 0) return next;
|
|
2255
|
+
const state = nonEmptyString2(payment.status) ?? "pending";
|
|
2256
|
+
const paidAt = optionalSafeInteger(payment.paid_at, "paid_at");
|
|
2257
|
+
let active = {
|
|
2258
|
+
...next.active,
|
|
2259
|
+
transaction_state: state === "not_found" ? "pending" : state,
|
|
2260
|
+
...paidAt === void 0 ? {} : { settled_at: paidAt }
|
|
2261
|
+
};
|
|
2262
|
+
if (active.rail === "swap" && active.swap !== void 0 && state !== "settled" && // A terminal provider state is final; re-reading it only adds provider load.
|
|
2263
|
+
!isTerminalSwapProviderState(nonEmptyString2(active.swap.provider_state))) {
|
|
2264
|
+
try {
|
|
2265
|
+
const swapResponse = await fetcher(routes.swapsStatus, {
|
|
2266
|
+
method: "POST",
|
|
2267
|
+
headers: requestHeaders(headers),
|
|
2268
|
+
body: JSON.stringify({ reference, payment_hash: activePaymentHash })
|
|
2269
|
+
});
|
|
2270
|
+
const swapBody = recordOrEmpty(
|
|
2271
|
+
await readJsonResponse(swapResponse, "Could not refresh swap status.")
|
|
2272
|
+
);
|
|
2273
|
+
active = mergeSwapStatusIntoInvoice(active, swapBody);
|
|
2274
|
+
swapStatusFailures = 0;
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
swapStatusFailures += 1;
|
|
2277
|
+
const definitive = error instanceof BrowserRequestError && error.retryable === false;
|
|
2278
|
+
const swap = active.swap;
|
|
2279
|
+
if (swap !== void 0 && (definitive || swapStatusFailures >= SWAP_STATUS_FAILURE_LIMIT)) {
|
|
2280
|
+
active = {
|
|
2281
|
+
...active,
|
|
2282
|
+
swap: { ...swap, attention: true, attention_reason: "swap_status_unavailable" }
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
const paymentMethods2 = normalizePaymentMethods(payment.payment_methods);
|
|
2288
|
+
const others = next.invoices.filter(
|
|
2289
|
+
(entry) => entry.invoice_id !== active.invoice_id && entry.rail !== "checkout_lock"
|
|
2139
2290
|
);
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
return snapshot
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
);
|
|
2153
|
-
const
|
|
2154
|
-
|
|
2155
|
-
if (
|
|
2156
|
-
const
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
{
|
|
2170
|
-
checkout_id: snapshot.checkout_id,
|
|
2171
|
-
reference: snapshot.reference,
|
|
2172
|
-
invoice_id: "",
|
|
2173
|
-
invoice: "",
|
|
2174
|
-
rail: "checkout_lock",
|
|
2175
|
-
lightning_uri: "",
|
|
2176
|
-
...snapshot.amount_msats === void 0 ? {} : { amount_msats: snapshot.amount_msats },
|
|
2177
|
-
...snapshot.fiat !== void 0 ? { fiat_quote: { fiat: snapshot.fiat } } : {},
|
|
2178
|
-
transaction_state: "pending",
|
|
2179
|
-
workflow_state: "invoice_created",
|
|
2180
|
-
paid: false
|
|
2181
|
-
},
|
|
2182
|
-
options.now ?? unixSeconds3()
|
|
2183
|
-
);
|
|
2184
|
-
}
|
|
2185
|
-
const invoice = invoiceRecord;
|
|
2186
|
-
const bolt11 = typeof invoice.invoice === "string" && invoice.invoice.length > 0 ? invoice.invoice : invoice.rail === "swap" || invoice.rail === "checkout_lock" ? "" : missingDisplayInvoice();
|
|
2187
|
-
const paid = isPaidCheckoutSnapshot(snapshot);
|
|
2188
|
-
const settledAt = snapshot.paid_at ?? invoice.settled_at;
|
|
2189
|
-
const transactionState = paid ? "settled" : invoice.transaction_state ?? "pending";
|
|
2190
|
-
const workflowState = paid ? "paid" : invoice.workflow_state ?? "invoice_created";
|
|
2191
|
-
const fiatQuote = invoice.fiat_quote === null && snapshot.fiat !== void 0 ? { fiat: snapshot.fiat } : invoice.fiat_quote ?? (snapshot.fiat === void 0 ? void 0 : { fiat: snapshot.fiat });
|
|
2192
|
-
const state = normalizeCheckoutState(
|
|
2193
|
-
{
|
|
2194
|
-
checkout_id: snapshot.checkout_id,
|
|
2195
|
-
reference: snapshot.reference,
|
|
2196
|
-
invoice_id: invoice.invoice_id,
|
|
2197
|
-
invoice: bolt11,
|
|
2198
|
-
rail: invoice.rail,
|
|
2199
|
-
// G4 divergence (d): the swap rail gets NO `lightning:` URI even when the
|
|
2200
|
-
// attempt happens to carry a bolt11. A swap is paid at the deposit
|
|
2201
|
-
// address, and a wallet-jump link on that screen sends the payer down the
|
|
2202
|
-
// wrong rail. `invoice` above still holds the bolt11 for anything that
|
|
2203
|
-
// legitimately needs it.
|
|
2204
|
-
lightning_uri: invoice.rail === "checkout_lock" || invoice.rail === "swap" || bolt11 === "" ? "" : createLightningUri(bolt11),
|
|
2205
|
-
...invoice.payment_hash === void 0 ? {} : { payment_hash: invoice.payment_hash },
|
|
2206
|
-
// G4 divergence (b): the checkout's amount is what is owed, so an attempt
|
|
2207
|
-
// that omits its own amount still shows one rather than no amount at all.
|
|
2208
|
-
amount_msats: invoice.amount_msats ?? snapshot.amount_msats,
|
|
2209
|
-
...fiatQuote === void 0 ? {} : { fiat_quote: fiatQuote },
|
|
2210
|
-
transaction_state: transactionState,
|
|
2211
|
-
workflow_state: workflowState,
|
|
2212
|
-
...invoice.expires_at === void 0 ? {} : { expires_at: invoice.expires_at },
|
|
2213
|
-
...settledAt === void 0 ? {} : { settled_at: settledAt },
|
|
2214
|
-
...invoice.swap === void 0 ? {} : { swap: invoice.swap },
|
|
2215
|
-
paid
|
|
2216
|
-
},
|
|
2217
|
-
options.now ?? unixSeconds3()
|
|
2218
|
-
);
|
|
2219
|
-
const source = options.source ?? "create";
|
|
2220
|
-
if (source === "create") {
|
|
2221
|
-
emitBrowserLog(
|
|
2222
|
-
options.logger,
|
|
2223
|
-
"info",
|
|
2224
|
-
"checkout.state.created",
|
|
2225
|
-
"Created checkout state from order snapshot.",
|
|
2226
|
-
checkoutLogFields(state)
|
|
2227
|
-
);
|
|
2228
|
-
} else if (source === "refresh") {
|
|
2229
|
-
emitBrowserLog(
|
|
2230
|
-
options.logger,
|
|
2231
|
-
"debug",
|
|
2232
|
-
"checkout.state.refreshed",
|
|
2233
|
-
"Refreshed checkout state from order status.",
|
|
2234
|
-
checkoutLogFields(state)
|
|
2235
|
-
);
|
|
2236
|
-
emitBrowserSwapTransition(options.logger, options.previousState, state);
|
|
2291
|
+
snapshot = {
|
|
2292
|
+
...next,
|
|
2293
|
+
active,
|
|
2294
|
+
invoices: [active, ...others],
|
|
2295
|
+
status: state === "settled" ? "paid" : state === "expired" || state === "failed" ? "expired" : "open",
|
|
2296
|
+
...paymentMethods2 === void 0 ? {} : { payment_methods: paymentMethods2 }
|
|
2297
|
+
};
|
|
2298
|
+
return snapshot;
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
function mergeSwapStatusIntoInvoice(invoice, status) {
|
|
2302
|
+
const swap = invoice.swap;
|
|
2303
|
+
if (swap === void 0) return invoice;
|
|
2304
|
+
const merged = { ...swap };
|
|
2305
|
+
const providerState = nonEmptyString2(status.provider_state);
|
|
2306
|
+
if (providerState !== void 0) merged.provider_state = providerState;
|
|
2307
|
+
const providerExpiresAt = optionalSafeInteger(status.provider_expires_at, "provider_expires_at");
|
|
2308
|
+
if (providerExpiresAt !== void 0) merged.provider_expires_at = providerExpiresAt;
|
|
2309
|
+
for (const key of [
|
|
2310
|
+
"deposit_tx_id",
|
|
2311
|
+
"payout_tx_id",
|
|
2312
|
+
"refund_tx_id",
|
|
2313
|
+
"refund_reason",
|
|
2314
|
+
"refund_amount",
|
|
2315
|
+
"deposit_received_amount",
|
|
2316
|
+
"refund_address",
|
|
2317
|
+
"attention_reason"
|
|
2318
|
+
]) {
|
|
2319
|
+
if (typeof status[key] === "string") merged[key] = status[key];
|
|
2237
2320
|
}
|
|
2238
|
-
|
|
2321
|
+
if (typeof status.attention === "boolean") merged.attention = status.attention;
|
|
2322
|
+
return {
|
|
2323
|
+
...invoice,
|
|
2324
|
+
swap: merged,
|
|
2325
|
+
...providerExpiresAt === void 0 ? {} : { expires_at: providerExpiresAt }
|
|
2326
|
+
};
|
|
2239
2327
|
}
|
|
2240
|
-
function
|
|
2241
|
-
const
|
|
2242
|
-
const
|
|
2243
|
-
const
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
...attempt.workflow_state === void 0 ? {} : { workflow_state: attempt.workflow_state },
|
|
2252
|
-
...attempt.expires_at === void 0 ? {} : { expires_at: attempt.expires_at },
|
|
2253
|
-
...attempt.settled_at === void 0 ? {} : { settled_at: attempt.settled_at },
|
|
2254
|
-
...attempt.swap === void 0 ? {} : { swap: attempt.swap }
|
|
2328
|
+
function checkoutLockSnapshotFromPrepareBody(body, fallbackReference) {
|
|
2329
|
+
const record = recordOrEmpty(body);
|
|
2330
|
+
const reference = requiredString(record.reference ?? fallbackReference, "reference");
|
|
2331
|
+
const amountMsats = requiredSafeInteger(record.amount_msats, "amount_msats");
|
|
2332
|
+
const lockId = `lock:${reference}`;
|
|
2333
|
+
const lockInvoice = {
|
|
2334
|
+
invoice_id: lockId,
|
|
2335
|
+
rail: "checkout_lock",
|
|
2336
|
+
amount_msats: amountMsats,
|
|
2337
|
+
transaction_state: "pending",
|
|
2338
|
+
workflow_state: "invoice_created"
|
|
2255
2339
|
};
|
|
2256
|
-
const
|
|
2340
|
+
const fiatQuote = record.fiat_quote;
|
|
2341
|
+
const fiatRecord = isRecord(fiatQuote) && isRecord(fiatQuote.fiat) ? fiatQuote.fiat : void 0;
|
|
2342
|
+
const fiatCurrency = nonEmptyString2(fiatRecord?.currency);
|
|
2343
|
+
const fiatValue = nonEmptyString2(fiatRecord?.value);
|
|
2344
|
+
const fiat = fiatCurrency === void 0 || fiatValue === void 0 ? void 0 : { currency: fiatCurrency, value: fiatValue };
|
|
2345
|
+
const paymentMethods2 = normalizePaymentMethods(record.payment_methods);
|
|
2257
2346
|
return {
|
|
2258
|
-
checkout_id:
|
|
2259
|
-
reference
|
|
2260
|
-
status:
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
active:
|
|
2264
|
-
invoices: [
|
|
2347
|
+
checkout_id: lockId,
|
|
2348
|
+
reference,
|
|
2349
|
+
status: "open",
|
|
2350
|
+
amount_msats: amountMsats,
|
|
2351
|
+
...fiat === void 0 ? {} : { fiat },
|
|
2352
|
+
active: lockInvoice,
|
|
2353
|
+
invoices: [lockInvoice],
|
|
2354
|
+
...paymentMethods2 === void 0 ? {} : { payment_methods: paymentMethods2 }
|
|
2265
2355
|
};
|
|
2266
2356
|
}
|
|
2267
|
-
function
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
previousState: options.previousState ?? state
|
|
2272
|
-
});
|
|
2273
|
-
}
|
|
2274
|
-
function shouldCheckoutShowWaiting(state, options = {}) {
|
|
2275
|
-
if (state.terminal || state.settled) return false;
|
|
2276
|
-
if (state.expires_at === void 0) return true;
|
|
2277
|
-
return state.expires_at > (options.now ?? unixSeconds3());
|
|
2278
|
-
}
|
|
2279
|
-
function createCheckoutStatusModel(source, options = {}) {
|
|
2280
|
-
const isCheckoutState = source !== void 0 && "invoice_id" in source && "invoice" in source;
|
|
2281
|
-
const phase = source?.phase ?? "invoice_created";
|
|
2282
|
-
const expiresInSeconds = source?.expires_in_seconds;
|
|
2283
|
-
const displayPhase = phase !== "settled" && phase !== "failed" && phase !== "cancelled" && expiresInSeconds === 0 ? "expired" : phase;
|
|
2284
|
-
const statusText = getPaymentStatusText(displayPhase);
|
|
2357
|
+
function checkoutSnapshotFromResponseBody(body) {
|
|
2358
|
+
const record = recordOrEmpty(body);
|
|
2359
|
+
const wrapped = recordOrEmpty(record.checkout);
|
|
2360
|
+
const paymentMethods2 = normalizePaymentMethods(record.payment_methods);
|
|
2285
2361
|
return {
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
title: statusText.title,
|
|
2289
|
-
detail: statusText.detail,
|
|
2290
|
-
countdownPrefix: checkoutLabels.countdownPrefix,
|
|
2291
|
-
// No countdown once settled — the invoice's remaining lifetime is meaningless
|
|
2292
|
-
// to a payer whose payment already landed.
|
|
2293
|
-
...expiresInSeconds === void 0 || displayPhase === "expired" || displayPhase === "settled" ? {} : {
|
|
2294
|
-
expires_in_seconds: expiresInSeconds,
|
|
2295
|
-
countdownLabel: formatCountdown(expiresInSeconds)
|
|
2296
|
-
}
|
|
2362
|
+
...checkoutSnapshot(wrapped),
|
|
2363
|
+
...paymentMethods2 === void 0 ? {} : { payment_methods: paymentMethods2 }
|
|
2297
2364
|
};
|
|
2298
2365
|
}
|
|
2299
|
-
function
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
const phase = statePhase === "invoice_created" || statePhase === "verifying" ? expiresInSeconds === 0 ? "expired" : statePhase : statePhase;
|
|
2315
|
-
const settled = base.paid || base.transaction_state === "settled";
|
|
2366
|
+
function checkoutSnapshot(checkout) {
|
|
2367
|
+
const paymentHash = requiredString(checkout.payment_hash, "payment_hash");
|
|
2368
|
+
const reference = requiredString(checkout.reference, "reference");
|
|
2369
|
+
const amountMsats = requiredSafeInteger(checkout.amount_msats, "amount_msats");
|
|
2370
|
+
const invoice = {
|
|
2371
|
+
invoice_id: paymentHash,
|
|
2372
|
+
rail: "lightning",
|
|
2373
|
+
invoice: requiredString(checkout.bolt11, "bolt11"),
|
|
2374
|
+
payment_hash: paymentHash,
|
|
2375
|
+
amount_msats: amountMsats,
|
|
2376
|
+
transaction_state: "pending",
|
|
2377
|
+
workflow_state: "invoice_created",
|
|
2378
|
+
expires_at: requiredSafeInteger(checkout.expires_at, "expires_at"),
|
|
2379
|
+
...isRecord(checkout.fiat_quote) || checkout.fiat_quote === null ? { fiat_quote: checkout.fiat_quote } : {}
|
|
2380
|
+
};
|
|
2316
2381
|
return {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
// both /payments/check and the provider instead of asking forever.
|
|
2324
|
-
terminal: isTerminalPhase(phase) || !settled && isTerminalSwapProviderState(base.swap?.provider_state),
|
|
2325
|
-
...expiresInSeconds === void 0 ? {} : { expires_in_seconds: expiresInSeconds }
|
|
2382
|
+
checkout_id: paymentHash,
|
|
2383
|
+
reference,
|
|
2384
|
+
status: "open",
|
|
2385
|
+
amount_msats: amountMsats,
|
|
2386
|
+
active: invoice,
|
|
2387
|
+
invoices: [invoice]
|
|
2326
2388
|
};
|
|
2327
2389
|
}
|
|
2328
|
-
function
|
|
2329
|
-
|
|
2390
|
+
function normalizePaymentMethods(value) {
|
|
2391
|
+
if (!Array.isArray(value)) return void 0;
|
|
2392
|
+
return value.map(normalizePaymentMethod);
|
|
2330
2393
|
}
|
|
2331
|
-
function
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (
|
|
2335
|
-
|
|
2336
|
-
}
|
|
2337
|
-
if (transactionState === "failed" || workflowState === "failed") {
|
|
2338
|
-
return "failed";
|
|
2339
|
-
}
|
|
2340
|
-
if (workflowState === "verifying") {
|
|
2341
|
-
return "verifying";
|
|
2394
|
+
function normalizePaymentMethod(input) {
|
|
2395
|
+
const record = recordOrEmpty(input);
|
|
2396
|
+
const provider = record.provider;
|
|
2397
|
+
if (typeof provider !== "string") {
|
|
2398
|
+
throw new TypeError("payment method provider must be a string");
|
|
2342
2399
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
transaction_state: state.transaction_state,
|
|
2357
|
-
workflow_state: state.workflow_state,
|
|
2358
|
-
...state.expires_at === void 0 ? {} : { expires_at: state.expires_at },
|
|
2359
|
-
...state.settled_at === void 0 ? {} : { settled_at: state.settled_at },
|
|
2360
|
-
...state.swap === void 0 ? {} : { swap: state.swap }
|
|
2361
|
-
};
|
|
2400
|
+
const unavailableReason = nonEmptyString2(record.unavailable_reason);
|
|
2401
|
+
const unavailableMessage = nonEmptyString2(record.unavailable_message);
|
|
2402
|
+
const payAmount = nonEmptyString2(record.pay_amount);
|
|
2403
|
+
const minimumPayAmount = nonEmptyString2(record.minimum_pay_amount);
|
|
2404
|
+
const maximumPayAmount = nonEmptyString2(record.maximum_pay_amount);
|
|
2405
|
+
const minimumInvoiceAmountMsats = optionalSafeInteger(
|
|
2406
|
+
record.minimum_invoice_amount_msats,
|
|
2407
|
+
"minimum_invoice_amount_msats"
|
|
2408
|
+
);
|
|
2409
|
+
const maximumInvoiceAmountMsats = optionalSafeInteger(
|
|
2410
|
+
record.maximum_invoice_amount_msats,
|
|
2411
|
+
"maximum_invoice_amount_msats"
|
|
2412
|
+
);
|
|
2362
2413
|
return {
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2414
|
+
pay_in_asset: requiredString(record.pay_in_asset, "pay_in_asset"),
|
|
2415
|
+
label: requiredString(record.label, "label"),
|
|
2416
|
+
network_label: requiredString(record.network_label, "network_label"),
|
|
2417
|
+
provider,
|
|
2418
|
+
available: record.available === true,
|
|
2419
|
+
...unavailableReason === void 0 ? {} : { unavailable_reason: unavailableReason },
|
|
2420
|
+
...unavailableMessage === void 0 ? {} : { unavailable_message: unavailableMessage },
|
|
2421
|
+
...payAmount === void 0 ? {} : { pay_amount: payAmount },
|
|
2422
|
+
...minimumPayAmount === void 0 ? {} : { minimum_pay_amount: minimumPayAmount },
|
|
2423
|
+
...maximumPayAmount === void 0 ? {} : { maximum_pay_amount: maximumPayAmount },
|
|
2424
|
+
...minimumInvoiceAmountMsats === void 0 ? {} : { minimum_invoice_amount_msats: minimumInvoiceAmountMsats },
|
|
2425
|
+
...maximumInvoiceAmountMsats === void 0 ? {} : { maximum_invoice_amount_msats: maximumInvoiceAmountMsats }
|
|
2370
2426
|
};
|
|
2371
2427
|
}
|
|
2372
2428
|
|
|
@@ -2460,7 +2516,7 @@ function isQrEncoder(value) {
|
|
|
2460
2516
|
}
|
|
2461
2517
|
|
|
2462
2518
|
// src/internal/checkout-watcher.ts
|
|
2463
|
-
import { unixSeconds as
|
|
2519
|
+
import { unixSeconds as unixSeconds3 } from "@openreceive/core";
|
|
2464
2520
|
var CheckoutWatcher = class {
|
|
2465
2521
|
options;
|
|
2466
2522
|
state;
|
|
@@ -2630,7 +2686,7 @@ var CheckoutWatcher = class {
|
|
|
2630
2686
|
this.pollTimer = void 0;
|
|
2631
2687
|
}
|
|
2632
2688
|
now() {
|
|
2633
|
-
return this.options.now?.() ??
|
|
2689
|
+
return this.options.now?.() ?? unixSeconds3();
|
|
2634
2690
|
}
|
|
2635
2691
|
setInterval() {
|
|
2636
2692
|
return this.options.setInterval ?? globalThis.setInterval;
|
|
@@ -2718,13 +2774,12 @@ function createCheckoutController(options) {
|
|
|
2718
2774
|
}
|
|
2719
2775
|
|
|
2720
2776
|
// src/status.ts
|
|
2721
|
-
import { unixSeconds as unixSeconds5 } from "@openreceive/core";
|
|
2722
2777
|
function deriveStatus(invoice, options = {}) {
|
|
2723
2778
|
if (invoice.transaction_state === "settled") return "settled";
|
|
2724
2779
|
if (invoice.transaction_state === "failed") return "failed";
|
|
2725
2780
|
if (invoice.transaction_state === "expired") return "expired";
|
|
2726
2781
|
const expiresAt = readUnixSeconds(invoice.expires_at);
|
|
2727
|
-
if (expiresAt !== void 0 && expiresAt <= (options.now
|
|
2782
|
+
if (expiresAt !== void 0 && expiresAt <= resolveNow(options.now)) {
|
|
2728
2783
|
return "expired";
|
|
2729
2784
|
}
|
|
2730
2785
|
return "pending";
|
|
@@ -2936,14 +2991,6 @@ export {
|
|
|
2936
2991
|
createTransactionDetailsFromState,
|
|
2937
2992
|
resolveTransactionDetailRows,
|
|
2938
2993
|
createPaymentDataEntries,
|
|
2939
|
-
optionalSafeInteger,
|
|
2940
|
-
requestHeaders,
|
|
2941
|
-
checkoutRoutes,
|
|
2942
|
-
BrowserRequestError,
|
|
2943
|
-
readJsonResponse,
|
|
2944
|
-
requestCheckout,
|
|
2945
|
-
prepareCheckout,
|
|
2946
|
-
createStatusFetcher,
|
|
2947
2994
|
getWizardEmptyMessage,
|
|
2948
2995
|
getRouteNetworkLabel,
|
|
2949
2996
|
createWizardRouteAssetDisplays,
|
|
@@ -2974,6 +3021,7 @@ export {
|
|
|
2974
3021
|
createPaymentWizardModel,
|
|
2975
3022
|
updatePaymentWizardSelection,
|
|
2976
3023
|
createPaymentWizardController,
|
|
3024
|
+
optionalSafeInteger,
|
|
2977
3025
|
sanitizeBrowserLogEntry,
|
|
2978
3026
|
createAppBrowserConsoleLogger,
|
|
2979
3027
|
resolveBrowserLogger,
|
|
@@ -2983,6 +3031,17 @@ export {
|
|
|
2983
3031
|
createCheckoutState,
|
|
2984
3032
|
createCheckoutSnapshotFromInvoice,
|
|
2985
3033
|
createCheckoutStatusModel,
|
|
3034
|
+
findReusableLightningInvoice,
|
|
3035
|
+
mergeAttemptIntoSnapshot,
|
|
3036
|
+
mergeAttemptIntoCheckout,
|
|
3037
|
+
mergeMintedCheckout,
|
|
3038
|
+
requestHeaders,
|
|
3039
|
+
checkoutRoutes,
|
|
3040
|
+
BrowserRequestError,
|
|
3041
|
+
readJsonResponse,
|
|
3042
|
+
requestCheckout,
|
|
3043
|
+
prepareCheckout,
|
|
3044
|
+
createStatusFetcher,
|
|
2986
3045
|
createQrSvg,
|
|
2987
3046
|
createQrPayloadSvg,
|
|
2988
3047
|
createQrPngDataUrl,
|