@nevios/storefront-kit 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -201,6 +201,37 @@ var en = {
201
201
  "order.terminal.cancelled": "This order was cancelled.",
202
202
  "order.terminal.refunded": "This order was refunded.",
203
203
  "order.terminal.returned": "This order was returned.",
204
+ // payment page — the four situations it can be in
205
+ "payment.title": "Pay for order {order}",
206
+ "payment.amountDue": "Amount due",
207
+ "payment.methodLabel": "Payment method",
208
+ "payment.payNow": "Pay now",
209
+ "payment.finish": "Finish payment",
210
+ "payment.loading": "Loading payment\u2026",
211
+ "payment.loadError": "Couldn't load this payment.",
212
+ "payment.actionError": "That didn't go through. Please try again.",
213
+ // …paid
214
+ "payment.paidTitle": "Paid",
215
+ "payment.paidBody": "Order {order} is paid. Nothing further is needed.",
216
+ "payment.paidAmount": "Paid",
217
+ // …returning from a gateway, callback still in flight
218
+ "payment.verifyingTitle": "Checking your payment\u2026",
219
+ "payment.verifyingBody": "This takes a few seconds. Don't pay again \u2014 we'll update this page.",
220
+ // …locked, one sentence per reason
221
+ "payment.blockedTitle": "This order can't be paid",
222
+ "payment.blocked.orderCancelled": "This order was cancelled, so there is nothing to pay.",
223
+ "payment.blocked.alreadyPaid": "This order is already paid.",
224
+ "payment.blocked.authorizationHeld": "Your bank is holding the funds for this order. Once the payment settles this page will show it as paid.",
225
+ "payment.blocked.documentIssued": "An invoice has been issued for this order, so the amount is fixed. Contact us to change how you pay.",
226
+ // …bank transfer
227
+ "payment.account": "Account",
228
+ "payment.beneficiary": "Beneficiary",
229
+ "payment.variableSymbol": "Variable symbol",
230
+ "payment.amount": "Amount",
231
+ "payment.copy": "Copy",
232
+ "payment.copied": "Copied",
233
+ "payment.qrAlt": "QR code for the payment",
234
+ "payment.qrCaption": "Scan in your banking app",
204
235
  // error pages
205
236
  "error.notFound.title": "Page not found",
206
237
  "error.notFound.body": "This page doesn't exist or isn't available in your region.",
@@ -357,6 +388,33 @@ var cs = {
357
388
  "order.terminal.cancelled": "Tato objedn\xE1vka byla zru\u0161ena.",
358
389
  "order.terminal.refunded": "Tato objedn\xE1vka byla refundov\xE1na.",
359
390
  "order.terminal.returned": "Tato objedn\xE1vka byla vr\xE1cena.",
391
+ // platební stránka — čtyři situace, ve kterých může být
392
+ "payment.title": "Zaplatit objedn\xE1vku {order}",
393
+ "payment.amountDue": "K \xFAhrad\u011B",
394
+ "payment.methodLabel": "Zp\u016Fsob platby",
395
+ "payment.payNow": "Zaplatit",
396
+ "payment.finish": "Dokon\u010Dit platbu",
397
+ "payment.loading": "Na\u010D\xEDt\xE1n\xED platby\u2026",
398
+ "payment.loadError": "Platbu se nepoda\u0159ilo na\u010D\xEDst.",
399
+ "payment.actionError": "Nepovedlo se to. Zkuste to pros\xEDm znovu.",
400
+ "payment.paidTitle": "Zaplaceno",
401
+ "payment.paidBody": "Objedn\xE1vka {order} je zaplacen\xE1. Nic dal\u0161\xEDho d\u011Blat nemus\xEDte.",
402
+ "payment.paidAmount": "Uhrazeno",
403
+ "payment.verifyingTitle": "Ov\u011B\u0159ujeme platbu\u2026",
404
+ "payment.verifyingBody": "Chvilku to trv\xE1. Nepla\u0165te pros\xEDm znovu \u2014 str\xE1nka se sama aktualizuje.",
405
+ "payment.blockedTitle": "Tuhle objedn\xE1vku te\u010F zaplatit nejde",
406
+ "payment.blocked.orderCancelled": "Objedn\xE1vka byla zru\u0161en\xE1, tak\u017Ee nen\xED co platit.",
407
+ "payment.blocked.alreadyPaid": "Objedn\xE1vka je u\u017E zaplacen\xE1.",
408
+ "payment.blocked.authorizationHeld": "Banka na tuhle objedn\xE1vku dr\u017E\xED pen\xEDze. Jakmile se platba dokon\u010D\xED, uvid\xEDte to tady.",
409
+ "payment.blocked.documentIssued": "K objedn\xE1vce je vystaven\xE1 faktura, tak\u017Ee je \u010D\xE1stka pevn\xE1. Pokud chcete platit jinak, ozv\u011Bte se n\xE1m.",
410
+ "payment.account": "\xDA\u010Det",
411
+ "payment.beneficiary": "P\u0159\xEDjemce",
412
+ "payment.variableSymbol": "Variabiln\xED symbol",
413
+ "payment.amount": "\u010C\xE1stka",
414
+ "payment.copy": "Kop\xEDrovat",
415
+ "payment.copied": "Zkop\xEDrov\xE1no",
416
+ "payment.qrAlt": "QR k\xF3d k platb\u011B",
417
+ "payment.qrCaption": "Na\u010Dt\u011Bte v bankovn\xED aplikaci",
360
418
  "error.notFound.title": "Str\xE1nka nenalezena",
361
419
  "error.notFound.body": "Tato str\xE1nka neexistuje nebo nen\xED ve va\u0161em regionu dostupn\xE1.",
362
420
  "error.generic.title": "N\u011Bco se pokazilo",
@@ -1164,6 +1222,7 @@ function AddToCart({
1164
1222
  }
1165
1223
  function CartLineItem({ line, locale }) {
1166
1224
  const t = useT();
1225
+ const loc = useFormatLocale(locale);
1167
1226
  const { updateLine, removeLine } = useCart();
1168
1227
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.85rem", alignItems: "flex-start", padding: "0.75rem 0" }, children: [
1169
1228
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1235,7 +1294,7 @@ function CartLineItem({ line, locale }) {
1235
1294
  )
1236
1295
  ] })
1237
1296
  ] }),
1238
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, whiteSpace: "nowrap" }, children: formatCents(line.line_total_cents, line.unit_price.currency, locale) })
1297
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, whiteSpace: "nowrap" }, children: formatCents(line.line_total_cents, line.unit_price.currency, loc) })
1239
1298
  ] });
1240
1299
  }
1241
1300
  function StepBtn({ label, onClick }) {
@@ -1267,6 +1326,7 @@ function CartSummary({
1267
1326
  locale
1268
1327
  }) {
1269
1328
  const tr = useT();
1329
+ const loc = useFormatLocale(locale);
1270
1330
  const { cart } = useCart();
1271
1331
  if (cart == null) return null;
1272
1332
  const t = cart.totals;
@@ -1287,10 +1347,10 @@ function CartSummary({
1287
1347
  opacity: empty ? 0.55 : 1
1288
1348
  };
1289
1349
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
1290
- /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.subtotal"), value: formatCents(t.subtotal_with_tax_cents, t.currency, locale) }),
1291
- t.tax_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, locale), muted: true }) : null,
1350
+ /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.subtotal"), value: formatCents(t.subtotal_with_tax_cents, t.currency, loc) }),
1351
+ t.tax_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, loc), muted: true }) : null,
1292
1352
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
1293
- /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency, locale), strong: true }),
1353
+ /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency, loc), strong: true }),
1294
1354
  checkoutHref && !empty ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: checkoutHref, style: ctaStyle, children: cta }) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", disabled: empty, onClick: onCheckout, style: ctaStyle, children: cta })
1295
1355
  ] });
1296
1356
  }
@@ -1693,6 +1753,7 @@ function ShippingPicker({
1693
1753
  locale
1694
1754
  }) {
1695
1755
  const t = useT();
1756
+ const loc = useFormatLocale(locale);
1696
1757
  const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
1697
1758
  const heading = title ?? t("checkout.shippingMethod");
1698
1759
  const [quotes, setQuotes] = React10__namespace.useState(null);
@@ -1733,7 +1794,7 @@ function ShippingPicker({
1733
1794
  {
1734
1795
  selected: selected === q.rate_id,
1735
1796
  onSelect: () => setSelected(q.rate_id),
1736
- trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency, locale),
1797
+ trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency, loc),
1737
1798
  children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
1738
1799
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: q.label }),
1739
1800
  days ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: days }) : null
@@ -1754,6 +1815,39 @@ function estDays(q, t) {
1754
1815
  const n = lo ?? hi;
1755
1816
  return t("shipping.days", { count: n });
1756
1817
  }
1818
+ function PaymentMethodPicker({
1819
+ methods,
1820
+ value,
1821
+ onSelect,
1822
+ currency,
1823
+ locale,
1824
+ disabled,
1825
+ label
1826
+ }) {
1827
+ const loc = useFormatLocale(locale);
1828
+ return /* @__PURE__ */ jsxRuntime.jsx(
1829
+ "div",
1830
+ {
1831
+ role: "radiogroup",
1832
+ "aria-label": label,
1833
+ style: { display: "flex", flexDirection: "column", gap: "0.6rem" },
1834
+ children: methods.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1835
+ SelectRow,
1836
+ {
1837
+ selected: value === m.method,
1838
+ disabled,
1839
+ onSelect: () => onSelect(m.method),
1840
+ trailing: m.surcharge_cents > 0 ? `+${formatCents(m.surcharge_cents, currency, loc)}` : void 0,
1841
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
1842
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: m.display_label }),
1843
+ m.display_description ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: m.display_description }) : null
1844
+ ] })
1845
+ },
1846
+ m.method
1847
+ ))
1848
+ }
1849
+ );
1850
+ }
1757
1851
  function PaymentSection({
1758
1852
  title,
1759
1853
  returnUrl,
@@ -1794,19 +1888,17 @@ function PaymentSection({
1794
1888
  return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: heading, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.noPayment") }) });
1795
1889
  }
1796
1890
  return /* @__PURE__ */ jsxRuntime.jsxs(Section, { title: heading, children: [
1797
- /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: "0.6rem" }, children: methods.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1798
- SelectRow,
1891
+ /* @__PURE__ */ jsxRuntime.jsx(
1892
+ PaymentMethodPicker,
1799
1893
  {
1800
- selected: selected === m.method,
1801
- onSelect: () => void setPaymentMethod(m.method),
1802
- trailing: m.surcharge_cents > 0 && checkout ? `+${formatCents(m.surcharge_cents, checkout.totals.currency, locale)}` : void 0,
1803
- children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
1804
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: m.display_label }),
1805
- m.display_description ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: m.display_description }) : null
1806
- ] })
1807
- },
1808
- m.method
1809
- )) }),
1894
+ methods,
1895
+ value: selected,
1896
+ onSelect: (method) => void setPaymentMethod(method),
1897
+ currency: checkout?.totals.currency ?? "CZK",
1898
+ locale,
1899
+ label: heading
1900
+ }
1901
+ ),
1810
1902
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1811
1903
  /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: placeOrder, busy: loading, disabled: selected == null, children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
1812
1904
  isGateway ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
@@ -1819,12 +1911,13 @@ function OrderSummary({
1819
1911
  locale
1820
1912
  }) {
1821
1913
  const tr = useT();
1914
+ const loc = useFormatLocale(locale);
1822
1915
  const ctx = useCheckout();
1823
1916
  const checkout = override ?? ctx.checkout;
1824
1917
  if (checkout == null) return null;
1825
1918
  const t = checkout.totals;
1826
1919
  const c = t.currency;
1827
- const money = (cents) => formatCents(cents, c, locale);
1920
+ const money = (cents) => formatCents(cents, c, loc);
1828
1921
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: [
1829
1922
  !totalsOnly && checkout.line_items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.75rem" }, children: checkout.line_items.map((li) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
1830
1923
  li.image_url ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2056,6 +2149,79 @@ function useOrderStatus(statusToken, initial) {
2056
2149
  }, [load, initial]);
2057
2150
  return { order, loading, error, reload: () => void load() };
2058
2151
  }
2152
+ function usePayment(token, initial) {
2153
+ const client = useStorefront();
2154
+ const [payment, setPayment] = React10__namespace.useState(initial ?? null);
2155
+ const [methods, setMethods] = React10__namespace.useState([]);
2156
+ const [loading, setLoading] = React10__namespace.useState(false);
2157
+ const [busy, setBusy] = React10__namespace.useState(false);
2158
+ const [error, setError] = React10__namespace.useState(null);
2159
+ const load = React10__namespace.useCallback(async () => {
2160
+ if (!token) return;
2161
+ setLoading(true);
2162
+ try {
2163
+ const [state, list] = await Promise.all([
2164
+ client.payment.get(token),
2165
+ client.payment.methods(token)
2166
+ ]);
2167
+ setPayment(state);
2168
+ setMethods(list);
2169
+ setError(null);
2170
+ } catch (e) {
2171
+ setError(e instanceof Error ? e : new Error(String(e)));
2172
+ } finally {
2173
+ setLoading(false);
2174
+ }
2175
+ }, [client, token]);
2176
+ React10__namespace.useEffect(() => {
2177
+ if (!token) return;
2178
+ if (initial && initial.token === token) {
2179
+ setPayment(initial);
2180
+ client.payment.methods(token).then(setMethods).catch(() => setMethods([]));
2181
+ return;
2182
+ }
2183
+ void load();
2184
+ }, [token, load]);
2185
+ const selectMethod = React10__namespace.useCallback(
2186
+ async (method) => {
2187
+ if (!token) return null;
2188
+ setBusy(true);
2189
+ try {
2190
+ const next = await client.payment.selectMethod(token, method);
2191
+ setPayment(next);
2192
+ setError(null);
2193
+ return next;
2194
+ } catch (e) {
2195
+ setError(e instanceof Error ? e : new Error(String(e)));
2196
+ return null;
2197
+ } finally {
2198
+ setBusy(false);
2199
+ }
2200
+ },
2201
+ [client, token]
2202
+ );
2203
+ const startPayment = React10__namespace.useCallback(
2204
+ async (input) => {
2205
+ if (!token) return null;
2206
+ setBusy(true);
2207
+ try {
2208
+ const result = await client.payment.start(token, {
2209
+ return_url: input?.returnUrl,
2210
+ cancel_url: input?.cancelUrl
2211
+ });
2212
+ setError(null);
2213
+ return result.redirect_url;
2214
+ } catch (e) {
2215
+ setError(e instanceof Error ? e : new Error(String(e)));
2216
+ return null;
2217
+ } finally {
2218
+ setBusy(false);
2219
+ }
2220
+ },
2221
+ [client, token]
2222
+ );
2223
+ return { payment, methods, loading, busy, error, reload: load, selectMethod, startPayment };
2224
+ }
2059
2225
  var PHASE_RANK = {
2060
2226
  placed: 0,
2061
2227
  paid: 1,
@@ -2151,6 +2317,7 @@ function OrderLines({
2151
2317
  order,
2152
2318
  locale
2153
2319
  }) {
2320
+ const loc = useFormatLocale(locale);
2154
2321
  return /* @__PURE__ */ jsxRuntime.jsxs("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2155
2322
  order.line_items.map((li) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
2156
2323
  li.image_url ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2171,11 +2338,11 @@ function OrderLines({
2171
2338
  li.quantity
2172
2339
  ] })
2173
2340
  ] }),
2174
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(li.total_with_tax_cents, order.currency, locale) })
2341
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(li.total_with_tax_cents, order.currency, loc) })
2175
2342
  ] }, li.id)),
2176
2343
  order.fee_lines.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
2177
2344
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", fontWeight: 500 }, children: f.title }) }),
2178
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(f.total_with_tax_cents, order.currency, locale) })
2345
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(f.total_with_tax_cents, order.currency, loc) })
2179
2346
  ] }, `fee-${i}`))
2180
2347
  ] });
2181
2348
  }
@@ -2184,9 +2351,10 @@ function OrderTotals({
2184
2351
  locale
2185
2352
  }) {
2186
2353
  const tr = useT();
2354
+ const loc = useFormatLocale(locale);
2187
2355
  const t = order.totals;
2188
2356
  const c = order.currency;
2189
- const money = (cents) => formatCents(cents, c, locale);
2357
+ const money = (cents) => formatCents(cents, c, loc);
2190
2358
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2191
2359
  /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.subtotal"), value: money(t.subtotal_cents) }),
2192
2360
  t.discount_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.discount"), value: `\u2212${money(t.discount_cents)}`, muted: true }) : null,
@@ -2292,6 +2460,7 @@ function OrderStatusPage({
2292
2460
  statusToken,
2293
2461
  order: initial,
2294
2462
  onContactSupport,
2463
+ payHref,
2295
2464
  locale
2296
2465
  }) {
2297
2466
  const t = useT();
@@ -2343,6 +2512,7 @@ function OrderStatusPage({
2343
2512
  /* @__PURE__ */ jsxRuntime.jsxs(Card, { title: t("order.payment"), children: [
2344
2513
  /* @__PURE__ */ jsxRuntime.jsx(Line, { label: t("order.method"), value: order.payment.method ?? "\u2014" }),
2345
2514
  /* @__PURE__ */ jsxRuntime.jsx(Line, { label: t("order.status"), value: order.payment.status }),
2515
+ order.payment.token && payHref ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: payHref(order.payment.token), style: { color: "var(--nv-brand)", fontWeight: 500 }, children: t("payment.finish") }) : null,
2346
2516
  order.payment.invoice_available && order.payment.invoice_url ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: order.payment.invoice_url, style: { color: "var(--nv-brand)" }, children: t("order.downloadInvoice") }) : null
2347
2517
  ] }),
2348
2518
  order.actions.can_contact_support && onContactSupport ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2394,6 +2564,265 @@ function Line({ label, value }) {
2394
2564
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { textAlign: "right" }, children: value })
2395
2565
  ] });
2396
2566
  }
2567
+ function TransferInstructions({
2568
+ instructions: i,
2569
+ locale
2570
+ }) {
2571
+ const t = useT();
2572
+ const loc = useFormatLocale(locale);
2573
+ const account = i.iban || i.account_number;
2574
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2575
+ "div",
2576
+ {
2577
+ style: {
2578
+ display: "flex",
2579
+ flexWrap: "wrap",
2580
+ gap: "1.25rem",
2581
+ alignItems: "flex-start",
2582
+ padding: "1rem",
2583
+ borderRadius: "var(--nv-radius)",
2584
+ border: "1px solid var(--nv-border)"
2585
+ },
2586
+ children: [
2587
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { style: { flex: "1 1 15rem", margin: 0, display: "flex", flexDirection: "column", gap: "0.7rem" }, children: [
2588
+ account ? /* @__PURE__ */ jsxRuntime.jsx(CopyRow, { label: t("payment.account"), value: account }) : null,
2589
+ i.beneficiary_name ? /* @__PURE__ */ jsxRuntime.jsx(Row4, { label: t("payment.beneficiary"), value: i.beneficiary_name }) : null,
2590
+ /* @__PURE__ */ jsxRuntime.jsx(CopyRow, { label: t("payment.variableSymbol"), value: i.variable_symbol }),
2591
+ /* @__PURE__ */ jsxRuntime.jsx(
2592
+ Row4,
2593
+ {
2594
+ label: t("payment.amount"),
2595
+ value: formatCents(i.amount_cents, i.currency, loc),
2596
+ strong: true
2597
+ }
2598
+ )
2599
+ ] }),
2600
+ i.qr_svg_data_uri ? /* @__PURE__ */ jsxRuntime.jsxs("figure", { style: { margin: 0, textAlign: "center" }, children: [
2601
+ /* @__PURE__ */ jsxRuntime.jsx(
2602
+ "img",
2603
+ {
2604
+ src: i.qr_svg_data_uri,
2605
+ alt: t("payment.qrAlt"),
2606
+ width: 148,
2607
+ height: 148,
2608
+ style: { display: "block", borderRadius: "var(--nv-radius)" }
2609
+ }
2610
+ ),
2611
+ /* @__PURE__ */ jsxRuntime.jsx("figcaption", { style: { fontSize: "0.75rem", color: "var(--nv-muted)", marginTop: "0.4rem" }, children: t("payment.qrCaption") })
2612
+ ] }) : null,
2613
+ i.instructions ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { flexBasis: "100%", margin: 0, fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: i.instructions }) : null
2614
+ ]
2615
+ }
2616
+ );
2617
+ }
2618
+ function Row4({
2619
+ label,
2620
+ value,
2621
+ strong
2622
+ }) {
2623
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.1rem" }, children: [
2624
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { style: { fontSize: "0.75rem", color: "var(--nv-muted)" }, children: label }),
2625
+ /* @__PURE__ */ jsxRuntime.jsx(
2626
+ "dd",
2627
+ {
2628
+ style: {
2629
+ margin: 0,
2630
+ fontWeight: strong ? 600 : 500,
2631
+ fontVariantNumeric: "tabular-nums"
2632
+ },
2633
+ children: value
2634
+ }
2635
+ )
2636
+ ] });
2637
+ }
2638
+ function CopyRow({ label, value }) {
2639
+ const t = useT();
2640
+ const [copied, setCopied] = React10__namespace.useState(false);
2641
+ React10__namespace.useEffect(() => {
2642
+ if (!copied) return;
2643
+ const id = setTimeout(() => setCopied(false), 1800);
2644
+ return () => clearTimeout(id);
2645
+ }, [copied]);
2646
+ async function copy() {
2647
+ try {
2648
+ await navigator.clipboard.writeText(value);
2649
+ setCopied(true);
2650
+ } catch {
2651
+ }
2652
+ }
2653
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.1rem" }, children: [
2654
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { style: { fontSize: "0.75rem", color: "var(--nv-muted)" }, children: label }),
2655
+ /* @__PURE__ */ jsxRuntime.jsxs("dd", { style: { margin: 0, display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
2656
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, fontVariantNumeric: "tabular-nums" }, children: value }),
2657
+ /* @__PURE__ */ jsxRuntime.jsx(
2658
+ "button",
2659
+ {
2660
+ type: "button",
2661
+ onClick: () => void copy(),
2662
+ style: {
2663
+ font: "inherit",
2664
+ fontSize: "0.75rem",
2665
+ cursor: "pointer",
2666
+ padding: "0.15rem 0.5rem",
2667
+ borderRadius: "var(--nv-radius)",
2668
+ border: "1px solid var(--nv-border)",
2669
+ background: "transparent",
2670
+ color: "var(--nv-muted)"
2671
+ },
2672
+ children: copied ? t("payment.copied") : t("payment.copy")
2673
+ }
2674
+ )
2675
+ ] })
2676
+ ] });
2677
+ }
2678
+ var HANDOFF_TTL_MS = 30 * 60 * 1e3;
2679
+ var POLL_MS = 2e3;
2680
+ var POLL_ATTEMPTS = 6;
2681
+ var BLOCKED_MESSAGE = {
2682
+ order_cancelled: "payment.blocked.orderCancelled",
2683
+ already_paid: "payment.blocked.alreadyPaid",
2684
+ authorization_held: "payment.blocked.authorizationHeld",
2685
+ document_issued: "payment.blocked.documentIssued"
2686
+ };
2687
+ function handoffKey(token) {
2688
+ return `nv_payment_handoff_${token}`;
2689
+ }
2690
+ function rememberHandoff(token) {
2691
+ try {
2692
+ sessionStorage.setItem(handoffKey(token), String(Date.now()));
2693
+ } catch {
2694
+ }
2695
+ }
2696
+ function consumeHandoff(token) {
2697
+ try {
2698
+ const raw = sessionStorage.getItem(handoffKey(token));
2699
+ if (!raw) return false;
2700
+ const fresh = Date.now() - Number(raw) < HANDOFF_TTL_MS;
2701
+ if (!fresh) sessionStorage.removeItem(handoffKey(token));
2702
+ return fresh;
2703
+ } catch {
2704
+ return false;
2705
+ }
2706
+ }
2707
+ function forgetHandoff(token) {
2708
+ try {
2709
+ sessionStorage.removeItem(handoffKey(token));
2710
+ } catch {
2711
+ }
2712
+ }
2713
+ function PaymentPage({
2714
+ token,
2715
+ payment: initial,
2716
+ returnUrl,
2717
+ cancelUrl,
2718
+ statusHref,
2719
+ locale,
2720
+ onPaid
2721
+ }) {
2722
+ const t = useT();
2723
+ const loc = useFormatLocale(locale);
2724
+ const { payment, methods, loading, busy, error, reload, selectMethod, startPayment } = usePayment(token, initial);
2725
+ const [verifying, setVerifying] = React10__namespace.useState(false);
2726
+ React10__namespace.useEffect(() => {
2727
+ if (token) setVerifying(consumeHandoff(token));
2728
+ }, [token]);
2729
+ const settled = payment != null && !payment.can_pay && payment.blocked_reason === "already_paid";
2730
+ React10__namespace.useEffect(() => {
2731
+ if (!verifying || !token || settled) return;
2732
+ let attempts = 0;
2733
+ const id = setInterval(() => {
2734
+ attempts += 1;
2735
+ if (attempts > POLL_ATTEMPTS) {
2736
+ setVerifying(false);
2737
+ forgetHandoff(token);
2738
+ clearInterval(id);
2739
+ return;
2740
+ }
2741
+ void reload();
2742
+ }, POLL_MS);
2743
+ return () => clearInterval(id);
2744
+ }, [verifying, token, settled, reload]);
2745
+ React10__namespace.useEffect(() => {
2746
+ if (!settled || !token || !payment) return;
2747
+ setVerifying(false);
2748
+ forgetHandoff(token);
2749
+ onPaid?.(payment);
2750
+ }, [settled, token, payment, onPaid]);
2751
+ if (!token) return null;
2752
+ if (payment == null) {
2753
+ return /* @__PURE__ */ jsxRuntime.jsx(Shell2, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: loading ? t("payment.loading") : t("payment.loadError") }) });
2754
+ }
2755
+ const money = (cents) => formatCents(cents, payment.currency, loc);
2756
+ if (settled) {
2757
+ return /* @__PURE__ */ jsxRuntime.jsxs(Shell2, { title: t("payment.paidTitle"), children: [
2758
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0 }, children: t("payment.paidBody", { order: payment.order_name ?? payment.order_number }) }),
2759
+ /* @__PURE__ */ jsxRuntime.jsx(Amount, { label: t("payment.paidAmount"), value: money(payment.total_cents) }),
2760
+ statusHref ? /* @__PURE__ */ jsxRuntime.jsx(Link, { href: statusHref, children: t("order.trackOrder") }) : null
2761
+ ] });
2762
+ }
2763
+ if (payment.blocked_reason) {
2764
+ return /* @__PURE__ */ jsxRuntime.jsxs(Shell2, { title: t("payment.blockedTitle"), children: [
2765
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0 }, children: t(BLOCKED_MESSAGE[payment.blocked_reason]) }),
2766
+ statusHref ? /* @__PURE__ */ jsxRuntime.jsx(Link, { href: statusHref, children: t("order.trackOrder") }) : null
2767
+ ] });
2768
+ }
2769
+ if (verifying) {
2770
+ return /* @__PURE__ */ jsxRuntime.jsx(Shell2, { title: t("payment.verifyingTitle"), children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: t("payment.verifyingBody") }) });
2771
+ }
2772
+ const isBank = payment.category === "bank";
2773
+ const isGateway = payment.category === "gateway";
2774
+ async function pay() {
2775
+ if (!token) return;
2776
+ rememberHandoff(token);
2777
+ const url = await startPayment({ returnUrl, cancelUrl });
2778
+ if (url) {
2779
+ window.location.assign(url);
2780
+ return;
2781
+ }
2782
+ forgetHandoff(token);
2783
+ await reload();
2784
+ }
2785
+ return /* @__PURE__ */ jsxRuntime.jsxs(Shell2, { title: t("payment.title", { order: payment.order_name ?? payment.order_number }), children: [
2786
+ /* @__PURE__ */ jsxRuntime.jsx(Amount, { label: t("payment.amountDue"), value: money(payment.amount_due_cents) }),
2787
+ methods.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2788
+ PaymentMethodPicker,
2789
+ {
2790
+ methods,
2791
+ value: payment.method,
2792
+ onSelect: (method) => void selectMethod(method),
2793
+ currency: payment.currency,
2794
+ locale: loc,
2795
+ disabled: busy,
2796
+ label: t("payment.methodLabel")
2797
+ }
2798
+ ) : null,
2799
+ isBank && payment.instructions ? /* @__PURE__ */ jsxRuntime.jsx(TransferInstructions, { instructions: payment.instructions, locale: loc }) : null,
2800
+ !isBank ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2801
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => void pay(), busy, children: t("payment.payNow") }),
2802
+ isGateway ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
2803
+ ] }) : null,
2804
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", style: { margin: 0, color: "var(--nv-danger, #b3261e)" }, children: t("payment.actionError") }) : null,
2805
+ statusHref ? /* @__PURE__ */ jsxRuntime.jsx(Link, { href: statusHref, children: t("order.trackOrder") }) : null
2806
+ ] });
2807
+ }
2808
+ function Shell2({
2809
+ title,
2810
+ children
2811
+ }) {
2812
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "1rem", maxWidth: "34rem" }, children: [
2813
+ title ? /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 600 }, children: title }) : null,
2814
+ children
2815
+ ] });
2816
+ }
2817
+ function Amount({ label, value }) {
2818
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline" }, children: [
2819
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)" }, children: label }),
2820
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontSize: "1.35rem", fontVariantNumeric: "tabular-nums" }, children: value })
2821
+ ] });
2822
+ }
2823
+ function Link({ href, children }) {
2824
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children });
2825
+ }
2397
2826
  var AccountContext = React10__namespace.createContext(null);
2398
2827
  function AccountProvider({ children }) {
2399
2828
  const client = useStorefront();
@@ -3018,10 +3447,15 @@ exports.MarketSwitcher = MarketSwitcher;
3018
3447
  exports.NeviosProvider = NeviosProvider;
3019
3448
  exports.OrderConfirmation = OrderConfirmation;
3020
3449
  exports.OrderHistory = OrderHistory;
3450
+ exports.OrderLines = OrderLines;
3021
3451
  exports.OrderStatusPage = OrderStatusPage;
3022
3452
  exports.OrderSummary = OrderSummary;
3453
+ exports.OrderTotals = OrderTotals;
3023
3454
  exports.OrganizationJsonLd = OrganizationJsonLd;
3455
+ exports.PaymentMethodPicker = PaymentMethodPicker;
3456
+ exports.PaymentPage = PaymentPage;
3024
3457
  exports.PaymentSection = PaymentSection;
3458
+ exports.PhaseTimeline = PhaseTimeline;
3025
3459
  exports.PriceMoney = PriceMoney;
3026
3460
  exports.PriceRangeFilter = PriceRangeFilter;
3027
3461
  exports.ProductCard = ProductCard;
@@ -3031,9 +3465,11 @@ exports.ProductJsonLd = ProductJsonLd;
3031
3465
  exports.ProfileEditor = ProfileEditor;
3032
3466
  exports.ShippingPicker = ShippingPicker;
3033
3467
  exports.SortSelect = SortSelect;
3468
+ exports.StatusBanner = StatusBanner;
3034
3469
  exports.StockBadge = StockBadge;
3035
3470
  exports.TagFilter = TagFilter;
3036
3471
  exports.ThemeProvider = ThemeProvider;
3472
+ exports.TransferInstructions = TransferInstructions;
3037
3473
  exports.VariantPicker = VariantPicker;
3038
3474
  exports.absoluteUrl = absoluteUrl;
3039
3475
  exports.bundledMessages = bundledMessages;
@@ -3041,6 +3477,7 @@ exports.cs = cs;
3041
3477
  exports.en = en;
3042
3478
  exports.formatCents = formatCents;
3043
3479
  exports.formatMoney = formatMoney;
3480
+ exports.isTerminal = isTerminal;
3044
3481
  exports.serializeJsonLd = serializeJsonLd;
3045
3482
  exports.stripHtml = stripHtml;
3046
3483
  exports.themeToCssVars = themeToCssVars;
@@ -3050,6 +3487,7 @@ exports.useCheckout = useCheckout;
3050
3487
  exports.useI18n = useI18n;
3051
3488
  exports.useMagicLinkVerify = useMagicLinkVerify;
3052
3489
  exports.useOrderStatus = useOrderStatus;
3490
+ exports.usePayment = usePayment;
3053
3491
  exports.useStorefront = useStorefront;
3054
3492
  exports.useStorefrontOptional = useStorefrontOptional;
3055
3493
  exports.useT = useT;