@openreceive/browser 0.1.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/chunk-WLLNZH26.js +3051 -0
- package/dist/headless.d.ts +804 -0
- package/dist/headless.js +891 -0
- package/dist/index.d.ts +32 -4
- package/dist/index.js +32 -2
- package/dist/status-B1vDOhvh.d.ts +1001 -0
- package/dist/styles.css +3 -759
- package/package.json +57 -15
- package/dist/assets/icons/bank.svg +0 -12
- package/dist/assets/icons/bnb.svg +0 -1
- package/dist/assets/icons/card.svg +0 -14
- package/dist/assets/icons/doge.svg +0 -1
- package/dist/assets/pay_tutorials/boltz-1.webp +0 -0
- package/dist/assets/pay_tutorials/boltz-2.webp +0 -0
- package/dist/assets/pay_tutorials/cashapp-1.webp +0 -0
- package/dist/assets/pay_tutorials/cashapp-2.webp +0 -0
- package/dist/assets/pay_tutorials/cashapp-3.webp +0 -0
- package/dist/assets/pay_tutorials/cashapp-4.webp +0 -0
- package/dist/assets/pay_tutorials/cashapp-5.webp +0 -0
- package/dist/assets/pay_tutorials/cashapp-6.webp +0 -0
- package/dist/assets/pay_tutorials/coinbase-1.webp +0 -0
- package/dist/assets/pay_tutorials/coinbase-2.webp +0 -0
- package/dist/assets/pay_tutorials/fixedfloat-1.webp +0 -0
- package/dist/assets/pay_tutorials/fixedfloat-2.webp +0 -0
- package/dist/assets/pay_tutorials/kraken-1.webp +0 -0
- package/dist/assets/pay_tutorials/kraken-2.webp +0 -0
- package/dist/assets/pay_tutorials/kraken-3.webp +0 -0
- package/dist/assets/pay_tutorials/kraken-4.webp +0 -0
- package/dist/assets/pay_tutorials/strike-1.webp +0 -0
- package/dist/assets/pay_tutorials/strike-2.webp +0 -0
- package/dist/assets/pay_tutorials/strike-3.webp +0 -0
- package/dist/assets/pay_tutorials/strike-4.webp +0 -0
- package/dist/assets/provider-icons/aqua.png +0 -0
- package/dist/assets/provider-icons/belo.png +0 -0
- package/dist/assets/provider-icons/binance.png +0 -0
- package/dist/assets/provider-icons/bipa.png +0 -0
- package/dist/assets/provider-icons/bitfinex.png +0 -0
- package/dist/assets/provider-icons/bitget.png +0 -0
- package/dist/assets/provider-icons/bitnob.png +0 -0
- package/dist/assets/provider-icons/bitso.png +0 -0
- package/dist/assets/provider-icons/bity.png +0 -0
- package/dist/assets/provider-icons/blink.png +0 -0
- package/dist/assets/provider-icons/bluewallet.png +0 -0
- package/dist/assets/provider-icons/boltz.png +0 -0
- package/dist/assets/provider-icons/bringin.png +0 -0
- package/dist/assets/provider-icons/bullbitcoin.png +0 -0
- package/dist/assets/provider-icons/cakewallet.png +0 -0
- package/dist/assets/provider-icons/cashapp.png +0 -0
- package/dist/assets/provider-icons/changenow.png +0 -0
- package/dist/assets/provider-icons/chivo.png +0 -0
- package/dist/assets/provider-icons/coinbase.png +0 -0
- package/dist/assets/provider-icons/coincorner.png +0 -0
- package/dist/assets/provider-icons/fixedfloat.png +0 -0
- package/dist/assets/provider-icons/getalby.png +0 -0
- package/dist/assets/provider-icons/kraken.png +0 -0
- package/dist/assets/provider-icons/kryptex.png +0 -0
- package/dist/assets/provider-icons/kucoin.png +0 -0
- package/dist/assets/provider-icons/manifest.json +0 -207
- package/dist/assets/provider-icons/mtpelerin.png +0 -0
- package/dist/assets/provider-icons/muun.png +0 -0
- package/dist/assets/provider-icons/okcoin.png +0 -0
- package/dist/assets/provider-icons/okx.png +0 -0
- package/dist/assets/provider-icons/phoenix.png +0 -0
- package/dist/assets/provider-icons/pouch.png +0 -0
- package/dist/assets/provider-icons/ripio.png +0 -0
- package/dist/assets/provider-icons/river.png +0 -0
- package/dist/assets/provider-icons/rizful.png +0 -0
- package/dist/assets/provider-icons/shakepay.png +0 -0
- package/dist/assets/provider-icons/sideshift.png +0 -0
- package/dist/assets/provider-icons/simpleswap.png +0 -0
- package/dist/assets/provider-icons/speed.png +0 -0
- package/dist/assets/provider-icons/strike.png +0 -0
- package/dist/assets/provider-icons/trocador.png +0 -0
- package/dist/assets/provider-icons/walletofsatoshi.png +0 -0
- package/dist/assets/provider-icons/zeus.png +0 -0
- package/dist/country-map.d.ts +0 -5
- package/dist/country-map.js +0 -17
- package/dist/internal.d.ts +0 -834
- package/dist/internal.js +0 -2615
- package/dist/pay-tutorials.d.ts +0 -1
- package/dist/pay-tutorials.js +0 -1
- package/dist/provider-icons.d.ts +0 -1
- package/dist/provider-icons.js +0 -1
- package/dist/qrcode.d.ts +0 -11
- package/dist/status.d.ts +0 -9
- package/dist/status.js +0 -25
package/dist/internal.js
DELETED
|
@@ -1,2615 +0,0 @@
|
|
|
1
|
-
import { getAssets, getCountries, getCountryRoutes, getPaymentWizardRoutes, openReceivePayTutorialUrls, openReceiveProviderIconUrls } from "@openreceive/provider-data";
|
|
2
|
-
import { status as deriveStatus } from "./status.js";
|
|
3
|
-
export { status } from "./status.js";
|
|
4
|
-
export const OPENRECEIVE_QR_QUIET_ZONE_MODULES = 4;
|
|
5
|
-
export const OPENRECEIVE_QR_DARK_COLOR = "#000000";
|
|
6
|
-
export const OPENRECEIVE_QR_LIGHT_COLOR = "#FFFFFFFF";
|
|
7
|
-
export const OPENRECEIVE_QR_ERROR_CORRECTION = "M";
|
|
8
|
-
export const OPENRECEIVE_COUNTRY_STORAGE_KEY = "openreceive.checkout.country";
|
|
9
|
-
export const OPENRECEIVE_LEGACY_DEMO_COUNTRY_STORAGE_KEY = "openreceive-demo.country";
|
|
10
|
-
export const OPENRECEIVE_THEME_STORAGE_KEY = "openreceive.theme";
|
|
11
|
-
export const OPENRECEIVE_DEFAULT_POLL_INTERVAL_MS = 3000;
|
|
12
|
-
export const OPENRECEIVE_COPY_FEEDBACK_MS = 1800;
|
|
13
|
-
export const OPENRECEIVE_PROVIDER_PREVIEW_LIMIT = 4;
|
|
14
|
-
export const OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME = "openreceive-checkout";
|
|
15
|
-
export const OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME = "openreceive-theme-toggle";
|
|
16
|
-
export const OPENRECEIVE_COUNTRY_MAP_WIDTH = 820;
|
|
17
|
-
export const OPENRECEIVE_COUNTRY_MAP_HEIGHT = 420;
|
|
18
|
-
export const OPENRECEIVE_COUNTRY_MAP_VIEW_BOX = `0 0 ${OPENRECEIVE_COUNTRY_MAP_WIDTH} ${OPENRECEIVE_COUNTRY_MAP_HEIGHT}`;
|
|
19
|
-
export const OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS = {
|
|
20
|
-
copy: "openreceive-copy",
|
|
21
|
-
openWallet: "openreceive-open-wallet",
|
|
22
|
-
state: "openreceive-state",
|
|
23
|
-
settled: "openreceive-settled",
|
|
24
|
-
providerCopy: "openreceive-provider-copy",
|
|
25
|
-
startOver: "openreceive-start-over",
|
|
26
|
-
error: "openreceive-error"
|
|
27
|
-
};
|
|
28
|
-
export const OPENRECEIVE_THEME_TOGGLE_ELEMENT_EVENTS = {
|
|
29
|
-
change: "openreceive-theme-change"
|
|
30
|
-
};
|
|
31
|
-
export const OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES = {
|
|
32
|
-
root: "data-openreceive-wizard",
|
|
33
|
-
breadcrumb: "data-or-breadcrumb",
|
|
34
|
-
method: "data-or-method",
|
|
35
|
-
region: "data-or-region",
|
|
36
|
-
regionShape: "data-or-region-shape",
|
|
37
|
-
country: "data-or-country",
|
|
38
|
-
switchCountry: "data-or-switch-country",
|
|
39
|
-
route: "data-or-route",
|
|
40
|
-
providerCopy: "data-or-provider-copy",
|
|
41
|
-
providerTutorial: "data-or-provider-tutorial",
|
|
42
|
-
providerTutorialIndex: "data-or-provider-tutorial-index"
|
|
43
|
-
};
|
|
44
|
-
export const OPENRECEIVE_PAYMENT_WIZARD_SELECTORS = {
|
|
45
|
-
root: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.root}]`,
|
|
46
|
-
breadcrumb: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.breadcrumb}]`,
|
|
47
|
-
method: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.method}]`,
|
|
48
|
-
region: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.region}]`,
|
|
49
|
-
regionShape: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.regionShape}]`,
|
|
50
|
-
country: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.country}]`,
|
|
51
|
-
switchCountry: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.switchCountry}]`,
|
|
52
|
-
route: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.route}]`,
|
|
53
|
-
providerCopy: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.providerCopy}]`,
|
|
54
|
-
providerTutorial: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.providerTutorial}]`,
|
|
55
|
-
providerTutorialIndex: `[${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.providerTutorialIndex}]`
|
|
56
|
-
};
|
|
57
|
-
export const OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES = {
|
|
58
|
-
root: "data-openreceive-checkout",
|
|
59
|
-
qr: "data-openreceive-qr",
|
|
60
|
-
meta: "data-openreceive-meta",
|
|
61
|
-
state: "data-openreceive-state",
|
|
62
|
-
actions: "data-openreceive-actions",
|
|
63
|
-
theme: "data-openreceive-theme",
|
|
64
|
-
themeToggle: "data-openreceive-theme-toggle"
|
|
65
|
-
};
|
|
66
|
-
export const OPENRECEIVE_CHECKOUT_DATA_SELECTORS = {
|
|
67
|
-
root: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.root}]`,
|
|
68
|
-
qr: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.qr}]`,
|
|
69
|
-
meta: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.meta}]`,
|
|
70
|
-
state: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.state}]`,
|
|
71
|
-
actions: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.actions}]`,
|
|
72
|
-
theme: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.theme}]`,
|
|
73
|
-
themeToggle: `[${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.themeToggle}]`
|
|
74
|
-
};
|
|
75
|
-
export const OPENRECEIVE_CHECKOUT_ELEMENT_PARTS = {
|
|
76
|
-
copy: "copy",
|
|
77
|
-
open: "open",
|
|
78
|
-
startOver: "start-over"
|
|
79
|
-
};
|
|
80
|
-
export const OPENRECEIVE_CHECKOUT_ELEMENT_PART_SELECTORS = {
|
|
81
|
-
copy: `[part="${OPENRECEIVE_CHECKOUT_ELEMENT_PARTS.copy}"]`,
|
|
82
|
-
open: `[part="${OPENRECEIVE_CHECKOUT_ELEMENT_PARTS.open}"]`,
|
|
83
|
-
startOver: `[part="${OPENRECEIVE_CHECKOUT_ELEMENT_PARTS.startOver}"]`
|
|
84
|
-
};
|
|
85
|
-
export const OPENRECEIVE_THEME_TOGGLE_ELEMENT_PARTS = {
|
|
86
|
-
button: "button"
|
|
87
|
-
};
|
|
88
|
-
export const OPENRECEIVE_THEME_TOGGLE_ELEMENT_PART_SELECTORS = {
|
|
89
|
-
button: `[part="${OPENRECEIVE_THEME_TOGGLE_ELEMENT_PARTS.button}"]`
|
|
90
|
-
};
|
|
91
|
-
export function createCheckoutProviderCopyEvent(providerId) {
|
|
92
|
-
return new CustomEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.providerCopy, {
|
|
93
|
-
detail: {
|
|
94
|
-
providerId
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
export function createCheckoutActionEvent(eventName) {
|
|
99
|
-
return new CustomEvent(eventName);
|
|
100
|
-
}
|
|
101
|
-
export function createCheckoutStateEvent(eventName, state) {
|
|
102
|
-
return new CustomEvent(eventName, {
|
|
103
|
-
detail: {
|
|
104
|
-
state
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
export function createCheckoutErrorEvent(error) {
|
|
109
|
-
return new CustomEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.error, {
|
|
110
|
-
detail: {
|
|
111
|
-
error
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
export function createOpenReceiveThemeChangeEvent(theme) {
|
|
116
|
-
return new CustomEvent(OPENRECEIVE_THEME_TOGGLE_ELEMENT_EVENTS.change, {
|
|
117
|
-
detail: {
|
|
118
|
-
theme: theme.theme,
|
|
119
|
-
resolvedTheme: theme.resolvedTheme
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
const bankIcon = new URL("./assets/icons/bank.svg", import.meta.url).href;
|
|
124
|
-
const bnbIcon = new URL("./assets/icons/bnb.svg", import.meta.url).href;
|
|
125
|
-
const btcIcon = new URL("./assets/icons/btc.svg", import.meta.url).href;
|
|
126
|
-
const cardIcon = new URL("./assets/icons/card.svg", import.meta.url).href;
|
|
127
|
-
const cryptoIcon = new URL("./assets/icons/crypto.svg", import.meta.url).href;
|
|
128
|
-
const dogeIcon = new URL("./assets/icons/doge.svg", import.meta.url).href;
|
|
129
|
-
const ethIcon = new URL("./assets/icons/eth.svg", import.meta.url).href;
|
|
130
|
-
const lightningIcon = new URL("./assets/icons/lightning.svg", import.meta.url).href;
|
|
131
|
-
const ltcIcon = new URL("./assets/icons/ltc.svg", import.meta.url).href;
|
|
132
|
-
const solIcon = new URL("./assets/icons/sol.svg", import.meta.url).href;
|
|
133
|
-
const trxIcon = new URL("./assets/icons/trx.svg", import.meta.url).href;
|
|
134
|
-
const usdcIcon = new URL("./assets/icons/usdc.svg", import.meta.url).href;
|
|
135
|
-
const usdtIcon = new URL("./assets/icons/usdt.svg", import.meta.url).href;
|
|
136
|
-
const xmrIcon = new URL("./assets/icons/xmr.svg", import.meta.url).href;
|
|
137
|
-
const xrpIcon = new URL("./assets/icons/xrp.svg", import.meta.url).href;
|
|
138
|
-
export const openReceivePaymentIconUrls = {
|
|
139
|
-
bank: bankIcon,
|
|
140
|
-
bnb: bnbIcon,
|
|
141
|
-
btc: btcIcon,
|
|
142
|
-
card: cardIcon,
|
|
143
|
-
crypto: cryptoIcon,
|
|
144
|
-
doge: dogeIcon,
|
|
145
|
-
eth: ethIcon,
|
|
146
|
-
lightning: lightningIcon,
|
|
147
|
-
ltc: ltcIcon,
|
|
148
|
-
sol: solIcon,
|
|
149
|
-
trx: trxIcon,
|
|
150
|
-
usdc: usdcIcon,
|
|
151
|
-
usdt: usdtIcon,
|
|
152
|
-
xmr: xmrIcon,
|
|
153
|
-
xrp: xrpIcon
|
|
154
|
-
};
|
|
155
|
-
export const openReceivePaymentMethodIconIds = {
|
|
156
|
-
card: "card",
|
|
157
|
-
bank: "bank",
|
|
158
|
-
bitcoin: "btc",
|
|
159
|
-
crypto: "crypto"
|
|
160
|
-
};
|
|
161
|
-
export const openReceiveAssetIconIds = {
|
|
162
|
-
bnb: "bnb",
|
|
163
|
-
btc: "btc",
|
|
164
|
-
doge: "doge",
|
|
165
|
-
eth: "eth",
|
|
166
|
-
ltc: "ltc",
|
|
167
|
-
sol: "sol",
|
|
168
|
-
trx: "trx",
|
|
169
|
-
usdc: "usdc",
|
|
170
|
-
usdt: "usdt",
|
|
171
|
-
xmr: "xmr",
|
|
172
|
-
xrp: "xrp"
|
|
173
|
-
};
|
|
174
|
-
export const OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES = {
|
|
175
|
-
invoiceId: "invoice-id",
|
|
176
|
-
invoice: "invoice",
|
|
177
|
-
paymentHash: "payment-hash",
|
|
178
|
-
amountMsats: "amount-msats",
|
|
179
|
-
fiatCurrency: "fiat-currency",
|
|
180
|
-
fiatValue: "fiat-value",
|
|
181
|
-
status: "status",
|
|
182
|
-
expiresAt: "expires-at",
|
|
183
|
-
lookupUrl: "lookup-url",
|
|
184
|
-
theme: "theme",
|
|
185
|
-
paymentWizard: "payment-wizard"
|
|
186
|
-
};
|
|
187
|
-
export const OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES = {
|
|
188
|
-
rootSelector: "root-selector",
|
|
189
|
-
checkoutSelector: "checkout-selector",
|
|
190
|
-
defaultTheme: "default-theme",
|
|
191
|
-
storageKey: "storage-key"
|
|
192
|
-
};
|
|
193
|
-
export const openReceiveCheckoutLabels = {
|
|
194
|
-
copyInvoice: "Copy invoice",
|
|
195
|
-
copied: "Copied!",
|
|
196
|
-
openWallet: "Open Wallet",
|
|
197
|
-
paymentStatus: {
|
|
198
|
-
waitingTitle: "Waiting for payment",
|
|
199
|
-
waitingDetail: "Keep this page open while we verify settlement.",
|
|
200
|
-
settledTitle: "Payment received",
|
|
201
|
-
settledDetail: "Backend settlement verified.",
|
|
202
|
-
expiredTitle: "Invoice expired",
|
|
203
|
-
expiredDetail: "Create a fresh invoice to keep going."
|
|
204
|
-
},
|
|
205
|
-
countdownPrefix: "Invoice expires in",
|
|
206
|
-
startOver: "Start over",
|
|
207
|
-
wizardTitle: "Pay this invoice",
|
|
208
|
-
wizardSubtitle: "Choose how you want to pay.",
|
|
209
|
-
paymentMethod: "Payment method",
|
|
210
|
-
emptyBitcoin: "Choose Bitcoin Lightning.",
|
|
211
|
-
emptyCrypto: "Choose an altcoin.",
|
|
212
|
-
emptyFiat: "No providers found for this country yet.",
|
|
213
|
-
recommended: "Recommended",
|
|
214
|
-
openProvider: "How To Pay",
|
|
215
|
-
tutorialTitlePrefix: "Pay a Lightning invoice with",
|
|
216
|
-
tutorialIntroPrefix: "It's easy to make this payment using",
|
|
217
|
-
tutorialIntroCopy: "The first step is to copy the invoice to your clipboard.",
|
|
218
|
-
tutorialCopiedContinue: "Copied! Click next below to continue with tutorial.",
|
|
219
|
-
tutorialExit: "Exit",
|
|
220
|
-
lightningNetwork: "Lightning Network",
|
|
221
|
-
chooseCountry: "Choose a country"
|
|
222
|
-
};
|
|
223
|
-
export const openReceiveCheckoutElementStyles = `
|
|
224
|
-
:host {
|
|
225
|
-
--or-bg: #ffffff;
|
|
226
|
-
--or-bg-soft: #fafafa;
|
|
227
|
-
--or-text: #171717;
|
|
228
|
-
--or-muted: #525252;
|
|
229
|
-
--or-border: #d4d4d4;
|
|
230
|
-
--or-warm: #f97316;
|
|
231
|
-
--or-good-bg: #e8f5ee;
|
|
232
|
-
--or-good: #11613b;
|
|
233
|
-
color: var(--or-text);
|
|
234
|
-
display: block;
|
|
235
|
-
font: 14px/1.4 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
:host([theme="dark"]),
|
|
239
|
-
[part="root"][data-theme="dark"] {
|
|
240
|
-
--or-bg: #1c2230;
|
|
241
|
-
--or-bg-soft: #121620;
|
|
242
|
-
--or-text: #f7f8fb;
|
|
243
|
-
--or-muted: #b7c0cf;
|
|
244
|
-
--or-border: #333c4d;
|
|
245
|
-
--or-warm: #ff9f1c;
|
|
246
|
-
--or-good-bg: #173828;
|
|
247
|
-
--or-good: #86efac;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
[part="root"] {
|
|
251
|
-
background: var(--or-bg);
|
|
252
|
-
border: 1px solid var(--or-border);
|
|
253
|
-
border-radius: 8px;
|
|
254
|
-
display: grid;
|
|
255
|
-
gap: 12px;
|
|
256
|
-
padding: 12px;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
[part="qr"] {
|
|
260
|
-
aspect-ratio: 1;
|
|
261
|
-
align-items: center;
|
|
262
|
-
background: #ffffff;
|
|
263
|
-
border: 1px solid var(--or-border);
|
|
264
|
-
border-radius: 6px;
|
|
265
|
-
display: flex;
|
|
266
|
-
justify-content: center;
|
|
267
|
-
justify-self: center;
|
|
268
|
-
min-width: 0;
|
|
269
|
-
overflow: hidden;
|
|
270
|
-
width: min(100%, 420px);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
[part="qr"] svg {
|
|
274
|
-
display: block;
|
|
275
|
-
height: 100%;
|
|
276
|
-
width: 100%;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
[part="meta"] {
|
|
280
|
-
align-items: center;
|
|
281
|
-
display: flex;
|
|
282
|
-
flex-wrap: wrap;
|
|
283
|
-
gap: 8px;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
[part="state"] {
|
|
287
|
-
background: var(--or-good-bg);
|
|
288
|
-
border-radius: 999px;
|
|
289
|
-
color: var(--or-good);
|
|
290
|
-
padding: 2px 8px;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
[part="payment-hash"] {
|
|
294
|
-
color: var(--or-muted);
|
|
295
|
-
font-size: 12px;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
[part="invoice"] {
|
|
299
|
-
background: var(--or-bg-soft);
|
|
300
|
-
border: 1px solid var(--or-border);
|
|
301
|
-
border-radius: 6px;
|
|
302
|
-
box-sizing: border-box;
|
|
303
|
-
color: var(--or-text);
|
|
304
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
305
|
-
min-height: 68px;
|
|
306
|
-
overflow-wrap: anywhere;
|
|
307
|
-
padding: 8px;
|
|
308
|
-
resize: vertical;
|
|
309
|
-
width: 100%;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
[part="status"] {
|
|
313
|
-
align-items: center;
|
|
314
|
-
background: var(--or-bg-soft);
|
|
315
|
-
border: 1px solid var(--or-border);
|
|
316
|
-
border-radius: 6px;
|
|
317
|
-
display: flex;
|
|
318
|
-
gap: 10px;
|
|
319
|
-
padding: 8px;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
[part="status"] p {
|
|
323
|
-
color: var(--or-muted);
|
|
324
|
-
margin: 2px 0 0;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
[part="spinner"] {
|
|
328
|
-
border: 3px solid var(--or-border);
|
|
329
|
-
border-radius: 999px;
|
|
330
|
-
border-top-color: var(--or-text);
|
|
331
|
-
flex: 0 0 auto;
|
|
332
|
-
height: 22px;
|
|
333
|
-
width: 22px;
|
|
334
|
-
animation: openreceive-spin 850ms linear infinite;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
[part="countdown"] {
|
|
338
|
-
color: var(--or-muted);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
[part="actions"] {
|
|
342
|
-
display: grid;
|
|
343
|
-
gap: 8px;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
[part="wizard"] {
|
|
347
|
-
border-top: 1px solid var(--or-border);
|
|
348
|
-
display: grid;
|
|
349
|
-
gap: 12px;
|
|
350
|
-
padding-top: 12px;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
[part="wizard"] h2,
|
|
354
|
-
[part="wizard"] h3,
|
|
355
|
-
[part="wizard"] h4,
|
|
356
|
-
[part="wizard"] p {
|
|
357
|
-
margin: 0;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
[part="wizard"] p,
|
|
361
|
-
[part="wizard"] small {
|
|
362
|
-
color: var(--or-muted);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
[part="method-grid"],
|
|
366
|
-
[part="route-picker"],
|
|
367
|
-
[part="country-grid"],
|
|
368
|
-
[part="provider-grid"],
|
|
369
|
-
[part="provider-actions"] {
|
|
370
|
-
display: grid;
|
|
371
|
-
gap: 8px;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
[part="method-grid"],
|
|
375
|
-
[part="route-picker"],
|
|
376
|
-
[part="country-grid"],
|
|
377
|
-
[part="provider-grid"],
|
|
378
|
-
[part="provider-actions"] {
|
|
379
|
-
grid-template-columns: 1fr 1fr;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
[part="method"],
|
|
383
|
-
[part="route"],
|
|
384
|
-
[part="country"],
|
|
385
|
-
[part="provider"] {
|
|
386
|
-
background: var(--or-bg);
|
|
387
|
-
border: 1px solid var(--or-border);
|
|
388
|
-
border-radius: 6px;
|
|
389
|
-
box-sizing: border-box;
|
|
390
|
-
color: var(--or-text);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
[part="method"],
|
|
394
|
-
[part="route"],
|
|
395
|
-
[part="country"],
|
|
396
|
-
[part="provider"] {
|
|
397
|
-
display: grid;
|
|
398
|
-
gap: 4px;
|
|
399
|
-
min-height: 64px;
|
|
400
|
-
padding: 8px;
|
|
401
|
-
text-align: left;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
[part="method"] img,
|
|
405
|
-
[part="route"] img {
|
|
406
|
-
height: 28px;
|
|
407
|
-
width: 28px;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
[part~="selected"] {
|
|
411
|
-
border-color: var(--or-text);
|
|
412
|
-
box-shadow: 0 0 0 1px var(--or-text);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
[part="country-summary"] {
|
|
416
|
-
align-items: center;
|
|
417
|
-
border: 1px solid var(--or-border);
|
|
418
|
-
border-radius: 6px;
|
|
419
|
-
display: flex;
|
|
420
|
-
gap: 8px;
|
|
421
|
-
justify-content: space-between;
|
|
422
|
-
padding: 8px;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
[part="wizard-results"] {
|
|
426
|
-
display: grid;
|
|
427
|
-
gap: 12px;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
[part="wizard-breadcrumbs"] {
|
|
431
|
-
align-items: center;
|
|
432
|
-
display: flex;
|
|
433
|
-
flex-wrap: wrap;
|
|
434
|
-
gap: 6px;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
[part="wizard-breadcrumb"] {
|
|
438
|
-
background: transparent;
|
|
439
|
-
border: 0;
|
|
440
|
-
color: var(--or-muted);
|
|
441
|
-
gap: 6px;
|
|
442
|
-
min-height: 32px;
|
|
443
|
-
padding: 0;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
[part="wizard-breadcrumb"]:hover {
|
|
447
|
-
color: var(--or-text);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
[part="wizard-breadcrumb-current"] {
|
|
451
|
-
color: var(--or-text);
|
|
452
|
-
font-weight: 700;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
[part="wizard-breadcrumb-separator"] {
|
|
456
|
-
color: var(--or-muted);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
[part="wizard-route"] {
|
|
460
|
-
display: grid;
|
|
461
|
-
gap: 18px;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
[part="wizard-route"] h3 {
|
|
465
|
-
align-items: center;
|
|
466
|
-
display: flex;
|
|
467
|
-
flex-wrap: wrap;
|
|
468
|
-
gap: 8px;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
[part="country-select"] {
|
|
472
|
-
align-items: center;
|
|
473
|
-
display: inline-flex;
|
|
474
|
-
font-size: 0.78em;
|
|
475
|
-
gap: 6px;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
[part="country-select"] span {
|
|
479
|
-
clip: rect(0 0 0 0);
|
|
480
|
-
height: 1px;
|
|
481
|
-
overflow: hidden;
|
|
482
|
-
position: absolute;
|
|
483
|
-
white-space: nowrap;
|
|
484
|
-
width: 1px;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
[part="country-select"] select {
|
|
488
|
-
background: var(--or-bg);
|
|
489
|
-
border: 1px solid var(--or-border);
|
|
490
|
-
border-radius: 6px;
|
|
491
|
-
color: var(--or-text);
|
|
492
|
-
font: inherit;
|
|
493
|
-
min-height: 34px;
|
|
494
|
-
padding: 0 8px;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
[part="provider"] {
|
|
498
|
-
align-items: center;
|
|
499
|
-
column-gap: 10px;
|
|
500
|
-
display: grid;
|
|
501
|
-
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
502
|
-
min-height: 0;
|
|
503
|
-
row-gap: 4px;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
[part="provider-badges"] {
|
|
507
|
-
display: flex;
|
|
508
|
-
flex-wrap: wrap;
|
|
509
|
-
gap: 6px;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
[part="provider-heading"] {
|
|
513
|
-
align-items: center;
|
|
514
|
-
display: flex;
|
|
515
|
-
gap: 8px;
|
|
516
|
-
grid-column: 1;
|
|
517
|
-
justify-content: start;
|
|
518
|
-
min-width: 0;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
[part="provider-heading"] img {
|
|
522
|
-
border-radius: 6px;
|
|
523
|
-
flex: 0 0 auto;
|
|
524
|
-
height: 28px;
|
|
525
|
-
width: 28px;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
[part="provider-heading"] h4 {
|
|
529
|
-
flex: 1 1 auto;
|
|
530
|
-
min-width: 0;
|
|
531
|
-
overflow: hidden;
|
|
532
|
-
text-overflow: ellipsis;
|
|
533
|
-
white-space: nowrap;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
[part="provider-kind"] {
|
|
537
|
-
grid-column: 1;
|
|
538
|
-
color: var(--or-muted);
|
|
539
|
-
font-size: 0.9em;
|
|
540
|
-
line-height: 1.3;
|
|
541
|
-
margin: -2px 0 0;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
[part="provider-badges"] span,
|
|
545
|
-
[part="recommended"] {
|
|
546
|
-
background: var(--or-bg-soft);
|
|
547
|
-
border-radius: 999px;
|
|
548
|
-
color: var(--or-muted);
|
|
549
|
-
font-size: 12px;
|
|
550
|
-
grid-column: 2;
|
|
551
|
-
justify-self: end;
|
|
552
|
-
padding: 2px 7px;
|
|
553
|
-
white-space: nowrap;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
button,
|
|
557
|
-
a[part="open"],
|
|
558
|
-
button[part="provider-open"] {
|
|
559
|
-
align-items: center;
|
|
560
|
-
border: 1px solid var(--or-text);
|
|
561
|
-
border-radius: 6px;
|
|
562
|
-
box-sizing: border-box;
|
|
563
|
-
color: var(--or-text);
|
|
564
|
-
cursor: pointer;
|
|
565
|
-
display: inline-flex;
|
|
566
|
-
font: inherit;
|
|
567
|
-
justify-content: center;
|
|
568
|
-
min-height: 40px;
|
|
569
|
-
padding: 8px 10px;
|
|
570
|
-
text-decoration: none;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
button {
|
|
574
|
-
background: var(--or-bg);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
a[part="open"],
|
|
578
|
-
button[part="provider-open"] {
|
|
579
|
-
background: var(--or-text);
|
|
580
|
-
color: var(--or-bg);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
[part="provider-actions"] {
|
|
584
|
-
align-self: center;
|
|
585
|
-
display: flex;
|
|
586
|
-
grid-column: 3;
|
|
587
|
-
grid-row: 1 / span 2;
|
|
588
|
-
justify-self: end;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
button[part="provider-open"],
|
|
592
|
-
[part="provider-actions"] a {
|
|
593
|
-
background: var(--or-bg-soft);
|
|
594
|
-
color: var(--or-text);
|
|
595
|
-
min-height: 36px;
|
|
596
|
-
padding: 7px 12px;
|
|
597
|
-
white-space: nowrap;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
[part="tutorial"] {
|
|
601
|
-
align-items: center;
|
|
602
|
-
background: rgb(0 0 0 / 0.6);
|
|
603
|
-
display: flex;
|
|
604
|
-
inset: 0;
|
|
605
|
-
justify-content: center;
|
|
606
|
-
padding: 16px;
|
|
607
|
-
position: fixed;
|
|
608
|
-
z-index: 10;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
[part="tutorial"][hidden] {
|
|
612
|
-
display: none;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
[part="tutorial-dialog"] {
|
|
616
|
-
background: var(--or-bg);
|
|
617
|
-
border: 1px solid var(--or-border);
|
|
618
|
-
border-radius: 8px;
|
|
619
|
-
box-shadow: 0 20px 70px rgb(0 0 0 / 0.32);
|
|
620
|
-
box-sizing: border-box;
|
|
621
|
-
display: grid;
|
|
622
|
-
gap: 12px;
|
|
623
|
-
max-height: min(92vh, 900px);
|
|
624
|
-
max-width: min(440px, 100%);
|
|
625
|
-
min-width: 0;
|
|
626
|
-
overflow: hidden;
|
|
627
|
-
padding: 12px;
|
|
628
|
-
width: 100%;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
[part="tutorial-header"],
|
|
632
|
-
[part="tutorial-controls"] {
|
|
633
|
-
align-items: center;
|
|
634
|
-
display: flex;
|
|
635
|
-
gap: 8px;
|
|
636
|
-
justify-content: space-between;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
[part="tutorial-header"] h3 {
|
|
640
|
-
font-size: 16px;
|
|
641
|
-
line-height: 1.25;
|
|
642
|
-
min-width: 0;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
[part="tutorial-title"] {
|
|
646
|
-
align-items: center;
|
|
647
|
-
display: flex;
|
|
648
|
-
gap: 10px;
|
|
649
|
-
min-width: 0;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
[part="tutorial-header-logo"] {
|
|
653
|
-
border-radius: 8px;
|
|
654
|
-
flex: 0 0 auto;
|
|
655
|
-
height: 36px;
|
|
656
|
-
width: 36px;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
[part="tutorial-close"] {
|
|
660
|
-
background: var(--or-bg-soft);
|
|
661
|
-
border-color: var(--or-border);
|
|
662
|
-
color: var(--or-text);
|
|
663
|
-
flex: 0 0 auto;
|
|
664
|
-
font-size: 16px;
|
|
665
|
-
font-weight: 700;
|
|
666
|
-
height: 36px;
|
|
667
|
-
min-height: 36px;
|
|
668
|
-
padding: 0;
|
|
669
|
-
width: 36px;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
[part="tutorial-frame"] {
|
|
673
|
-
align-items: center;
|
|
674
|
-
background: var(--or-bg-soft);
|
|
675
|
-
border: 1px solid var(--or-border);
|
|
676
|
-
border-radius: 8px;
|
|
677
|
-
display: flex;
|
|
678
|
-
justify-content: center;
|
|
679
|
-
min-height: 0;
|
|
680
|
-
overflow: hidden;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
[part="tutorial-image"] {
|
|
684
|
-
display: block;
|
|
685
|
-
height: auto;
|
|
686
|
-
max-height: min(66vh, 720px);
|
|
687
|
-
max-width: 100%;
|
|
688
|
-
object-fit: contain;
|
|
689
|
-
width: auto;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
[part="tutorial-caption"] {
|
|
693
|
-
color: var(--or-text);
|
|
694
|
-
font-size: 16px;
|
|
695
|
-
font-weight: 700;
|
|
696
|
-
text-align: center;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
[part="tutorial-intro"] {
|
|
700
|
-
display: grid;
|
|
701
|
-
gap: 12px;
|
|
702
|
-
padding: 16px 8px;
|
|
703
|
-
text-align: center;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
[part="tutorial-provider-logo"] {
|
|
707
|
-
border-radius: 8px;
|
|
708
|
-
height: 52px;
|
|
709
|
-
justify-self: center;
|
|
710
|
-
width: 52px;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
[part="tutorial-intro"] p {
|
|
714
|
-
color: var(--or-text);
|
|
715
|
-
font-size: 15px;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
[part="tutorial-copy"] {
|
|
719
|
-
background: var(--or-warm);
|
|
720
|
-
border-color: var(--or-warm);
|
|
721
|
-
color: #111827;
|
|
722
|
-
justify-self: center;
|
|
723
|
-
min-width: min(240px, 100%);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
[part="tutorial-copy-message"] {
|
|
727
|
-
color: var(--or-good);
|
|
728
|
-
font-weight: 700;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
[part="tutorial-progress"] {
|
|
732
|
-
color: var(--or-muted);
|
|
733
|
-
font-size: 12px;
|
|
734
|
-
text-align: center;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
[part="tutorial-steps"] {
|
|
738
|
-
display: flex;
|
|
739
|
-
gap: 6px;
|
|
740
|
-
justify-content: center;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
[part="tutorial-step"],
|
|
744
|
-
[part="tutorial-step-active"] {
|
|
745
|
-
background: var(--or-border);
|
|
746
|
-
border-radius: 999px;
|
|
747
|
-
height: 7px;
|
|
748
|
-
width: 7px;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
[part="tutorial-step-active"] {
|
|
752
|
-
background: var(--or-text);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
[part="tutorial-nav"] {
|
|
756
|
-
flex: 1 1 0;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
[part="tutorial-controls"] [part="tutorial-nav"]:last-child:not(:disabled) {
|
|
760
|
-
background: var(--or-warm);
|
|
761
|
-
border-color: var(--or-warm);
|
|
762
|
-
color: #111827;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
[part="tutorial-nav"]:disabled {
|
|
766
|
-
background: var(--or-bg-soft);
|
|
767
|
-
border-color: var(--or-border);
|
|
768
|
-
color: var(--or-muted);
|
|
769
|
-
cursor: not-allowed;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
@media (max-width: 420px) {
|
|
773
|
-
[part="method-grid"],
|
|
774
|
-
[part="route-picker"],
|
|
775
|
-
[part="country-grid"],
|
|
776
|
-
[part="provider-grid"],
|
|
777
|
-
[part="provider-actions"] {
|
|
778
|
-
grid-template-columns: 1fr;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
[part="tutorial"] {
|
|
782
|
-
align-items: stretch;
|
|
783
|
-
padding: 8px;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
[part="tutorial-dialog"] {
|
|
787
|
-
max-height: calc(100vh - 16px);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
[part="tutorial-controls"] {
|
|
791
|
-
display: grid;
|
|
792
|
-
grid-template-columns: 1fr 1fr;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
@keyframes openreceive-spin {
|
|
797
|
-
to {
|
|
798
|
-
transform: rotate(360deg);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
`;
|
|
802
|
-
export const openReceiveThemeToggleElementStyles = `
|
|
803
|
-
:host {
|
|
804
|
-
display: inline-block;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
button {
|
|
808
|
-
align-items: center;
|
|
809
|
-
background: var(--or-theme-toggle-bg, transparent);
|
|
810
|
-
border: var(--or-theme-toggle-border, 1px solid currentColor);
|
|
811
|
-
border-radius: var(--or-theme-toggle-radius, 999px);
|
|
812
|
-
color: inherit;
|
|
813
|
-
cursor: pointer;
|
|
814
|
-
display: inline-flex;
|
|
815
|
-
gap: 8px;
|
|
816
|
-
font: inherit;
|
|
817
|
-
min-height: var(--or-theme-toggle-min-height, 34px);
|
|
818
|
-
padding: var(--or-theme-toggle-padding, 4px 12px 4px 4px);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
.or-theme-toggle-track {
|
|
822
|
-
align-items: center;
|
|
823
|
-
background: transparent;
|
|
824
|
-
border: 0;
|
|
825
|
-
border-radius: 999px;
|
|
826
|
-
display: grid;
|
|
827
|
-
flex: 0 0 auto;
|
|
828
|
-
grid-template-columns: 1fr;
|
|
829
|
-
height: 24px;
|
|
830
|
-
padding: 0;
|
|
831
|
-
width: 24px;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
.or-theme-toggle-icon {
|
|
835
|
-
border-radius: 999px;
|
|
836
|
-
display: block;
|
|
837
|
-
grid-area: 1 / 1;
|
|
838
|
-
height: 24px;
|
|
839
|
-
position: relative;
|
|
840
|
-
transition: background 160ms ease, box-shadow 160ms ease, opacity 160ms ease;
|
|
841
|
-
width: 24px;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
.or-theme-toggle-icon-light {
|
|
845
|
-
opacity: 1;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
.or-theme-toggle-dark .or-theme-toggle-icon-light {
|
|
849
|
-
background: #94a3b8;
|
|
850
|
-
box-shadow: none;
|
|
851
|
-
opacity: 0.72;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
.or-theme-toggle-label {
|
|
855
|
-
font-size: 14px;
|
|
856
|
-
font-weight: 700;
|
|
857
|
-
line-height: 1;
|
|
858
|
-
white-space: nowrap;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
.or-theme-toggle-icon-light {
|
|
862
|
-
background: #facc15;
|
|
863
|
-
box-shadow: 0 0 0 2px rgb(250 204 21 / 0.2);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
.or-theme-toggle-icon-light::before,
|
|
867
|
-
.or-theme-toggle-icon-light::after {
|
|
868
|
-
content: "";
|
|
869
|
-
position: absolute;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
.or-theme-toggle-icon-light::before {
|
|
873
|
-
border: 2px solid rgb(255 255 255 / 0.72);
|
|
874
|
-
border-radius: 999px;
|
|
875
|
-
inset: 5px;
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
.or-theme-toggle-icon-light::after {
|
|
879
|
-
background:
|
|
880
|
-
radial-gradient(circle, transparent 50%, #facc15 52%),
|
|
881
|
-
conic-gradient(
|
|
882
|
-
from 0deg,
|
|
883
|
-
transparent 0 8deg,
|
|
884
|
-
rgb(255 255 255 / 0.75) 8deg 14deg,
|
|
885
|
-
transparent 14deg 45deg,
|
|
886
|
-
rgb(255 255 255 / 0.75) 45deg 51deg,
|
|
887
|
-
transparent 51deg 82deg,
|
|
888
|
-
rgb(255 255 255 / 0.75) 82deg 88deg,
|
|
889
|
-
transparent 88deg 127deg,
|
|
890
|
-
rgb(255 255 255 / 0.75) 127deg 133deg,
|
|
891
|
-
transparent 133deg 172deg,
|
|
892
|
-
rgb(255 255 255 / 0.75) 172deg 178deg,
|
|
893
|
-
transparent 178deg 217deg,
|
|
894
|
-
rgb(255 255 255 / 0.75) 217deg 223deg,
|
|
895
|
-
transparent 223deg 262deg,
|
|
896
|
-
rgb(255 255 255 / 0.75) 262deg 268deg,
|
|
897
|
-
transparent 268deg 307deg,
|
|
898
|
-
rgb(255 255 255 / 0.75) 307deg 313deg,
|
|
899
|
-
transparent 313deg 360deg
|
|
900
|
-
);
|
|
901
|
-
border-radius: 999px;
|
|
902
|
-
inset: 3px;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
`;
|
|
906
|
-
export const openReceivePaymentMethods = [
|
|
907
|
-
{
|
|
908
|
-
id: "card",
|
|
909
|
-
title: "Credit Card",
|
|
910
|
-
detail: "Pick your country, then use a card-friendly provider."
|
|
911
|
-
},
|
|
912
|
-
{
|
|
913
|
-
id: "bank",
|
|
914
|
-
title: "Bank Transfer",
|
|
915
|
-
detail: "Choose a country for local bank rails and cash apps."
|
|
916
|
-
},
|
|
917
|
-
{
|
|
918
|
-
id: "bitcoin",
|
|
919
|
-
title: "Bitcoin",
|
|
920
|
-
detail: "Pay from Lightning or send on-chain into a swap."
|
|
921
|
-
},
|
|
922
|
-
{
|
|
923
|
-
id: "crypto",
|
|
924
|
-
title: "Crypto",
|
|
925
|
-
detail: "Use stablecoins or altcoins through Lightning-capable services."
|
|
926
|
-
}
|
|
927
|
-
];
|
|
928
|
-
export const openReceiveRegionLabels = {
|
|
929
|
-
"north-america": "North America",
|
|
930
|
-
"latin-america": "Latin America",
|
|
931
|
-
europe: "Europe",
|
|
932
|
-
africa: "Africa",
|
|
933
|
-
"middle-east": "Middle East",
|
|
934
|
-
"asia-pacific": "Asia Pacific"
|
|
935
|
-
};
|
|
936
|
-
export const openReceiveRegionOrder = [
|
|
937
|
-
"north-america",
|
|
938
|
-
"latin-america",
|
|
939
|
-
"europe",
|
|
940
|
-
"africa",
|
|
941
|
-
"middle-east",
|
|
942
|
-
"asia-pacific"
|
|
943
|
-
];
|
|
944
|
-
export function parseOpenReceiveOptionalInteger(value, options = {}) {
|
|
945
|
-
if (value === null || value === undefined || value === "")
|
|
946
|
-
return undefined;
|
|
947
|
-
const parsed = Number(value);
|
|
948
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
949
|
-
throw new RangeError(`${options.label ?? "value"} must be a non-negative safe integer`);
|
|
950
|
-
}
|
|
951
|
-
return parsed;
|
|
952
|
-
}
|
|
953
|
-
export function parseOpenReceiveBooleanAttribute(value) {
|
|
954
|
-
if (value === null || value === undefined)
|
|
955
|
-
return undefined;
|
|
956
|
-
return value !== "false";
|
|
957
|
-
}
|
|
958
|
-
export function parseOpenReceiveResolvedTheme(value) {
|
|
959
|
-
return value === "light" || value === "dark" ? value : undefined;
|
|
960
|
-
}
|
|
961
|
-
export function parseOpenReceiveThemePreference(value) {
|
|
962
|
-
return value === "light" || value === "dark" || value === "system"
|
|
963
|
-
? value
|
|
964
|
-
: undefined;
|
|
965
|
-
}
|
|
966
|
-
export function parseOpenReceivePaymentMethod(value) {
|
|
967
|
-
return value === "card" ||
|
|
968
|
-
value === "bank" ||
|
|
969
|
-
value === "bitcoin" ||
|
|
970
|
-
value === "crypto"
|
|
971
|
-
? value
|
|
972
|
-
: null;
|
|
973
|
-
}
|
|
974
|
-
export function parseOpenReceiveRegion(value) {
|
|
975
|
-
return openReceiveRegionOrder.includes(value)
|
|
976
|
-
? value
|
|
977
|
-
: null;
|
|
978
|
-
}
|
|
979
|
-
export const openReceiveCountryMapRegions = [
|
|
980
|
-
{
|
|
981
|
-
id: "north-america",
|
|
982
|
-
cx: 180,
|
|
983
|
-
cy: 125,
|
|
984
|
-
rx: 150,
|
|
985
|
-
ry: 78
|
|
986
|
-
},
|
|
987
|
-
{
|
|
988
|
-
id: "latin-america",
|
|
989
|
-
cx: 265,
|
|
990
|
-
cy: 260,
|
|
991
|
-
rx: 82,
|
|
992
|
-
ry: 130
|
|
993
|
-
},
|
|
994
|
-
{
|
|
995
|
-
id: "europe",
|
|
996
|
-
cx: 425,
|
|
997
|
-
cy: 145,
|
|
998
|
-
rx: 105,
|
|
999
|
-
ry: 64
|
|
1000
|
-
},
|
|
1001
|
-
{
|
|
1002
|
-
id: "africa",
|
|
1003
|
-
cx: 455,
|
|
1004
|
-
cy: 255,
|
|
1005
|
-
rx: 76,
|
|
1006
|
-
ry: 102
|
|
1007
|
-
},
|
|
1008
|
-
{
|
|
1009
|
-
id: "middle-east",
|
|
1010
|
-
cx: 535,
|
|
1011
|
-
cy: 213,
|
|
1012
|
-
rx: 78,
|
|
1013
|
-
ry: 58
|
|
1014
|
-
},
|
|
1015
|
-
{
|
|
1016
|
-
id: "asia-pacific",
|
|
1017
|
-
cx: 655,
|
|
1018
|
-
cy: 215,
|
|
1019
|
-
rx: 165,
|
|
1020
|
-
ry: 120
|
|
1021
|
-
}
|
|
1022
|
-
];
|
|
1023
|
-
export const openReceiveCountryPins = {
|
|
1024
|
-
AR: { region: "latin-america", coordinates: [-64, -34] },
|
|
1025
|
-
AU: { region: "asia-pacific", coordinates: [134, -25] },
|
|
1026
|
-
BD: { region: "asia-pacific", coordinates: [90, 24] },
|
|
1027
|
-
BR: { region: "latin-america", coordinates: [-52, -10] },
|
|
1028
|
-
CA: { region: "north-america", coordinates: [-106, 56] },
|
|
1029
|
-
CH: { region: "europe", coordinates: [8, 47] },
|
|
1030
|
-
CL: { region: "latin-america", coordinates: [-71, -30] },
|
|
1031
|
-
CO: { region: "latin-america", coordinates: [-74, 4] },
|
|
1032
|
-
DE: { region: "europe", coordinates: [10, 51] },
|
|
1033
|
-
EG: { region: "africa", coordinates: [30, 27] },
|
|
1034
|
-
ES: { region: "europe", coordinates: [-4, 40] },
|
|
1035
|
-
FR: { region: "europe", coordinates: [2, 47] },
|
|
1036
|
-
GB: { region: "europe", coordinates: [-2, 54] },
|
|
1037
|
-
GH: { region: "africa", coordinates: [-1, 8] },
|
|
1038
|
-
IE: { region: "europe", coordinates: [-8, 53] },
|
|
1039
|
-
ID: { region: "asia-pacific", coordinates: [118, -2] },
|
|
1040
|
-
IN: { region: "asia-pacific", coordinates: [78, 22] },
|
|
1041
|
-
IT: { region: "europe", coordinates: [12, 43] },
|
|
1042
|
-
JP: { region: "asia-pacific", coordinates: [138, 37] },
|
|
1043
|
-
KE: { region: "africa", coordinates: [38, 0] },
|
|
1044
|
-
KR: { region: "asia-pacific", coordinates: [128, 36] },
|
|
1045
|
-
MX: { region: "latin-america", coordinates: [-102, 23] },
|
|
1046
|
-
NG: { region: "africa", coordinates: [8, 9] },
|
|
1047
|
-
NL: { region: "europe", coordinates: [5, 52] },
|
|
1048
|
-
PH: { region: "asia-pacific", coordinates: [122, 13] },
|
|
1049
|
-
PK: { region: "asia-pacific", coordinates: [70, 30] },
|
|
1050
|
-
PL: { region: "europe", coordinates: [19, 52] },
|
|
1051
|
-
PT: { region: "europe", coordinates: [-8, 39] },
|
|
1052
|
-
SA: { region: "middle-east", coordinates: [45, 24] },
|
|
1053
|
-
SG: { region: "asia-pacific", coordinates: [104, 1.3] },
|
|
1054
|
-
SV: { region: "latin-america", coordinates: [-89, 13.8] },
|
|
1055
|
-
TH: { region: "asia-pacific", coordinates: [101, 15] },
|
|
1056
|
-
TR: { region: "middle-east", coordinates: [35, 39] },
|
|
1057
|
-
UA: { region: "europe", coordinates: [31, 49] },
|
|
1058
|
-
US: { region: "north-america", coordinates: [-98, 39] },
|
|
1059
|
-
VE: { region: "latin-america", coordinates: [-66, 7] },
|
|
1060
|
-
VN: { region: "asia-pacific", coordinates: [108, 16] },
|
|
1061
|
-
ZA: { region: "africa", coordinates: [24, -29] },
|
|
1062
|
-
AE: { region: "middle-east", coordinates: [54, 24] }
|
|
1063
|
-
};
|
|
1064
|
-
export function getOpenReceiveDefaultCountryCode() {
|
|
1065
|
-
return getCountries().find((country) => country.code === "US")?.code ?? getCountries()[0]?.code ?? "";
|
|
1066
|
-
}
|
|
1067
|
-
export function getOpenReceiveBitcoinAssets() {
|
|
1068
|
-
return getAssets().filter((asset) => asset.symbol === "btc" && asset.route !== undefined);
|
|
1069
|
-
}
|
|
1070
|
-
function getOpenReceiveDefaultBitcoinRoute() {
|
|
1071
|
-
const routes = [
|
|
1072
|
-
...new Set(getOpenReceiveBitcoinAssets().flatMap((asset) => asset.route === undefined ? [] : [asset.route]))
|
|
1073
|
-
];
|
|
1074
|
-
return routes.length === 1 ? routes[0] ?? null : null;
|
|
1075
|
-
}
|
|
1076
|
-
export function getOpenReceiveAltcoinAssets() {
|
|
1077
|
-
return getAssets().filter((asset) => asset.route !== undefined &&
|
|
1078
|
-
asset.symbol !== "btc" &&
|
|
1079
|
-
!["usd", "eur", "gbp"].includes(asset.symbol));
|
|
1080
|
-
}
|
|
1081
|
-
export function getOpenReceiveRegionForCountry(countryCode) {
|
|
1082
|
-
return openReceiveCountryPins[countryCode]?.region ?? "north-america";
|
|
1083
|
-
}
|
|
1084
|
-
export function getOpenReceiveCoverageLabel(coverage) {
|
|
1085
|
-
void coverage;
|
|
1086
|
-
return "";
|
|
1087
|
-
}
|
|
1088
|
-
export function formatOpenReceiveCountryMetaLabel(country) {
|
|
1089
|
-
return country.currency;
|
|
1090
|
-
}
|
|
1091
|
-
export function createOpenReceiveCountryDisplay(country, options = {}) {
|
|
1092
|
-
return {
|
|
1093
|
-
country,
|
|
1094
|
-
code: country.code,
|
|
1095
|
-
label: country.name,
|
|
1096
|
-
metaLabel: formatOpenReceiveCountryMetaLabel(country),
|
|
1097
|
-
selected: country.code === options.selectedCountryCode
|
|
1098
|
-
};
|
|
1099
|
-
}
|
|
1100
|
-
export function getOpenReceivePaymentStatusText(phase) {
|
|
1101
|
-
if (phase === "settled") {
|
|
1102
|
-
return {
|
|
1103
|
-
title: openReceiveCheckoutLabels.paymentStatus.settledTitle,
|
|
1104
|
-
detail: openReceiveCheckoutLabels.paymentStatus.settledDetail
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
if (phase === "expired") {
|
|
1108
|
-
return {
|
|
1109
|
-
title: openReceiveCheckoutLabels.paymentStatus.expiredTitle,
|
|
1110
|
-
detail: openReceiveCheckoutLabels.paymentStatus.expiredDetail
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
1113
|
-
return {
|
|
1114
|
-
title: openReceiveCheckoutLabels.paymentStatus.waitingTitle,
|
|
1115
|
-
detail: openReceiveCheckoutLabels.paymentStatus.waitingDetail
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
export function getOpenReceiveWizardEmptyMessage(method) {
|
|
1119
|
-
if (method === "bitcoin")
|
|
1120
|
-
return openReceiveCheckoutLabels.emptyBitcoin;
|
|
1121
|
-
if (method === "crypto")
|
|
1122
|
-
return openReceiveCheckoutLabels.emptyCrypto;
|
|
1123
|
-
return openReceiveCheckoutLabels.emptyFiat;
|
|
1124
|
-
}
|
|
1125
|
-
export function getCheckoutProviderUsBadge(us) {
|
|
1126
|
-
void us;
|
|
1127
|
-
return null;
|
|
1128
|
-
}
|
|
1129
|
-
export function getCheckoutProviderOpenLabel(providerName) {
|
|
1130
|
-
void providerName;
|
|
1131
|
-
return openReceiveCheckoutLabels.openProvider;
|
|
1132
|
-
}
|
|
1133
|
-
export function getCheckoutProviderIcon(provider) {
|
|
1134
|
-
return openReceiveProviderIconUrls[provider.icon_path] ?? openReceivePaymentIconUrls.crypto;
|
|
1135
|
-
}
|
|
1136
|
-
export function getCheckoutProviderTutorials(provider) {
|
|
1137
|
-
return (provider.tutorials ?? []).map((tutorial) => ({
|
|
1138
|
-
index: tutorial.index,
|
|
1139
|
-
path: tutorial.path,
|
|
1140
|
-
image: openReceivePayTutorialUrls[tutorial.path] ?? tutorial.path,
|
|
1141
|
-
caption: tutorial.caption
|
|
1142
|
-
}));
|
|
1143
|
-
}
|
|
1144
|
-
export function getOpenReceiveRouteNetworkLabel(routeId) {
|
|
1145
|
-
return routeId === "lightning" || routeId === "btc-lightning"
|
|
1146
|
-
? openReceiveCheckoutLabels.lightningNetwork
|
|
1147
|
-
: routeId;
|
|
1148
|
-
}
|
|
1149
|
-
export function createOpenReceiveWizardRouteAssetDisplays(assets, options = {}) {
|
|
1150
|
-
return assets.map((asset) => {
|
|
1151
|
-
const id = asset.route ?? asset.symbol;
|
|
1152
|
-
return {
|
|
1153
|
-
id,
|
|
1154
|
-
label: asset.label,
|
|
1155
|
-
subtitle: getOpenReceiveRouteNetworkLabel(id),
|
|
1156
|
-
icon: getOpenReceiveRouteIcon(asset),
|
|
1157
|
-
selected: options.selectedRoute === id
|
|
1158
|
-
};
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
export function createOpenReceiveWizardRouteDisplays(routes, options = {}) {
|
|
1162
|
-
return routes.map((route) => ({
|
|
1163
|
-
key: getOpenReceiveWizardRouteDisplayKey(route),
|
|
1164
|
-
title: getOpenReceiveWizardRouteDisplayTitle(route),
|
|
1165
|
-
subtitle: getOpenReceiveWizardRouteDisplaySubtitle(route),
|
|
1166
|
-
providers: (options.providerPreviewLimit === undefined
|
|
1167
|
-
? route.providers
|
|
1168
|
-
: route.providers.slice(0, options.providerPreviewLimit)).map((entry) => createOpenReceiveWizardProviderDisplay(entry))
|
|
1169
|
-
}));
|
|
1170
|
-
}
|
|
1171
|
-
function getOpenReceiveWizardRouteDisplayKey(route) {
|
|
1172
|
-
return route.kind === "crypto"
|
|
1173
|
-
? route.route.id
|
|
1174
|
-
: `${route.rail.id}:${route.country.code}`;
|
|
1175
|
-
}
|
|
1176
|
-
function getOpenReceiveWizardRouteDisplayTitle(route) {
|
|
1177
|
-
return route.kind === "crypto"
|
|
1178
|
-
? route.route.label
|
|
1179
|
-
: route.rail.label;
|
|
1180
|
-
}
|
|
1181
|
-
function getOpenReceiveWizardRouteDisplaySubtitle(route) {
|
|
1182
|
-
return route.kind === "crypto" ? route.route.symbol.toUpperCase() : route.country.currency;
|
|
1183
|
-
}
|
|
1184
|
-
function createOpenReceiveWizardProviderDisplay(entry) {
|
|
1185
|
-
return {
|
|
1186
|
-
id: entry.provider.id,
|
|
1187
|
-
name: entry.provider.name,
|
|
1188
|
-
kind: entry.provider.kind,
|
|
1189
|
-
url: entry.provider.lightning_docs_url ?? entry.provider.url,
|
|
1190
|
-
icon: getCheckoutProviderIcon(entry.provider),
|
|
1191
|
-
tutorials: getCheckoutProviderTutorials(entry.provider),
|
|
1192
|
-
recommended: entry.flagship,
|
|
1193
|
-
recommendedLabel: entry.flagship ? openReceiveCheckoutLabels.recommended : null,
|
|
1194
|
-
usBadge: null,
|
|
1195
|
-
copyLabel: openReceiveCheckoutLabels.copyInvoice,
|
|
1196
|
-
copiedLabel: openReceiveCheckoutLabels.copied,
|
|
1197
|
-
openLabel: getCheckoutProviderOpenLabel(entry.provider.name)
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
export function getOpenReceivePaymentMethodIcon(method) {
|
|
1201
|
-
return openReceivePaymentIconUrls[openReceivePaymentMethodIconIds[method]];
|
|
1202
|
-
}
|
|
1203
|
-
export function getOpenReceiveAssetIcon(symbol) {
|
|
1204
|
-
return openReceivePaymentIconUrls[openReceiveAssetIconIds[symbol] ?? "crypto"];
|
|
1205
|
-
}
|
|
1206
|
-
export function getOpenReceiveRouteIcon(asset) {
|
|
1207
|
-
const routeId = asset.route ?? asset.symbol;
|
|
1208
|
-
if (asset.symbol === "btc" && routeId.includes("lightning")) {
|
|
1209
|
-
return openReceivePaymentIconUrls.lightning;
|
|
1210
|
-
}
|
|
1211
|
-
return getOpenReceiveAssetIcon(asset.symbol);
|
|
1212
|
-
}
|
|
1213
|
-
export function getOpenReceiveCountriesForRail(rail) {
|
|
1214
|
-
return getCountries()
|
|
1215
|
-
.filter((country) => openReceiveCountryPins[country.code] !== undefined &&
|
|
1216
|
-
getCountryRoutes(country.code).some((route) => route.rail.id === rail))
|
|
1217
|
-
.sort((left, right) => left.name.localeCompare(right.name, "en"));
|
|
1218
|
-
}
|
|
1219
|
-
export function projectOpenReceiveCountryMapPoint(coordinates, options = {}) {
|
|
1220
|
-
const width = options.width ?? OPENRECEIVE_COUNTRY_MAP_WIDTH;
|
|
1221
|
-
const height = options.height ?? OPENRECEIVE_COUNTRY_MAP_HEIGHT;
|
|
1222
|
-
const longitude = Math.max(-180, Math.min(180, coordinates[0]));
|
|
1223
|
-
const latitude = Math.max(-85, Math.min(85, coordinates[1]));
|
|
1224
|
-
return [
|
|
1225
|
-
((longitude + 180) / 360) * width,
|
|
1226
|
-
((85 - latitude) / 170) * height
|
|
1227
|
-
];
|
|
1228
|
-
}
|
|
1229
|
-
export function createOpenReceiveCountryPickerModel(request) {
|
|
1230
|
-
const selectedCountry = request.countries.find((country) => country.code === request.selectedCountryCode);
|
|
1231
|
-
const hoveredCountry = request.hoveredCountryCode === undefined || request.hoveredCountryCode === null
|
|
1232
|
-
? undefined
|
|
1233
|
-
: request.countries.find((country) => country.code === request.hoveredCountryCode);
|
|
1234
|
-
const visibleRegionCountries = request.countries.filter((country) => getOpenReceiveRegionForCountry(country.code) === request.selectedRegion);
|
|
1235
|
-
const selectedCountryDisplay = selectedCountry === undefined
|
|
1236
|
-
? undefined
|
|
1237
|
-
: createOpenReceiveCountryDisplay(selectedCountry, {
|
|
1238
|
-
selectedCountryCode: request.selectedCountryCode
|
|
1239
|
-
});
|
|
1240
|
-
const hoveredCountryDisplay = hoveredCountry === undefined
|
|
1241
|
-
? undefined
|
|
1242
|
-
: createOpenReceiveCountryDisplay(hoveredCountry, {
|
|
1243
|
-
selectedCountryCode: request.selectedCountryCode
|
|
1244
|
-
});
|
|
1245
|
-
const visibleRegionCountryDisplays = visibleRegionCountries.map((country) => createOpenReceiveCountryDisplay(country, {
|
|
1246
|
-
selectedCountryCode: request.selectedCountryCode
|
|
1247
|
-
}));
|
|
1248
|
-
const regions = openReceiveRegionOrder.map((region) => {
|
|
1249
|
-
const count = request.countries.filter((country) => getOpenReceiveRegionForCountry(country.code) === region).length;
|
|
1250
|
-
return {
|
|
1251
|
-
id: region,
|
|
1252
|
-
label: openReceiveRegionLabels[region],
|
|
1253
|
-
count,
|
|
1254
|
-
enabled: count > 0,
|
|
1255
|
-
selected: region === request.selectedRegion
|
|
1256
|
-
};
|
|
1257
|
-
});
|
|
1258
|
-
const mapCountries = request.countries.flatMap((country) => {
|
|
1259
|
-
const pin = openReceiveCountryPins[country.code];
|
|
1260
|
-
if (pin === undefined)
|
|
1261
|
-
return [];
|
|
1262
|
-
return [
|
|
1263
|
-
{
|
|
1264
|
-
country,
|
|
1265
|
-
region: pin.region,
|
|
1266
|
-
coordinates: pin.coordinates,
|
|
1267
|
-
point: projectOpenReceiveCountryMapPoint(pin.coordinates),
|
|
1268
|
-
selected: country.code === request.selectedCountryCode,
|
|
1269
|
-
hovered: country.code === request.hoveredCountryCode,
|
|
1270
|
-
label: country.name,
|
|
1271
|
-
metaLabel: formatOpenReceiveCountryMetaLabel(country)
|
|
1272
|
-
}
|
|
1273
|
-
];
|
|
1274
|
-
});
|
|
1275
|
-
return {
|
|
1276
|
-
countries: request.countries,
|
|
1277
|
-
regions,
|
|
1278
|
-
selectedCountry,
|
|
1279
|
-
hoveredCountry,
|
|
1280
|
-
...(selectedCountryDisplay === undefined ? {} : { selectedCountryDisplay }),
|
|
1281
|
-
...(hoveredCountryDisplay === undefined ? {} : { hoveredCountryDisplay }),
|
|
1282
|
-
readoutLabel: hoveredCountryDisplay?.label ??
|
|
1283
|
-
selectedCountryDisplay?.label ??
|
|
1284
|
-
openReceiveCheckoutLabels.chooseCountry,
|
|
1285
|
-
...((hoveredCountryDisplay ?? selectedCountryDisplay) === undefined
|
|
1286
|
-
? {}
|
|
1287
|
-
: { readoutMetaLabel: (hoveredCountryDisplay ?? selectedCountryDisplay)?.metaLabel }),
|
|
1288
|
-
visibleRegionCountries,
|
|
1289
|
-
visibleRegionCountryDisplays,
|
|
1290
|
-
mapCountries
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
export function createOpenReceivePaymentWizardState(request) {
|
|
1294
|
-
const selectedRail = getRailForPaymentMethod(request.selectedMethod);
|
|
1295
|
-
const railCountries = selectedRail === null ? [] : getOpenReceiveCountriesForRail(selectedRail);
|
|
1296
|
-
const selectedCountry = request.selectedCountryCode === undefined
|
|
1297
|
-
? railCountries[0]
|
|
1298
|
-
: railCountries.find((country) => country.code === request.selectedCountryCode) ??
|
|
1299
|
-
railCountries[0];
|
|
1300
|
-
const selectedRouteId = request.selectedMethod === "bitcoin"
|
|
1301
|
-
? request.selectedBitcoinRoute ?? getOpenReceiveDefaultBitcoinRoute()
|
|
1302
|
-
: request.selectedMethod === "crypto"
|
|
1303
|
-
? request.selectedCryptoRoute ?? null
|
|
1304
|
-
: null;
|
|
1305
|
-
const routes = selectedRail !== null && selectedCountry !== undefined
|
|
1306
|
-
? getPaymentWizardRoutes({
|
|
1307
|
-
country: selectedCountry.code,
|
|
1308
|
-
rail: selectedRail
|
|
1309
|
-
})
|
|
1310
|
-
: selectedRouteId === null
|
|
1311
|
-
? []
|
|
1312
|
-
: getPaymentWizardRoutes({ route: selectedRouteId });
|
|
1313
|
-
return {
|
|
1314
|
-
selectedRail,
|
|
1315
|
-
...(selectedCountry === undefined ? {} : { selectedCountry }),
|
|
1316
|
-
railCountries,
|
|
1317
|
-
selectedRouteId,
|
|
1318
|
-
routes
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
export function createOpenReceivePaymentWizardSelection(options = {}) {
|
|
1322
|
-
const selectedCountryCode = options.storedCountryCode ?? options.defaultCountryCode ?? getOpenReceiveDefaultCountryCode();
|
|
1323
|
-
return {
|
|
1324
|
-
selectedMethod: null,
|
|
1325
|
-
selectedCountryCode,
|
|
1326
|
-
selectedBitcoinRoute: null,
|
|
1327
|
-
selectedCryptoRoute: null,
|
|
1328
|
-
selectedRegion: getOpenReceiveRegionForCountry(selectedCountryCode),
|
|
1329
|
-
countryPickerOpen: options.storedCountryCode === undefined
|
|
1330
|
-
? true
|
|
1331
|
-
: options.storedCountryCode === null
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
export function createOpenReceivePaymentWizardModel(selection) {
|
|
1335
|
-
const wizard = createOpenReceivePaymentWizardState({
|
|
1336
|
-
selectedMethod: selection.selectedMethod,
|
|
1337
|
-
selectedCountryCode: selection.selectedCountryCode,
|
|
1338
|
-
selectedBitcoinRoute: selection.selectedBitcoinRoute,
|
|
1339
|
-
selectedCryptoRoute: selection.selectedCryptoRoute
|
|
1340
|
-
});
|
|
1341
|
-
const routeAssets = selection.selectedMethod === "bitcoin"
|
|
1342
|
-
? getOpenReceiveBitcoinAssets()
|
|
1343
|
-
: selection.selectedMethod === "crypto"
|
|
1344
|
-
? getOpenReceiveAltcoinAssets()
|
|
1345
|
-
: [];
|
|
1346
|
-
const selectedRoute = wizard.selectedRouteId;
|
|
1347
|
-
const countryPicker = createOpenReceiveCountryPickerModel({
|
|
1348
|
-
countries: wizard.railCountries,
|
|
1349
|
-
selectedCountryCode: selection.selectedCountryCode,
|
|
1350
|
-
selectedRegion: selection.selectedRegion
|
|
1351
|
-
});
|
|
1352
|
-
const countryDisplays = wizard.railCountries.map((country) => createOpenReceiveCountryDisplay(country, {
|
|
1353
|
-
selectedCountryCode: selection.selectedCountryCode
|
|
1354
|
-
}));
|
|
1355
|
-
return {
|
|
1356
|
-
selection,
|
|
1357
|
-
wizard,
|
|
1358
|
-
countryPicker,
|
|
1359
|
-
countryDisplays,
|
|
1360
|
-
visibleRegionCountries: countryPicker.visibleRegionCountries,
|
|
1361
|
-
visibleRegionCountryDisplays: countryPicker.visibleRegionCountryDisplays,
|
|
1362
|
-
...(countryPicker.selectedCountryDisplay === undefined
|
|
1363
|
-
? {}
|
|
1364
|
-
: { selectedCountryDisplay: countryPicker.selectedCountryDisplay }),
|
|
1365
|
-
routeAssets,
|
|
1366
|
-
selectedRoute
|
|
1367
|
-
};
|
|
1368
|
-
}
|
|
1369
|
-
export function updateOpenReceivePaymentWizardSelection(selection, action) {
|
|
1370
|
-
switch (action.type) {
|
|
1371
|
-
case "select_method": {
|
|
1372
|
-
if (action.method !== "card" && action.method !== "bank") {
|
|
1373
|
-
return {
|
|
1374
|
-
...selection,
|
|
1375
|
-
selectedMethod: action.method,
|
|
1376
|
-
selectedBitcoinRoute: action.method === "bitcoin"
|
|
1377
|
-
? selection.selectedBitcoinRoute ?? getOpenReceiveDefaultBitcoinRoute()
|
|
1378
|
-
: selection.selectedBitcoinRoute,
|
|
1379
|
-
countryPickerOpen: false
|
|
1380
|
-
};
|
|
1381
|
-
}
|
|
1382
|
-
const selectedCountryCode = action.storedCountryCode ?? selection.selectedCountryCode;
|
|
1383
|
-
return {
|
|
1384
|
-
...selection,
|
|
1385
|
-
selectedMethod: action.method,
|
|
1386
|
-
selectedCountryCode,
|
|
1387
|
-
selectedRegion: getOpenReceiveRegionForCountry(selectedCountryCode),
|
|
1388
|
-
countryPickerOpen: false
|
|
1389
|
-
};
|
|
1390
|
-
}
|
|
1391
|
-
case "change_method": {
|
|
1392
|
-
return {
|
|
1393
|
-
...selection,
|
|
1394
|
-
selectedMethod: null,
|
|
1395
|
-
selectedBitcoinRoute: null,
|
|
1396
|
-
selectedCryptoRoute: null,
|
|
1397
|
-
countryPickerOpen: false
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
case "change_route": {
|
|
1401
|
-
if (selection.selectedMethod === "bitcoin") {
|
|
1402
|
-
return {
|
|
1403
|
-
...selection,
|
|
1404
|
-
selectedBitcoinRoute: null
|
|
1405
|
-
};
|
|
1406
|
-
}
|
|
1407
|
-
if (selection.selectedMethod === "crypto") {
|
|
1408
|
-
return {
|
|
1409
|
-
...selection,
|
|
1410
|
-
selectedCryptoRoute: null
|
|
1411
|
-
};
|
|
1412
|
-
}
|
|
1413
|
-
return selection;
|
|
1414
|
-
}
|
|
1415
|
-
case "select_region": {
|
|
1416
|
-
const nextSelection = {
|
|
1417
|
-
...selection,
|
|
1418
|
-
selectedRegion: action.region
|
|
1419
|
-
};
|
|
1420
|
-
const regionCountries = createOpenReceivePaymentWizardModel(nextSelection)
|
|
1421
|
-
.wizard
|
|
1422
|
-
.railCountries
|
|
1423
|
-
.filter((country) => getOpenReceiveRegionForCountry(country.code) === action.region);
|
|
1424
|
-
if (regionCountries.some((country) => country.code === selection.selectedCountryCode)) {
|
|
1425
|
-
return nextSelection;
|
|
1426
|
-
}
|
|
1427
|
-
const first = regionCountries[0];
|
|
1428
|
-
return first === undefined
|
|
1429
|
-
? nextSelection
|
|
1430
|
-
: {
|
|
1431
|
-
...nextSelection,
|
|
1432
|
-
selectedCountryCode: first.code
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
case "select_country": {
|
|
1436
|
-
return {
|
|
1437
|
-
...selection,
|
|
1438
|
-
selectedCountryCode: action.countryCode,
|
|
1439
|
-
selectedRegion: getOpenReceiveRegionForCountry(action.countryCode),
|
|
1440
|
-
countryPickerOpen: false
|
|
1441
|
-
};
|
|
1442
|
-
}
|
|
1443
|
-
case "open_country_picker": {
|
|
1444
|
-
return {
|
|
1445
|
-
...selection,
|
|
1446
|
-
countryPickerOpen: true
|
|
1447
|
-
};
|
|
1448
|
-
}
|
|
1449
|
-
case "select_route": {
|
|
1450
|
-
if (selection.selectedMethod === "bitcoin") {
|
|
1451
|
-
return {
|
|
1452
|
-
...selection,
|
|
1453
|
-
selectedBitcoinRoute: action.route
|
|
1454
|
-
};
|
|
1455
|
-
}
|
|
1456
|
-
if (selection.selectedMethod === "crypto") {
|
|
1457
|
-
return {
|
|
1458
|
-
...selection,
|
|
1459
|
-
selectedCryptoRoute: action.route
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
return selection;
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
export class OpenReceiveBrowserPaymentWizardController {
|
|
1467
|
-
options;
|
|
1468
|
-
selection;
|
|
1469
|
-
constructor(options = {}) {
|
|
1470
|
-
this.options = options;
|
|
1471
|
-
this.selection =
|
|
1472
|
-
options.selection ??
|
|
1473
|
-
createOpenReceivePaymentWizardSelection({
|
|
1474
|
-
storedCountryCode: options.storedCountryCode ??
|
|
1475
|
-
readOpenReceiveStoredCountryCode({
|
|
1476
|
-
storage: options.storage,
|
|
1477
|
-
storageKey: options.storageKey
|
|
1478
|
-
}),
|
|
1479
|
-
defaultCountryCode: options.defaultCountryCode
|
|
1480
|
-
});
|
|
1481
|
-
}
|
|
1482
|
-
getSelection() {
|
|
1483
|
-
return this.selection;
|
|
1484
|
-
}
|
|
1485
|
-
getModel() {
|
|
1486
|
-
return createOpenReceivePaymentWizardModel(this.selection);
|
|
1487
|
-
}
|
|
1488
|
-
update(action) {
|
|
1489
|
-
const normalizedAction = action.type === "select_method" &&
|
|
1490
|
-
(action.method === "card" || action.method === "bank") &&
|
|
1491
|
-
action.storedCountryCode === undefined
|
|
1492
|
-
? {
|
|
1493
|
-
...action,
|
|
1494
|
-
storedCountryCode: readOpenReceiveStoredCountryCode({
|
|
1495
|
-
storage: this.options.storage,
|
|
1496
|
-
storageKey: this.options.storageKey
|
|
1497
|
-
})
|
|
1498
|
-
}
|
|
1499
|
-
: action;
|
|
1500
|
-
const next = updateOpenReceivePaymentWizardSelection(this.selection, normalizedAction);
|
|
1501
|
-
if (action.type === "select_country") {
|
|
1502
|
-
writeOpenReceiveStoredCountryCode(action.countryCode, {
|
|
1503
|
-
storage: this.options.storage,
|
|
1504
|
-
storageKey: this.options.storageKey
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
this.selection = next;
|
|
1508
|
-
this.options.onSelection?.(next);
|
|
1509
|
-
return next;
|
|
1510
|
-
}
|
|
1511
|
-
selectMethod(method) {
|
|
1512
|
-
return this.update({
|
|
1513
|
-
type: "select_method",
|
|
1514
|
-
method
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
changeMethod() {
|
|
1518
|
-
return this.update({
|
|
1519
|
-
type: "change_method"
|
|
1520
|
-
});
|
|
1521
|
-
}
|
|
1522
|
-
selectRegion(region) {
|
|
1523
|
-
return this.update({
|
|
1524
|
-
type: "select_region",
|
|
1525
|
-
region
|
|
1526
|
-
});
|
|
1527
|
-
}
|
|
1528
|
-
selectCountry(countryCode) {
|
|
1529
|
-
return this.update({
|
|
1530
|
-
type: "select_country",
|
|
1531
|
-
countryCode
|
|
1532
|
-
});
|
|
1533
|
-
}
|
|
1534
|
-
openCountryPicker() {
|
|
1535
|
-
return this.update({
|
|
1536
|
-
type: "open_country_picker"
|
|
1537
|
-
});
|
|
1538
|
-
}
|
|
1539
|
-
selectRoute(route) {
|
|
1540
|
-
return this.update({
|
|
1541
|
-
type: "select_route",
|
|
1542
|
-
route
|
|
1543
|
-
});
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
export function createOpenReceivePaymentWizardController(options = {}) {
|
|
1547
|
-
return new OpenReceiveBrowserPaymentWizardController(options);
|
|
1548
|
-
}
|
|
1549
|
-
export function readOpenReceiveStoredCountryCode(options = {}) {
|
|
1550
|
-
const storage = options.storage ?? getBrowserStorage();
|
|
1551
|
-
const storageKey = options.storageKey ?? OPENRECEIVE_COUNTRY_STORAGE_KEY;
|
|
1552
|
-
const legacyStorageKey = options.legacyStorageKey ?? OPENRECEIVE_LEGACY_DEMO_COUNTRY_STORAGE_KEY;
|
|
1553
|
-
const countryCode = readStorageValue(storage, storageKey) ?? readStorageValue(storage, legacyStorageKey);
|
|
1554
|
-
if (countryCode === null)
|
|
1555
|
-
return null;
|
|
1556
|
-
const normalizedCountryCode = countryCode.trim().toUpperCase();
|
|
1557
|
-
return isKnownCountryCode(normalizedCountryCode) ? normalizedCountryCode : null;
|
|
1558
|
-
}
|
|
1559
|
-
export function writeOpenReceiveStoredCountryCode(countryCode, options = {}) {
|
|
1560
|
-
const normalizedCountryCode = countryCode.trim().toUpperCase();
|
|
1561
|
-
if (!isKnownCountryCode(normalizedCountryCode))
|
|
1562
|
-
return;
|
|
1563
|
-
writeStorageValue(options.storage ?? getBrowserStorage(), options.storageKey ?? OPENRECEIVE_COUNTRY_STORAGE_KEY, normalizedCountryCode);
|
|
1564
|
-
}
|
|
1565
|
-
export function readOpenReceiveThemePreference(options = {}) {
|
|
1566
|
-
const value = readStorageValue(options.storage ?? getBrowserStorage(), options.storageKey ?? OPENRECEIVE_THEME_STORAGE_KEY);
|
|
1567
|
-
return value === "light" || value === "dark" || value === "system"
|
|
1568
|
-
? value
|
|
1569
|
-
: options.defaultTheme ?? "system";
|
|
1570
|
-
}
|
|
1571
|
-
export function writeOpenReceiveThemePreference(theme, options = {}) {
|
|
1572
|
-
writeStorageValue(options.storage ?? getBrowserStorage(), options.storageKey ?? OPENRECEIVE_THEME_STORAGE_KEY, theme);
|
|
1573
|
-
}
|
|
1574
|
-
export function resolveOpenReceiveTheme(theme, options = {}) {
|
|
1575
|
-
if (theme === "light" || theme === "dark")
|
|
1576
|
-
return theme;
|
|
1577
|
-
if (options.systemDark !== undefined)
|
|
1578
|
-
return options.systemDark ? "dark" : "light";
|
|
1579
|
-
return globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches
|
|
1580
|
-
? "dark"
|
|
1581
|
-
: "light";
|
|
1582
|
-
}
|
|
1583
|
-
export function getOpenReceiveNextThemePreference(theme, options = {}) {
|
|
1584
|
-
return resolveOpenReceiveTheme(theme, options) === "dark" ? "light" : "dark";
|
|
1585
|
-
}
|
|
1586
|
-
export function getOpenReceiveThemeToggleLabel(resolvedTheme) {
|
|
1587
|
-
return `${resolvedTheme} mode`;
|
|
1588
|
-
}
|
|
1589
|
-
export function createOpenReceiveThemeModel(theme, options = {}) {
|
|
1590
|
-
const resolvedTheme = resolveOpenReceiveTheme(theme, options);
|
|
1591
|
-
return {
|
|
1592
|
-
theme,
|
|
1593
|
-
resolvedTheme,
|
|
1594
|
-
nextTheme: getOpenReceiveNextThemePreference(theme, options),
|
|
1595
|
-
toggleLabel: getOpenReceiveThemeToggleLabel(resolvedTheme),
|
|
1596
|
-
attributes: {
|
|
1597
|
-
"data-theme": resolvedTheme,
|
|
1598
|
-
"data-openreceive-theme": resolvedTheme
|
|
1599
|
-
},
|
|
1600
|
-
checkoutElementAttributes: {
|
|
1601
|
-
theme: resolvedTheme
|
|
1602
|
-
}
|
|
1603
|
-
};
|
|
1604
|
-
}
|
|
1605
|
-
export function createOpenReceiveStoredThemeModel(options = {}) {
|
|
1606
|
-
const theme = readOpenReceiveThemePreference(options);
|
|
1607
|
-
return createOpenReceiveThemeModel(theme, { systemDark: options.systemDark });
|
|
1608
|
-
}
|
|
1609
|
-
export function toggleOpenReceiveStoredThemePreference(options = {}) {
|
|
1610
|
-
const currentTheme = createOpenReceiveStoredThemeModel(options);
|
|
1611
|
-
writeOpenReceiveThemePreference(currentTheme.nextTheme, options);
|
|
1612
|
-
return createOpenReceiveStoredThemeModel(options);
|
|
1613
|
-
}
|
|
1614
|
-
export function applyOpenReceiveThemeAttributes(target, theme) {
|
|
1615
|
-
if (target === null || target === undefined)
|
|
1616
|
-
return;
|
|
1617
|
-
for (const [name, value] of Object.entries(theme.attributes)) {
|
|
1618
|
-
target.setAttribute(name, value);
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
export function applyCheckoutThemeAttributes(target, theme) {
|
|
1622
|
-
if (target === null || target === undefined)
|
|
1623
|
-
return;
|
|
1624
|
-
for (const [name, value] of Object.entries(theme.checkoutElementAttributes)) {
|
|
1625
|
-
target.setAttribute(name, value);
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
export function applyOpenReceiveThemeControls(targets, theme) {
|
|
1629
|
-
applyOpenReceiveThemeAttributes(targets.root, theme);
|
|
1630
|
-
applyCheckoutThemeAttributes(targets.checkout, theme);
|
|
1631
|
-
if (targets.toggle !== null && targets.toggle !== undefined) {
|
|
1632
|
-
targets.toggle.textContent = theme.toggleLabel;
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
export function syncOpenReceiveStoredThemeControls(targets, options = {}) {
|
|
1636
|
-
const theme = createOpenReceiveStoredThemeModel(options);
|
|
1637
|
-
applyOpenReceiveThemeControls(targets, theme);
|
|
1638
|
-
return theme;
|
|
1639
|
-
}
|
|
1640
|
-
export function toggleOpenReceiveStoredThemeControls(targets, options = {}) {
|
|
1641
|
-
const theme = toggleOpenReceiveStoredThemePreference(options);
|
|
1642
|
-
applyOpenReceiveThemeControls(targets, theme);
|
|
1643
|
-
return theme;
|
|
1644
|
-
}
|
|
1645
|
-
export function createOpenReceiveTransientFeedbackController(options) {
|
|
1646
|
-
const delayMs = options.delayMs ?? OPENRECEIVE_COPY_FEEDBACK_MS;
|
|
1647
|
-
const setTimeoutFn = options.setTimeout ?? globalThis.setTimeout;
|
|
1648
|
-
const clearTimeoutFn = options.clearTimeout ?? globalThis.clearTimeout;
|
|
1649
|
-
let timeout;
|
|
1650
|
-
const clear = () => {
|
|
1651
|
-
if (timeout === undefined)
|
|
1652
|
-
return;
|
|
1653
|
-
clearTimeoutFn(timeout);
|
|
1654
|
-
timeout = undefined;
|
|
1655
|
-
};
|
|
1656
|
-
return {
|
|
1657
|
-
show(value) {
|
|
1658
|
-
clear();
|
|
1659
|
-
options.onValue(value);
|
|
1660
|
-
timeout = setTimeoutFn(() => {
|
|
1661
|
-
timeout = undefined;
|
|
1662
|
-
options.onValue(options.resetValue);
|
|
1663
|
-
}, delayMs);
|
|
1664
|
-
},
|
|
1665
|
-
clear
|
|
1666
|
-
};
|
|
1667
|
-
}
|
|
1668
|
-
export function formatOpenReceiveCountdown(seconds) {
|
|
1669
|
-
const safeSeconds = Math.max(0, Math.trunc(seconds));
|
|
1670
|
-
const minutes = Math.floor(safeSeconds / 60);
|
|
1671
|
-
const remainderSeconds = safeSeconds % 60;
|
|
1672
|
-
return `${minutes}:${remainderSeconds.toString().padStart(2, "0")}`;
|
|
1673
|
-
}
|
|
1674
|
-
export function escapeOpenReceiveHtml(value) {
|
|
1675
|
-
return value
|
|
1676
|
-
.replaceAll("&", "&")
|
|
1677
|
-
.replaceAll("<", "<")
|
|
1678
|
-
.replaceAll(">", ">")
|
|
1679
|
-
.replaceAll('"', """);
|
|
1680
|
-
}
|
|
1681
|
-
export function formatOpenReceiveMsats(amountMsats) {
|
|
1682
|
-
if (!Number.isSafeInteger(amountMsats) || amountMsats < 0) {
|
|
1683
|
-
throw new RangeError("amount_msats must be a non-negative safe integer");
|
|
1684
|
-
}
|
|
1685
|
-
if (amountMsats % 1000 === 0) {
|
|
1686
|
-
const sats = amountMsats / 1000;
|
|
1687
|
-
return `${sats} ${sats === 1 ? "sat" : "sats"}`;
|
|
1688
|
-
}
|
|
1689
|
-
return `${amountMsats} msats`;
|
|
1690
|
-
}
|
|
1691
|
-
export function formatOpenReceiveFiatAmount(fiat) {
|
|
1692
|
-
if (fiat?.currency === undefined || fiat.value === undefined)
|
|
1693
|
-
return undefined;
|
|
1694
|
-
return fiat.currency === "USD" ? `$${fiat.value}` : `${fiat.value} ${fiat.currency}`;
|
|
1695
|
-
}
|
|
1696
|
-
export function formatOpenReceivePaymentHashLabel(hash) {
|
|
1697
|
-
return hash.length <= 16 ? hash : `${hash.slice(0, 8)}...${hash.slice(-8)}`;
|
|
1698
|
-
}
|
|
1699
|
-
export function assertOpenReceiveDisplayInvoice(invoice) {
|
|
1700
|
-
assertInvoice(invoice);
|
|
1701
|
-
}
|
|
1702
|
-
export function createCheckoutDisplayModel(data) {
|
|
1703
|
-
return {
|
|
1704
|
-
...data,
|
|
1705
|
-
lightningUri: createLightningUri(data.invoice),
|
|
1706
|
-
...(data.amount_msats === undefined
|
|
1707
|
-
? {}
|
|
1708
|
-
: { amountLabel: formatOpenReceiveMsats(data.amount_msats) }),
|
|
1709
|
-
...(formatOpenReceiveFiatAmount(data.fiat_quote?.fiat) === undefined
|
|
1710
|
-
? {}
|
|
1711
|
-
: { fiatLabel: formatOpenReceiveFiatAmount(data.fiat_quote?.fiat) }),
|
|
1712
|
-
...(data.payment_hash === undefined
|
|
1713
|
-
? {}
|
|
1714
|
-
: { paymentHashLabel: formatOpenReceivePaymentHashLabel(data.payment_hash) }),
|
|
1715
|
-
...(data.transaction_state === undefined
|
|
1716
|
-
? {}
|
|
1717
|
-
: { transactionStateLabel: data.transaction_state })
|
|
1718
|
-
};
|
|
1719
|
-
}
|
|
1720
|
-
export function createLightningUri(invoice) {
|
|
1721
|
-
assertInvoice(invoice);
|
|
1722
|
-
return `lightning:${invoice}`;
|
|
1723
|
-
}
|
|
1724
|
-
export function createCheckoutElementAttributes(snapshot, options = {}) {
|
|
1725
|
-
assertInvoice(snapshot.invoice);
|
|
1726
|
-
const attributes = {
|
|
1727
|
-
[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.invoiceId]: snapshot.invoice_id,
|
|
1728
|
-
[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.invoice]: snapshot.invoice
|
|
1729
|
-
};
|
|
1730
|
-
if (snapshot.payment_hash !== undefined) {
|
|
1731
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.paymentHash] = snapshot.payment_hash;
|
|
1732
|
-
}
|
|
1733
|
-
if (snapshot.amount_msats !== undefined) {
|
|
1734
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.amountMsats] = String(snapshot.amount_msats);
|
|
1735
|
-
}
|
|
1736
|
-
const fiat = snapshot.fiat_quote?.fiat;
|
|
1737
|
-
if (fiat?.currency !== undefined) {
|
|
1738
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.fiatCurrency] = fiat.currency;
|
|
1739
|
-
}
|
|
1740
|
-
if (fiat?.value !== undefined) {
|
|
1741
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.fiatValue] = fiat.value;
|
|
1742
|
-
}
|
|
1743
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.status] = deriveStatus(snapshot);
|
|
1744
|
-
if (snapshot.expires_at !== undefined) {
|
|
1745
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.expiresAt] = String(snapshot.expires_at);
|
|
1746
|
-
}
|
|
1747
|
-
if (options.lookupUrl !== undefined) {
|
|
1748
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.lookupUrl] = options.lookupUrl;
|
|
1749
|
-
}
|
|
1750
|
-
if (options.theme !== undefined) {
|
|
1751
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.theme] = options.theme;
|
|
1752
|
-
}
|
|
1753
|
-
if (options.paymentWizard !== undefined) {
|
|
1754
|
-
attributes[OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.paymentWizard] = String(options.paymentWizard);
|
|
1755
|
-
}
|
|
1756
|
-
return attributes;
|
|
1757
|
-
}
|
|
1758
|
-
export function createOpenReceiveThemeToggleElementAttributes(options = {}) {
|
|
1759
|
-
return {
|
|
1760
|
-
...(options.rootSelector === undefined
|
|
1761
|
-
? {}
|
|
1762
|
-
: { [OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.rootSelector]: options.rootSelector }),
|
|
1763
|
-
...(options.checkoutSelector === undefined
|
|
1764
|
-
? {}
|
|
1765
|
-
: { [OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.checkoutSelector]: options.checkoutSelector }),
|
|
1766
|
-
...(options.defaultTheme === undefined
|
|
1767
|
-
? {}
|
|
1768
|
-
: { [OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.defaultTheme]: options.defaultTheme }),
|
|
1769
|
-
...(options.storageKey === undefined
|
|
1770
|
-
? {}
|
|
1771
|
-
: { [OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.storageKey]: options.storageKey })
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
export function createCheckoutElementListeners(handlers = {}) {
|
|
1775
|
-
return {
|
|
1776
|
-
...(handlers.onCopy === undefined
|
|
1777
|
-
? {}
|
|
1778
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.copy]: handlers.onCopy }),
|
|
1779
|
-
...(handlers.onOpenWallet === undefined
|
|
1780
|
-
? {}
|
|
1781
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.openWallet]: handlers.onOpenWallet }),
|
|
1782
|
-
...(handlers.onState === undefined
|
|
1783
|
-
? {}
|
|
1784
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.state]: handlers.onState }),
|
|
1785
|
-
...(handlers.onSettled === undefined
|
|
1786
|
-
? {}
|
|
1787
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.settled]: handlers.onSettled }),
|
|
1788
|
-
...(handlers.onProviderCopy === undefined
|
|
1789
|
-
? {}
|
|
1790
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.providerCopy]: handlers.onProviderCopy }),
|
|
1791
|
-
...(handlers.onStartOver === undefined
|
|
1792
|
-
? {}
|
|
1793
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.startOver]: handlers.onStartOver }),
|
|
1794
|
-
...(handlers.onError === undefined
|
|
1795
|
-
? {}
|
|
1796
|
-
: { [OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.error]: handlers.onError })
|
|
1797
|
-
};
|
|
1798
|
-
}
|
|
1799
|
-
export function createCheckoutShellModel(snapshot, options = {}) {
|
|
1800
|
-
const theme = createOpenReceiveStoredThemeModel(options);
|
|
1801
|
-
return {
|
|
1802
|
-
theme,
|
|
1803
|
-
rootAttributes: theme.attributes,
|
|
1804
|
-
checkout: {
|
|
1805
|
-
tagName: OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME,
|
|
1806
|
-
attributes: createCheckoutElementAttributes(snapshot, {
|
|
1807
|
-
...options,
|
|
1808
|
-
theme: theme.resolvedTheme
|
|
1809
|
-
}),
|
|
1810
|
-
listeners: createCheckoutElementListeners(options)
|
|
1811
|
-
},
|
|
1812
|
-
themeToggle: {
|
|
1813
|
-
tagName: OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME,
|
|
1814
|
-
attributes: createOpenReceiveThemeToggleElementAttributes({
|
|
1815
|
-
rootSelector: options.rootSelector,
|
|
1816
|
-
checkoutSelector: options.checkoutSelector ?? OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME,
|
|
1817
|
-
defaultTheme: options.defaultTheme,
|
|
1818
|
-
storageKey: options.storageKey
|
|
1819
|
-
})
|
|
1820
|
-
}
|
|
1821
|
-
};
|
|
1822
|
-
}
|
|
1823
|
-
export function applyCheckoutElementAttributes(target, attributes) {
|
|
1824
|
-
for (const [name, value] of Object.entries(attributes)) {
|
|
1825
|
-
if (value !== undefined)
|
|
1826
|
-
target.setAttribute(name, value);
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
export function applyCheckoutElementListeners(target, listeners) {
|
|
1830
|
-
for (const [name, listener] of Object.entries(listeners)) {
|
|
1831
|
-
if (listener !== undefined)
|
|
1832
|
-
target.addEventListener(name, listener);
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
export function applyOpenReceiveThemeToggleElementAttributes(target, attributes) {
|
|
1836
|
-
for (const [name, value] of Object.entries(attributes)) {
|
|
1837
|
-
if (value !== undefined)
|
|
1838
|
-
target.setAttribute(name, value);
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
export function createCheckoutElement(snapshot, options = {}) {
|
|
1842
|
-
const ownerDocument = options.document ?? globalThis.document;
|
|
1843
|
-
if (ownerDocument === undefined) {
|
|
1844
|
-
throw new Error("OpenReceive checkout element creation requires document.");
|
|
1845
|
-
}
|
|
1846
|
-
const element = ownerDocument.createElement(OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME);
|
|
1847
|
-
applyCheckoutElementAttributes(element, createCheckoutElementAttributes(snapshot, options));
|
|
1848
|
-
applyCheckoutElementListeners(element, createCheckoutElementListeners(options));
|
|
1849
|
-
return element;
|
|
1850
|
-
}
|
|
1851
|
-
export function createOpenReceiveThemeToggleElement(options = {}) {
|
|
1852
|
-
const ownerDocument = options.document ?? globalThis.document;
|
|
1853
|
-
if (ownerDocument === undefined) {
|
|
1854
|
-
throw new Error("OpenReceive theme toggle element creation requires document.");
|
|
1855
|
-
}
|
|
1856
|
-
const element = ownerDocument.createElement(OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME);
|
|
1857
|
-
applyOpenReceiveThemeToggleElementAttributes(element, createOpenReceiveThemeToggleElementAttributes(options));
|
|
1858
|
-
return element;
|
|
1859
|
-
}
|
|
1860
|
-
export function createCheckoutShell(snapshot, options = {}) {
|
|
1861
|
-
const ownerDocument = options.document ?? globalThis.document;
|
|
1862
|
-
if (ownerDocument === undefined) {
|
|
1863
|
-
throw new Error("OpenReceive checkout shell creation requires document.");
|
|
1864
|
-
}
|
|
1865
|
-
const shell = createCheckoutShellModel(snapshot, options);
|
|
1866
|
-
applyOpenReceiveThemeAttributes(options.root, shell.theme);
|
|
1867
|
-
const checkout = ownerDocument.createElement(shell.checkout.tagName);
|
|
1868
|
-
applyCheckoutElementAttributes(checkout, shell.checkout.attributes);
|
|
1869
|
-
applyCheckoutElementListeners(checkout, shell.checkout.listeners);
|
|
1870
|
-
const themeToggle = ownerDocument.createElement(shell.themeToggle.tagName);
|
|
1871
|
-
applyOpenReceiveThemeToggleElementAttributes(themeToggle, shell.themeToggle.attributes);
|
|
1872
|
-
return {
|
|
1873
|
-
theme: shell.theme,
|
|
1874
|
-
rootAttributes: shell.rootAttributes,
|
|
1875
|
-
checkout,
|
|
1876
|
-
themeToggle
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
export async function createInvoice(options) {
|
|
1880
|
-
if (options.orderUuid.length === 0) {
|
|
1881
|
-
throw new Error("OpenReceive invoice creation requires an orderUuid.");
|
|
1882
|
-
}
|
|
1883
|
-
const fetcher = options.fetch ?? globalThis.fetch;
|
|
1884
|
-
if (fetcher === undefined) {
|
|
1885
|
-
throw new Error("OpenReceive invoice creation requires fetch.");
|
|
1886
|
-
}
|
|
1887
|
-
const amountSourceCount = [
|
|
1888
|
-
options.amount !== undefined,
|
|
1889
|
-
options.amountInSatoshis !== undefined,
|
|
1890
|
-
options.amount_msats !== undefined,
|
|
1891
|
-
options.fiat !== undefined
|
|
1892
|
-
].filter(Boolean).length;
|
|
1893
|
-
if (amountSourceCount !== 1) {
|
|
1894
|
-
throw new Error("OpenReceive invoice creation requires exactly one of amount, fiat, amountInSatoshis, or amount_msats.");
|
|
1895
|
-
}
|
|
1896
|
-
if (options.optionalInvoiceDescription !== undefined &&
|
|
1897
|
-
options.optionalInvoiceDescription.length > 500) {
|
|
1898
|
-
throw new Error("OpenReceive optionalInvoiceDescription must be 500 characters or fewer.");
|
|
1899
|
-
}
|
|
1900
|
-
const requestBody = {
|
|
1901
|
-
order_uuid: options.orderUuid,
|
|
1902
|
-
...(options.amount === undefined ? {} : { amount: options.amount }),
|
|
1903
|
-
...(options.amountInSatoshis === undefined ? {} : { amount_sats: options.amountInSatoshis }),
|
|
1904
|
-
...(options.amount_msats === undefined ? {} : { amount_msats: options.amount_msats }),
|
|
1905
|
-
...(options.fiat === undefined ? {} : { fiat: options.fiat }),
|
|
1906
|
-
...(options.optionalInvoiceDescription === undefined
|
|
1907
|
-
? {}
|
|
1908
|
-
: { optional_invoice_description: options.optionalInvoiceDescription }),
|
|
1909
|
-
...(options.description_hash === undefined ? {} : { description_hash: options.description_hash }),
|
|
1910
|
-
...(options.expiry === undefined ? {} : { expiry: options.expiry })
|
|
1911
|
-
};
|
|
1912
|
-
assertOpenReceiveBrowserPayloadSafe(requestBody);
|
|
1913
|
-
const response = await fetcher(options.invoiceUrl ?? "/openreceive/v1/invoices", {
|
|
1914
|
-
method: "POST",
|
|
1915
|
-
headers: {
|
|
1916
|
-
"Content-Type": "application/json",
|
|
1917
|
-
...(options.headers ?? {})
|
|
1918
|
-
},
|
|
1919
|
-
body: JSON.stringify(requestBody)
|
|
1920
|
-
});
|
|
1921
|
-
const body = await response.json();
|
|
1922
|
-
if (!response.ok) {
|
|
1923
|
-
throw new Error(typeof body?.message === "string"
|
|
1924
|
-
? body.message
|
|
1925
|
-
: "Could not create invoice.");
|
|
1926
|
-
}
|
|
1927
|
-
if (typeof body?.invoice === "string") {
|
|
1928
|
-
assertOpenReceiveDisplayInvoice(body.invoice);
|
|
1929
|
-
}
|
|
1930
|
-
return body;
|
|
1931
|
-
}
|
|
1932
|
-
export function createOpenReceiveLookupInvoiceFetcher(options) {
|
|
1933
|
-
return async (state) => {
|
|
1934
|
-
if (state.payment_hash === undefined) {
|
|
1935
|
-
throw new Error("OpenReceive lookup requires payment_hash.");
|
|
1936
|
-
}
|
|
1937
|
-
const fetcher = options.fetch ?? globalThis.fetch;
|
|
1938
|
-
if (fetcher === undefined) {
|
|
1939
|
-
throw new Error("OpenReceive lookup requires fetch.");
|
|
1940
|
-
}
|
|
1941
|
-
const response = await fetcher(options.lookupUrl, {
|
|
1942
|
-
method: "POST",
|
|
1943
|
-
headers: {
|
|
1944
|
-
"Content-Type": "application/json",
|
|
1945
|
-
...(options.headers ?? {})
|
|
1946
|
-
},
|
|
1947
|
-
body: JSON.stringify({
|
|
1948
|
-
payment_hash: state.payment_hash
|
|
1949
|
-
})
|
|
1950
|
-
});
|
|
1951
|
-
const body = await response.json();
|
|
1952
|
-
if (!response.ok) {
|
|
1953
|
-
throw new Error(typeof body?.message === "string"
|
|
1954
|
-
? body.message
|
|
1955
|
-
: "Could not look up invoice.");
|
|
1956
|
-
}
|
|
1957
|
-
return body;
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1960
|
-
export function createOpenReceiveRefreshInvoiceFetcher(options) {
|
|
1961
|
-
return async (state) => {
|
|
1962
|
-
const fetcher = options.fetch ?? globalThis.fetch;
|
|
1963
|
-
if (fetcher === undefined) {
|
|
1964
|
-
throw new Error("OpenReceive refresh requires fetch.");
|
|
1965
|
-
}
|
|
1966
|
-
const refreshUrl = typeof options.refreshUrl === "function"
|
|
1967
|
-
? options.refreshUrl(state)
|
|
1968
|
-
: options.refreshUrl;
|
|
1969
|
-
const idempotencyKey = typeof options.idempotencyKey === "function"
|
|
1970
|
-
? options.idempotencyKey(state)
|
|
1971
|
-
: options.idempotencyKey;
|
|
1972
|
-
if (idempotencyKey.length === 0) {
|
|
1973
|
-
throw new Error("OpenReceive refresh requires an Idempotency-Key.");
|
|
1974
|
-
}
|
|
1975
|
-
const reason = typeof options.reason === "function"
|
|
1976
|
-
? options.reason(state)
|
|
1977
|
-
: options.reason ?? state.phase;
|
|
1978
|
-
const response = await fetcher(refreshUrl, {
|
|
1979
|
-
method: "POST",
|
|
1980
|
-
headers: {
|
|
1981
|
-
"Content-Type": "application/json",
|
|
1982
|
-
"Idempotency-Key": idempotencyKey,
|
|
1983
|
-
...(options.headers ?? {})
|
|
1984
|
-
},
|
|
1985
|
-
body: JSON.stringify({ reason })
|
|
1986
|
-
});
|
|
1987
|
-
const body = await response.json();
|
|
1988
|
-
if (!response.ok) {
|
|
1989
|
-
throw new Error(typeof body?.message === "string"
|
|
1990
|
-
? body.message
|
|
1991
|
-
: "Could not refresh invoice.");
|
|
1992
|
-
}
|
|
1993
|
-
if (asRecord(body).invoice === undefined) {
|
|
1994
|
-
throw new Error("OpenReceive refresh response did not include an invoice.");
|
|
1995
|
-
}
|
|
1996
|
-
return body;
|
|
1997
|
-
};
|
|
1998
|
-
}
|
|
1999
|
-
export function createCheckoutState(snapshot, options = {}) {
|
|
2000
|
-
const transactionState = snapshot.transaction_state ?? "pending";
|
|
2001
|
-
const workflowState = snapshot.workflow_state ?? "invoice_created";
|
|
2002
|
-
const state = normalizeCheckoutState({
|
|
2003
|
-
invoice_id: snapshot.invoice_id,
|
|
2004
|
-
invoice: snapshot.invoice,
|
|
2005
|
-
lightningUri: createLightningUri(snapshot.invoice),
|
|
2006
|
-
...(snapshot.payment_hash === undefined
|
|
2007
|
-
? {}
|
|
2008
|
-
: { payment_hash: snapshot.payment_hash }),
|
|
2009
|
-
...(snapshot.amount_msats === undefined
|
|
2010
|
-
? {}
|
|
2011
|
-
: { amount_msats: snapshot.amount_msats }),
|
|
2012
|
-
...(snapshot.fiat_quote === undefined ? {} : { fiat_quote: snapshot.fiat_quote }),
|
|
2013
|
-
transaction_state: transactionState,
|
|
2014
|
-
workflow_state: workflowState,
|
|
2015
|
-
...(snapshot.expires_at === undefined
|
|
2016
|
-
? {}
|
|
2017
|
-
: { expires_at: snapshot.expires_at }),
|
|
2018
|
-
...(snapshot.settled_at === undefined
|
|
2019
|
-
? {}
|
|
2020
|
-
: { settled_at: snapshot.settled_at })
|
|
2021
|
-
}, options.now ?? currentUnixSeconds());
|
|
2022
|
-
emitBrowserLog(options.logger, "info", "checkout.state.created", "Created checkout state from invoice snapshot.", checkoutLogFields(state));
|
|
2023
|
-
return state;
|
|
2024
|
-
}
|
|
2025
|
-
export function createCheckoutSnapshotFromDisplayData(data) {
|
|
2026
|
-
if (data.invoice_id === undefined) {
|
|
2027
|
-
throw new TypeError("invoice_id is required for checkout state");
|
|
2028
|
-
}
|
|
2029
|
-
return {
|
|
2030
|
-
invoice_id: data.invoice_id,
|
|
2031
|
-
invoice: data.invoice,
|
|
2032
|
-
...(data.payment_hash === undefined ? {} : { payment_hash: data.payment_hash }),
|
|
2033
|
-
...(data.amount_msats === undefined ? {} : { amount_msats: data.amount_msats }),
|
|
2034
|
-
...(data.fiat_quote === undefined ? {} : { fiat_quote: data.fiat_quote }),
|
|
2035
|
-
...(data.transaction_state === undefined
|
|
2036
|
-
? {}
|
|
2037
|
-
: { transaction_state: data.transaction_state }),
|
|
2038
|
-
...(data.workflow_state === undefined ? {} : { workflow_state: data.workflow_state }),
|
|
2039
|
-
...(data.expires_at === undefined ? {} : { expires_at: data.expires_at }),
|
|
2040
|
-
...(data.settled_at === undefined ? {} : { settled_at: data.settled_at })
|
|
2041
|
-
};
|
|
2042
|
-
}
|
|
2043
|
-
export function createCheckoutStateFromDisplayData(data, options = {}) {
|
|
2044
|
-
return createCheckoutState(createCheckoutSnapshotFromDisplayData(data), options);
|
|
2045
|
-
}
|
|
2046
|
-
export function refreshCheckoutState(state, options = {}) {
|
|
2047
|
-
return createCheckoutState(snapshotFromCheckoutState(state), options);
|
|
2048
|
-
}
|
|
2049
|
-
export function mergeCheckoutSnapshot(current, next) {
|
|
2050
|
-
return {
|
|
2051
|
-
invoice_id: next.invoice_id ?? current.invoice_id,
|
|
2052
|
-
invoice: next.invoice ?? current.invoice,
|
|
2053
|
-
...((next.payment_hash ?? current.payment_hash) === undefined
|
|
2054
|
-
? {}
|
|
2055
|
-
: { payment_hash: next.payment_hash ?? current.payment_hash }),
|
|
2056
|
-
...((next.amount_msats ?? current.amount_msats) === undefined
|
|
2057
|
-
? {}
|
|
2058
|
-
: { amount_msats: next.amount_msats ?? current.amount_msats }),
|
|
2059
|
-
...((next.fiat_quote ?? current.fiat_quote) === undefined
|
|
2060
|
-
? {}
|
|
2061
|
-
: { fiat_quote: next.fiat_quote ?? current.fiat_quote }),
|
|
2062
|
-
transaction_state: next.transaction_state ?? current.transaction_state,
|
|
2063
|
-
workflow_state: next.workflow_state ?? current.workflow_state,
|
|
2064
|
-
...((next.expires_at ?? current.expires_at) === undefined
|
|
2065
|
-
? {}
|
|
2066
|
-
: { expires_at: next.expires_at ?? current.expires_at }),
|
|
2067
|
-
...((next.settled_at ?? current.settled_at) === undefined
|
|
2068
|
-
? {}
|
|
2069
|
-
: { settled_at: next.settled_at ?? current.settled_at })
|
|
2070
|
-
};
|
|
2071
|
-
}
|
|
2072
|
-
export function shouldCheckoutShowWaiting(state, options = {}) {
|
|
2073
|
-
if (state.terminal || state.settled)
|
|
2074
|
-
return false;
|
|
2075
|
-
if (state.expires_at === undefined)
|
|
2076
|
-
return true;
|
|
2077
|
-
return state.expires_at > (options.now ?? currentUnixSeconds());
|
|
2078
|
-
}
|
|
2079
|
-
export function createCheckoutStatusModel(source, options = {}) {
|
|
2080
|
-
const isCheckoutState = source !== undefined && "invoice_id" in source && "invoice" in source;
|
|
2081
|
-
const phase = source?.phase ?? "invoice_created";
|
|
2082
|
-
const expiresInSeconds = source?.expiresInSeconds;
|
|
2083
|
-
const displayPhase = phase !== "settled" &&
|
|
2084
|
-
phase !== "failed" &&
|
|
2085
|
-
phase !== "cancelled" &&
|
|
2086
|
-
expiresInSeconds === 0
|
|
2087
|
-
? "expired"
|
|
2088
|
-
: phase;
|
|
2089
|
-
const statusText = getOpenReceivePaymentStatusText(displayPhase);
|
|
2090
|
-
return {
|
|
2091
|
-
phase: displayPhase,
|
|
2092
|
-
waiting: displayPhase === "expired"
|
|
2093
|
-
? false
|
|
2094
|
-
: source === undefined
|
|
2095
|
-
? false
|
|
2096
|
-
: isCheckoutState
|
|
2097
|
-
? shouldCheckoutShowWaiting(source, options)
|
|
2098
|
-
: source.waiting ?? false,
|
|
2099
|
-
title: statusText.title,
|
|
2100
|
-
detail: statusText.detail,
|
|
2101
|
-
countdownPrefix: openReceiveCheckoutLabels.countdownPrefix,
|
|
2102
|
-
...(expiresInSeconds === undefined || displayPhase === "expired"
|
|
2103
|
-
? {}
|
|
2104
|
-
: {
|
|
2105
|
-
expiresInSeconds,
|
|
2106
|
-
countdownLabel: formatOpenReceiveCountdown(expiresInSeconds)
|
|
2107
|
-
})
|
|
2108
|
-
};
|
|
2109
|
-
}
|
|
2110
|
-
export class CheckoutWatcher {
|
|
2111
|
-
options;
|
|
2112
|
-
state;
|
|
2113
|
-
countdownTimer;
|
|
2114
|
-
pollTimer;
|
|
2115
|
-
running = false;
|
|
2116
|
-
constructor(options) {
|
|
2117
|
-
this.options = options;
|
|
2118
|
-
}
|
|
2119
|
-
start() {
|
|
2120
|
-
this.stop();
|
|
2121
|
-
this.running = true;
|
|
2122
|
-
const state = createCheckoutState(this.options.snapshot, {
|
|
2123
|
-
now: this.now(),
|
|
2124
|
-
logger: this.options.logger
|
|
2125
|
-
});
|
|
2126
|
-
this.applyState(state);
|
|
2127
|
-
return state;
|
|
2128
|
-
}
|
|
2129
|
-
update(options) {
|
|
2130
|
-
this.options = options;
|
|
2131
|
-
return this.start();
|
|
2132
|
-
}
|
|
2133
|
-
stop() {
|
|
2134
|
-
this.running = false;
|
|
2135
|
-
this.stopCountdown();
|
|
2136
|
-
this.stopPolling();
|
|
2137
|
-
}
|
|
2138
|
-
getState() {
|
|
2139
|
-
return this.state;
|
|
2140
|
-
}
|
|
2141
|
-
async reloadState() {
|
|
2142
|
-
const current = this.state ?? createCheckoutState(this.options.snapshot, {
|
|
2143
|
-
now: this.now(),
|
|
2144
|
-
logger: this.options.logger
|
|
2145
|
-
});
|
|
2146
|
-
const lookupInvoice = this.options.lookupInvoice;
|
|
2147
|
-
if (lookupInvoice === undefined || current.payment_hash === undefined) {
|
|
2148
|
-
return current;
|
|
2149
|
-
}
|
|
2150
|
-
try {
|
|
2151
|
-
const next = await lookupInvoice(current);
|
|
2152
|
-
const nextState = createCheckoutState(mergeCheckoutSnapshot(current, next), {
|
|
2153
|
-
now: this.now(),
|
|
2154
|
-
logger: this.options.logger
|
|
2155
|
-
});
|
|
2156
|
-
if (this.running) {
|
|
2157
|
-
this.applyState(nextState);
|
|
2158
|
-
}
|
|
2159
|
-
else {
|
|
2160
|
-
this.state = nextState;
|
|
2161
|
-
}
|
|
2162
|
-
return nextState;
|
|
2163
|
-
}
|
|
2164
|
-
catch (error) {
|
|
2165
|
-
this.options.onError?.(error);
|
|
2166
|
-
throw error;
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
applyState(state) {
|
|
2170
|
-
if (!this.running)
|
|
2171
|
-
return;
|
|
2172
|
-
this.state = state;
|
|
2173
|
-
this.options.onState(state);
|
|
2174
|
-
this.syncWatchers();
|
|
2175
|
-
}
|
|
2176
|
-
syncWatchers() {
|
|
2177
|
-
const state = this.state;
|
|
2178
|
-
if (state === undefined || !this.running)
|
|
2179
|
-
return;
|
|
2180
|
-
if (state.terminal) {
|
|
2181
|
-
this.stop();
|
|
2182
|
-
return;
|
|
2183
|
-
}
|
|
2184
|
-
if (state.settled || state.expires_at === undefined) {
|
|
2185
|
-
this.stopCountdown();
|
|
2186
|
-
}
|
|
2187
|
-
else if (this.countdownTimer === undefined) {
|
|
2188
|
-
this.countdownTimer = this.setInterval()(() => {
|
|
2189
|
-
const current = this.state;
|
|
2190
|
-
if (current === undefined)
|
|
2191
|
-
return;
|
|
2192
|
-
this.applyState(refreshCheckoutState(current, {
|
|
2193
|
-
now: this.now(),
|
|
2194
|
-
logger: this.options.logger
|
|
2195
|
-
}));
|
|
2196
|
-
}, 1000);
|
|
2197
|
-
}
|
|
2198
|
-
if (state.settled || this.options.lookupInvoice === undefined || state.payment_hash === undefined) {
|
|
2199
|
-
this.stopPolling();
|
|
2200
|
-
}
|
|
2201
|
-
else if (this.pollTimer === undefined) {
|
|
2202
|
-
this.pollTimer = this.setInterval()(() => {
|
|
2203
|
-
void this.poll();
|
|
2204
|
-
}, this.options.pollIntervalMs ?? OPENRECEIVE_DEFAULT_POLL_INTERVAL_MS);
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
async poll() {
|
|
2208
|
-
const lookupInvoice = this.options.lookupInvoice;
|
|
2209
|
-
const current = this.state;
|
|
2210
|
-
if (!this.running || lookupInvoice === undefined || current === undefined)
|
|
2211
|
-
return;
|
|
2212
|
-
if (current.terminal || current.settled) {
|
|
2213
|
-
this.stopPolling();
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
|
-
try {
|
|
2217
|
-
const next = await lookupInvoice(current);
|
|
2218
|
-
if (!this.running || this.state === undefined)
|
|
2219
|
-
return;
|
|
2220
|
-
this.applyState(createCheckoutState(mergeCheckoutSnapshot(this.state, next), {
|
|
2221
|
-
now: this.now(),
|
|
2222
|
-
logger: this.options.logger
|
|
2223
|
-
}));
|
|
2224
|
-
}
|
|
2225
|
-
catch (error) {
|
|
2226
|
-
this.options.onError?.(error);
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
stopCountdown() {
|
|
2230
|
-
if (this.countdownTimer === undefined)
|
|
2231
|
-
return;
|
|
2232
|
-
this.clearInterval()(this.countdownTimer);
|
|
2233
|
-
this.countdownTimer = undefined;
|
|
2234
|
-
}
|
|
2235
|
-
stopPolling() {
|
|
2236
|
-
if (this.pollTimer === undefined)
|
|
2237
|
-
return;
|
|
2238
|
-
this.clearInterval()(this.pollTimer);
|
|
2239
|
-
this.pollTimer = undefined;
|
|
2240
|
-
}
|
|
2241
|
-
now() {
|
|
2242
|
-
return this.options.now?.() ?? currentUnixSeconds();
|
|
2243
|
-
}
|
|
2244
|
-
setInterval() {
|
|
2245
|
-
return this.options.setInterval ?? globalThis.setInterval;
|
|
2246
|
-
}
|
|
2247
|
-
clearInterval() {
|
|
2248
|
-
return this.options.clearInterval ?? globalThis.clearInterval;
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
export class OpenReceiveBrowserCheckoutController {
|
|
2252
|
-
options;
|
|
2253
|
-
watcher;
|
|
2254
|
-
state;
|
|
2255
|
-
constructor(options) {
|
|
2256
|
-
this.options = options;
|
|
2257
|
-
this.watcher = this.createWatcher(options);
|
|
2258
|
-
}
|
|
2259
|
-
start() {
|
|
2260
|
-
this.state = this.watcher.start();
|
|
2261
|
-
return this.state;
|
|
2262
|
-
}
|
|
2263
|
-
update(options) {
|
|
2264
|
-
this.options = options;
|
|
2265
|
-
this.watcher.stop();
|
|
2266
|
-
this.watcher = this.createWatcher(options);
|
|
2267
|
-
return this.start();
|
|
2268
|
-
}
|
|
2269
|
-
stop() {
|
|
2270
|
-
this.watcher.stop();
|
|
2271
|
-
}
|
|
2272
|
-
getState() {
|
|
2273
|
-
return this.state ?? this.watcher.getState();
|
|
2274
|
-
}
|
|
2275
|
-
async copyInvoice() {
|
|
2276
|
-
const state = this.currentState();
|
|
2277
|
-
await copyInvoice({
|
|
2278
|
-
invoice: state.invoice,
|
|
2279
|
-
clipboard: this.options.clipboard,
|
|
2280
|
-
logger: this.options.logger,
|
|
2281
|
-
logContext: checkoutLogFields(state)
|
|
2282
|
-
});
|
|
2283
|
-
}
|
|
2284
|
-
openWallet() {
|
|
2285
|
-
const state = this.currentState();
|
|
2286
|
-
return openWallet({
|
|
2287
|
-
invoice: state.invoice,
|
|
2288
|
-
open: this.options.open,
|
|
2289
|
-
logger: this.options.logger,
|
|
2290
|
-
logContext: checkoutLogFields(state)
|
|
2291
|
-
});
|
|
2292
|
-
}
|
|
2293
|
-
async reloadState() {
|
|
2294
|
-
return this.watcher.reloadState();
|
|
2295
|
-
}
|
|
2296
|
-
async retry() {
|
|
2297
|
-
return this.reloadState();
|
|
2298
|
-
}
|
|
2299
|
-
async refreshExpiredInvoice() {
|
|
2300
|
-
const refreshInvoice = this.createRefreshInvoice(this.options);
|
|
2301
|
-
if (refreshInvoice === undefined) {
|
|
2302
|
-
throw new Error("OpenReceive refresh requires refreshInvoice or refreshUrl.");
|
|
2303
|
-
}
|
|
2304
|
-
const current = this.currentState();
|
|
2305
|
-
const result = await refreshInvoice(current);
|
|
2306
|
-
const snapshot = "new_invoice_id" in result ? result.invoice : result;
|
|
2307
|
-
return this.update({
|
|
2308
|
-
...this.options,
|
|
2309
|
-
snapshot
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
cancel() {
|
|
2313
|
-
this.stop();
|
|
2314
|
-
this.state = this.currentState();
|
|
2315
|
-
emitBrowserLog(this.options.logger, "info", "checkout.cancelled", "Stopped checkout watcher after cancel action.", checkoutLogFields(this.state));
|
|
2316
|
-
return this.state;
|
|
2317
|
-
}
|
|
2318
|
-
createWatcher(options) {
|
|
2319
|
-
const lookupInvoice = options.lookupInvoice ??
|
|
2320
|
-
(options.lookupUrl === undefined
|
|
2321
|
-
? undefined
|
|
2322
|
-
: createOpenReceiveLookupInvoiceFetcher({
|
|
2323
|
-
lookupUrl: options.lookupUrl,
|
|
2324
|
-
fetch: options.fetch,
|
|
2325
|
-
headers: options.lookupHeaders
|
|
2326
|
-
}));
|
|
2327
|
-
return new CheckoutWatcher({
|
|
2328
|
-
...options,
|
|
2329
|
-
...(lookupInvoice === undefined ? {} : { lookupInvoice }),
|
|
2330
|
-
onState: (state) => {
|
|
2331
|
-
this.state = state;
|
|
2332
|
-
options.onState?.(state);
|
|
2333
|
-
}
|
|
2334
|
-
});
|
|
2335
|
-
}
|
|
2336
|
-
createRefreshInvoice(options) {
|
|
2337
|
-
if (options.refreshInvoice !== undefined)
|
|
2338
|
-
return options.refreshInvoice;
|
|
2339
|
-
if (options.refreshUrl === undefined || options.refreshIdempotencyKey === undefined) {
|
|
2340
|
-
return undefined;
|
|
2341
|
-
}
|
|
2342
|
-
return createOpenReceiveRefreshInvoiceFetcher({
|
|
2343
|
-
refreshUrl: options.refreshUrl,
|
|
2344
|
-
idempotencyKey: options.refreshIdempotencyKey,
|
|
2345
|
-
fetch: options.fetch,
|
|
2346
|
-
headers: options.refreshHeaders,
|
|
2347
|
-
reason: options.refreshReason
|
|
2348
|
-
});
|
|
2349
|
-
}
|
|
2350
|
-
currentState() {
|
|
2351
|
-
return this.getState() ?? createCheckoutState(this.options.snapshot, {
|
|
2352
|
-
now: this.options.now?.(),
|
|
2353
|
-
logger: this.options.logger
|
|
2354
|
-
});
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
export function createCheckoutController(options) {
|
|
2358
|
-
return new OpenReceiveBrowserCheckoutController(options);
|
|
2359
|
-
}
|
|
2360
|
-
export async function createQrSvg(invoice, options = {}) {
|
|
2361
|
-
const encoder = await getQrEncoder(options.encoder);
|
|
2362
|
-
const svg = await encoder.toString(createLightningUri(invoice), {
|
|
2363
|
-
type: "svg",
|
|
2364
|
-
errorCorrectionLevel: OPENRECEIVE_QR_ERROR_CORRECTION,
|
|
2365
|
-
margin: OPENRECEIVE_QR_QUIET_ZONE_MODULES,
|
|
2366
|
-
width: options.width,
|
|
2367
|
-
color: {
|
|
2368
|
-
dark: OPENRECEIVE_QR_DARK_COLOR,
|
|
2369
|
-
light: OPENRECEIVE_QR_LIGHT_COLOR
|
|
2370
|
-
}
|
|
2371
|
-
});
|
|
2372
|
-
return String(svg);
|
|
2373
|
-
}
|
|
2374
|
-
export async function createQrPngDataUrl(invoice, options = {}) {
|
|
2375
|
-
const encoder = await getQrEncoder(options.encoder);
|
|
2376
|
-
if (encoder.toDataURL === undefined) {
|
|
2377
|
-
throw new Error("QR encoder does not support PNG data URL output.");
|
|
2378
|
-
}
|
|
2379
|
-
const png = await encoder.toDataURL(createLightningUri(invoice), {
|
|
2380
|
-
type: "image/png",
|
|
2381
|
-
errorCorrectionLevel: OPENRECEIVE_QR_ERROR_CORRECTION,
|
|
2382
|
-
margin: OPENRECEIVE_QR_QUIET_ZONE_MODULES,
|
|
2383
|
-
width: options.width,
|
|
2384
|
-
color: {
|
|
2385
|
-
dark: OPENRECEIVE_QR_DARK_COLOR,
|
|
2386
|
-
light: OPENRECEIVE_QR_LIGHT_COLOR
|
|
2387
|
-
}
|
|
2388
|
-
});
|
|
2389
|
-
return String(png);
|
|
2390
|
-
}
|
|
2391
|
-
// Spec-named alias for the canonical QR helper trio
|
|
2392
|
-
// (createQrSvg / createQrPng / createLightningUri). createQrPng returns a
|
|
2393
|
-
// PNG data URL using the same safe quiet-zone, contrast, and payload defaults.
|
|
2394
|
-
export const createQrPng = createQrPngDataUrl;
|
|
2395
|
-
export async function copyInvoice(options) {
|
|
2396
|
-
assertInvoice(options.invoice);
|
|
2397
|
-
const clipboard = options.clipboard ?? globalThis.navigator?.clipboard;
|
|
2398
|
-
if (clipboard === undefined) {
|
|
2399
|
-
throw new Error("Clipboard API is unavailable.");
|
|
2400
|
-
}
|
|
2401
|
-
await clipboard.writeText(options.invoice);
|
|
2402
|
-
emitBrowserLog(options.logger, "info", "checkout.invoice.copied", "Copied Lightning invoice to clipboard.", options.logContext);
|
|
2403
|
-
}
|
|
2404
|
-
export function openWallet(options) {
|
|
2405
|
-
const uri = createLightningUri(options.invoice);
|
|
2406
|
-
if (options.open !== undefined) {
|
|
2407
|
-
options.open(uri);
|
|
2408
|
-
emitBrowserLog(options.logger, "info", "checkout.wallet.opened", "Opened Lightning invoice URI.", options.logContext);
|
|
2409
|
-
return uri;
|
|
2410
|
-
}
|
|
2411
|
-
const location = globalThis.window?.location;
|
|
2412
|
-
if (location === undefined) {
|
|
2413
|
-
throw new Error("window.location is unavailable.");
|
|
2414
|
-
}
|
|
2415
|
-
location.assign(uri);
|
|
2416
|
-
emitBrowserLog(options.logger, "info", "checkout.wallet.opened", "Opened Lightning invoice URI.", options.logContext);
|
|
2417
|
-
return uri;
|
|
2418
|
-
}
|
|
2419
|
-
async function getQrEncoder(encoder) {
|
|
2420
|
-
if (encoder !== undefined)
|
|
2421
|
-
return encoder;
|
|
2422
|
-
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
2423
|
-
const imported = asRecord(await dynamicImport("qrcode"));
|
|
2424
|
-
const candidate = (imported.default ?? imported);
|
|
2425
|
-
if (isQrEncoder(candidate))
|
|
2426
|
-
return candidate;
|
|
2427
|
-
throw new Error("qrcode package did not expose a compatible encoder.");
|
|
2428
|
-
}
|
|
2429
|
-
function isQrEncoder(value) {
|
|
2430
|
-
const record = asRecord(value);
|
|
2431
|
-
return typeof record.toString === "function";
|
|
2432
|
-
}
|
|
2433
|
-
function assertInvoice(invoice) {
|
|
2434
|
-
if (typeof invoice !== "string" || invoice.length === 0) {
|
|
2435
|
-
throw new TypeError("invoice must be a non-empty BOLT11 string");
|
|
2436
|
-
}
|
|
2437
|
-
if (invoice.startsWith("nostr+walletconnect://")) {
|
|
2438
|
-
throw new TypeError("invoice must not be an NWC connection string");
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
function assertOpenReceiveBrowserPayloadSafe(value) {
|
|
2442
|
-
if (typeof value === "string") {
|
|
2443
|
-
if (value.startsWith("nostr+walletconnect://")) {
|
|
2444
|
-
throw new TypeError("OpenReceive browser payload must not include an NWC connection string");
|
|
2445
|
-
}
|
|
2446
|
-
return;
|
|
2447
|
-
}
|
|
2448
|
-
if (Array.isArray(value)) {
|
|
2449
|
-
for (const item of value)
|
|
2450
|
-
assertOpenReceiveBrowserPayloadSafe(item);
|
|
2451
|
-
return;
|
|
2452
|
-
}
|
|
2453
|
-
if (value !== null && typeof value === "object") {
|
|
2454
|
-
for (const item of Object.values(value)) {
|
|
2455
|
-
assertOpenReceiveBrowserPayloadSafe(item);
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
function normalizeCheckoutState(state, now) {
|
|
2460
|
-
const { phase: _phase, settled: _settled, terminal: _terminal, expiresInSeconds: _expiresInSeconds, ...base } = state;
|
|
2461
|
-
const phase = getCheckoutPhase(state.transaction_state, state.workflow_state);
|
|
2462
|
-
return {
|
|
2463
|
-
...base,
|
|
2464
|
-
phase,
|
|
2465
|
-
settled: base.transaction_state === "settled",
|
|
2466
|
-
terminal: isTerminalPhase(phase),
|
|
2467
|
-
...(base.expires_at === undefined || now === undefined
|
|
2468
|
-
? {}
|
|
2469
|
-
: { expiresInSeconds: Math.max(0, base.expires_at - now) })
|
|
2470
|
-
};
|
|
2471
|
-
}
|
|
2472
|
-
function getCheckoutPhase(transactionState, workflowState) {
|
|
2473
|
-
if (workflowState === "cancelled")
|
|
2474
|
-
return "cancelled";
|
|
2475
|
-
if (transactionState === "settled")
|
|
2476
|
-
return "settled";
|
|
2477
|
-
if (transactionState === "expired" || workflowState === "expired_closed") {
|
|
2478
|
-
return "expired";
|
|
2479
|
-
}
|
|
2480
|
-
if (transactionState === "failed" || workflowState === "failed_closed") {
|
|
2481
|
-
return "failed";
|
|
2482
|
-
}
|
|
2483
|
-
if (workflowState === "verifying" ||
|
|
2484
|
-
workflowState === "expiry_pending_verification") {
|
|
2485
|
-
return "verifying";
|
|
2486
|
-
}
|
|
2487
|
-
return "invoice_created";
|
|
2488
|
-
}
|
|
2489
|
-
function isTerminalPhase(phase) {
|
|
2490
|
-
return (phase === "expired" ||
|
|
2491
|
-
phase === "failed" ||
|
|
2492
|
-
phase === "cancelled");
|
|
2493
|
-
}
|
|
2494
|
-
function snapshotFromCheckoutState(state) {
|
|
2495
|
-
return {
|
|
2496
|
-
invoice_id: state.invoice_id,
|
|
2497
|
-
invoice: state.invoice,
|
|
2498
|
-
...(state.payment_hash === undefined ? {} : { payment_hash: state.payment_hash }),
|
|
2499
|
-
...(state.amount_msats === undefined ? {} : { amount_msats: state.amount_msats }),
|
|
2500
|
-
...(state.fiat_quote === undefined ? {} : { fiat_quote: state.fiat_quote }),
|
|
2501
|
-
transaction_state: state.transaction_state,
|
|
2502
|
-
workflow_state: state.workflow_state,
|
|
2503
|
-
...(state.expires_at === undefined ? {} : { expires_at: state.expires_at }),
|
|
2504
|
-
...(state.settled_at === undefined ? {} : { settled_at: state.settled_at })
|
|
2505
|
-
};
|
|
2506
|
-
}
|
|
2507
|
-
function currentUnixSeconds() {
|
|
2508
|
-
return Math.floor(Date.now() / 1000);
|
|
2509
|
-
}
|
|
2510
|
-
function getRailForPaymentMethod(method) {
|
|
2511
|
-
if (method === "bank")
|
|
2512
|
-
return "bank";
|
|
2513
|
-
if (method === "card")
|
|
2514
|
-
return "card";
|
|
2515
|
-
return null;
|
|
2516
|
-
}
|
|
2517
|
-
function isKnownCountryCode(countryCode) {
|
|
2518
|
-
return getCountries().some((country) => country.code === countryCode);
|
|
2519
|
-
}
|
|
2520
|
-
function readStorageValue(storage, key) {
|
|
2521
|
-
try {
|
|
2522
|
-
return storage?.getItem(key) ?? null;
|
|
2523
|
-
}
|
|
2524
|
-
catch {
|
|
2525
|
-
return null;
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
function writeStorageValue(storage, key, value) {
|
|
2529
|
-
try {
|
|
2530
|
-
storage?.setItem(key, value);
|
|
2531
|
-
}
|
|
2532
|
-
catch {
|
|
2533
|
-
// Browser storage is convenience only; checkout must keep working without it.
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
function getBrowserStorage() {
|
|
2537
|
-
try {
|
|
2538
|
-
return globalThis.localStorage;
|
|
2539
|
-
}
|
|
2540
|
-
catch {
|
|
2541
|
-
return undefined;
|
|
2542
|
-
}
|
|
2543
|
-
}
|
|
2544
|
-
function checkoutLogFields(state) {
|
|
2545
|
-
return {
|
|
2546
|
-
...(state.invoice_id === undefined ? {} : { invoice_id: state.invoice_id }),
|
|
2547
|
-
...(state.payment_hash === undefined ? {} : { payment_hash: state.payment_hash }),
|
|
2548
|
-
...(state.amount_msats === undefined ? {} : { amount_msats: state.amount_msats }),
|
|
2549
|
-
...(state.transaction_state === undefined
|
|
2550
|
-
? {}
|
|
2551
|
-
: { transaction_state: state.transaction_state }),
|
|
2552
|
-
...(state.workflow_state === undefined
|
|
2553
|
-
? {}
|
|
2554
|
-
: { workflow_state: state.workflow_state }),
|
|
2555
|
-
...(state.phase === undefined ? {} : { phase: state.phase }),
|
|
2556
|
-
...(state.expiresInSeconds === undefined
|
|
2557
|
-
? {}
|
|
2558
|
-
: { expires_in_seconds: state.expiresInSeconds })
|
|
2559
|
-
};
|
|
2560
|
-
}
|
|
2561
|
-
function emitBrowserLog(logger, level, event, message, fields = {}) {
|
|
2562
|
-
if (logger === undefined)
|
|
2563
|
-
return;
|
|
2564
|
-
try {
|
|
2565
|
-
logger(sanitizeBrowserLogEntry({
|
|
2566
|
-
level,
|
|
2567
|
-
event,
|
|
2568
|
-
message,
|
|
2569
|
-
...fields
|
|
2570
|
-
}));
|
|
2571
|
-
}
|
|
2572
|
-
catch {
|
|
2573
|
-
// Checkout logs are diagnostic only and must not affect user actions.
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
function sanitizeBrowserLogEntry(entry) {
|
|
2577
|
-
const clean = {};
|
|
2578
|
-
for (const [key, value] of Object.entries(entry)) {
|
|
2579
|
-
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
2580
|
-
clean[key] = "[REDACTED]";
|
|
2581
|
-
}
|
|
2582
|
-
else {
|
|
2583
|
-
clean[key] = sanitizeBrowserLogValue(value);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
return clean;
|
|
2587
|
-
}
|
|
2588
|
-
function sanitizeBrowserLogValue(value) {
|
|
2589
|
-
if (typeof value === "string")
|
|
2590
|
-
return redactBrowserSecrets(value);
|
|
2591
|
-
if (Array.isArray(value))
|
|
2592
|
-
return value.map(sanitizeBrowserLogValue);
|
|
2593
|
-
if (typeof value !== "object" || value === null)
|
|
2594
|
-
return value;
|
|
2595
|
-
const clean = {};
|
|
2596
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
2597
|
-
if (/secret|token|authorization|cookie|nwc/i.test(key)) {
|
|
2598
|
-
clean[key] = "[REDACTED]";
|
|
2599
|
-
}
|
|
2600
|
-
else {
|
|
2601
|
-
clean[key] = sanitizeBrowserLogValue(nested);
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
return clean;
|
|
2605
|
-
}
|
|
2606
|
-
function redactBrowserSecrets(value) {
|
|
2607
|
-
return value
|
|
2608
|
-
.replace(/nostr\+walletconnect:\/\/[^\s"'`<>]+/g, "[REDACTED_NWC]")
|
|
2609
|
-
.replace(/([?&](?:_or_evt|token|secret)=)[^&\s"'`<>]+/gi, "$1[REDACTED]");
|
|
2610
|
-
}
|
|
2611
|
-
function asRecord(value) {
|
|
2612
|
-
if (typeof value !== "object" || value === null)
|
|
2613
|
-
return {};
|
|
2614
|
-
return value;
|
|
2615
|
-
}
|