@solvapay/react 2.1.1 → 2.2.0-preview-e3e546693c002c32f128d83bf6c5fe76ac182a79

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.
@@ -152,7 +152,8 @@ var DEFAULT_ROUTES = {
152
152
  getPaymentMethod: "/api/payment-method",
153
153
  autoRecharge: "/api/auto-recharge",
154
154
  getUsage: "/api/usage",
155
- getLimits: "/api/limits"
155
+ getLimits: "/api/limits",
156
+ getHistory: "/api/history"
156
157
  };
157
158
  function routeFor(config, key) {
158
159
  const configured = config?.api?.[key];
@@ -322,8 +323,22 @@ function createHttpTransport(config) {
322
323
  overage: data.overage,
323
324
  needsTopUp: data.needsTopUp,
324
325
  needsUpgrade: data.needsUpgrade,
325
- upgraded: data.upgraded
326
+ upgraded: data.upgraded,
327
+ ...data.used !== void 0 ? { used: data.used } : {},
328
+ ...data.limit !== void 0 ? { limit: data.limit } : {}
326
329
  };
330
+ },
331
+ getHistory: async (params = {}) => {
332
+ const base = routeFor(config, "getHistory");
333
+ const search = new URLSearchParams();
334
+ if (params.productRef) search.set("productRef", params.productRef);
335
+ if (params.limit !== void 0) search.set("limit", String(params.limit));
336
+ const qs = search.toString();
337
+ return request(config, qs ? `${base}?${qs}` : base, {
338
+ method: "GET",
339
+ onErrorContext: "getHistory",
340
+ errorPrefix: "Failed to load history"
341
+ });
327
342
  }
328
343
  };
329
344
  }
@@ -467,7 +482,7 @@ var enCopy = {
467
482
  invalidThreshold: "Enter a valid balance threshold.",
468
483
  thresholdTooLow: "Balance threshold must be greater than zero.",
469
484
  minTopupAmount: "Top-up amount must be at least {amount}.",
470
- topupBelowThreshold: "Top-up amount must be at least your balance threshold ({amount}).",
485
+ deepDrawdownExplainer: "If your balance has fallen well below the threshold, we add a multiple of this amount in one charge.",
471
486
  invalidMaxMonthlySpend: "Maximum monthly spend must be a positive amount.",
472
487
  maxMonthlySpendBelowTopup: "Maximum monthly spend must be at least your top-up amount ({amount}).",
473
488
  maxMonthlySpendLabel: "Maximum monthly spend (optional)",
@@ -483,6 +498,24 @@ var enCopy = {
483
498
  taxDisclosure: "Plus applicable tax.",
484
499
  statusFailed: "Payment failed \u2014 update your card to resume"
485
500
  },
501
+ autoRechargeView: {
502
+ heading: "Auto-recharge",
503
+ description: "Tops your balance up automatically so calls do not fail. Nothing is charged today.",
504
+ thresholdLabel: "When balance falls below",
505
+ topupLabel: "Add each time",
506
+ maxMonthlySpendLabel: "Maximum monthly spend",
507
+ maxMonthlySpendPlaceholder: "No limit",
508
+ explainer: "Each recharge adds about {credits} credits, plus applicable tax. If your balance has fallen well below the threshold we add a multiple of this amount in one charge. Leave the spend limit blank for unlimited recharges.",
509
+ explainerNoEstimate: "Each recharge includes applicable tax. If your balance has fallen well below the threshold we add a multiple of this amount in one charge. Leave the spend limit blank for unlimited recharges.",
510
+ footerCaption: "Recharges use the payment method on your account. First charge happens when your balance drops below {threshold}.",
511
+ cancel: "Cancel",
512
+ turnOn: "Turn on auto-recharge",
513
+ save: "Save changes",
514
+ turnOff: "Turn off auto-recharge",
515
+ statusOn: "On",
516
+ back: "Back to account",
517
+ setupUnexpected: "Your saved card is no longer reusable. Add a card from checkout, then try again."
518
+ },
486
519
  activationFlow: {
487
520
  heading: "Confirm your plan",
488
521
  activateButton: "Activate",
@@ -526,6 +559,9 @@ var enCopy = {
526
559
  expiresOn: "Expires {date}",
527
560
  validIndefinitely: "Valid indefinitely",
528
561
  startedOn: "Started {date}",
562
+ priceFieldLabel: "Price",
563
+ rateFieldLabel: "Rate",
564
+ balanceFieldLabel: "Balance",
529
565
  paymentMethod: "{brand} \u2022\u2022\u2022\u2022 {last4}",
530
566
  paymentMethodExpires: "expires {month}/{year}",
531
567
  noPaymentMethod: "No payment method on file",
@@ -545,7 +581,65 @@ var enCopy = {
545
581
  noPlanTitle: "Pick a plan",
546
582
  noPlanBody: "Choose a free, pay-as-you-go, or paid plan to start using this MCP server.",
547
583
  seePlansButton: "See plans",
548
- pickPlanButton: "Pick a plan"
584
+ seePlans: "See plans \u2192",
585
+ pickPlanButton: "Pick a plan",
586
+ upgradeButton: "Upgrade",
587
+ changePlanButton: "Change plan",
588
+ needMoreAllowance: "Need more than {total} {unit} a {interval}?",
589
+ upgradePaygCaption: "Pay as you go starts without payment and uses your credits.",
590
+ noPlanStatus: "No plan",
591
+ choosePlanCaption: "Choose a plan to start using it. Calls fail until one is active.",
592
+ plansEyebrow: "Plans",
593
+ carryOnEyebrow: "Carry on with",
594
+ plansStartCaption: "Free and Pay as you go start immediately. Paid plans take payment first.",
595
+ activatePlanButton: "Activate",
596
+ switchPlanButton: "Switch",
597
+ activatingPlanButton: "Activating\u2026",
598
+ usedUpTitle: "Your {plan} {unit} are used up",
599
+ usedUpBody: "Calls fail until the limit resets on {date}, in {days} days. Choose a paid plan to carry on now.",
600
+ usedUpBodyNoDate: "Calls fail until you choose a paid plan.",
601
+ antiTrapCredits: "You have {credits} credits. {plan} does not spend them, so adding funds will not restore calls.",
602
+ waitUntilReset: "Or wait until {date} and keep the {plan} plan. Nothing is charged automatically.",
603
+ notStartedStatus: "Not started",
604
+ readyToClaim: "{total} free {unit} a {interval}, ready to claim",
605
+ noCardCaption: "No card, no charge. You can change plan later.",
606
+ startFreePlan: "Start free plan",
607
+ stillWorking: "Calls are still working.",
608
+ seeHigherLimit: "See plans with a higher limit \u2192",
609
+ usedOfAllowance: "{used} of {total} {unit}",
610
+ activeUntil: "Active until {date}",
611
+ cancelledPlanLine: "{plan} \xB7 cancelled {date}",
612
+ daysLeftTitle: "{days} days left on this plan",
613
+ cancelledBody: "Calls keep working until {date}, then stop. You will not be charged again.",
614
+ reactivatePlan: "Reactivate {plan}",
615
+ creditBalance: "Credit balance",
616
+ addFunds: "Add funds",
617
+ autoRechargeOn: "Auto-recharge on",
618
+ autoRechargeOff: "Auto-recharge off",
619
+ turnOn: "Turn on \u2192",
620
+ manage: "Manage \u2192",
621
+ autoRechargeOffCaption: "Calls fail the moment the balance runs out.",
622
+ autoRechargeOffFixCaption: "Turning it on stops this happening again.",
623
+ callsFailingCaption: "The plan is active, but calls fail until you add credits.",
624
+ activeProducts: "Active products",
625
+ worksAcross: "Works across every {merchant} product.",
626
+ soldBy: "Sold by {merchant}",
627
+ fullHistory: "Full history",
628
+ fullAccount: "Full account",
629
+ creditActivityEyebrow: "Credit activity",
630
+ chargesEyebrow: "Charges for this product",
631
+ creditActivityCaption: "Every credit event on your account, newest first, including other products and top-ups. Credits are shared, so the balance only makes sense account-wide.",
632
+ creditActivityEmpty: "No credit activity yet.",
633
+ creditActivityFailed: "Couldn't load credit activity.",
634
+ chargesEmpty: "No charges yet.",
635
+ chargesFailed: "Couldn't load charges.",
636
+ eventColumn: "Event",
637
+ whenColumn: "When",
638
+ creditsColumn: "Credits",
639
+ balanceColumn: "Balance",
640
+ chargeColumn: "Charge",
641
+ dateColumn: "Date",
642
+ amountColumn: "Amount"
549
643
  },
550
644
  customerPortal: {
551
645
  launchButton: "Manage account",
@@ -624,7 +718,30 @@ var enCopy = {
624
718
  atLimit: "You've reached your usage limit.",
625
719
  topUpCta: "Add credits",
626
720
  upgradeCta: "Upgrade plan",
627
- refreshCta: "Refresh"
721
+ refreshCta: "Refresh",
722
+ remainingEyebrow: "Remaining",
723
+ renewsEyebrow: "Renews",
724
+ resetsEyebrow: "Resets",
725
+ creditsEyebrow: "Credits",
726
+ usedEyebrow: "Used",
727
+ remainingCalls: "{remaining} {unit}",
728
+ remainingOfTotal: "{remaining} of {total} {unit}",
729
+ ofTotalThisPeriod: "Of {total} this period.",
730
+ usedOfTotalPercent: "{used} of {total} {unit} used, {percent}%.",
731
+ warningThresholdHint: "A warning shows at 80%, and calls stop at 100%.",
732
+ lastCallHint: "One remaining {unit} is the difference between working and blocked.",
733
+ afterFirstCall: "After your first call",
734
+ inDays: "In {days} days.",
735
+ notKnownYet: "Not known yet",
736
+ creditsNotUsed: "Not used",
737
+ creditsUntouched: "Balance is untouched.",
738
+ creditsDoNotSpend: "This plan does not spend your balance.",
739
+ rateConfirmedAtCheckout: "Rate confirmed at checkout",
740
+ fromCreditsPerCall: "from {credits} credits per call",
741
+ creditsPerCall: "{credits} credits per call",
742
+ callsFailing: "Calls failing",
743
+ overAllowance: "Over the allowance",
744
+ limitReached: "{plan} limit reached"
628
745
  }
629
746
  };
630
747
 
@@ -1435,6 +1552,45 @@ function useLocale() {
1435
1552
  return useCopyContext().locale;
1436
1553
  }
1437
1554
 
1555
+ // src/hooks/useExternalLink.tsx
1556
+ import { createContext as createContext3, useCallback as useCallback2, useContext as useContext2 } from "react";
1557
+ import { jsx as jsx4 } from "react/jsx-runtime";
1558
+ var ExternalLinkContext = createContext3(null);
1559
+ function ExternalLinkProvider({ opener, children }) {
1560
+ return /* @__PURE__ */ jsx4(ExternalLinkContext.Provider, { value: opener, children });
1561
+ }
1562
+ function useOpenExternal() {
1563
+ const opener = useContext2(ExternalLinkContext);
1564
+ return useCallback2(
1565
+ async (url) => {
1566
+ if (opener?.canOpen()) return opener.open(url);
1567
+ if (typeof window !== "undefined") {
1568
+ window.open(url, "_blank", "noopener,noreferrer");
1569
+ }
1570
+ return true;
1571
+ },
1572
+ [opener]
1573
+ );
1574
+ }
1575
+ function useExternalLinkClick() {
1576
+ const opener = useContext2(ExternalLinkContext);
1577
+ return useCallback2(
1578
+ (event) => {
1579
+ if (event.defaultPrevented) return;
1580
+ if (!opener?.canOpen()) return;
1581
+ const href = event.currentTarget.href;
1582
+ if (!href) return;
1583
+ event.preventDefault();
1584
+ void opener.open(href).then((opened) => {
1585
+ if (!opened) {
1586
+ console.warn(`[solvapay] host declined to open ${href}`);
1587
+ }
1588
+ });
1589
+ },
1590
+ [opener]
1591
+ );
1592
+ }
1593
+
1438
1594
  // src/primitives/LegalFooter.tsx
1439
1595
  import { forwardRef } from "react";
1440
1596
 
@@ -1444,7 +1600,7 @@ var SOLVAPAY_PRIVACY_URL = "https://solvapay.com/legal/privacy";
1444
1600
  var SOLVAPAY_WEBSITE_URL = "https://solvapay.com";
1445
1601
 
1446
1602
  // src/primitives/LegalFooter.tsx
1447
- import { Fragment as Fragment2, jsx as jsx4, jsxs } from "react/jsx-runtime";
1603
+ import { Fragment as Fragment2, jsx as jsx5, jsxs } from "react/jsx-runtime";
1448
1604
  var LegalFooter = forwardRef(
1449
1605
  function LegalFooter2({
1450
1606
  attribution = "provided",
@@ -1455,39 +1611,43 @@ var LegalFooter = forwardRef(
1455
1611
  ...rest
1456
1612
  }, forwardedRef) {
1457
1613
  const copy = useCopy();
1614
+ const handleExternalClick = useExternalLinkClick();
1458
1615
  const attributionLabel = attribution === "provided" ? copy.legalFooter.providedBy : attribution === "powered" ? copy.legalFooter.poweredBy : null;
1459
1616
  const Comp = asChild ? Slot : "div";
1460
- return /* @__PURE__ */ jsx4(Comp, { ref: forwardedRef, "data-solvapay-legal-footer": "", ...rest, children: children ?? /* @__PURE__ */ jsxs(Fragment2, { children: [
1617
+ return /* @__PURE__ */ jsx5(Comp, { ref: forwardedRef, "data-solvapay-legal-footer": "", ...rest, children: children ?? /* @__PURE__ */ jsxs(Fragment2, { children: [
1461
1618
  /* @__PURE__ */ jsxs("div", { "data-solvapay-legal-footer-links": "", children: [
1462
- /* @__PURE__ */ jsx4(
1619
+ /* @__PURE__ */ jsx5(
1463
1620
  "a",
1464
1621
  {
1465
1622
  "data-solvapay-legal-footer-link": "",
1466
1623
  href: termsUrl,
1467
1624
  target: "_blank",
1468
1625
  rel: "noopener noreferrer",
1626
+ onClick: handleExternalClick,
1469
1627
  children: copy.legalFooter.terms
1470
1628
  }
1471
1629
  ),
1472
- /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: " \xB7 " }),
1473
- /* @__PURE__ */ jsx4(
1630
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", children: " \xB7 " }),
1631
+ /* @__PURE__ */ jsx5(
1474
1632
  "a",
1475
1633
  {
1476
1634
  "data-solvapay-legal-footer-link": "",
1477
1635
  href: privacyUrl,
1478
1636
  target: "_blank",
1479
1637
  rel: "noopener noreferrer",
1638
+ onClick: handleExternalClick,
1480
1639
  children: copy.legalFooter.privacy
1481
1640
  }
1482
1641
  )
1483
1642
  ] }),
1484
- attributionLabel ? /* @__PURE__ */ jsx4(
1643
+ attributionLabel ? /* @__PURE__ */ jsx5(
1485
1644
  "a",
1486
1645
  {
1487
1646
  "data-solvapay-legal-footer-attribution": "",
1488
1647
  href: SOLVAPAY_WEBSITE_URL,
1489
1648
  target: "_blank",
1490
1649
  rel: "noopener noreferrer",
1650
+ onClick: handleExternalClick,
1491
1651
  children: attributionLabel
1492
1652
  }
1493
1653
  ) : null
@@ -1496,9 +1656,9 @@ var LegalFooter = forwardRef(
1496
1656
  );
1497
1657
 
1498
1658
  // src/hooks/useSolvaPay.ts
1499
- import { useContext as useContext2 } from "react";
1659
+ import { useContext as useContext3 } from "react";
1500
1660
  function useSolvaPay() {
1501
- const context = useContext2(SolvaPayContext);
1661
+ const context = useContext3(SolvaPayContext);
1502
1662
  if (!context) {
1503
1663
  throw new Error(
1504
1664
  "useSolvaPay must be used within a SolvaPayProvider. Wrap your component tree with <SolvaPayProvider> to use this hook."
@@ -1508,19 +1668,19 @@ function useSolvaPay() {
1508
1668
  }
1509
1669
 
1510
1670
  // src/hooks/useCheckout.ts
1511
- import { useState as useState2, useCallback as useCallback2, useRef as useRef2 } from "react";
1671
+ import { useState as useState2, useCallback as useCallback3, useRef as useRef2 } from "react";
1512
1672
  import { loadStripe } from "@stripe/stripe-js";
1513
1673
 
1514
1674
  // src/components/PlanSelectionContext.tsx
1515
- import { createContext as createContext3, useContext as useContext3 } from "react";
1516
- import { jsx as jsx5 } from "react/jsx-runtime";
1517
- var PlanSelectionContext = createContext3(null);
1675
+ import { createContext as createContext4, useContext as useContext4 } from "react";
1676
+ import { jsx as jsx6 } from "react/jsx-runtime";
1677
+ var PlanSelectionContext = createContext4(null);
1518
1678
  var PlanSelectionProvider = ({
1519
1679
  value,
1520
1680
  children
1521
- }) => /* @__PURE__ */ jsx5(PlanSelectionContext.Provider, { value, children });
1681
+ }) => /* @__PURE__ */ jsx6(PlanSelectionContext.Provider, { value, children });
1522
1682
  function usePlanSelection() {
1523
- return useContext3(PlanSelectionContext);
1683
+ return useContext4(PlanSelectionContext);
1524
1684
  }
1525
1685
 
1526
1686
  // src/hooks/useCheckout.ts
@@ -1571,7 +1731,7 @@ function useCheckout(options) {
1571
1731
  const [processorPaymentId, setProcessorPaymentId] = useState2(null);
1572
1732
  const [resolvedPlanRef, setResolvedPlanRef] = useState2(planRef || null);
1573
1733
  const isStartingRef = useRef2(false);
1574
- const startCheckout = useCallback2(async () => {
1734
+ const startCheckout = useCallback3(async () => {
1575
1735
  if (isStartingRef.current || loading) {
1576
1736
  return;
1577
1737
  }
@@ -1647,7 +1807,7 @@ function useCheckout(options) {
1647
1807
  loading,
1648
1808
  _config
1649
1809
  ]);
1650
- const reset = useCallback2(() => {
1810
+ const reset = useCallback3(() => {
1651
1811
  isStartingRef.current = false;
1652
1812
  setLoading(false);
1653
1813
  setError(null);
@@ -1689,7 +1849,7 @@ function useCustomer() {
1689
1849
  }
1690
1850
 
1691
1851
  // src/hooks/usePlans.ts
1692
- import { useContext as useContext4, useState as useState3, useEffect as useEffect2, useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3 } from "react";
1852
+ import { useContext as useContext5, useState as useState3, useEffect as useEffect2, useCallback as useCallback4, useMemo as useMemo3, useRef as useRef3 } from "react";
1693
1853
 
1694
1854
  // src/transport/list-plans.ts
1695
1855
  async function defaultListPlans(productRef, config) {
@@ -1742,7 +1902,7 @@ function usePlans(options) {
1742
1902
  initialPlanRef,
1743
1903
  selectionReady = true
1744
1904
  } = options;
1745
- const solvaContext = useContext4(SolvaPayContext);
1905
+ const solvaContext = useContext5(SolvaPayContext);
1746
1906
  const config = solvaContext?._config;
1747
1907
  const effectiveFetcher = useMemo3(
1748
1908
  () => fetcher ?? ((ref) => defaultListPlans(ref, config)),
@@ -1796,11 +1956,11 @@ function usePlans(options) {
1796
1956
  useEffect2(() => {
1797
1957
  selectionReadyRef.current = selectionReady;
1798
1958
  }, [selectionReady]);
1799
- const setSelectedPlanIndex = useCallback3((index) => {
1959
+ const setSelectedPlanIndex = useCallback4((index) => {
1800
1960
  userHasSelectedRef.current = true;
1801
1961
  setSelectedPlanIndexState(index);
1802
1962
  }, []);
1803
- const applyInitialSelection = useCallback3((processedPlans) => {
1963
+ const applyInitialSelection = useCallback4((processedPlans) => {
1804
1964
  if (hasAppliedInitialRef.current || userHasSelectedRef.current) return;
1805
1965
  if (!selectionReadyRef.current || processedPlans.length === 0) return;
1806
1966
  hasAppliedInitialRef.current = true;
@@ -1811,7 +1971,7 @@ function usePlans(options) {
1811
1971
  );
1812
1972
  setSelectedPlanIndexState(idx);
1813
1973
  }, []);
1814
- const fetchPlans = useCallback3(
1974
+ const fetchPlans = useCallback4(
1815
1975
  async (force = false) => {
1816
1976
  if (!productRef) {
1817
1977
  setError(new Error("Product reference not configured"));
@@ -1883,7 +2043,7 @@ function usePlans(options) {
1883
2043
  applyInitialSelection(plans);
1884
2044
  }, [selectionReady, plans, applyInitialSelection]);
1885
2045
  const selectedPlan = useMemo3(() => plans[selectedPlanIndex] || null, [plans, selectedPlanIndex]);
1886
- const selectPlan = useCallback3(
2046
+ const selectPlan = useCallback4(
1887
2047
  (planRef) => {
1888
2048
  const index = plans.findIndex((p) => p.reference === planRef);
1889
2049
  if (index >= 0) {
@@ -1906,11 +2066,11 @@ function usePlans(options) {
1906
2066
  }
1907
2067
 
1908
2068
  // src/hooks/usePlan.ts
1909
- import { useCallback as useCallback4, useEffect as useEffect3, useState as useState4 } from "react";
2069
+ import { useCallback as useCallback5, useEffect as useEffect3, useState as useState4 } from "react";
1910
2070
  function usePlan(options) {
1911
2071
  const { planRef, productRef } = options;
1912
2072
  const { _config } = useSolvaPay();
1913
- const findPlan = useCallback4(
2073
+ const findPlan = useCallback5(
1914
2074
  (plans) => {
1915
2075
  if (!planRef) return null;
1916
2076
  return plans.find((p) => p.reference === planRef) || null;
@@ -1925,7 +2085,7 @@ function usePlan(options) {
1925
2085
  });
1926
2086
  const [loading, setLoading] = useState4(() => !!(planRef && !plan));
1927
2087
  const [error, setError] = useState4(null);
1928
- const load = useCallback4(
2088
+ const load = useCallback5(
1929
2089
  async (force = false) => {
1930
2090
  if (!planRef) {
1931
2091
  setPlan(null);
@@ -2004,7 +2164,7 @@ function usePlan(options) {
2004
2164
  }
2005
2165
 
2006
2166
  // src/hooks/useProduct.ts
2007
- import { useCallback as useCallback5, useEffect as useEffect4, useState as useState5 } from "react";
2167
+ import { useCallback as useCallback6, useEffect as useEffect4, useState as useState5 } from "react";
2008
2168
  var productCache = /* @__PURE__ */ new Map();
2009
2169
  var CACHE_DURATION4 = 5 * 60 * 1e3;
2010
2170
  function cacheKeyFor(config, productRef) {
@@ -2027,7 +2187,7 @@ function useProduct(productRef) {
2027
2187
  return !cached || !cached.product && !cached.promise;
2028
2188
  });
2029
2189
  const [error, setError] = useState5(null);
2030
- const load = useCallback5(
2190
+ const load = useCallback6(
2031
2191
  async (force = false) => {
2032
2192
  if (!productRef) {
2033
2193
  setProduct(null);
@@ -2100,14 +2260,14 @@ function useProduct(productRef) {
2100
2260
  }
2101
2261
 
2102
2262
  // src/hooks/useActivation.ts
2103
- import { useState as useState6, useCallback as useCallback6 } from "react";
2263
+ import { useState as useState6, useCallback as useCallback7 } from "react";
2104
2264
  function useActivation() {
2105
2265
  const { activatePlan } = useSolvaPay();
2106
2266
  const copy = useCopy();
2107
2267
  const [state, setState] = useState6("idle");
2108
2268
  const [error, setError] = useState6(null);
2109
2269
  const [result, setResult] = useState6(null);
2110
- const activate = useCallback6(
2270
+ const activate = useCallback7(
2111
2271
  async (params) => {
2112
2272
  setState("activating");
2113
2273
  setError(null);
@@ -2119,30 +2279,32 @@ function useActivation() {
2119
2279
  case "activated":
2120
2280
  case "already_active":
2121
2281
  setState("activated");
2122
- break;
2282
+ return "activated";
2123
2283
  case "topup_required":
2124
2284
  setState("topup_required");
2125
- break;
2285
+ return "topup_required";
2126
2286
  case "payment_required":
2127
2287
  setError(copy.activation.paymentRequired);
2128
2288
  setState("payment_required");
2129
- break;
2289
+ return "payment_required";
2130
2290
  case "invalid":
2131
2291
  setError(data.message || copy.activation.invalidConfiguration);
2132
2292
  setState("error");
2133
- break;
2293
+ return "error";
2134
2294
  default:
2135
2295
  setError(copy.activation.unexpectedResponse);
2136
2296
  setState("error");
2297
+ return "error";
2137
2298
  }
2138
2299
  } catch (err) {
2139
2300
  setError(err instanceof Error ? err.message : copy.activation.failed);
2140
2301
  setState("error");
2302
+ return "error";
2141
2303
  }
2142
2304
  },
2143
2305
  [activatePlan, copy]
2144
2306
  );
2145
- const reset = useCallback6(() => {
2307
+ const reset = useCallback7(() => {
2146
2308
  setState("idle");
2147
2309
  setError(null);
2148
2310
  setResult(null);
@@ -2151,11 +2313,11 @@ function useActivation() {
2151
2313
  }
2152
2314
 
2153
2315
  // src/components/PaymentFormContext.tsx
2154
- import { createContext as createContext4, useContext as useContext5 } from "react";
2155
- import { jsx as jsx6 } from "react/jsx-runtime";
2156
- var PaymentFormContext = createContext4(null);
2316
+ import { createContext as createContext5, useContext as useContext6 } from "react";
2317
+ import { jsx as jsx7 } from "react/jsx-runtime";
2318
+ var PaymentFormContext = createContext5(null);
2157
2319
  function usePaymentForm() {
2158
- const ctx = useContext5(PaymentFormContext);
2320
+ const ctx = useContext6(PaymentFormContext);
2159
2321
  if (!ctx) {
2160
2322
  throw new Error(
2161
2323
  "PaymentForm subcomponents must be used inside a <PaymentForm>. Wrap the slots with <PaymentForm planRef=... productRef=...>."
@@ -2163,7 +2325,7 @@ function usePaymentForm() {
2163
2325
  }
2164
2326
  return ctx;
2165
2327
  }
2166
- var PaymentFormProvider = ({ value, children }) => /* @__PURE__ */ jsx6(PaymentFormContext.Provider, { value, children });
2328
+ var PaymentFormProvider = ({ value, children }) => /* @__PURE__ */ jsx7(PaymentFormContext.Provider, { value, children });
2167
2329
 
2168
2330
  // src/utils/format.ts
2169
2331
  var ZERO_DECIMAL_CURRENCIES = /* @__PURE__ */ new Set([
@@ -2230,7 +2392,7 @@ function interpolate(template, vars) {
2230
2392
  }
2231
2393
 
2232
2394
  // src/primitives/CheckoutSummary.tsx
2233
- import { createContext as createContext5, forwardRef as forwardRef3, useContext as useContext6, useMemo as useMemo4 } from "react";
2395
+ import { createContext as createContext6, forwardRef as forwardRef3, useContext as useContext7, useMemo as useMemo4 } from "react";
2234
2396
 
2235
2397
  // src/components/businessCheckoutParts.tsx
2236
2398
  import { forwardRef as forwardRef2 } from "react";
@@ -2238,11 +2400,17 @@ import {
2238
2400
  BUSINESS_COUNTRY_OPTIONS,
2239
2401
  SUPPORTED_BUSINESS_COUNTRIES,
2240
2402
  validateBusinessDetails,
2403
+ getPostalCodeFieldLabel,
2404
+ getPostalCodePlaceholder,
2405
+ getStateFieldLabel,
2241
2406
  getTaxIdFieldLabel,
2242
2407
  getTaxIdExample,
2243
- getTaxIdHelperText
2408
+ getTaxIdHelperText,
2409
+ isPostalCodeRequired,
2410
+ isStateRequired,
2411
+ resolveBuyerCountry
2244
2412
  } from "@solvapay/core";
2245
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
2413
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
2246
2414
  function mapBusinessFieldErrors(input) {
2247
2415
  const result = validateBusinessDetails(input);
2248
2416
  if (result.success) return {};
@@ -2256,7 +2424,7 @@ function mapBusinessFieldErrors(input) {
2256
2424
  return errors;
2257
2425
  }
2258
2426
  function isBusinessDetailsKey(key) {
2259
- return key === "isBusiness" || key === "businessName" || key === "country" || key === "customerCountry" || key === "customerName" || key === "taxId" || key === "taxIdType";
2427
+ return key === "isBusiness" || key === "businessName" || key === "country" || key === "customerCountry" || key === "customerName" || key === "customerState" || key === "customerPostalCode" || key === "taxId" || key === "taxIdType";
2260
2428
  }
2261
2429
  function attr(prefix, suffix) {
2262
2430
  return `data-solvapay-${prefix}-${suffix}`;
@@ -2305,7 +2473,7 @@ function createBusinessDetailsParts(useCtx, prefix) {
2305
2473
  const Root8 = forwardRef2(function BusinessDetailsRoot({ asChild, children, ...rest }, forwardedRef) {
2306
2474
  useCtx("BusinessDetails");
2307
2475
  const Comp = asChild ? Slot : "section";
2308
- return /* @__PURE__ */ jsx7(Comp, { ref: forwardedRef, ...{ [attr(prefix, "business-details")]: "" }, ...rest, children });
2476
+ return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, ...{ [attr(prefix, "business-details")]: "" }, ...rest, children });
2309
2477
  });
2310
2478
  const Toggle = forwardRef2(function BusinessDetailsToggle({ asChild, onChange, ...rest }, forwardedRef) {
2311
2479
  const ctx = useCtx("BusinessDetails.Toggle");
@@ -2319,9 +2487,9 @@ function createBusinessDetailsParts(useCtx, prefix) {
2319
2487
  ...rest
2320
2488
  };
2321
2489
  if (asChild) {
2322
- return /* @__PURE__ */ jsx7(Slot, { ref: forwardedRef, ...commonProps });
2490
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...commonProps });
2323
2491
  }
2324
- return /* @__PURE__ */ jsx7("input", { ref: forwardedRef, ...commonProps });
2492
+ return /* @__PURE__ */ jsx8("input", { ref: forwardedRef, ...commonProps });
2325
2493
  });
2326
2494
  const BusinessName = forwardRef2(
2327
2495
  function BusinessDetailsBusinessName({ asChild, onChange, ...rest }, forwardedRef) {
@@ -2338,31 +2506,80 @@ function createBusinessDetailsParts(useCtx, prefix) {
2338
2506
  ...rest
2339
2507
  };
2340
2508
  if (asChild) {
2341
- return /* @__PURE__ */ jsx7(Slot, { ref: forwardedRef, ...commonProps });
2509
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...commonProps });
2342
2510
  }
2343
- return /* @__PURE__ */ jsx7("input", { ref: forwardedRef, ...commonProps });
2511
+ return /* @__PURE__ */ jsx8("input", { ref: forwardedRef, ...commonProps });
2344
2512
  }
2345
2513
  );
2346
2514
  const Country = forwardRef2(function BusinessDetailsCountry({ asChild, onChange, children, ...rest }, forwardedRef) {
2347
2515
  const ctx = useCtx("BusinessDetails.Country");
2348
- if (!ctx.businessDetails.isBusiness) return null;
2516
+ const country = ctx.businessDetails.customerCountry ?? ctx.businessDetails.country ?? "";
2517
+ const countryError = ctx.fieldErrors.customerCountry ?? ctx.fieldErrors.country ? true : void 0;
2349
2518
  const commonProps = {
2519
+ ...rest,
2350
2520
  [attr(prefix, "business-details-country")]: "",
2351
- value: ctx.businessDetails.country ?? "",
2352
- "aria-invalid": ctx.fieldErrors.country ? true : void 0,
2521
+ value: country,
2522
+ "aria-invalid": countryError,
2523
+ "aria-label": rest["aria-label"] ?? "Country",
2353
2524
  onChange: composeEventHandlers(onChange, (e) => {
2354
- ctx.setBusinessDetails({ country: e.target.value });
2355
- }),
2356
- ...rest
2525
+ const nextCountry = e.target.value;
2526
+ ctx.setBusinessDetails({
2527
+ customerCountry: nextCountry || void 0,
2528
+ ...ctx.businessDetails.isBusiness ? { country: nextCountry || void 0 } : {},
2529
+ ...!isStateRequired(nextCountry) ? { customerState: void 0 } : {},
2530
+ ...!isPostalCodeRequired(nextCountry) ? { customerPostalCode: void 0 } : {}
2531
+ });
2532
+ })
2357
2533
  };
2358
2534
  const defaultOptions = /* @__PURE__ */ jsxs2(Fragment3, { children: [
2359
- /* @__PURE__ */ jsx7("option", { value: "", children: "Select country" }),
2360
- BUSINESS_COUNTRY_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx7("option", { value, children: label }, value))
2535
+ /* @__PURE__ */ jsx8("option", { value: "", children: "Select country" }),
2536
+ BUSINESS_COUNTRY_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx8("option", { value, children: label }, value))
2361
2537
  ] });
2362
2538
  if (asChild) {
2363
- return /* @__PURE__ */ jsx7(Slot, { ref: forwardedRef, ...commonProps, children: children ?? defaultOptions });
2539
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...commonProps, children: children ?? defaultOptions });
2540
+ }
2541
+ return /* @__PURE__ */ jsx8("select", { ref: forwardedRef, ...commonProps, children: children ?? defaultOptions });
2542
+ });
2543
+ const State = forwardRef2(function BusinessDetailsState({ asChild, onChange, ...rest }, forwardedRef) {
2544
+ const ctx = useCtx("BusinessDetails.State");
2545
+ const country = resolveBuyerCountry(ctx.businessDetails);
2546
+ if (!country || !isStateRequired(country)) return null;
2547
+ const commonProps = {
2548
+ ...rest,
2549
+ [attr(prefix, "business-details-state")]: "",
2550
+ type: "text",
2551
+ value: ctx.businessDetails.customerState ?? "",
2552
+ "aria-invalid": ctx.fieldErrors.customerState ? true : void 0,
2553
+ "aria-label": rest["aria-label"] ?? getStateFieldLabel(country),
2554
+ onChange: composeEventHandlers(onChange, (e) => {
2555
+ ctx.setBusinessDetails({ customerState: e.target.value || void 0 });
2556
+ })
2557
+ };
2558
+ if (asChild) {
2559
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...commonProps });
2560
+ }
2561
+ return /* @__PURE__ */ jsx8("input", { ref: forwardedRef, ...commonProps });
2562
+ });
2563
+ const PostalCode = forwardRef2(function BusinessDetailsPostalCode({ asChild, onChange, ...rest }, forwardedRef) {
2564
+ const ctx = useCtx("BusinessDetails.PostalCode");
2565
+ const country = resolveBuyerCountry(ctx.businessDetails);
2566
+ if (!country || !isPostalCodeRequired(country)) return null;
2567
+ const commonProps = {
2568
+ ...rest,
2569
+ [attr(prefix, "business-details-postal")]: "",
2570
+ type: "text",
2571
+ value: ctx.businessDetails.customerPostalCode ?? "",
2572
+ placeholder: rest.placeholder ?? getPostalCodePlaceholder(country),
2573
+ "aria-invalid": ctx.fieldErrors.customerPostalCode ? true : void 0,
2574
+ "aria-label": rest["aria-label"] ?? getPostalCodeFieldLabel(country),
2575
+ onChange: composeEventHandlers(onChange, (e) => {
2576
+ ctx.setBusinessDetails({ customerPostalCode: e.target.value || void 0 });
2577
+ })
2578
+ };
2579
+ if (asChild) {
2580
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...commonProps });
2364
2581
  }
2365
- return /* @__PURE__ */ jsx7("select", { ref: forwardedRef, ...commonProps, children: children ?? defaultOptions });
2582
+ return /* @__PURE__ */ jsx8("input", { ref: forwardedRef, ...commonProps });
2366
2583
  });
2367
2584
  const TaxId = forwardRef2(function BusinessDetailsTaxId({ asChild, onChange, ...rest }, forwardedRef) {
2368
2585
  const ctx = useCtx("BusinessDetails.TaxId");
@@ -2378,48 +2595,60 @@ function createBusinessDetailsParts(useCtx, prefix) {
2378
2595
  ...rest
2379
2596
  };
2380
2597
  if (asChild) {
2381
- return /* @__PURE__ */ jsx7(Slot, { ref: forwardedRef, ...commonProps });
2598
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...commonProps });
2382
2599
  }
2383
- return /* @__PURE__ */ jsx7("input", { ref: forwardedRef, ...commonProps });
2600
+ return /* @__PURE__ */ jsx8("input", { ref: forwardedRef, ...commonProps });
2384
2601
  });
2385
2602
  const Fields = forwardRef2(function BusinessDetailsFields({ asChild, children, ...rest }, forwardedRef) {
2386
2603
  const ctx = useCtx("BusinessDetails.Fields");
2387
- const country = ctx.businessDetails.country ?? "";
2604
+ const country = resolveBuyerCountry(ctx.businessDetails) ?? "";
2388
2605
  const taxIdLabel = resolveTaxIdLabel(country);
2389
2606
  const taxIdPlaceholder = resolveTaxIdPlaceholder(country);
2390
2607
  const taxIdHelperText = resolveTaxIdHelperText(country);
2608
+ const showState = !!country && isStateRequired(country);
2609
+ const showPostal = !!country && isPostalCodeRequired(country);
2391
2610
  const content = /* @__PURE__ */ jsxs2(Fragment3, { children: [
2611
+ /* @__PURE__ */ jsxs2("label", { className: "solvapay-business-field", children: [
2612
+ /* @__PURE__ */ jsx8("span", { className: "solvapay-business-field-label", children: "Country" }),
2613
+ /* @__PURE__ */ jsx8(Country, {})
2614
+ ] }),
2615
+ showState ? /* @__PURE__ */ jsxs2("label", { className: "solvapay-business-field", children: [
2616
+ /* @__PURE__ */ jsx8("span", { className: "solvapay-business-field-label", children: getStateFieldLabel(country) }),
2617
+ /* @__PURE__ */ jsx8(State, {})
2618
+ ] }) : null,
2619
+ showPostal ? /* @__PURE__ */ jsxs2("label", { className: "solvapay-business-field", children: [
2620
+ /* @__PURE__ */ jsx8("span", { className: "solvapay-business-field-label", children: getPostalCodeFieldLabel(country) }),
2621
+ /* @__PURE__ */ jsx8(PostalCode, {})
2622
+ ] }) : null,
2392
2623
  /* @__PURE__ */ jsxs2("label", { className: "solvapay-checkout-business-toggle", children: [
2393
- /* @__PURE__ */ jsx7(Toggle, {}),
2624
+ /* @__PURE__ */ jsx8(Toggle, {}),
2394
2625
  "I'm purchasing as a business"
2395
2626
  ] }),
2396
2627
  ctx.businessDetails.isBusiness ? /* @__PURE__ */ jsxs2("fieldset", { className: "solvapay-business-fields", children: [
2397
2628
  /* @__PURE__ */ jsxs2("label", { className: "solvapay-business-field", children: [
2398
- /* @__PURE__ */ jsx7("span", { className: "solvapay-business-field-label", children: "Business name" }),
2399
- /* @__PURE__ */ jsx7(BusinessName, { placeholder: "Acme GmbH" })
2400
- ] }),
2401
- /* @__PURE__ */ jsxs2("label", { className: "solvapay-business-field", children: [
2402
- /* @__PURE__ */ jsx7("span", { className: "solvapay-business-field-label", children: "Country" }),
2403
- /* @__PURE__ */ jsx7(Country, {})
2629
+ /* @__PURE__ */ jsx8("span", { className: "solvapay-business-field-label", children: "Business name" }),
2630
+ /* @__PURE__ */ jsx8(BusinessName, { placeholder: "Acme GmbH" })
2404
2631
  ] }),
2405
2632
  /* @__PURE__ */ jsxs2("label", { className: "solvapay-business-field", children: [
2406
- /* @__PURE__ */ jsx7("span", { className: "solvapay-business-field-label", children: taxIdLabel }),
2407
- /* @__PURE__ */ jsx7(TaxId, { placeholder: taxIdPlaceholder }),
2408
- taxIdHelperText ? /* @__PURE__ */ jsx7("p", { className: "solvapay-business-field-hint", children: taxIdHelperText }) : null
2633
+ /* @__PURE__ */ jsx8("span", { className: "solvapay-business-field-label", children: taxIdLabel }),
2634
+ /* @__PURE__ */ jsx8(TaxId, { placeholder: taxIdPlaceholder }),
2635
+ taxIdHelperText ? /* @__PURE__ */ jsx8("p", { className: "solvapay-business-field-hint", children: taxIdHelperText }) : null
2409
2636
  ] })
2410
2637
  ] }) : null,
2411
2638
  children
2412
2639
  ] });
2413
2640
  if (asChild) {
2414
- return /* @__PURE__ */ jsx7(Slot, { ref: forwardedRef, ...rest, children: content });
2641
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...rest, children: content });
2415
2642
  }
2416
- return /* @__PURE__ */ jsx7("section", { ref: forwardedRef, ...{ [attr(prefix, "business-details-fields")]: "" }, ...rest, children: content });
2643
+ return /* @__PURE__ */ jsx8("section", { ref: forwardedRef, ...{ [attr(prefix, "business-details-fields")]: "" }, ...rest, children: content });
2417
2644
  });
2418
2645
  return {
2419
2646
  Root: Root8,
2420
2647
  Toggle,
2421
2648
  BusinessName,
2422
2649
  Country,
2650
+ State,
2651
+ PostalCode,
2423
2652
  TaxId,
2424
2653
  Fields
2425
2654
  };
@@ -2448,14 +2677,14 @@ function createTaxSummaryParts(useCtx, prefix) {
2448
2677
  const ctx = useCtx("Summary");
2449
2678
  if (!ctx.isBusiness) return null;
2450
2679
  const Comp = asChild ? Slot : "section";
2451
- return /* @__PURE__ */ jsx7(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary")]: "" }, ...rest, children });
2680
+ return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary")]: "" }, ...rest, children });
2452
2681
  });
2453
2682
  const Subtotal = forwardRef2(function TaxSummarySubtotal({ asChild, children, ...rest }, forwardedRef) {
2454
2683
  const ctx = useCtx("Summary.Subtotal");
2455
2684
  const { subtotalFormatted } = useSummaryAmounts(useCtx, "Summary.Subtotal");
2456
2685
  if (!ctx.isBusiness) return null;
2457
2686
  const Comp = asChild ? Slot : "span";
2458
- return /* @__PURE__ */ jsx7(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-subtotal")]: "" }, ...rest, children: children ?? subtotalFormatted });
2687
+ return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-subtotal")]: "" }, ...rest, children: children ?? subtotalFormatted });
2459
2688
  });
2460
2689
  const Tax = forwardRef2(function TaxSummaryTax({ asChild, children, ...rest }, forwardedRef) {
2461
2690
  const ctx = useCtx("Summary.Tax");
@@ -2467,10 +2696,10 @@ function createTaxSummaryParts(useCtx, prefix) {
2467
2696
  treatment: treatment ?? "standard",
2468
2697
  taxRate
2469
2698
  });
2470
- return /* @__PURE__ */ jsx7(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-tax")]: "" }, ...rest, children: children ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
2471
- /* @__PURE__ */ jsx7("span", { children: defaultLabel }),
2699
+ return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-tax")]: "" }, ...rest, children: children ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
2700
+ /* @__PURE__ */ jsx8("span", { children: defaultLabel }),
2472
2701
  " ",
2473
- /* @__PURE__ */ jsx7("span", { ...{ [attr(prefix, "summary-tax-amount")]: "" }, children: taxFormatted })
2702
+ /* @__PURE__ */ jsx8("span", { ...{ [attr(prefix, "summary-tax-amount")]: "" }, children: taxFormatted })
2474
2703
  ] }) });
2475
2704
  });
2476
2705
  const Total = forwardRef2(function TaxSummaryTotal({ asChild, children, ...rest }, forwardedRef) {
@@ -2478,7 +2707,7 @@ function createTaxSummaryParts(useCtx, prefix) {
2478
2707
  const { totalFormatted } = useSummaryAmounts(useCtx, "Summary.Total");
2479
2708
  if (!ctx.isBusiness) return null;
2480
2709
  const Comp = asChild ? Slot : "span";
2481
- return /* @__PURE__ */ jsx7(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-total")]: "" }, ...rest, children: children ?? totalFormatted });
2710
+ return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-total")]: "" }, ...rest, children: children ?? totalFormatted });
2482
2711
  });
2483
2712
  const TaxNote = forwardRef2(function TaxSummaryTaxNote({ asChild, children, ...rest }, forwardedRef) {
2484
2713
  const ctx = useCtx("Summary.TaxNote");
@@ -2488,7 +2717,7 @@ function createTaxSummaryParts(useCtx, prefix) {
2488
2717
  const Comp = asChild ? Slot : "p";
2489
2718
  const defaultNote = treatment === "reverse_charge" ? REVERSE_CHARGE_NOTE : treatment === "not_collecting" || treatment === "not_supported" ? TAX_NOT_COLLECTED_NOTE : null;
2490
2719
  if (!defaultNote && !children) return null;
2491
- return /* @__PURE__ */ jsx7(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-tax-note")]: "" }, ...rest, children: children ?? defaultNote });
2720
+ return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, ...{ [attr(prefix, "summary-tax-note")]: "" }, ...rest, children: children ?? defaultNote });
2492
2721
  });
2493
2722
  const Rows = forwardRef2(function TaxSummaryRows({ asChild, children, ...rest }, forwardedRef) {
2494
2723
  const ctx = useCtx("Summary.Rows");
@@ -2501,24 +2730,24 @@ function createTaxSummaryParts(useCtx, prefix) {
2501
2730
  });
2502
2731
  const content = /* @__PURE__ */ jsxs2("dl", { className: "solvapay-tax-summary-rows", children: [
2503
2732
  /* @__PURE__ */ jsxs2("div", { className: "solvapay-tax-summary-row", children: [
2504
- /* @__PURE__ */ jsx7("dt", { children: formatSubtotalLabel(treatment) }),
2505
- /* @__PURE__ */ jsx7("dd", { children: /* @__PURE__ */ jsx7(Subtotal, {}) })
2733
+ /* @__PURE__ */ jsx8("dt", { children: formatSubtotalLabel(treatment) }),
2734
+ /* @__PURE__ */ jsx8("dd", { children: /* @__PURE__ */ jsx8(Subtotal, {}) })
2506
2735
  ] }),
2507
2736
  showTaxRow ? /* @__PURE__ */ jsxs2("div", { className: "solvapay-tax-summary-row", children: [
2508
- /* @__PURE__ */ jsx7("dt", { children: vatLabel }),
2509
- /* @__PURE__ */ jsx7("dd", { children: /* @__PURE__ */ jsx7("span", { ...{ [attr(prefix, "summary-tax-amount")]: "" }, children: taxFormatted }) })
2737
+ /* @__PURE__ */ jsx8("dt", { children: vatLabel }),
2738
+ /* @__PURE__ */ jsx8("dd", { children: /* @__PURE__ */ jsx8("span", { ...{ [attr(prefix, "summary-tax-amount")]: "" }, children: taxFormatted }) })
2510
2739
  ] }) : null,
2511
2740
  /* @__PURE__ */ jsxs2("div", { className: "solvapay-tax-summary-row solvapay-tax-summary-row--total", children: [
2512
- /* @__PURE__ */ jsx7("dt", { children: "Total" }),
2513
- /* @__PURE__ */ jsx7("dd", { children: /* @__PURE__ */ jsx7(Total, {}) })
2741
+ /* @__PURE__ */ jsx8("dt", { children: "Total" }),
2742
+ /* @__PURE__ */ jsx8("dd", { children: /* @__PURE__ */ jsx8(Total, {}) })
2514
2743
  ] }),
2515
- /* @__PURE__ */ jsx7(TaxNote, {}),
2744
+ /* @__PURE__ */ jsx8(TaxNote, {}),
2516
2745
  children
2517
2746
  ] });
2518
2747
  if (asChild) {
2519
- return /* @__PURE__ */ jsx7(Slot, { ref: forwardedRef, ...rest, children: content });
2748
+ return /* @__PURE__ */ jsx8(Slot, { ref: forwardedRef, ...rest, children: content });
2520
2749
  }
2521
- return /* @__PURE__ */ jsx7("section", { ref: forwardedRef, ...{ [attr(prefix, "summary-rows")]: "" }, ...rest, children: content });
2750
+ return /* @__PURE__ */ jsx8("section", { ref: forwardedRef, ...{ [attr(prefix, "summary-rows")]: "" }, ...rest, children: content });
2522
2751
  });
2523
2752
  return {
2524
2753
  Root: Root8,
@@ -2566,10 +2795,10 @@ var MissingProductRefError = class extends SolvaPayError2 {
2566
2795
  };
2567
2796
 
2568
2797
  // src/primitives/CheckoutSummary.tsx
2569
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
2570
- var CheckoutSummaryContext = createContext5(null);
2798
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
2799
+ var CheckoutSummaryContext = createContext6(null);
2571
2800
  function useCheckoutSummaryContext(part) {
2572
- const ctx = useContext6(CheckoutSummaryContext);
2801
+ const ctx = useContext7(CheckoutSummaryContext);
2573
2802
  if (!ctx) {
2574
2803
  throw new Error(
2575
2804
  `CheckoutSummary.${part} must be rendered inside <CheckoutSummary.Root>.`
@@ -2578,7 +2807,7 @@ function useCheckoutSummaryContext(part) {
2578
2807
  return ctx;
2579
2808
  }
2580
2809
  var Root = forwardRef3(function CheckoutSummaryRoot({ planRef, productRef, taxBreakdown = null, baseAmountMinor = 0, asChild, children, ...rest }, forwardedRef) {
2581
- const solva = useContext6(SolvaPayContext);
2810
+ const solva = useContext7(SolvaPayContext);
2582
2811
  if (!solva) throw new MissingProviderError("CheckoutSummary");
2583
2812
  const locale = useLocale();
2584
2813
  const copy = useCopy();
@@ -2640,35 +2869,35 @@ var Root = forwardRef3(function CheckoutSummaryRoot({ planRef, productRef, taxBr
2640
2869
  ]
2641
2870
  );
2642
2871
  const Comp = asChild ? Slot : "section";
2643
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary": "", ...rest, children: /* @__PURE__ */ jsx8(CheckoutSummaryContext.Provider, { value: ctx, children }) });
2872
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary": "", ...rest, children: /* @__PURE__ */ jsx9(CheckoutSummaryContext.Provider, { value: ctx, children }) });
2644
2873
  });
2645
2874
  var ProductSlot = forwardRef3(function CheckoutSummaryProduct({ asChild, children, ...rest }, forwardedRef) {
2646
2875
  const ctx = useCheckoutSummaryContext("Product");
2647
2876
  const name = ctx.product?.name;
2648
2877
  if (!name) return null;
2649
2878
  const Comp = asChild ? Slot : "span";
2650
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-product": "", ...rest, children: children ?? name });
2879
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-product": "", ...rest, children: children ?? name });
2651
2880
  });
2652
2881
  var PlanSlot = forwardRef3(function CheckoutSummaryPlan({ asChild, children, ...rest }, forwardedRef) {
2653
2882
  const ctx = useCheckoutSummaryContext("Plan");
2654
2883
  const name = ctx.plan?.name;
2655
2884
  if (!name) return null;
2656
2885
  const Comp = asChild ? Slot : "span";
2657
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-plan": "", ...rest, children: children ?? name });
2886
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-plan": "", ...rest, children: children ?? name });
2658
2887
  });
2659
2888
  var TaxNoteSlot = forwardRef3(
2660
2889
  function CheckoutSummaryTaxNote({ asChild, children, ...rest }, forwardedRef) {
2661
2890
  const ctx = useCheckoutSummaryContext("TaxNote");
2662
2891
  if (ctx.taxBreakdown) return null;
2663
2892
  const Comp = asChild ? Slot : "p";
2664
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-tax-note": "", ...rest, children: children ?? "Taxes calculated at checkout" });
2893
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-tax-note": "", ...rest, children: children ?? "Taxes calculated at checkout" });
2665
2894
  }
2666
2895
  );
2667
2896
  var SubtotalSlot = forwardRef3(function CheckoutSummarySubtotal({ asChild, children, ...rest }, forwardedRef) {
2668
2897
  const ctx = useCheckoutSummaryContext("Subtotal");
2669
2898
  if (!ctx.taxBreakdown) return null;
2670
2899
  const Comp = asChild ? Slot : "span";
2671
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-subtotal": "", ...rest, children: children ?? ctx.subtotalFormatted });
2900
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-subtotal": "", ...rest, children: children ?? ctx.subtotalFormatted });
2672
2901
  });
2673
2902
  var TaxSlot = forwardRef3(function CheckoutSummaryTax({ asChild, children, ...rest }, forwardedRef) {
2674
2903
  const ctx = useCheckoutSummaryContext("Tax");
@@ -2677,17 +2906,17 @@ var TaxSlot = forwardRef3(function CheckoutSummaryTax({ asChild, children, ...re
2677
2906
  if (!shouldShowTaxRow(treatment)) return null;
2678
2907
  const Comp = asChild ? Slot : "span";
2679
2908
  const defaultLabel = formatVatSummaryLabel({ treatment, taxRate: taxRate ?? 0 });
2680
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-tax": "", ...rest, children: children ?? /* @__PURE__ */ jsxs3(Fragment4, { children: [
2681
- /* @__PURE__ */ jsx8("span", { children: defaultLabel }),
2909
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-tax": "", ...rest, children: children ?? /* @__PURE__ */ jsxs3(Fragment4, { children: [
2910
+ /* @__PURE__ */ jsx9("span", { children: defaultLabel }),
2682
2911
  " ",
2683
- /* @__PURE__ */ jsx8("span", { "data-solvapay-checkout-summary-tax-amount": "", children: ctx.taxFormatted })
2912
+ /* @__PURE__ */ jsx9("span", { "data-solvapay-checkout-summary-tax-amount": "", children: ctx.taxFormatted })
2684
2913
  ] }) });
2685
2914
  });
2686
2915
  var TotalSlot = forwardRef3(function CheckoutSummaryTotal({ asChild, children, ...rest }, forwardedRef) {
2687
2916
  const ctx = useCheckoutSummaryContext("Total");
2688
2917
  if (!ctx.taxBreakdown) return null;
2689
2918
  const Comp = asChild ? Slot : "span";
2690
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-total": "", ...rest, children: children ?? ctx.totalFormatted });
2919
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-total": "", ...rest, children: children ?? ctx.totalFormatted });
2691
2920
  });
2692
2921
  var TaxTreatmentNoteSlot = forwardRef3(
2693
2922
  function CheckoutSummaryTaxTreatmentNote({ asChild, children, ...rest }, forwardedRef) {
@@ -2697,21 +2926,21 @@ var TaxTreatmentNoteSlot = forwardRef3(
2697
2926
  const Comp = asChild ? Slot : "p";
2698
2927
  const defaultNote = treatment === "reverse_charge" ? REVERSE_CHARGE_NOTE : treatment === "not_collecting" || treatment === "not_supported" ? TAX_NOT_COLLECTED_NOTE : null;
2699
2928
  if (!defaultNote && !children) return null;
2700
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-tax-treatment-note": "", ...rest, children: children ?? defaultNote });
2929
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-tax-treatment-note": "", ...rest, children: children ?? defaultNote });
2701
2930
  }
2702
2931
  );
2703
2932
  var PriceSlot = forwardRef3(function CheckoutSummaryPrice({ asChild, children, ...rest }, forwardedRef) {
2704
2933
  const ctx = useCheckoutSummaryContext("Price");
2705
2934
  if (ctx.taxBreakdown) return null;
2706
2935
  const Comp = asChild ? Slot : "span";
2707
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-price": "", ...rest, children: children ?? ctx.priceFormatted });
2936
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-price": "", ...rest, children: children ?? ctx.priceFormatted });
2708
2937
  });
2709
2938
  var TrialSlot = forwardRef3(
2710
2939
  function CheckoutSummaryTrial({ asChild, children, ...rest }, forwardedRef) {
2711
2940
  const ctx = useCheckoutSummaryContext("Trial");
2712
2941
  if (!ctx.trialBanner || ctx.taxBreakdown) return null;
2713
2942
  const Comp = asChild ? Slot : "p";
2714
- return /* @__PURE__ */ jsx8(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-trial": "", ...rest, children: children ?? ctx.trialBanner });
2943
+ return /* @__PURE__ */ jsx9(Comp, { ref: forwardedRef, "data-solvapay-checkout-summary-trial": "", ...rest, children: children ?? ctx.trialBanner });
2715
2944
  }
2716
2945
  );
2717
2946
  var CheckoutSummaryRoot2 = Root;
@@ -2741,7 +2970,7 @@ function useCheckoutSummary() {
2741
2970
  }
2742
2971
 
2743
2972
  // src/components/CheckoutSummary.tsx
2744
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
2973
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
2745
2974
  var CheckoutSummary2 = ({
2746
2975
  planRef,
2747
2976
  productRef,
@@ -2762,25 +2991,25 @@ var CheckoutSummary2 = ({
2762
2991
  baseAmountMinor,
2763
2992
  className: rootClass,
2764
2993
  children: [
2765
- /* @__PURE__ */ jsx9(CheckoutSummary.Product, { className: "solvapay-checkout-summary-product" }),
2994
+ /* @__PURE__ */ jsx10(CheckoutSummary.Product, { className: "solvapay-checkout-summary-product" }),
2766
2995
  /* @__PURE__ */ jsxs4("div", { className: "solvapay-checkout-summary-row", children: [
2767
- /* @__PURE__ */ jsx9(CheckoutSummary.Plan, { className: "solvapay-checkout-summary-plan" }),
2768
- !hasTax && /* @__PURE__ */ jsx9(CheckoutSummary.Price, { className: "solvapay-checkout-summary-price" })
2996
+ /* @__PURE__ */ jsx10(CheckoutSummary.Plan, { className: "solvapay-checkout-summary-plan" }),
2997
+ !hasTax && /* @__PURE__ */ jsx10(CheckoutSummary.Price, { className: "solvapay-checkout-summary-price" })
2769
2998
  ] }),
2770
2999
  hasTax ? /* @__PURE__ */ jsxs4(Fragment5, { children: [
2771
3000
  /* @__PURE__ */ jsxs4("div", { className: "solvapay-checkout-summary-row", children: [
2772
- /* @__PURE__ */ jsx9("span", { children: "Subtotal" }),
2773
- /* @__PURE__ */ jsx9(CheckoutSummary.Subtotal, { className: "solvapay-checkout-summary-subtotal" })
3001
+ /* @__PURE__ */ jsx10("span", { children: "Subtotal" }),
3002
+ /* @__PURE__ */ jsx10(CheckoutSummary.Subtotal, { className: "solvapay-checkout-summary-subtotal" })
2774
3003
  ] }),
2775
- /* @__PURE__ */ jsx9("div", { className: "solvapay-checkout-summary-row", children: /* @__PURE__ */ jsx9(CheckoutSummary.Tax, { className: "solvapay-checkout-summary-tax" }) }),
3004
+ /* @__PURE__ */ jsx10("div", { className: "solvapay-checkout-summary-row", children: /* @__PURE__ */ jsx10(CheckoutSummary.Tax, { className: "solvapay-checkout-summary-tax" }) }),
2776
3005
  /* @__PURE__ */ jsxs4("div", { className: "solvapay-checkout-summary-row", children: [
2777
- /* @__PURE__ */ jsx9("span", { children: "Total" }),
2778
- /* @__PURE__ */ jsx9(CheckoutSummary.Total, { className: "solvapay-checkout-summary-total" })
3006
+ /* @__PURE__ */ jsx10("span", { children: "Total" }),
3007
+ /* @__PURE__ */ jsx10(CheckoutSummary.Total, { className: "solvapay-checkout-summary-total" })
2779
3008
  ] }),
2780
- /* @__PURE__ */ jsx9(CheckoutSummary.TaxTreatmentNote, { className: "solvapay-checkout-summary-tax-treatment-note" })
3009
+ /* @__PURE__ */ jsx10(CheckoutSummary.TaxTreatmentNote, { className: "solvapay-checkout-summary-tax-treatment-note" })
2781
3010
  ] }) : /* @__PURE__ */ jsxs4(Fragment5, { children: [
2782
- showTrial && /* @__PURE__ */ jsx9(CheckoutSummary.Trial, { className: "solvapay-checkout-summary-trial" }),
2783
- showTaxNote && /* @__PURE__ */ jsx9(CheckoutSummary.TaxNote, { className: "solvapay-checkout-summary-tax-note" })
3011
+ showTrial && /* @__PURE__ */ jsx10(CheckoutSummary.Trial, { className: "solvapay-checkout-summary-trial" }),
3012
+ showTaxNote && /* @__PURE__ */ jsx10(CheckoutSummary.TaxNote, { className: "solvapay-checkout-summary-tax-note" })
2784
3013
  ] })
2785
3014
  ]
2786
3015
  }
@@ -2788,7 +3017,7 @@ var CheckoutSummary2 = ({
2788
3017
  };
2789
3018
 
2790
3019
  // src/hooks/useMerchant.ts
2791
- import { useCallback as useCallback7, useEffect as useEffect5, useState as useState7 } from "react";
3020
+ import { useCallback as useCallback8, useEffect as useEffect5, useState as useState7 } from "react";
2792
3021
  var merchantCache = /* @__PURE__ */ new Map();
2793
3022
  var CACHE_DURATION5 = 5 * 60 * 1e3;
2794
3023
  function cacheKeyFor2(config) {
@@ -2810,7 +3039,7 @@ function useMerchant() {
2810
3039
  return !cached || !cached.merchant && !cached.promise;
2811
3040
  });
2812
3041
  const [error, setError] = useState7(null);
2813
- const load = useCallback7(
3042
+ const load = useCallback8(
2814
3043
  async (force = false) => {
2815
3044
  const cached = merchantCache.get(key);
2816
3045
  const now = Date.now();
@@ -2893,8 +3122,8 @@ function deriveVariant(plan, mode) {
2893
3122
  }
2894
3123
 
2895
3124
  // src/primitives/MandateText.tsx
2896
- import { forwardRef as forwardRef4, useContext as useContext7, useMemo as useMemo5 } from "react";
2897
- import { jsx as jsx10 } from "react/jsx-runtime";
3125
+ import { forwardRef as forwardRef4, useContext as useContext8, useMemo as useMemo5 } from "react";
3126
+ import { jsx as jsx11 } from "react/jsx-runtime";
2898
3127
  var MandateText = forwardRef4(
2899
3128
  function MandateText2({
2900
3129
  planRef,
@@ -2907,10 +3136,11 @@ var MandateText = forwardRef4(
2907
3136
  children,
2908
3137
  ...rest
2909
3138
  }, forwardedRef) {
2910
- const solva = useContext7(SolvaPayContext);
3139
+ const solva = useContext8(SolvaPayContext);
2911
3140
  if (!solva) throw new MissingProviderError("MandateText");
2912
3141
  const locale = useLocale();
2913
3142
  const copy = useCopy();
3143
+ const handleExternalClick = useExternalLinkClick();
2914
3144
  const planSelection = usePlanSelection();
2915
3145
  const resolvedPlanRef = planRef ?? planSelection?.selectedPlanRef ?? void 0;
2916
3146
  const resolvedProductRef = productRef ?? planSelection?.productRef;
@@ -2951,19 +3181,19 @@ var MandateText = forwardRef4(
2951
3181
  const text = typeof template === "function" ? template(ctx) : template;
2952
3182
  if (!text) return null;
2953
3183
  const Comp = asChild ? Slot : "p";
2954
- return /* @__PURE__ */ jsx10(
3184
+ return /* @__PURE__ */ jsx11(
2955
3185
  Comp,
2956
3186
  {
2957
3187
  ref: forwardedRef,
2958
3188
  "data-solvapay-mandate-text": "",
2959
3189
  "data-variant": resolvedVariant,
2960
3190
  ...rest,
2961
- children: children ?? linkifyMandateText(text, ctx.merchant, copy)
3191
+ children: children ?? linkifyMandateText(text, ctx.merchant, copy, handleExternalClick)
2962
3192
  }
2963
3193
  );
2964
3194
  }
2965
3195
  );
2966
- function linkifyMandateText(text, merchant, copy) {
3196
+ function linkifyMandateText(text, merchant, copy, onLinkClick) {
2967
3197
  const entries = [
2968
3198
  { url: merchant.termsUrl, label: copy.legalFooter.terms },
2969
3199
  { url: merchant.privacyUrl, label: copy.legalFooter.privacy }
@@ -2973,13 +3203,14 @@ function linkifyMandateText(text, merchant, copy) {
2973
3203
  return text.split(pattern).map((part, i) => {
2974
3204
  const match = entries.find((e) => e.url === part);
2975
3205
  if (!match) return part;
2976
- return /* @__PURE__ */ jsx10(
3206
+ return /* @__PURE__ */ jsx11(
2977
3207
  "a",
2978
3208
  {
2979
3209
  href: match.url,
2980
3210
  target: "_blank",
2981
3211
  rel: "noopener noreferrer",
2982
3212
  "data-solvapay-mandate-link": "",
3213
+ onClick: onLinkClick,
2983
3214
  children: match.label || match.url
2984
3215
  },
2985
3216
  i
@@ -2991,22 +3222,22 @@ function escapeRegExp(value) {
2991
3222
  }
2992
3223
 
2993
3224
  // src/components/Spinner.tsx
2994
- import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
3225
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
2995
3226
  var SIZE_PX = { sm: 16, md: 20, lg: 24 };
2996
3227
  var Spinner = ({ className, size = "md" }) => {
2997
- const px = SIZE_PX[size];
3228
+ const px2 = SIZE_PX[size];
2998
3229
  return /* @__PURE__ */ jsxs5(
2999
3230
  "svg",
3000
3231
  {
3001
3232
  "data-solvapay-spinner": "",
3002
3233
  className,
3003
- width: px,
3004
- height: px,
3234
+ width: px2,
3235
+ height: px2,
3005
3236
  viewBox: "0 0 24 24",
3006
3237
  fill: "none",
3007
3238
  "aria-hidden": "true",
3008
3239
  children: [
3009
- /* @__PURE__ */ jsx11(
3240
+ /* @__PURE__ */ jsx12(
3010
3241
  "circle",
3011
3242
  {
3012
3243
  cx: "12",
@@ -3017,7 +3248,7 @@ var Spinner = ({ className, size = "md" }) => {
3017
3248
  strokeOpacity: "0.25"
3018
3249
  }
3019
3250
  ),
3020
- /* @__PURE__ */ jsx11(
3251
+ /* @__PURE__ */ jsx12(
3021
3252
  "path",
3022
3253
  {
3023
3254
  fill: "currentColor",
@@ -3031,6 +3262,26 @@ var Spinner = ({ className, size = "md" }) => {
3031
3262
  };
3032
3263
 
3033
3264
  // src/utils/confirmPayment.ts
3265
+ function buildConfirmBillingDetails(input) {
3266
+ const name = input.name?.trim();
3267
+ const email = input.email?.trim();
3268
+ const country = input.country?.trim();
3269
+ if (!name && !email && !country) return void 0;
3270
+ return {
3271
+ ...name && { name },
3272
+ ...email && { email },
3273
+ ...country && {
3274
+ address: {
3275
+ line1: "",
3276
+ line2: "",
3277
+ city: "",
3278
+ state: input.state?.trim() ?? "",
3279
+ postal_code: input.postalCode?.trim() ?? "",
3280
+ country
3281
+ }
3282
+ }
3283
+ };
3284
+ }
3034
3285
  async function confirmPayment(input) {
3035
3286
  const { stripe, elements, clientSecret, returnUrl, billingDetails, copy } = input;
3036
3287
  const mode = input.mode ?? "payment-element";
@@ -3157,14 +3408,14 @@ function resolveCta(input) {
3157
3408
  }
3158
3409
 
3159
3410
  // src/primitives/PaymentForm.tsx
3160
- import React10, {
3411
+ import React11, {
3161
3412
  forwardRef as forwardRef5,
3162
- useCallback as useCallback9,
3163
- useContext as useContext8,
3164
- useEffect as useEffect7,
3165
- useMemo as useMemo6,
3413
+ useCallback as useCallback10,
3414
+ useContext as useContext9,
3415
+ useEffect as useEffect8,
3416
+ useMemo as useMemo7,
3166
3417
  useRef as useRef5,
3167
- useState as useState9
3418
+ useState as useState10
3168
3419
  } from "react";
3169
3420
  import {
3170
3421
  Elements,
@@ -3237,18 +3488,205 @@ function toStripeElementLocale(locale) {
3237
3488
 
3238
3489
  // src/primitives/paymentElementDefaults.ts
3239
3490
  var DEFAULT_PAYMENT_ELEMENT_OPTIONS = {
3240
- wallets: { link: "never" }
3491
+ wallets: { link: "never" },
3492
+ fields: { billingDetails: { address: "never" } },
3493
+ layout: { type: "tabs" }
3241
3494
  };
3242
3495
  function withPaymentElementDefaults(options) {
3496
+ const defaultBilling = DEFAULT_PAYMENT_ELEMENT_OPTIONS.fields.billingDetails;
3497
+ const callerBilling = options?.fields?.billingDetails;
3498
+ const billingDetails = callerBilling && typeof callerBilling === "object" ? { ...defaultBilling, ...callerBilling } : callerBilling ?? defaultBilling;
3243
3499
  return {
3500
+ ...DEFAULT_PAYMENT_ELEMENT_OPTIONS,
3244
3501
  ...options,
3245
3502
  wallets: {
3246
3503
  ...DEFAULT_PAYMENT_ELEMENT_OPTIONS.wallets,
3247
3504
  ...options?.wallets
3505
+ },
3506
+ fields: {
3507
+ ...DEFAULT_PAYMENT_ELEMENT_OPTIONS.fields,
3508
+ ...options?.fields,
3509
+ billingDetails
3248
3510
  }
3249
3511
  };
3250
3512
  }
3251
3513
 
3514
+ // src/primitives/useStripeAppearance.ts
3515
+ import { useEffect as useEffect6, useLayoutEffect, useMemo as useMemo6, useState as useState8 } from "react";
3516
+
3517
+ // src/primitives/buildStripeAppearance.ts
3518
+ var SENTINEL_TOKEN = "--solvapay-radius";
3519
+ var COLOR_VARIABLES = {
3520
+ colorBackground: "--solvapay-surface",
3521
+ colorText: "--solvapay-accent",
3522
+ colorTextSecondary: "--solvapay-muted-foreground",
3523
+ colorPrimary: "--solvapay-selection",
3524
+ colorDanger: "--solvapay-danger"
3525
+ };
3526
+ var DIRECT_VARIABLES = {
3527
+ borderRadius: "--solvapay-radius",
3528
+ fontFamily: "--solvapay-font"
3529
+ };
3530
+ var CSS_WIDE_KEYWORDS = /* @__PURE__ */ new Set([
3531
+ "",
3532
+ "inherit",
3533
+ "initial",
3534
+ "unset",
3535
+ "revert",
3536
+ "revert-layer"
3537
+ ]);
3538
+ var CONTROL_HEIGHT_RATIO = 2.5;
3539
+ var CONTROL_PADDING_X_RATIO = 0.75;
3540
+ var LINE_HEIGHT_RATIO = 1.5;
3541
+ var LABEL_FONT_RATIO = 0.8125;
3542
+ var LABEL_MARGIN_RATIO = 0.5;
3543
+ var ERROR_FONT_RATIO = 0.75;
3544
+ var ERROR_MARGIN_RATIO = 0.375;
3545
+ function readRootFontPx(root) {
3546
+ const px2 = parseFloat(getComputedStyle(root).fontSize);
3547
+ return Number.isFinite(px2) && px2 > 0 ? px2 : 16;
3548
+ }
3549
+ function px(value) {
3550
+ return `${value}px`;
3551
+ }
3552
+ function deriveControlMetrics(rootPx) {
3553
+ const lineHeight = LINE_HEIGHT_RATIO * rootPx;
3554
+ const paddingY = (CONTROL_HEIGHT_RATIO * rootPx - 2 - lineHeight) / 2;
3555
+ const paddingX = CONTROL_PADDING_X_RATIO * rootPx;
3556
+ const gridGap = CONTROL_PADDING_X_RATIO * rootPx;
3557
+ return {
3558
+ fontSize: px(rootPx),
3559
+ lineHeight: px(lineHeight),
3560
+ padding: `${px(paddingY)} ${px(paddingX)}`,
3561
+ labelFontSize: px(LABEL_FONT_RATIO * rootPx),
3562
+ labelMarginBottom: px(LABEL_MARGIN_RATIO * rootPx),
3563
+ errorFontSize: px(ERROR_FONT_RATIO * rootPx),
3564
+ errorMarginTop: px(ERROR_MARGIN_RATIO * rootPx),
3565
+ gridGap: px(gridGap)
3566
+ };
3567
+ }
3568
+ function isUsableToken(value) {
3569
+ return !CSS_WIDE_KEYWORDS.has(value.trim().toLowerCase());
3570
+ }
3571
+ function readToken(style, name) {
3572
+ const value = style.getPropertyValue(name).trim();
3573
+ return isUsableToken(value) ? value : void 0;
3574
+ }
3575
+ function resolveColor(probe, raw) {
3576
+ probe.style.color = raw;
3577
+ const resolved = getComputedStyle(probe).color.trim();
3578
+ if (!isUsableToken(resolved)) return void 0;
3579
+ if (resolved.toLowerCase().includes("light-dark")) return void 0;
3580
+ return resolved;
3581
+ }
3582
+ function buildStripeAppearance(root) {
3583
+ const style = getComputedStyle(root);
3584
+ if (!readToken(style, SENTINEL_TOKEN)) return void 0;
3585
+ const probe = document.createElement("span");
3586
+ probe.style.cssText = "position:absolute;left:-9999px;top:0";
3587
+ root.appendChild(probe);
3588
+ const rootPx = readRootFontPx(root);
3589
+ const metrics = deriveControlMetrics(rootPx);
3590
+ const variables = {
3591
+ fontSizeBase: metrics.fontSize,
3592
+ gridRowSpacing: metrics.gridGap,
3593
+ gridColumnSpacing: metrics.gridGap
3594
+ };
3595
+ for (const [stripeKey, token] of Object.entries(COLOR_VARIABLES)) {
3596
+ const raw = readToken(style, token);
3597
+ if (!raw) continue;
3598
+ const resolved = resolveColor(probe, raw);
3599
+ if (!resolved) continue;
3600
+ variables[stripeKey] = resolved;
3601
+ }
3602
+ for (const [stripeKey, token] of Object.entries(DIRECT_VARIABLES)) {
3603
+ const raw = readToken(style, token);
3604
+ if (!raw) continue;
3605
+ variables[stripeKey] = raw;
3606
+ }
3607
+ const border = (() => {
3608
+ const raw = readToken(style, "--solvapay-border");
3609
+ return raw ? resolveColor(probe, raw) : void 0;
3610
+ })();
3611
+ const accent = variables.colorText;
3612
+ const muted = variables.colorTextSecondary;
3613
+ const danger = variables.colorDanger;
3614
+ const radius = variables.borderRadius;
3615
+ const surface = variables.colorBackground;
3616
+ const fontFamily = variables.fontFamily;
3617
+ probe.remove();
3618
+ const rules = {
3619
+ ".Input": {
3620
+ fontSize: metrics.fontSize,
3621
+ lineHeight: metrics.lineHeight,
3622
+ padding: metrics.padding,
3623
+ boxShadow: "none",
3624
+ ...fontFamily ? { fontFamily } : {},
3625
+ ...border ? { border: `1px solid ${border}` } : {},
3626
+ ...radius ? { borderRadius: radius } : {}
3627
+ },
3628
+ ".Input:focus": {
3629
+ outline: "none",
3630
+ ...accent ? {
3631
+ borderColor: accent,
3632
+ boxShadow: `0 0 0 1px ${accent}`
3633
+ } : {}
3634
+ },
3635
+ ".Label": {
3636
+ fontSize: metrics.labelFontSize,
3637
+ fontWeight: "500",
3638
+ marginBottom: metrics.labelMarginBottom,
3639
+ ...muted ? { color: muted } : {}
3640
+ },
3641
+ ".Error": {
3642
+ fontSize: metrics.errorFontSize,
3643
+ marginTop: metrics.errorMarginTop,
3644
+ ...danger ? { color: danger } : {}
3645
+ },
3646
+ ".Tab": {
3647
+ ...border ? { border: `1px solid ${border}` } : {}
3648
+ },
3649
+ ".Tab--selected": {
3650
+ ...surface ? { backgroundColor: surface } : {},
3651
+ ...border ? { border: `1px solid ${border}` } : {},
3652
+ ...accent ? { color: accent } : {}
3653
+ },
3654
+ ".AccordionItem": {
3655
+ border: "none",
3656
+ padding: "0"
3657
+ }
3658
+ };
3659
+ return { variables, rules };
3660
+ }
3661
+
3662
+ // src/primitives/useStripeAppearance.ts
3663
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect6;
3664
+ function useStripeAppearance(root, appearance) {
3665
+ const [themeTick, setThemeTick] = useState8(0);
3666
+ useIsomorphicLayoutEffect(() => {
3667
+ if (appearance !== void 0) return;
3668
+ if (!root) return;
3669
+ const apply = () => setThemeTick((tick) => tick + 1);
3670
+ const media = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : void 0;
3671
+ media?.addEventListener("change", apply);
3672
+ const observer = new MutationObserver(apply);
3673
+ observer.observe(document.documentElement, {
3674
+ attributes: true,
3675
+ attributeFilter: ["style"]
3676
+ });
3677
+ return () => {
3678
+ media?.removeEventListener("change", apply);
3679
+ observer.disconnect();
3680
+ };
3681
+ }, [root, appearance]);
3682
+ return useMemo6(() => {
3683
+ if (appearance === null) return void 0;
3684
+ if (appearance !== void 0) return appearance;
3685
+ if (!root) return void 0;
3686
+ return buildStripeAppearance(root);
3687
+ }, [root, appearance, themeTick]);
3688
+ }
3689
+
3252
3690
  // src/utils/processPaymentResult.ts
3253
3691
  async function reconcilePayment(input) {
3254
3692
  const { paymentIntentId, productRef, planRef, processPayment, refetchPurchase, copy } = input;
@@ -3321,8 +3759,10 @@ function normalizeOneTimePurchase(input) {
3321
3759
  }
3322
3760
 
3323
3761
  // src/hooks/useBusinessDetailsAttach.ts
3324
- import { useCallback as useCallback8, useEffect as useEffect6, useRef as useRef4, useState as useState8 } from "react";
3762
+ import { useCallback as useCallback9, useEffect as useEffect7, useRef as useRef4, useState as useState9 } from "react";
3325
3763
  import {
3764
+ getCustomerAddressFieldErrors,
3765
+ isCustomerAddressComplete,
3326
3766
  validateBusinessDetails as validateBusinessDetails2
3327
3767
  } from "@solvapay/core";
3328
3768
  var defaultBusinessDetails = { isBusiness: false };
@@ -3334,32 +3774,45 @@ function useBusinessDetailsAttach(options) {
3334
3774
  onTaxChange,
3335
3775
  refreshElements
3336
3776
  } = options;
3337
- const [businessDetails, setBusinessDetailsState] = useState8(defaultBusinessDetails);
3338
- const [taxBreakdown, setTaxBreakdown] = useState8(null);
3339
- const [businessDetailsAttached, setBusinessDetailsAttached] = useState8(false);
3340
- const [businessDetailsAttaching, setBusinessDetailsAttaching] = useState8(false);
3341
- const [businessDetailsError, setBusinessDetailsError] = useState8(null);
3342
- const [fieldErrors, setFieldErrors] = useState8({});
3777
+ const [businessDetails, setBusinessDetailsState] = useState9(defaultBusinessDetails);
3778
+ const [taxBreakdown, setTaxBreakdown] = useState9(null);
3779
+ const [businessDetailsAttached, setBusinessDetailsAttached] = useState9(false);
3780
+ const [businessDetailsAttaching, setBusinessDetailsAttaching] = useState9(false);
3781
+ const [businessDetailsError, setBusinessDetailsError] = useState9(null);
3782
+ const [fieldErrors, setFieldErrors] = useState9({});
3343
3783
  const attachRequestIdRef = useRef4(0);
3344
- const setBusinessDetails = useCallback8((patch) => {
3784
+ const setBusinessDetails = useCallback9((patch) => {
3345
3785
  setBusinessDetailsState((prev) => {
3346
3786
  const next = { ...prev, ...patch };
3347
3787
  if (patch.isBusiness === false) {
3348
- return { isBusiness: false };
3788
+ return {
3789
+ isBusiness: false,
3790
+ ...next.customerCountry && { customerCountry: next.customerCountry },
3791
+ ...next.customerState && { customerState: next.customerState },
3792
+ ...next.customerPostalCode && { customerPostalCode: next.customerPostalCode },
3793
+ ...next.customerName && { customerName: next.customerName }
3794
+ };
3795
+ }
3796
+ if (patch.isBusiness === true && !next.country && next.customerCountry) {
3797
+ return { ...next, country: next.customerCountry };
3349
3798
  }
3350
3799
  return next;
3351
3800
  });
3352
3801
  setBusinessDetailsAttached(false);
3353
3802
  setBusinessDetailsError(null);
3354
3803
  }, []);
3355
- const runAttach = useCallback8(
3804
+ const runAttach = useCallback9(
3356
3805
  async (input) => {
3357
3806
  if (!processorPaymentId || !attachBusinessDetails) {
3358
3807
  return !attachBusinessDetails;
3359
3808
  }
3809
+ const addressErrors = getCustomerAddressFieldErrors(input);
3360
3810
  const validation = validateBusinessDetails2(input);
3361
- if (!validation.success) {
3362
- setFieldErrors(mapBusinessFieldErrors(input));
3811
+ if (!validation.success || !isCustomerAddressComplete(input)) {
3812
+ setFieldErrors({
3813
+ ...mapBusinessFieldErrors(input),
3814
+ ...addressErrors
3815
+ });
3363
3816
  return false;
3364
3817
  }
3365
3818
  setFieldErrors({});
@@ -3394,11 +3847,15 @@ function useBusinessDetailsAttach(options) {
3394
3847
  },
3395
3848
  [processorPaymentId, attachBusinessDetails, customerRef, onTaxChange, refreshElements]
3396
3849
  );
3397
- useEffect6(() => {
3850
+ useEffect7(() => {
3398
3851
  if (!processorPaymentId || !attachBusinessDetails) return;
3399
3852
  const validation = validateBusinessDetails2(businessDetails);
3400
- if (!validation.success) {
3401
- setFieldErrors(mapBusinessFieldErrors(businessDetails));
3853
+ if (!validation.success || !isCustomerAddressComplete(businessDetails)) {
3854
+ const countrySelected = !!(businessDetails.customerCountry?.trim() || businessDetails.isBusiness && businessDetails.country?.trim());
3855
+ setFieldErrors({
3856
+ ...mapBusinessFieldErrors(businessDetails),
3857
+ ...countrySelected ? getCustomerAddressFieldErrors(businessDetails) : {}
3858
+ });
3402
3859
  setBusinessDetailsAttached(false);
3403
3860
  return;
3404
3861
  }
@@ -3423,7 +3880,8 @@ function useBusinessDetailsAttach(options) {
3423
3880
  }
3424
3881
 
3425
3882
  // src/primitives/PaymentForm.tsx
3426
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
3883
+ import { isCustomerAddressComplete as isCustomerAddressComplete2, resolveBuyerCountry as resolveBuyerCountry2 } from "@solvapay/core";
3884
+ import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
3427
3885
  function toSubmitVariant(variant) {
3428
3886
  switch (variant) {
3429
3887
  case "freeTier":
@@ -3451,9 +3909,10 @@ var Root2 = forwardRef5(
3451
3909
  buttonClassName,
3452
3910
  prefillCustomer,
3453
3911
  requireTermsAcceptance = false,
3912
+ appearance,
3454
3913
  children
3455
3914
  } = props;
3456
- const solva = useContext8(SolvaPayContext);
3915
+ const solva = useContext9(SolvaPayContext);
3457
3916
  if (!solva) throw new MissingProviderError("PaymentForm");
3458
3917
  const { attachBusinessDetails, customerRef } = solva;
3459
3918
  const copy = useCopy();
@@ -3481,7 +3940,7 @@ var Root2 = forwardRef5(
3481
3940
  });
3482
3941
  const hasInitializedRef = useRef5(false);
3483
3942
  const hasPlanOrProduct = !!(effectivePlanRef || effectiveProductRef);
3484
- useEffect7(() => {
3943
+ useEffect8(() => {
3485
3944
  if (isFreePlan) return;
3486
3945
  if (!hasInitializedRef.current && hasPlanOrProduct && !checkoutLoading && !checkoutError && !clientSecret) {
3487
3946
  hasInitializedRef.current = true;
@@ -3495,111 +3954,85 @@ var Root2 = forwardRef5(
3495
3954
  }
3496
3955
  }, [hasPlanOrProduct, checkoutLoading, checkoutError, clientSecret, startCheckout, isFreePlan]);
3497
3956
  const finalReturnUrl = returnUrl || (typeof window !== "undefined" ? window.location.href : "/");
3498
- const elementsOptions = useMemo6(() => {
3957
+ const [rootEl, setRootEl] = useState10(null);
3958
+ const attachRoot = useCallback10((node) => {
3959
+ if (!node) return;
3960
+ setRootEl((prev) => prev === node ? prev : node);
3961
+ }, []);
3962
+ const sectionRef = composeRefs(forwardedRef, attachRoot);
3963
+ const resolvedAppearance = useStripeAppearance(rootEl, appearance);
3964
+ const elementsOptions = useMemo7(() => {
3499
3965
  if (!clientSecret) return void 0;
3500
- return { clientSecret, locale: toStripeElementLocale(locale) };
3501
- }, [clientSecret, locale]);
3502
- const shouldRenderElements = !!(stripePromise && clientSecret);
3503
- if (!hasPlanOrProduct) {
3504
- return /* @__PURE__ */ jsx12(
3505
- "section",
3506
- {
3507
- ref: forwardedRef,
3508
- className,
3509
- "data-solvapay-payment-form": "",
3510
- "data-state": "error",
3511
- children: /* @__PURE__ */ jsx12("p", { role: "alert", "data-solvapay-payment-form-error": "", children: copy.errors.configMissingPlanOrProduct })
3512
- }
3513
- );
3514
- }
3515
- if (checkoutError) {
3516
- return /* @__PURE__ */ jsx12(
3517
- "section",
3518
- {
3519
- ref: forwardedRef,
3520
- className,
3521
- "data-solvapay-payment-form": "",
3522
- "data-state": "error",
3523
- children: /* @__PURE__ */ jsxs6("p", { role: "alert", "data-solvapay-payment-form-error": "", children: [
3524
- copy.errors.paymentInitFailed,
3525
- " ",
3526
- checkoutError.message || copy.errors.unknownError
3527
- ] })
3528
- }
3529
- );
3530
- }
3531
- if (isFreePlan && resolvedPlan) {
3532
- return /* @__PURE__ */ jsx12(
3533
- "section",
3534
- {
3535
- ref: forwardedRef,
3536
- className,
3537
- "data-solvapay-payment-form": "",
3538
- "data-state": "ready",
3539
- "data-variant": "free",
3540
- children: /* @__PURE__ */ jsx12(
3541
- FreeInner,
3542
- {
3543
- planRef: effectivePlanRef,
3544
- productRef: effectiveProductRef,
3545
- plan: resolvedPlan,
3546
- resolvedPlanRef,
3547
- requireTermsAcceptance,
3548
- submitButtonText,
3549
- buttonClassName,
3550
- onFreePlan,
3551
- onResult,
3552
- onError,
3553
- children
3554
- }
3555
- )
3556
- }
3557
- );
3558
- }
3559
- if (shouldRenderElements && elementsOptions && clientSecret) {
3560
- const resolvedClientSecret = clientSecret;
3561
- return /* @__PURE__ */ jsx12(
3562
- "section",
3563
- {
3564
- ref: forwardedRef,
3565
- className,
3566
- "data-solvapay-payment-form": "",
3567
- "data-state": "ready",
3568
- "data-variant": "paid",
3569
- children: /* @__PURE__ */ jsx12(Elements, { stripe: stripePromise, options: elementsOptions, children: /* @__PURE__ */ jsx12(
3570
- PaidInner,
3571
- {
3572
- planRef: effectivePlanRef,
3573
- productRef: effectiveProductRef,
3574
- prefillCustomer,
3575
- resolvedPlanRef,
3576
- plan: resolvedPlan ?? null,
3577
- clientSecret: resolvedClientSecret,
3578
- processorPaymentId,
3579
- returnUrl: finalReturnUrl,
3580
- submitButtonText,
3581
- buttonClassName,
3582
- requireTermsAcceptance,
3583
- onSuccess,
3584
- onResult,
3585
- onError,
3586
- onTaxChange: props.onTaxChange,
3587
- attachBusinessDetails,
3588
- customerRef,
3589
- children
3590
- }
3591
- ) }, resolvedClientSecret)
3592
- }
3593
- );
3594
- }
3595
- return /* @__PURE__ */ jsx12(
3966
+ return {
3967
+ clientSecret,
3968
+ locale: toStripeElementLocale(locale),
3969
+ ...resolvedAppearance ? { appearance: resolvedAppearance } : {}
3970
+ };
3971
+ }, [clientSecret, locale, resolvedAppearance]);
3972
+ const appearanceReady = appearance !== void 0 || rootEl !== null;
3973
+ const shouldRenderElements = !!(stripePromise && clientSecret && appearanceReady);
3974
+ const dataState = !hasPlanOrProduct || checkoutError ? "error" : isFreePlan && resolvedPlan ? "ready" : shouldRenderElements ? "ready" : "loading";
3975
+ const dataVariant = isFreePlan && resolvedPlan ? "free" : shouldRenderElements ? "paid" : void 0;
3976
+ const pending = /* @__PURE__ */ jsx13(
3977
+ PendingInner,
3978
+ {
3979
+ planRef: effectivePlanRef,
3980
+ productRef: effectiveProductRef,
3981
+ resolvedPlanRef,
3982
+ plan: resolvedPlan ?? null,
3983
+ returnUrl: finalReturnUrl,
3984
+ submitButtonText,
3985
+ buttonClassName,
3986
+ error: checkoutError ? `${copy.errors.paymentInitFailed} ${checkoutError.message || copy.errors.unknownError}` : null,
3987
+ children
3988
+ }
3989
+ );
3990
+ return /* @__PURE__ */ jsx13(
3596
3991
  "section",
3597
3992
  {
3598
- ref: forwardedRef,
3993
+ ref: sectionRef,
3599
3994
  className,
3600
3995
  "data-solvapay-payment-form": "",
3601
- "data-state": "loading",
3602
- children: /* @__PURE__ */ jsx12("output", { "data-solvapay-payment-form-loading": "", children: /* @__PURE__ */ jsx12(Spinner, { size: "md" }) })
3996
+ "data-state": dataState,
3997
+ ...dataVariant ? { "data-variant": dataVariant } : {},
3998
+ children: !hasPlanOrProduct ? /* @__PURE__ */ jsx13("p", { role: "alert", "data-solvapay-payment-form-error": "", children: copy.errors.configMissingPlanOrProduct }) : checkoutError ? pending : isFreePlan && resolvedPlan ? /* @__PURE__ */ jsx13(
3999
+ FreeInner,
4000
+ {
4001
+ planRef: effectivePlanRef,
4002
+ productRef: effectiveProductRef,
4003
+ plan: resolvedPlan,
4004
+ resolvedPlanRef,
4005
+ requireTermsAcceptance,
4006
+ submitButtonText,
4007
+ buttonClassName,
4008
+ onFreePlan,
4009
+ onResult,
4010
+ onError,
4011
+ children
4012
+ }
4013
+ ) : shouldRenderElements && elementsOptions && clientSecret ? /* @__PURE__ */ jsx13(Elements, { stripe: stripePromise, options: elementsOptions, children: /* @__PURE__ */ jsx13(
4014
+ PaidInner,
4015
+ {
4016
+ planRef: effectivePlanRef,
4017
+ productRef: effectiveProductRef,
4018
+ prefillCustomer,
4019
+ resolvedPlanRef,
4020
+ plan: resolvedPlan ?? null,
4021
+ clientSecret,
4022
+ processorPaymentId,
4023
+ returnUrl: finalReturnUrl,
4024
+ submitButtonText,
4025
+ buttonClassName,
4026
+ requireTermsAcceptance,
4027
+ onSuccess,
4028
+ onResult,
4029
+ onError,
4030
+ onTaxChange: props.onTaxChange,
4031
+ attachBusinessDetails,
4032
+ customerRef,
4033
+ children
4034
+ }
4035
+ ) }, clientSecret) : pending
3603
4036
  }
3604
4037
  );
3605
4038
  }
@@ -3625,12 +4058,14 @@ var PaidInner = ({
3625
4058
  children
3626
4059
  }) => {
3627
4060
  const stripe = useStripe();
4061
+ const stripeAvailable = !!stripe;
4062
+ const stripeRef = useRef5(stripe);
3628
4063
  const elements = useElements();
3629
4064
  const copy = useCopy();
3630
4065
  const customer = useCustomer();
3631
4066
  const { processPayment, upsertPurchase } = useSolvaPay();
3632
4067
  const { refetch } = usePurchase();
3633
- const refreshElements = useCallback9(async () => {
4068
+ const refreshElements = useCallback10(async () => {
3634
4069
  if (elements) {
3635
4070
  await elements.fetchUpdates();
3636
4071
  }
@@ -3652,17 +4087,23 @@ var PaidInner = ({
3652
4087
  onTaxChange,
3653
4088
  refreshElements
3654
4089
  });
3655
- const [elementKind, setElementKind] = useState9(
4090
+ const [elementKind, setElementKind] = useState10(
3656
4091
  children ? null : "payment-element"
3657
4092
  );
3658
- const [customerName, setCustomerName] = useState9("");
3659
- const [paymentInputComplete, setPaymentInputComplete] = useState9(false);
3660
- const [termsAccepted, setTermsAccepted] = useState9(false);
3661
- const [isProcessing, setIsProcessing] = useState9(false);
3662
- const [error, setError] = useState9(null);
4093
+ const [customerName, setCustomerName] = useState10("");
4094
+ const [paymentInputComplete, setPaymentInputComplete] = useState10(false);
4095
+ const [termsAccepted, setTermsAccepted] = useState10(false);
4096
+ const [isProcessing, setIsProcessing] = useState10(false);
4097
+ const [error, setError] = useState10(null);
3663
4098
  const returnResumeStarted = useRef5(false);
3664
- useEffect7(() => {
3665
- if (!stripe || returnResumeStarted.current || typeof window === "undefined") return;
4099
+ useEffect8(() => {
4100
+ stripeRef.current = stripe;
4101
+ });
4102
+ useEffect8(() => {
4103
+ const stripeApi = stripeRef.current;
4104
+ if (!stripeAvailable || !stripeApi || returnResumeStarted.current || typeof window === "undefined") {
4105
+ return;
4106
+ }
3666
4107
  const returnClientSecret = readPaymentIntentClientSecret(window.location.search);
3667
4108
  if (!returnClientSecret) return;
3668
4109
  returnResumeStarted.current = true;
@@ -3670,7 +4111,7 @@ var PaidInner = ({
3670
4111
  void (async () => {
3671
4112
  setIsProcessing(true);
3672
4113
  setError(null);
3673
- const retrieved = await stripe.retrievePaymentIntent(returnClientSecret);
4114
+ const retrieved = await stripeApi.retrievePaymentIntent(returnClientSecret);
3674
4115
  if (cancelled) return;
3675
4116
  stripPaymentIntentParams();
3676
4117
  if (retrieved.error || !retrieved.paymentIntent) {
@@ -3680,7 +4121,7 @@ var PaidInner = ({
3680
4121
  }
3681
4122
  let paymentIntent = retrieved.paymentIntent;
3682
4123
  if (paymentIntent.status === "requires_action") {
3683
- const actionResult = await stripe.handleNextAction({ clientSecret: returnClientSecret });
4124
+ const actionResult = await stripeApi.handleNextAction({ clientSecret: returnClientSecret });
3684
4125
  if (cancelled) return;
3685
4126
  if (actionResult.error || !actionResult.paymentIntent) {
3686
4127
  setError(copy.errors.paymentRequires3ds);
@@ -3735,7 +4176,7 @@ var PaidInner = ({
3735
4176
  cancelled = true;
3736
4177
  };
3737
4178
  }, [
3738
- stripe,
4179
+ stripeAvailable,
3739
4180
  copy,
3740
4181
  productRef,
3741
4182
  planRef,
@@ -3748,8 +4189,8 @@ var PaidInner = ({
3748
4189
  onError
3749
4190
  ]);
3750
4191
  const isReady = !!(stripe && elements);
3751
- const canSubmit = isReady && !!clientSecret && !!elementKind && paymentInputComplete && (!requireTermsAcceptance || termsAccepted) && (!requiresBusinessAttach || businessDetailsAttached) && !businessDetailsAttaching && !isProcessing;
3752
- const submit = useCallback9(async () => {
4192
+ const canSubmit = isReady && !!clientSecret && !!elementKind && paymentInputComplete && (!requireTermsAcceptance || termsAccepted) && (!requiresBusinessAttach || businessDetailsAttached) && isCustomerAddressComplete2(businessDetails) && !businessDetailsAttaching && !isProcessing;
4193
+ const submit = useCallback10(async () => {
3753
4194
  if (!stripe || !elements || !clientSecret || !elementKind) {
3754
4195
  const msg = !stripe || !elements ? copy.errors.stripeUnavailable : copy.errors.paymentIntentUnavailable;
3755
4196
  setError(msg);
@@ -3776,10 +4217,13 @@ var PaidInner = ({
3776
4217
  clientSecret,
3777
4218
  mode: elementKind === "card-element" ? "card-element" : "payment-element",
3778
4219
  returnUrl,
3779
- billingDetails: {
3780
- ...customerName.trim() && { name: customerName.trim() },
3781
- email: customer.email ?? prefillCustomer?.email
3782
- },
4220
+ billingDetails: buildConfirmBillingDetails({
4221
+ name: customerName.trim() || customer.name,
4222
+ email: customer.email ?? prefillCustomer?.email,
4223
+ country: resolveBuyerCountry2(businessDetails),
4224
+ state: businessDetails.customerState,
4225
+ postalCode: businessDetails.customerPostalCode
4226
+ }),
3783
4227
  copy
3784
4228
  });
3785
4229
  if (result.status === "error") {
@@ -3853,7 +4297,7 @@ var PaidInner = ({
3853
4297
  businessDetailsError
3854
4298
  ]);
3855
4299
  const effectiveError = error ?? businessDetailsError;
3856
- const contextValue = useMemo6(
4300
+ const contextValue = useMemo7(
3857
4301
  () => ({
3858
4302
  planRef,
3859
4303
  productRef,
@@ -3922,7 +4366,7 @@ var PaidInner = ({
3922
4366
  submit
3923
4367
  ]
3924
4368
  );
3925
- return /* @__PURE__ */ jsx12(PaymentFormProvider, { value: contextValue, children });
4369
+ return /* @__PURE__ */ jsx13(PaymentFormProvider, { value: contextValue, children });
3926
4370
  };
3927
4371
  var FreeInner = ({
3928
4372
  planRef,
@@ -3940,10 +4384,10 @@ var FreeInner = ({
3940
4384
  const copy = useCopy();
3941
4385
  const { refetch } = usePurchase();
3942
4386
  const { activate, state, error: activationError, result: activationResult } = useActivation();
3943
- const [termsAccepted, setTermsAccepted] = useState9(false);
3944
- const [localError, setLocalError] = useState9(void 0);
4387
+ const [termsAccepted, setTermsAccepted] = useState10(false);
4388
+ const [localError, setLocalError] = useState10(void 0);
3945
4389
  const resultFiredRef = useRef5(false);
3946
- useEffect7(() => {
4390
+ useEffect8(() => {
3947
4391
  if (state === "activated" && activationResult && !resultFiredRef.current) {
3948
4392
  resultFiredRef.current = true;
3949
4393
  const res = { kind: "activated", result: activationResult };
@@ -3955,7 +4399,7 @@ var FreeInner = ({
3955
4399
  }, [state, activationResult, onResult, refetch]);
3956
4400
  const isProcessing = state === "activating";
3957
4401
  const canSubmit = !isProcessing && (!requireTermsAcceptance || termsAccepted) && !!productRef;
3958
- const submit = useCallback9(async () => {
4402
+ const submit = useCallback10(async () => {
3959
4403
  if (!productRef) {
3960
4404
  const msg = copy.errors.configMissingPlanOrProduct;
3961
4405
  setLocalError(msg);
@@ -3979,7 +4423,7 @@ var FreeInner = ({
3979
4423
  }
3980
4424
  }, [productRef, plan, onFreePlan, activate, onResult, onError, copy]);
3981
4425
  const error = localError ?? activationError;
3982
- const contextValue = useMemo6(
4426
+ const contextValue = useMemo7(
3983
4427
  () => ({
3984
4428
  planRef,
3985
4429
  productRef,
@@ -4034,11 +4478,77 @@ var FreeInner = ({
4034
4478
  submit
4035
4479
  ]
4036
4480
  );
4037
- return /* @__PURE__ */ jsx12(PaymentFormProvider, { value: contextValue, children });
4481
+ return /* @__PURE__ */ jsx13(PaymentFormProvider, { value: contextValue, children });
4482
+ };
4483
+ var PendingInner = ({
4484
+ planRef,
4485
+ productRef,
4486
+ resolvedPlanRef,
4487
+ plan,
4488
+ returnUrl,
4489
+ submitButtonText,
4490
+ buttonClassName,
4491
+ error,
4492
+ children
4493
+ }) => {
4494
+ const contextValue = useMemo7(
4495
+ () => ({
4496
+ planRef,
4497
+ productRef,
4498
+ prefillCustomer: void 0,
4499
+ resolvedPlanRef,
4500
+ plan,
4501
+ clientSecret: null,
4502
+ processorPaymentId: null,
4503
+ stripe: null,
4504
+ elements: null,
4505
+ isProcessing: false,
4506
+ isReady: false,
4507
+ paymentInputComplete: false,
4508
+ termsAccepted: false,
4509
+ requireTermsAcceptance: false,
4510
+ canSubmit: false,
4511
+ error,
4512
+ elementKind: null,
4513
+ returnUrl,
4514
+ submitButtonText,
4515
+ buttonClassName,
4516
+ customerName: "",
4517
+ setCustomerName: () => {
4518
+ },
4519
+ businessDetails: defaultBusinessDetails,
4520
+ taxBreakdown: null,
4521
+ businessDetailsAttached: false,
4522
+ businessDetailsAttaching: false,
4523
+ businessDetailsError: null,
4524
+ fieldErrors: {},
4525
+ setBusinessDetails: () => {
4526
+ },
4527
+ setElementKind: () => {
4528
+ },
4529
+ setPaymentInputComplete: () => {
4530
+ },
4531
+ setTermsAccepted: () => {
4532
+ },
4533
+ submit: async () => {
4534
+ }
4535
+ }),
4536
+ [
4537
+ planRef,
4538
+ productRef,
4539
+ resolvedPlanRef,
4540
+ plan,
4541
+ returnUrl,
4542
+ submitButtonText,
4543
+ buttonClassName,
4544
+ error
4545
+ ]
4546
+ );
4547
+ return /* @__PURE__ */ jsx13(PaymentFormProvider, { value: contextValue, children });
4038
4548
  };
4039
4549
  var Summary = (props) => {
4040
4550
  const ctx = usePaymentForm();
4041
- return /* @__PURE__ */ jsx12(
4551
+ return /* @__PURE__ */ jsx13(
4042
4552
  CheckoutSummary2,
4043
4553
  {
4044
4554
  ...props,
@@ -4051,7 +4561,7 @@ var Summary = (props) => {
4051
4561
  };
4052
4562
  var MandateTextPrimitive = (props) => {
4053
4563
  const ctx = usePaymentForm();
4054
- return /* @__PURE__ */ jsx12(
4564
+ return /* @__PURE__ */ jsx13(
4055
4565
  MandateText,
4056
4566
  {
4057
4567
  ...props,
@@ -4068,14 +4578,14 @@ var CustomerFields = forwardRef5(
4068
4578
  const email = customer.email ?? prefillCustomer?.email;
4069
4579
  if (!email) return null;
4070
4580
  const Comp = asChild ? Slot : "section";
4071
- return /* @__PURE__ */ jsx12(Comp, { ref, "data-solvapay-payment-form-customer-fields": "", ...rest, children: children ?? /* @__PURE__ */ jsxs6(Fragment6, { children: [
4581
+ return /* @__PURE__ */ jsx13(Comp, { ref, "data-solvapay-payment-form-customer-fields": "", ...rest, children: children ?? /* @__PURE__ */ jsxs6(Fragment6, { children: [
4072
4582
  email && /* @__PURE__ */ jsxs6("dl", { "data-solvapay-payment-form-customer-email": "", children: [
4073
- /* @__PURE__ */ jsx12("dt", { children: copy.customer.emailLabel }),
4074
- /* @__PURE__ */ jsx12("dd", { children: email })
4583
+ /* @__PURE__ */ jsx13("dt", { children: copy.customer.emailLabel }),
4584
+ /* @__PURE__ */ jsx13("dd", { children: email })
4075
4585
  ] }),
4076
4586
  /* @__PURE__ */ jsxs6("dl", { "data-solvapay-payment-form-customer-name": "", children: [
4077
- /* @__PURE__ */ jsx12("dt", { children: copy.customer.nameLabel }),
4078
- /* @__PURE__ */ jsx12("dd", { children: /* @__PURE__ */ jsx12(
4587
+ /* @__PURE__ */ jsx13("dt", { children: copy.customer.nameLabel }),
4588
+ /* @__PURE__ */ jsx13("dd", { children: /* @__PURE__ */ jsx13(
4079
4589
  "input",
4080
4590
  {
4081
4591
  type: "text",
@@ -4098,14 +4608,14 @@ var CustomerFields = forwardRef5(
4098
4608
  var PaymentElementSlot = ({ options }) => {
4099
4609
  const { setElementKind, setPaymentInputComplete, isReady, stripe, elements } = usePaymentForm();
4100
4610
  const locale = useLocale();
4101
- useEffect7(() => {
4611
+ useEffect8(() => {
4102
4612
  if (stripe && elements) setElementKind("payment-element");
4103
4613
  }, [setElementKind, stripe, elements]);
4104
4614
  if (!stripe || !elements) return null;
4105
4615
  if (!isReady) {
4106
- return /* @__PURE__ */ jsx12("output", { "data-solvapay-payment-form-loading": "", children: /* @__PURE__ */ jsx12(Spinner, { size: "sm" }) });
4616
+ return /* @__PURE__ */ jsx13("output", { "data-solvapay-payment-form-loading": "", children: /* @__PURE__ */ jsx13(Spinner, { size: "sm" }) });
4107
4617
  }
4108
- return /* @__PURE__ */ jsx12("section", { "data-solvapay-payment-form-payment-element": "", children: /* @__PURE__ */ jsx12(
4618
+ return /* @__PURE__ */ jsx13("section", { "data-solvapay-payment-form-payment-element": "", children: /* @__PURE__ */ jsx13(
4109
4619
  StripePaymentElement,
4110
4620
  {
4111
4621
  options: withPaymentElementDefaults(options),
@@ -4116,14 +4626,14 @@ var PaymentElementSlot = ({ options }) => {
4116
4626
  };
4117
4627
  var CardElementSlot = ({ options }) => {
4118
4628
  const { setElementKind, setPaymentInputComplete, isReady, stripe, elements } = usePaymentForm();
4119
- useEffect7(() => {
4629
+ useEffect8(() => {
4120
4630
  if (stripe && elements) setElementKind("card-element");
4121
4631
  }, [setElementKind, stripe, elements]);
4122
4632
  if (!stripe || !elements) return null;
4123
4633
  if (!isReady) {
4124
- return /* @__PURE__ */ jsx12("output", { "data-solvapay-payment-form-loading": "", children: /* @__PURE__ */ jsx12(Spinner, { size: "sm" }) });
4634
+ return /* @__PURE__ */ jsx13("output", { "data-solvapay-payment-form-loading": "", children: /* @__PURE__ */ jsx13(Spinner, { size: "sm" }) });
4125
4635
  }
4126
- return /* @__PURE__ */ jsx12("section", { "data-solvapay-payment-form-card-element": "", children: /* @__PURE__ */ jsx12(StripeCardElement, { options, onChange: (e) => setPaymentInputComplete(e.complete) }) });
4636
+ return /* @__PURE__ */ jsx13("section", { "data-solvapay-payment-form-card-element": "", children: /* @__PURE__ */ jsx13(StripeCardElement, { options, onChange: (e) => setPaymentInputComplete(e.complete) }) });
4127
4637
  };
4128
4638
  var TermsCheckbox = forwardRef5(
4129
4639
  function PaymentFormTermsCheckbox({ asChild, label, children, ...rest }, ref) {
@@ -4131,10 +4641,10 @@ var TermsCheckbox = forwardRef5(
4131
4641
  const copy = useCopy();
4132
4642
  const id = "solvapay-terms-checkbox";
4133
4643
  if (asChild) {
4134
- return /* @__PURE__ */ jsx12(Slot, { ref, "data-solvapay-payment-form-terms": "", ...rest, children });
4644
+ return /* @__PURE__ */ jsx13(Slot, { ref, "data-solvapay-payment-form-terms": "", ...rest, children });
4135
4645
  }
4136
4646
  return /* @__PURE__ */ jsxs6("label", { ref, htmlFor: id, "data-solvapay-payment-form-terms": "", ...rest, children: [
4137
- /* @__PURE__ */ jsx12(
4647
+ /* @__PURE__ */ jsx13(
4138
4648
  "input",
4139
4649
  {
4140
4650
  id,
@@ -4143,7 +4653,7 @@ var TermsCheckbox = forwardRef5(
4143
4653
  onChange: (e) => setTermsAccepted(e.target.checked)
4144
4654
  }
4145
4655
  ),
4146
- /* @__PURE__ */ jsx12("span", { children: label ?? copy.terms.checkboxLabel })
4656
+ /* @__PURE__ */ jsx13("span", { children: label ?? copy.terms.checkboxLabel })
4147
4657
  ] });
4148
4658
  }
4149
4659
  );
@@ -4177,8 +4687,8 @@ var SubmitButton = forwardRef5(
4177
4687
  override: typeof children === "string" ? children : ctx.submitButtonText
4178
4688
  });
4179
4689
  const processingContent = /* @__PURE__ */ jsxs6(Fragment6, { children: [
4180
- /* @__PURE__ */ jsx12(Spinner, { size: "sm" }),
4181
- /* @__PURE__ */ jsx12("span", { children: copy.cta.processing })
4690
+ /* @__PURE__ */ jsx13(Spinner, { size: "sm" }),
4691
+ /* @__PURE__ */ jsx13("span", { children: copy.cta.processing })
4182
4692
  ] });
4183
4693
  const buttonProps = {
4184
4694
  "data-solvapay-payment-form-submit": "",
@@ -4196,30 +4706,30 @@ var SubmitButton = forwardRef5(
4196
4706
  ...rest
4197
4707
  };
4198
4708
  if (asChild) {
4199
- const slotChild = ctx.isProcessing && React10.isValidElement(children) ? React10.cloneElement(
4709
+ const slotChild = ctx.isProcessing && React11.isValidElement(children) ? React11.cloneElement(
4200
4710
  children,
4201
4711
  void 0,
4202
4712
  processingContent
4203
4713
  ) : children;
4204
- return /* @__PURE__ */ jsx12(Slot, { ref, ...buttonProps, children: slotChild });
4714
+ return /* @__PURE__ */ jsx13(Slot, { ref, ...buttonProps, children: slotChild });
4205
4715
  }
4206
4716
  const content = ctx.isProcessing ? processingContent : children && typeof children !== "string" ? children : label;
4207
- return /* @__PURE__ */ jsx12("button", { ref, type: "submit", ...buttonProps, children: content });
4717
+ return /* @__PURE__ */ jsx13("button", { ref, type: "submit", ...buttonProps, children: content });
4208
4718
  }
4209
4719
  );
4210
4720
  var Loading = forwardRef5(function PaymentFormLoading({ asChild, children, ...rest }, ref) {
4211
4721
  const ctx = usePaymentForm();
4212
4722
  if (ctx.isReady && ctx.clientSecret) return null;
4213
4723
  if (asChild) {
4214
- return /* @__PURE__ */ jsx12(Slot, { ref, "data-solvapay-payment-form-loading": "", ...rest, children: children ?? /* @__PURE__ */ jsx12(Spinner, { size: "sm" }) });
4724
+ return /* @__PURE__ */ jsx13(Slot, { ref, "data-solvapay-payment-form-loading": "", ...rest, children: children ?? /* @__PURE__ */ jsx13(Spinner, { size: "sm" }) });
4215
4725
  }
4216
- return /* @__PURE__ */ jsx12("output", { ref, "data-solvapay-payment-form-loading": "", ...rest, children: children ?? /* @__PURE__ */ jsx12(Spinner, { size: "sm" }) });
4726
+ return /* @__PURE__ */ jsx13("output", { ref, "data-solvapay-payment-form-loading": "", ...rest, children: children ?? /* @__PURE__ */ jsx13(Spinner, { size: "sm" }) });
4217
4727
  });
4218
4728
  var ErrorSlot = forwardRef5(function PaymentFormError({ asChild, children, ...rest }, ref) {
4219
4729
  const ctx = usePaymentForm();
4220
4730
  if (!ctx.error) return null;
4221
4731
  if (asChild) {
4222
- return /* @__PURE__ */ jsx12(
4732
+ return /* @__PURE__ */ jsx13(
4223
4733
  Slot,
4224
4734
  {
4225
4735
  ref,
@@ -4232,7 +4742,7 @@ var ErrorSlot = forwardRef5(function PaymentFormError({ asChild, children, ...re
4232
4742
  }
4233
4743
  );
4234
4744
  }
4235
- return /* @__PURE__ */ jsx12(
4745
+ return /* @__PURE__ */ jsx13(
4236
4746
  "p",
4237
4747
  {
4238
4748
  ref,
@@ -4295,7 +4805,7 @@ var PaymentForm = {
4295
4805
  };
4296
4806
 
4297
4807
  // src/hooks/useTopupAmountSelector.ts
4298
- import { useState as useState10, useCallback as useCallback10, useMemo as useMemo7 } from "react";
4808
+ import { useState as useState11, useCallback as useCallback11, useMemo as useMemo8 } from "react";
4299
4809
  function getQuickAmounts(currency) {
4300
4810
  switch (currency.toUpperCase()) {
4301
4811
  case "SEK":
@@ -4315,7 +4825,11 @@ function getQuickAmounts(currency) {
4315
4825
  }
4316
4826
  function getCurrencySymbol(currency) {
4317
4827
  try {
4318
- const parts = new Intl.NumberFormat("en", { style: "currency", currency }).formatToParts(0);
4828
+ const parts = new Intl.NumberFormat("en", {
4829
+ style: "currency",
4830
+ currency,
4831
+ currencyDisplay: "narrowSymbol"
4832
+ }).formatToParts(0);
4319
4833
  const sym = parts.find((p) => p.type === "currency");
4320
4834
  return sym?.value || currency;
4321
4835
  } catch {
@@ -4327,19 +4841,19 @@ var DEFAULT_MAX = 1e5;
4327
4841
  function useTopupAmountSelector(options) {
4328
4842
  const { currency, minAmount = DEFAULT_MIN, maxAmount = DEFAULT_MAX } = options;
4329
4843
  const copy = useCopy();
4330
- const [selectedAmount, setSelectedAmount] = useState10(null);
4331
- const [customAmount, setCustomAmountRaw] = useState10("");
4332
- const [error, setError] = useState10(null);
4333
- const quickAmounts = useMemo7(() => getQuickAmounts(currency), [currency]);
4334
- const currencySymbol = useMemo7(() => getCurrencySymbol(currency), [currency]);
4335
- const resolvedAmount = useMemo7(() => {
4844
+ const [selectedAmount, setSelectedAmount] = useState11(null);
4845
+ const [customAmount, setCustomAmountRaw] = useState11("");
4846
+ const [error, setError] = useState11(null);
4847
+ const quickAmounts = useMemo8(() => getQuickAmounts(currency), [currency]);
4848
+ const currencySymbol = useMemo8(() => getCurrencySymbol(currency), [currency]);
4849
+ const resolvedAmount = useMemo8(() => {
4336
4850
  if (customAmount) {
4337
4851
  const parsed = parseFloat(customAmount);
4338
4852
  return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
4339
4853
  }
4340
4854
  return selectedAmount;
4341
4855
  }, [selectedAmount, customAmount]);
4342
- const selectQuickAmount = useCallback10(
4856
+ const selectQuickAmount = useCallback11(
4343
4857
  (amount) => {
4344
4858
  setSelectedAmount(amount);
4345
4859
  setCustomAmountRaw("");
@@ -4347,7 +4861,7 @@ function useTopupAmountSelector(options) {
4347
4861
  },
4348
4862
  []
4349
4863
  );
4350
- const setCustomAmount = useCallback10(
4864
+ const setCustomAmount = useCallback11(
4351
4865
  (value) => {
4352
4866
  const sanitized = value.replace(/[^0-9.]/g, "");
4353
4867
  const dotIndex = sanitized.indexOf(".");
@@ -4358,7 +4872,7 @@ function useTopupAmountSelector(options) {
4358
4872
  },
4359
4873
  []
4360
4874
  );
4361
- const validate = useCallback10(() => {
4875
+ const validate = useCallback11(() => {
4362
4876
  if (resolvedAmount == null) {
4363
4877
  setError(copy.topup.selectOrEnterAmount);
4364
4878
  return false;
@@ -4386,7 +4900,7 @@ function useTopupAmountSelector(options) {
4386
4900
  setError(null);
4387
4901
  return true;
4388
4902
  }, [resolvedAmount, minAmount, maxAmount, currency, copy]);
4389
- const reset = useCallback10(() => {
4903
+ const reset = useCallback11(() => {
4390
4904
  setSelectedAmount(null);
4391
4905
  setCustomAmountRaw("");
4392
4906
  setError(null);
@@ -4406,10 +4920,10 @@ function useTopupAmountSelector(options) {
4406
4920
  }
4407
4921
 
4408
4922
  // src/hooks/useBalance.ts
4409
- import { useEffect as useEffect8 } from "react";
4923
+ import { useEffect as useEffect9 } from "react";
4410
4924
  function useBalance() {
4411
4925
  const { balance } = useSolvaPay();
4412
- useEffect8(() => {
4926
+ useEffect9(() => {
4413
4927
  balance.refetch();
4414
4928
  }, [balance.refetch]);
4415
4929
  return balance;
@@ -4417,17 +4931,17 @@ function useBalance() {
4417
4931
 
4418
4932
  // src/primitives/AmountPicker.tsx
4419
4933
  import {
4420
- createContext as createContext6,
4934
+ createContext as createContext7,
4421
4935
  forwardRef as forwardRef6,
4422
- useCallback as useCallback11,
4423
- useContext as useContext9,
4424
- useEffect as useEffect9,
4425
- useMemo as useMemo8
4936
+ useCallback as useCallback12,
4937
+ useContext as useContext10,
4938
+ useEffect as useEffect10,
4939
+ useMemo as useMemo9
4426
4940
  } from "react";
4427
- import { jsx as jsx13 } from "react/jsx-runtime";
4428
- var AmountPickerContext = createContext6(null);
4941
+ import { jsx as jsx14 } from "react/jsx-runtime";
4942
+ var AmountPickerContext = createContext7(null);
4429
4943
  function usePickerCtx(part) {
4430
- const ctx = useContext9(AmountPickerContext);
4944
+ const ctx = useContext10(AmountPickerContext);
4431
4945
  if (!ctx) {
4432
4946
  throw new Error(`AmountPicker.${part} must be rendered inside <AmountPicker.Root>.`);
4433
4947
  }
@@ -4444,14 +4958,14 @@ var Root3 = forwardRef6(function AmountPickerRoot({
4444
4958
  children,
4445
4959
  ...rest
4446
4960
  }, forwardedRef) {
4447
- const solva = useContext9(SolvaPayContext);
4961
+ const solva = useContext10(SolvaPayContext);
4448
4962
  if (!solva) throw new MissingProviderError("AmountPicker");
4449
4963
  const internalSelector = useTopupAmountSelector({ currency, minAmount, maxAmount });
4450
4964
  const selector = externalSelector ?? internalSelector;
4451
4965
  const { creditsPerMinorUnit, displayExchangeRate } = useBalance();
4452
4966
  const minorPerMajor = getMinorUnitsPerMajor(currency);
4453
4967
  const resolvedAmountMinor = selector.resolvedAmount != null && selector.resolvedAmount > 0 ? Math.round(selector.resolvedAmount * minorPerMajor) : null;
4454
- useEffect9(() => {
4968
+ useEffect10(() => {
4455
4969
  if (emit === "minor") {
4456
4970
  onChange?.(resolvedAmountMinor);
4457
4971
  } else {
@@ -4461,7 +4975,7 @@ var Root3 = forwardRef6(function AmountPickerRoot({
4461
4975
  const rate = displayExchangeRate ?? 1;
4462
4976
  const estimatedCredits = creditsPerMinorUnit != null && creditsPerMinorUnit > 0 && resolvedAmountMinor != null ? Math.floor(resolvedAmountMinor / rate * creditsPerMinorUnit) : null;
4463
4977
  const isApproximate = rate !== 1;
4464
- const ctx = useMemo8(
4978
+ const ctx = useMemo9(
4465
4979
  () => ({
4466
4980
  ...selector,
4467
4981
  currency,
@@ -4484,7 +4998,7 @@ var Root3 = forwardRef6(function AmountPickerRoot({
4484
4998
  ]
4485
4999
  );
4486
5000
  const Comp = asChild ? Slot : "div";
4487
- return /* @__PURE__ */ jsx13(AmountPickerContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx13(Comp, { ref: forwardedRef, "data-solvapay-amount-picker": "", ...rest, children }) });
5001
+ return /* @__PURE__ */ jsx14(AmountPickerContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx14(Comp, { ref: forwardedRef, "data-solvapay-amount-picker": "", ...rest, children }) });
4488
5002
  });
4489
5003
  var Option = forwardRef6(function AmountPickerOption({ asChild, amount, onClick, children, ...rest }, forwardedRef) {
4490
5004
  const ctx = usePickerCtx("Option");
@@ -4508,10 +5022,10 @@ var Option = forwardRef6(function AmountPickerOption({ asChild, amount, onClick,
4508
5022
  if (asChild) {
4509
5023
  return (
4510
5024
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4511
- /* @__PURE__ */ jsx13(Slot, { ref: forwardedRef, ...commonProps, children: children ?? defaultLabel })
5025
+ /* @__PURE__ */ jsx14(Slot, { ref: forwardedRef, ...commonProps, children: children ?? defaultLabel })
4512
5026
  );
4513
5027
  }
4514
- return /* @__PURE__ */ jsx13("button", { ref: forwardedRef, type: "button", ...commonProps, children: children ?? defaultLabel });
5028
+ return /* @__PURE__ */ jsx14("button", { ref: forwardedRef, type: "button", ...commonProps, children: children ?? defaultLabel });
4515
5029
  });
4516
5030
  var Custom = forwardRef6(function AmountPickerCustom({ asChild, onChange, onFocus, ...rest }, forwardedRef) {
4517
5031
  const ctx = usePickerCtx("Custom");
@@ -4534,15 +5048,15 @@ var Custom = forwardRef6(function AmountPickerCustom({ asChild, onChange, onFocu
4534
5048
  if (asChild) {
4535
5049
  return (
4536
5050
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4537
- /* @__PURE__ */ jsx13(Slot, { ref: forwardedRef, ...commonProps })
5051
+ /* @__PURE__ */ jsx14(Slot, { ref: forwardedRef, ...commonProps })
4538
5052
  );
4539
5053
  }
4540
- return /* @__PURE__ */ jsx13("input", { ref: forwardedRef, ...commonProps });
5054
+ return /* @__PURE__ */ jsx14("input", { ref: forwardedRef, ...commonProps });
4541
5055
  });
4542
5056
  var Confirm = forwardRef6(function AmountPickerConfirm({ asChild, onClick, onConfirm, disabled, children, ...rest }, forwardedRef) {
4543
5057
  const ctx = usePickerCtx("Confirm");
4544
5058
  const isDisabled = disabled || !ctx.resolvedAmount;
4545
- const handleConfirm = useCallback11(() => {
5059
+ const handleConfirm = useCallback12(() => {
4546
5060
  if (!ctx.validate()) return;
4547
5061
  if (ctx.emit === "minor") {
4548
5062
  if (ctx.resolvedAmountMinor != null) onConfirm?.(ctx.resolvedAmountMinor);
@@ -4561,10 +5075,10 @@ var Confirm = forwardRef6(function AmountPickerConfirm({ asChild, onClick, onCon
4561
5075
  if (asChild) {
4562
5076
  return (
4563
5077
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4564
- /* @__PURE__ */ jsx13(Slot, { ref: forwardedRef, ...commonProps, children })
5078
+ /* @__PURE__ */ jsx14(Slot, { ref: forwardedRef, ...commonProps, children })
4565
5079
  );
4566
5080
  }
4567
- return /* @__PURE__ */ jsx13("button", { ref: forwardedRef, type: "button", ...commonProps, children });
5081
+ return /* @__PURE__ */ jsx14("button", { ref: forwardedRef, type: "button", ...commonProps, children });
4568
5082
  });
4569
5083
  var AmountPickerRoot2 = Root3;
4570
5084
  var AmountPickerOption2 = Option;
@@ -4593,7 +5107,7 @@ function useAmountPickerCopy() {
4593
5107
  }
4594
5108
 
4595
5109
  // src/hooks/useTopup.ts
4596
- import { useState as useState11, useCallback as useCallback12, useRef as useRef6 } from "react";
5110
+ import { useState as useState12, useCallback as useCallback13, useRef as useRef6 } from "react";
4597
5111
  import { loadStripe as loadStripe2 } from "@stripe/stripe-js";
4598
5112
  var stripePromiseCache2 = /* @__PURE__ */ new Map();
4599
5113
  function getStripeCacheKey2(publishableKey, accountId) {
@@ -4602,13 +5116,13 @@ function getStripeCacheKey2(publishableKey, accountId) {
4602
5116
  function useTopup(options) {
4603
5117
  const { amount, currency, autoRecharge } = options;
4604
5118
  const { createTopupPayment, customerRef, updateCustomerRef } = useSolvaPay();
4605
- const [loading, setLoading] = useState11(false);
4606
- const [error, setError] = useState11(null);
4607
- const [stripePromise, setStripePromise] = useState11(null);
4608
- const [clientSecret, setClientSecret] = useState11(null);
4609
- const [processorPaymentId, setProcessorPaymentId] = useState11(null);
5119
+ const [loading, setLoading] = useState12(false);
5120
+ const [error, setError] = useState12(null);
5121
+ const [stripePromise, setStripePromise] = useState12(null);
5122
+ const [clientSecret, setClientSecret] = useState12(null);
5123
+ const [processorPaymentId, setProcessorPaymentId] = useState12(null);
4610
5124
  const isStartingRef = useRef6(false);
4611
- const startTopup = useCallback12(async () => {
5125
+ const startTopup = useCallback13(async () => {
4612
5126
  if (isStartingRef.current || loading) {
4613
5127
  return;
4614
5128
  }
@@ -4656,7 +5170,7 @@ function useTopup(options) {
4656
5170
  isStartingRef.current = false;
4657
5171
  }
4658
5172
  }, [amount, currency, autoRecharge, createTopupPayment, customerRef, updateCustomerRef, loading]);
4659
- const reset = useCallback12(() => {
5173
+ const reset = useCallback13(() => {
4660
5174
  isStartingRef.current = false;
4661
5175
  setLoading(false);
4662
5176
  setError(null);
@@ -4676,15 +5190,15 @@ function useTopup(options) {
4676
5190
  }
4677
5191
 
4678
5192
  // src/primitives/TopupForm.tsx
4679
- import React12, {
4680
- createContext as createContext7,
5193
+ import React13, {
5194
+ createContext as createContext8,
4681
5195
  forwardRef as forwardRef7,
4682
- useCallback as useCallback13,
4683
- useContext as useContext10,
4684
- useEffect as useEffect10,
4685
- useMemo as useMemo9,
5196
+ useCallback as useCallback14,
5197
+ useContext as useContext11,
5198
+ useEffect as useEffect11,
5199
+ useMemo as useMemo10,
4686
5200
  useRef as useRef7,
4687
- useState as useState12
5201
+ useState as useState13
4688
5202
  } from "react";
4689
5203
  import {
4690
5204
  Elements as Elements2,
@@ -4692,10 +5206,14 @@ import {
4692
5206
  useElements as useElements2,
4693
5207
  PaymentElement as StripePaymentElement2
4694
5208
  } from "@stripe/react-stripe-js";
4695
- import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
4696
- var TopupFormContext = createContext7(null);
5209
+ import {
5210
+ isCustomerAddressComplete as isCustomerAddressComplete3,
5211
+ resolveBuyerCountry as resolveBuyerCountry3
5212
+ } from "@solvapay/core";
5213
+ import { Fragment as Fragment7, jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
5214
+ var TopupFormContext = createContext8(null);
4697
5215
  function useTopupCtx(part) {
4698
- const ctx = useContext10(TopupFormContext);
5216
+ const ctx = useContext11(TopupFormContext);
4699
5217
  if (!ctx) {
4700
5218
  throw new Error(`TopupForm.${part} must be rendered inside <TopupForm.Root>.`);
4701
5219
  }
@@ -4712,12 +5230,13 @@ var Root4 = forwardRef7(function TopupFormRoot(props, forwardedRef) {
4712
5230
  returnUrl,
4713
5231
  submitButtonText: _submitButtonText,
4714
5232
  buttonClassName: _buttonClassName,
5233
+ appearance,
4715
5234
  className,
4716
5235
  asChild,
4717
5236
  children,
4718
5237
  ...rest
4719
5238
  } = props;
4720
- const solva = useContext10(SolvaPayContext);
5239
+ const solva = useContext11(SolvaPayContext);
4721
5240
  if (!solva) throw new MissingProviderError("TopupForm");
4722
5241
  const { processTopupPayment, attachBusinessDetails, customerRef } = solva;
4723
5242
  const copy = useCopy();
@@ -4736,7 +5255,7 @@ var Root4 = forwardRef7(function TopupFormRoot(props, forwardedRef) {
4736
5255
  });
4737
5256
  const hasInitializedRef = useRef7(false);
4738
5257
  const hasAmount = amount > 0;
4739
- useEffect10(() => {
5258
+ useEffect11(() => {
4740
5259
  if (!hasInitializedRef.current && hasAmount && !loading && !topupError && !clientSecret) {
4741
5260
  hasInitializedRef.current = true;
4742
5261
  startTopup().catch((error) => {
@@ -4747,14 +5266,30 @@ var Root4 = forwardRef7(function TopupFormRoot(props, forwardedRef) {
4747
5266
  if (hasAmount && clientSecret) hasInitializedRef.current = true;
4748
5267
  }, [hasAmount, loading, topupError, clientSecret, startTopup]);
4749
5268
  const finalReturnUrl = returnUrl || (typeof window !== "undefined" ? window.location.href : "/");
4750
- const elementsOptions = useMemo9(() => {
5269
+ const [rootEl, setRootEl] = useState13(null);
5270
+ const attachRoot = useCallback14((node) => {
5271
+ if (!node) return;
5272
+ setRootEl((prev) => prev === node ? prev : node);
5273
+ }, []);
5274
+ const resolvedAppearance = useStripeAppearance(rootEl, appearance);
5275
+ const elementsOptions = useMemo10(() => {
4751
5276
  if (!clientSecret) return void 0;
4752
- return { clientSecret, locale: toStripeElementLocale(locale) };
4753
- }, [clientSecret, locale]);
4754
- const Comp = asChild ? Slot : "section";
5277
+ return {
5278
+ clientSecret,
5279
+ locale: toStripeElementLocale(locale),
5280
+ ...resolvedAppearance ? { appearance: resolvedAppearance } : {}
5281
+ };
5282
+ }, [clientSecret, locale, resolvedAppearance]);
4755
5283
  const outerError = !hasAmount ? copy.errors.configMissingAmount : topupError ? `${copy.errors.topupInitFailed} ${topupError.message || copy.errors.unknownError}` : null;
4756
5284
  const dataState = outerError ? "error" : clientSecret && stripePromise ? "ready" : "loading";
4757
- const canMountElements = !!(stripePromise && clientSecret && elementsOptions);
5285
+ const appearanceReady = appearance !== void 0 || rootEl !== null;
5286
+ const canMountElements = !!(stripePromise && clientSecret && elementsOptions && appearanceReady);
5287
+ const businessAttach = useBusinessDetailsAttach({
5288
+ processorPaymentId,
5289
+ attachBusinessDetails,
5290
+ customerRef,
5291
+ onTaxChange
5292
+ });
4758
5293
  const innerCommon = {
4759
5294
  amount,
4760
5295
  currency,
@@ -4765,26 +5300,38 @@ var Root4 = forwardRef7(function TopupFormRoot(props, forwardedRef) {
4765
5300
  state: dataState,
4766
5301
  onSuccess,
4767
5302
  onError,
4768
- onTaxChange,
4769
5303
  processTopupPayment,
4770
- attachBusinessDetails,
4771
- customerRef
5304
+ businessAttach
4772
5305
  };
4773
- const shell = /* @__PURE__ */ jsx14(
4774
- Comp,
5306
+ const rootRef = composeRefs(forwardedRef, attachRoot);
5307
+ if (asChild) {
5308
+ const slotted = /* @__PURE__ */ jsx15(
5309
+ Slot,
5310
+ {
5311
+ ref: rootRef,
5312
+ className,
5313
+ "data-solvapay-topup-form": "",
5314
+ "data-state": dataState,
5315
+ ...rest,
5316
+ children
5317
+ }
5318
+ );
5319
+ if (canMountElements) {
5320
+ return /* @__PURE__ */ jsx15(Elements2, { stripe: stripePromise, options: elementsOptions, children: /* @__PURE__ */ jsx15(Inner, { ...innerCommon, children: slotted }) }, clientSecret);
5321
+ }
5322
+ return /* @__PURE__ */ jsx15(OfflineInner, { ...innerCommon, children: slotted });
5323
+ }
5324
+ return /* @__PURE__ */ jsx15(
5325
+ "section",
4775
5326
  {
4776
- ref: forwardedRef,
5327
+ ref: rootRef,
4777
5328
  className,
4778
5329
  "data-solvapay-topup-form": "",
4779
5330
  "data-state": dataState,
4780
5331
  ...rest,
4781
- children
5332
+ children: canMountElements ? /* @__PURE__ */ jsx15(Elements2, { stripe: stripePromise, options: elementsOptions, children: /* @__PURE__ */ jsx15(Inner, { ...innerCommon, children }) }, clientSecret) : /* @__PURE__ */ jsx15(OfflineInner, { ...innerCommon, children })
4782
5333
  }
4783
5334
  );
4784
- if (canMountElements) {
4785
- return /* @__PURE__ */ jsx14(Elements2, { stripe: stripePromise, options: elementsOptions, children: /* @__PURE__ */ jsx14(Inner, { ...innerCommon, children: shell }) }, clientSecret);
4786
- }
4787
- return /* @__PURE__ */ jsx14(OfflineInner, { ...innerCommon, children: shell });
4788
5335
  });
4789
5336
  var Inner = ({
4790
5337
  amount,
@@ -4796,21 +5343,28 @@ var Inner = ({
4796
5343
  state,
4797
5344
  onSuccess,
4798
5345
  onError,
4799
- onTaxChange,
4800
5346
  processTopupPayment,
4801
- attachBusinessDetails,
4802
- customerRef,
5347
+ businessAttach,
4803
5348
  children
4804
5349
  }) => {
4805
5350
  const stripe = useStripe2();
5351
+ const stripeAvailable = !!stripe;
5352
+ const stripeRef = useRef7(stripe);
4806
5353
  const elements = useElements2();
4807
5354
  const copy = useCopy();
4808
- const [paymentInputComplete, setPaymentInputComplete] = useState12(false);
4809
- const [isProcessing, setIsProcessing] = useState12(false);
4810
- const [error, setError] = useState12(null);
5355
+ const customer = useCustomer();
5356
+ const [paymentInputComplete, setPaymentInputComplete] = useState13(false);
5357
+ const [isProcessing, setIsProcessing] = useState13(false);
5358
+ const [error, setError] = useState13(null);
4811
5359
  const returnResumeStarted = useRef7(false);
4812
- useEffect10(() => {
4813
- if (!stripe || returnResumeStarted.current || typeof window === "undefined") return;
5360
+ useEffect11(() => {
5361
+ stripeRef.current = stripe;
5362
+ });
5363
+ useEffect11(() => {
5364
+ const stripeApi = stripeRef.current;
5365
+ if (!stripeAvailable || !stripeApi || returnResumeStarted.current || typeof window === "undefined") {
5366
+ return;
5367
+ }
4814
5368
  const returnClientSecret = readPaymentIntentClientSecret(window.location.search);
4815
5369
  if (!returnClientSecret) return;
4816
5370
  returnResumeStarted.current = true;
@@ -4818,7 +5372,7 @@ var Inner = ({
4818
5372
  void (async () => {
4819
5373
  setIsProcessing(true);
4820
5374
  setError(null);
4821
- const retrieved = await stripe.retrievePaymentIntent(returnClientSecret);
5375
+ const retrieved = await stripeApi.retrievePaymentIntent(returnClientSecret);
4822
5376
  if (cancelled) return;
4823
5377
  stripPaymentIntentParams();
4824
5378
  if (retrieved.error || !retrieved.paymentIntent) {
@@ -4828,7 +5382,7 @@ var Inner = ({
4828
5382
  }
4829
5383
  let paymentIntent = retrieved.paymentIntent;
4830
5384
  if (paymentIntent.status === "requires_action") {
4831
- const actionResult = await stripe.handleNextAction({ clientSecret: returnClientSecret });
5385
+ const actionResult = await stripeApi.handleNextAction({ clientSecret: returnClientSecret });
4832
5386
  if (cancelled) return;
4833
5387
  if (actionResult.error || !actionResult.paymentIntent) {
4834
5388
  setError(copy.errors.paymentRequires3ds);
@@ -4880,7 +5434,7 @@ var Inner = ({
4880
5434
  return () => {
4881
5435
  cancelled = true;
4882
5436
  };
4883
- }, [stripe, copy, processTopupPayment, onSuccess, onError]);
5437
+ }, [stripeAvailable, copy, processTopupPayment, onSuccess, onError]);
4884
5438
  const {
4885
5439
  businessDetails,
4886
5440
  setBusinessDetails,
@@ -4891,15 +5445,10 @@ var Inner = ({
4891
5445
  businessDetailsError,
4892
5446
  requiresBusinessAttach,
4893
5447
  runAttach
4894
- } = useBusinessDetailsAttach({
4895
- processorPaymentId,
4896
- attachBusinessDetails,
4897
- customerRef,
4898
- onTaxChange
4899
- });
5448
+ } = businessAttach;
4900
5449
  const isReady = !!(stripe && elements);
4901
- const canSubmit = isReady && paymentInputComplete && !isProcessing && !!clientSecret && (!requiresBusinessAttach || businessDetailsAttached) && !businessDetailsAttaching;
4902
- const submit = useCallback13(async () => {
5450
+ const canSubmit = isReady && paymentInputComplete && !isProcessing && !!clientSecret && (!requiresBusinessAttach || businessDetailsAttached) && isCustomerAddressComplete3(businessDetails) && !businessDetailsAttaching;
5451
+ const submit = useCallback14(async () => {
4903
5452
  if (!stripe || !elements || !clientSecret) {
4904
5453
  const msg = copy.errors.stripeUnavailable;
4905
5454
  setError(msg);
@@ -4917,44 +5466,51 @@ var Inner = ({
4917
5466
  }
4918
5467
  setError(null);
4919
5468
  setIsProcessing(true);
4920
- const { error: submitError } = await elements.submit();
4921
- if (submitError) {
4922
- const msg = submitError.message || copy.errors.paymentUnexpected;
4923
- setError(msg);
4924
- setIsProcessing(false);
4925
- onError?.(new Error(msg));
4926
- return;
4927
- }
4928
- const { error: confirmError, paymentIntent } = await stripe.confirmPayment({
5469
+ const result = await confirmPayment({
5470
+ stripe,
4929
5471
  elements,
4930
5472
  clientSecret,
4931
- confirmParams: { return_url: returnUrl },
4932
- redirect: "if_required"
5473
+ returnUrl,
5474
+ billingDetails: buildConfirmBillingDetails({
5475
+ name: customer.name,
5476
+ email: customer.email,
5477
+ country: resolveBuyerCountry3(businessDetails),
5478
+ state: businessDetails.customerState,
5479
+ postalCode: businessDetails.customerPostalCode
5480
+ }),
5481
+ copy
4933
5482
  });
4934
- if (confirmError) {
4935
- const msg = confirmError.message || copy.errors.paymentUnexpected;
4936
- setError(msg);
5483
+ if (result.status === "error" || result.status === "requires_action" || result.status === "other") {
5484
+ setError(result.message);
4937
5485
  setIsProcessing(false);
4938
- onError?.(new Error(msg));
5486
+ if (result.status === "error") {
5487
+ onError?.(new Error(result.message));
5488
+ }
4939
5489
  return;
4940
5490
  }
5491
+ if (result.status === "pending") {
5492
+ setError(result.message);
5493
+ setIsProcessing(false);
5494
+ return;
5495
+ }
5496
+ const paymentIntent = result.paymentIntent;
4941
5497
  let creditsAdded;
4942
5498
  if (paymentIntent && processTopupPayment) {
4943
5499
  try {
4944
- const result = await processTopupPayment({ paymentIntentId: paymentIntent.id });
4945
- if (result.status === "processing") {
5500
+ const result2 = await processTopupPayment({ paymentIntentId: paymentIntent.id });
5501
+ if (result2.status === "processing") {
4946
5502
  setError(copy.errors.paymentPending);
4947
5503
  setIsProcessing(false);
4948
5504
  return;
4949
5505
  }
4950
- if (result.status === "failed" || result.status === "cancelled") {
5506
+ if (result2.status === "failed" || result2.status === "cancelled") {
4951
5507
  setError(copy.errors.paymentUnexpected);
4952
5508
  setIsProcessing(false);
4953
- onError?.(new Error(`Topup ${result.status}`));
5509
+ onError?.(new Error(`Topup ${result2.status}`));
4954
5510
  return;
4955
5511
  }
4956
- if (result.status === "succeeded" && typeof result.creditsAdded === "number") {
4957
- creditsAdded = result.creditsAdded;
5512
+ if (result2.status === "succeeded" && typeof result2.creditsAdded === "number") {
5513
+ creditsAdded = result2.creditsAdded;
4958
5514
  }
4959
5515
  } catch (err) {
4960
5516
  const msg = err instanceof Error ? err.message : String(err);
@@ -4976,6 +5532,7 @@ var Inner = ({
4976
5532
  elements,
4977
5533
  clientSecret,
4978
5534
  returnUrl,
5535
+ customer,
4979
5536
  copy,
4980
5537
  onSuccess,
4981
5538
  onError,
@@ -4987,7 +5544,7 @@ var Inner = ({
4987
5544
  businessDetailsError
4988
5545
  ]);
4989
5546
  const effectiveError = error ?? outerError ?? businessDetailsError;
4990
- const ctx = useMemo9(
5547
+ const ctx = useMemo10(
4991
5548
  () => ({
4992
5549
  amount,
4993
5550
  currency,
@@ -5036,7 +5593,7 @@ var Inner = ({
5036
5593
  submit
5037
5594
  ]
5038
5595
  );
5039
- return /* @__PURE__ */ jsx14(TopupFormContext.Provider, { value: ctx, children });
5596
+ return /* @__PURE__ */ jsx15(TopupFormContext.Provider, { value: ctx, children });
5040
5597
  };
5041
5598
  var OfflineInner = ({
5042
5599
  amount,
@@ -5046,15 +5603,14 @@ var OfflineInner = ({
5046
5603
  returnUrl,
5047
5604
  outerError,
5048
5605
  state,
5606
+ businessAttach,
5049
5607
  children
5050
5608
  }) => {
5051
- const noopSubmit = useCallback13(async () => {
5609
+ const noopSubmit = useCallback14(async () => {
5052
5610
  }, []);
5053
- const noopSet = useCallback13(() => {
5611
+ const noopSet = useCallback14(() => {
5054
5612
  }, []);
5055
- const noopBusinessSet = useCallback13(() => {
5056
- }, []);
5057
- const ctx = useMemo9(
5613
+ const ctx = useMemo10(
5058
5614
  () => ({
5059
5615
  amount,
5060
5616
  currency,
@@ -5069,13 +5625,13 @@ var OfflineInner = ({
5069
5625
  canSubmit: false,
5070
5626
  error: outerError,
5071
5627
  returnUrl,
5072
- businessDetails: defaultBusinessDetails,
5073
- taxBreakdown: null,
5074
- businessDetailsAttached: false,
5075
- businessDetailsAttaching: false,
5076
- businessDetailsError: null,
5077
- fieldErrors: {},
5078
- setBusinessDetails: noopBusinessSet,
5628
+ businessDetails: businessAttach.businessDetails,
5629
+ taxBreakdown: businessAttach.taxBreakdown,
5630
+ businessDetailsAttached: businessAttach.businessDetailsAttached,
5631
+ businessDetailsAttaching: businessAttach.businessDetailsAttaching,
5632
+ businessDetailsError: businessAttach.businessDetailsError,
5633
+ fieldErrors: businessAttach.fieldErrors,
5634
+ setBusinessDetails: businessAttach.setBusinessDetails,
5079
5635
  setPaymentInputComplete: noopSet,
5080
5636
  submit: noopSubmit
5081
5637
  }),
@@ -5087,21 +5643,21 @@ var OfflineInner = ({
5087
5643
  processorPaymentId,
5088
5644
  outerError,
5089
5645
  returnUrl,
5646
+ businessAttach,
5090
5647
  noopSet,
5091
- noopSubmit,
5092
- noopBusinessSet
5648
+ noopSubmit
5093
5649
  ]
5094
5650
  );
5095
- return /* @__PURE__ */ jsx14(TopupFormContext.Provider, { value: ctx, children });
5651
+ return /* @__PURE__ */ jsx15(TopupFormContext.Provider, { value: ctx, children });
5096
5652
  };
5097
5653
  var PaymentElementSlot2 = ({ options }) => {
5098
5654
  const { setPaymentInputComplete, isReady, stripe, elements } = useTopupCtx("PaymentElement");
5099
5655
  const locale = useLocale();
5100
5656
  if (!stripe || !elements) return null;
5101
5657
  if (!isReady) {
5102
- return /* @__PURE__ */ jsx14("output", { "data-solvapay-topup-form-loading": "", children: /* @__PURE__ */ jsx14(Spinner, { size: "sm" }) });
5658
+ return /* @__PURE__ */ jsx15("output", { "data-solvapay-topup-form-loading": "", children: /* @__PURE__ */ jsx15(Spinner, { size: "sm" }) });
5103
5659
  }
5104
- return /* @__PURE__ */ jsx14("section", { "data-solvapay-topup-form-payment-element": "", children: /* @__PURE__ */ jsx14(
5660
+ return /* @__PURE__ */ jsx15("section", { "data-solvapay-topup-form-payment-element": "", children: /* @__PURE__ */ jsx15(
5105
5661
  StripePaymentElement2,
5106
5662
  {
5107
5663
  options: withPaymentElementDefaults(options),
@@ -5116,8 +5672,8 @@ var SubmitButton2 = forwardRef7(
5116
5672
  const copy = useCopy();
5117
5673
  const dataState = ctx.isProcessing ? "processing" : !ctx.canSubmit ? "disabled" : "idle";
5118
5674
  const processingContent = /* @__PURE__ */ jsxs7(Fragment7, { children: [
5119
- /* @__PURE__ */ jsx14(Spinner, { size: "sm" }),
5120
- /* @__PURE__ */ jsx14("span", { children: copy.cta.processing })
5675
+ /* @__PURE__ */ jsx15(Spinner, { size: "sm" }),
5676
+ /* @__PURE__ */ jsx15("span", { children: copy.cta.processing })
5121
5677
  ] });
5122
5678
  const commonProps = {
5123
5679
  "data-solvapay-topup-form-submit": "",
@@ -5132,38 +5688,38 @@ var SubmitButton2 = forwardRef7(
5132
5688
  ...rest
5133
5689
  };
5134
5690
  if (asChild) {
5135
- const slotChild = ctx.isProcessing && React12.isValidElement(children) ? React12.cloneElement(
5691
+ const slotChild = ctx.isProcessing && React13.isValidElement(children) ? React13.cloneElement(
5136
5692
  children,
5137
5693
  void 0,
5138
5694
  processingContent
5139
5695
  ) : children;
5140
- return /* @__PURE__ */ jsx14(Slot, { ref: forwardedRef, ...commonProps, children: slotChild });
5696
+ return /* @__PURE__ */ jsx15(Slot, { ref: forwardedRef, ...commonProps, children: slotChild });
5141
5697
  }
5142
5698
  const content = ctx.isProcessing ? processingContent : children ? children : copy.cta.topUp;
5143
- return /* @__PURE__ */ jsx14("button", { ref: forwardedRef, type: "submit", ...commonProps, children: content });
5699
+ return /* @__PURE__ */ jsx15("button", { ref: forwardedRef, type: "submit", ...commonProps, children: content });
5144
5700
  }
5145
5701
  );
5146
5702
  var Loading2 = forwardRef7(function TopupFormLoading({ asChild, children, ...rest }, forwardedRef) {
5147
5703
  const ctx = useTopupCtx("Loading");
5148
5704
  if (ctx.state !== "loading") return null;
5149
5705
  if (asChild) {
5150
- return /* @__PURE__ */ jsx14(
5706
+ return /* @__PURE__ */ jsx15(
5151
5707
  Slot,
5152
5708
  {
5153
5709
  ref: forwardedRef,
5154
5710
  "data-solvapay-topup-form-loading": "",
5155
5711
  ...rest,
5156
- children: children ?? /* @__PURE__ */ jsx14(Spinner, { size: "sm" })
5712
+ children: children ?? /* @__PURE__ */ jsx15(Spinner, { size: "sm" })
5157
5713
  }
5158
5714
  );
5159
5715
  }
5160
- return /* @__PURE__ */ jsx14("output", { ref: forwardedRef, "data-solvapay-topup-form-loading": "", ...rest, children: children ?? /* @__PURE__ */ jsx14(Spinner, { size: "sm" }) });
5716
+ return /* @__PURE__ */ jsx15("output", { ref: forwardedRef, "data-solvapay-topup-form-loading": "", ...rest, children: children ?? /* @__PURE__ */ jsx15(Spinner, { size: "sm" }) });
5161
5717
  });
5162
5718
  var ErrorSlot2 = forwardRef7(function TopupFormError({ asChild, children, ...rest }, forwardedRef) {
5163
5719
  const ctx = useTopupCtx("Error");
5164
5720
  if (!ctx.error) return null;
5165
5721
  if (asChild) {
5166
- return /* @__PURE__ */ jsx14(
5722
+ return /* @__PURE__ */ jsx15(
5167
5723
  Slot,
5168
5724
  {
5169
5725
  ref: forwardedRef,
@@ -5176,7 +5732,7 @@ var ErrorSlot2 = forwardRef7(function TopupFormError({ asChild, children, ...res
5176
5732
  }
5177
5733
  );
5178
5734
  }
5179
- return /* @__PURE__ */ jsx14(
5735
+ return /* @__PURE__ */ jsx15(
5180
5736
  "p",
5181
5737
  {
5182
5738
  ref: forwardedRef,
@@ -5233,11 +5789,11 @@ function useTopupForm() {
5233
5789
  }
5234
5790
 
5235
5791
  // src/primitives/BalanceBadge.tsx
5236
- import { forwardRef as forwardRef8, useContext as useContext11 } from "react";
5792
+ import { forwardRef as forwardRef8, useContext as useContext12 } from "react";
5237
5793
  import { creditsToDisplayMinorUnits, minorUnitsPerMajor } from "@solvapay/core";
5238
- import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
5794
+ import { Fragment as Fragment8, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
5239
5795
  var BalanceBadge = forwardRef8(function BalanceBadge2({ asChild, numberOnly, lowThreshold = 10, children, ...rest }, forwardedRef) {
5240
- const solva = useContext11(SolvaPayContext);
5796
+ const solva = useContext12(SolvaPayContext);
5241
5797
  if (!solva) throw new MissingProviderError("BalanceBadge");
5242
5798
  const { credits, displayCurrency, creditsPerMinorUnit, displayExchangeRate, loading } = useBalance();
5243
5799
  const copy = useCopy();
@@ -5254,10 +5810,10 @@ var BalanceBadge = forwardRef8(function BalanceBadge2({ asChild, numberOnly, low
5254
5810
  if (asChild) {
5255
5811
  return (
5256
5812
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5257
- /* @__PURE__ */ jsx15(Slot, { ref: forwardedRef, ...commonProps, children: children ?? /* @__PURE__ */ jsx15(Fragment8, {}) })
5813
+ /* @__PURE__ */ jsx16(Slot, { ref: forwardedRef, ...commonProps, children: children ?? /* @__PURE__ */ jsx16(Fragment8, {}) })
5258
5814
  );
5259
5815
  }
5260
- return /* @__PURE__ */ jsx15("span", { ref: forwardedRef, ...commonProps });
5816
+ return /* @__PURE__ */ jsx16("span", { ref: forwardedRef, ...commonProps });
5261
5817
  }
5262
5818
  if (credits == null) return null;
5263
5819
  const formattedCredits = new Intl.NumberFormat(locale).format(credits);
@@ -5287,22 +5843,22 @@ var BalanceBadge = forwardRef8(function BalanceBadge2({ asChild, numberOnly, low
5287
5843
  if (asChild) {
5288
5844
  return (
5289
5845
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5290
- /* @__PURE__ */ jsx15(Slot, { ref: forwardedRef, ...commonProps, children: content })
5846
+ /* @__PURE__ */ jsx16(Slot, { ref: forwardedRef, ...commonProps, children: content })
5291
5847
  );
5292
5848
  }
5293
- return /* @__PURE__ */ jsx15("span", { ref: forwardedRef, ...commonProps, children: content });
5849
+ return /* @__PURE__ */ jsx16("span", { ref: forwardedRef, ...commonProps, children: content });
5294
5850
  });
5295
5851
 
5296
5852
  // src/primitives/PlanSelector.tsx
5297
5853
  import {
5298
- createContext as createContext8,
5854
+ createContext as createContext9,
5299
5855
  forwardRef as forwardRef9,
5300
- useCallback as useCallback14,
5301
- useContext as useContext12,
5302
- useEffect as useEffect11,
5303
- useMemo as useMemo10,
5856
+ useCallback as useCallback15,
5857
+ useContext as useContext13,
5858
+ useEffect as useEffect12,
5859
+ useMemo as useMemo11,
5304
5860
  useRef as useRef8,
5305
- useState as useState13
5861
+ useState as useState14
5306
5862
  } from "react";
5307
5863
 
5308
5864
  // src/utils/isPayg.ts
@@ -5343,18 +5899,18 @@ function resolvePlanPricingOption(plan, currency) {
5343
5899
  }
5344
5900
 
5345
5901
  // src/primitives/PlanSelector.tsx
5346
- import { jsx as jsx16 } from "react/jsx-runtime";
5347
- var PlanSelectorContext = createContext8(null);
5902
+ import { jsx as jsx17 } from "react/jsx-runtime";
5903
+ var PlanSelectorContext = createContext9(null);
5348
5904
  function usePlanSelectorContext(part) {
5349
- const ctx = useContext12(PlanSelectorContext);
5905
+ const ctx = useContext13(PlanSelectorContext);
5350
5906
  if (!ctx) {
5351
5907
  throw new Error(`PlanSelector.${part} must be rendered inside <PlanSelector.Root>.`);
5352
5908
  }
5353
5909
  return ctx;
5354
5910
  }
5355
- var CardContext = createContext8(null);
5911
+ var CardContext = createContext9(null);
5356
5912
  function useCardContext(part) {
5357
- const ctx = useContext12(CardContext);
5913
+ const ctx = useContext13(CardContext);
5358
5914
  if (!ctx) {
5359
5915
  throw new Error(`PlanSelector.${part} must be rendered inside <PlanSelector.Card>.`);
5360
5916
  }
@@ -5374,12 +5930,12 @@ var Root5 = forwardRef9(function PlanSelectorRoot(props, forwardedRef) {
5374
5930
  children,
5375
5931
  ...rest
5376
5932
  } = props;
5377
- const solva = useContext12(SolvaPayContext);
5933
+ const solva = useContext13(SolvaPayContext);
5378
5934
  if (!solva) throw new MissingProviderError("PlanSelector");
5379
5935
  if (!productRef) throw new MissingProductRefError("PlanSelector");
5380
5936
  const { _config } = solva;
5381
5937
  const { purchases } = usePurchase();
5382
- const effectiveFetcher = useMemo10(
5938
+ const effectiveFetcher = useMemo11(
5383
5939
  () => fetcher ?? ((ref) => defaultListPlans(ref, _config)),
5384
5940
  [fetcher, _config]
5385
5941
  );
@@ -5391,24 +5947,24 @@ var Root5 = forwardRef9(function PlanSelectorRoot(props, forwardedRef) {
5391
5947
  autoSelectFirstPaid,
5392
5948
  initialPlanRef
5393
5949
  });
5394
- const autoCurrentPlanRef = useMemo10(() => {
5950
+ const autoCurrentPlanRef = useMemo11(() => {
5395
5951
  const active = purchases.filter((p) => p.status === "active").sort((a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime())[0];
5396
5952
  return active?.planSnapshot?.reference ?? null;
5397
5953
  }, [purchases]);
5398
5954
  const resolvedCurrentPlanRef = currentPlanRef === null ? null : currentPlanRef ?? autoCurrentPlanRef;
5399
- const isCurrent = useCallback14(
5955
+ const isCurrent = useCallback15(
5400
5956
  (ref) => resolvedCurrentPlanRef === ref,
5401
5957
  [resolvedCurrentPlanRef]
5402
5958
  );
5403
- const isFree = useCallback14(
5959
+ const isFree = useCallback15(
5404
5960
  (ref) => plans.find((p) => p.reference === ref)?.requiresPayment === false,
5405
5961
  [plans]
5406
5962
  );
5407
- const isPopular = useCallback14(
5963
+ const isPopular = useCallback15(
5408
5964
  (ref) => popularPlanRef === ref,
5409
5965
  [popularPlanRef]
5410
5966
  );
5411
- const select = useCallback14(
5967
+ const select = useCallback15(
5412
5968
  (ref) => {
5413
5969
  const plan = plans.find((p) => p.reference === ref);
5414
5970
  if (!plan) return;
@@ -5418,29 +5974,29 @@ var Root5 = forwardRef9(function PlanSelectorRoot(props, forwardedRef) {
5418
5974
  },
5419
5975
  [plans, selectPlan, onSelect]
5420
5976
  );
5421
- const clearSelection = useCallback14(() => {
5977
+ const clearSelection = useCallback15(() => {
5422
5978
  setSelectedPlanIndex(-1);
5423
5979
  }, [setSelectedPlanIndex]);
5424
5980
  const selectedPlanRef = selectedPlan?.reference ?? null;
5425
- const [preferredCurrency, setPreferredCurrencyState] = useState13(null);
5426
- const [selectedCurrencies, setSelectedCurrencies] = useState13({});
5427
- const getSelectedOption = useCallback14(
5981
+ const [preferredCurrency, setPreferredCurrencyState] = useState14(null);
5982
+ const [selectedCurrencies, setSelectedCurrencies] = useState14({});
5983
+ const getSelectedOption = useCallback15(
5428
5984
  (plan) => resolvePlanPricingOption(
5429
5985
  plan,
5430
5986
  selectedCurrencies[plan.reference] ?? preferredCurrency ?? null
5431
5987
  ),
5432
5988
  [selectedCurrencies, preferredCurrency]
5433
5989
  );
5434
- const setPreferredCurrency = useCallback14((currency) => {
5990
+ const setPreferredCurrency = useCallback15((currency) => {
5435
5991
  setPreferredCurrencyState(currency.toUpperCase());
5436
5992
  setSelectedCurrencies({});
5437
5993
  }, []);
5438
- const setPlanCurrency = useCallback14((planRef, currency) => {
5994
+ const setPlanCurrency = useCallback15((planRef, currency) => {
5439
5995
  setSelectedCurrencies((current) => ({ ...current, [planRef]: currency }));
5440
5996
  }, []);
5441
5997
  const selectedCurrency = selectedPlan ? getSelectedOption(selectedPlan).currency : null;
5442
5998
  const autoCurrentAppliedKeyRef = useRef8(null);
5443
- useEffect11(() => {
5999
+ useEffect12(() => {
5444
6000
  if (autoCurrentAppliedKeyRef.current === productRef) return;
5445
6001
  if (selectedPlanRef) {
5446
6002
  autoCurrentAppliedKeyRef.current = productRef;
@@ -5455,7 +6011,7 @@ var Root5 = forwardRef9(function PlanSelectorRoot(props, forwardedRef) {
5455
6011
  autoCurrentAppliedKeyRef.current = productRef;
5456
6012
  select(resolvedCurrentPlanRef);
5457
6013
  }, [productRef, plans, resolvedCurrentPlanRef, selectedPlanRef, select]);
5458
- const ctx = useMemo10(
6014
+ const ctx = useMemo11(
5459
6015
  () => ({
5460
6016
  plans,
5461
6017
  loading,
@@ -5495,7 +6051,7 @@ var Root5 = forwardRef9(function PlanSelectorRoot(props, forwardedRef) {
5495
6051
  getSelectedOption
5496
6052
  ]
5497
6053
  );
5498
- return /* @__PURE__ */ jsx16(
6054
+ return /* @__PURE__ */ jsx17(
5499
6055
  PlanSelectionProvider,
5500
6056
  {
5501
6057
  value: {
@@ -5514,7 +6070,7 @@ var Root5 = forwardRef9(function PlanSelectorRoot(props, forwardedRef) {
5514
6070
  loading,
5515
6071
  error
5516
6072
  },
5517
- children: /* @__PURE__ */ jsx16("div", { ref: forwardedRef, "data-solvapay-plan-selector": "", ...rest, children: /* @__PURE__ */ jsx16(PlanSelectorContext.Provider, { value: ctx, children }) })
6073
+ children: /* @__PURE__ */ jsx17("div", { ref: forwardedRef, "data-solvapay-plan-selector": "", ...rest, children: /* @__PURE__ */ jsx17(PlanSelectorContext.Provider, { value: ctx, children }) })
5518
6074
  }
5519
6075
  );
5520
6076
  });
@@ -5522,11 +6078,11 @@ var Heading = forwardRef9(function PlanSelectorHeading({ asChild, children, ...r
5522
6078
  usePlanSelectorContext("Heading");
5523
6079
  const copy = useCopy();
5524
6080
  const Comp = asChild ? Slot : "h3";
5525
- return /* @__PURE__ */ jsx16(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-heading": "", ...rest, children: children ?? copy.planSelector.heading });
6081
+ return /* @__PURE__ */ jsx17(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-heading": "", ...rest, children: children ?? copy.planSelector.heading });
5526
6082
  });
5527
6083
  var Grid = forwardRef9(function PlanSelectorGrid({ children, ...rest }, forwardedRef) {
5528
6084
  const ctx = usePlanSelectorContext("Grid");
5529
- return /* @__PURE__ */ jsx16("div", { ref: forwardedRef, "data-solvapay-plan-selector-grid": "", ...rest, children: ctx.plans.map((plan) => {
6085
+ return /* @__PURE__ */ jsx17("div", { ref: forwardedRef, "data-solvapay-plan-selector-grid": "", ...rest, children: ctx.plans.map((plan) => {
5530
6086
  const isCurrent = ctx.isCurrent(plan.reference);
5531
6087
  const isFree = ctx.isFree(plan.reference);
5532
6088
  const isPopular = ctx.isPopular(plan.reference);
@@ -5548,7 +6104,7 @@ var Grid = forwardRef9(function PlanSelectorGrid({ children, ...rest }, forwarde
5548
6104
  select: () => ctx.select(plan.reference),
5549
6105
  setCurrency: (currency) => ctx.setPlanCurrency(plan.reference, currency)
5550
6106
  };
5551
- return /* @__PURE__ */ jsx16(CardContext.Provider, { value: cardCtx, children }, plan.reference);
6107
+ return /* @__PURE__ */ jsx17(CardContext.Provider, { value: cardCtx, children }, plan.reference);
5552
6108
  }) });
5553
6109
  });
5554
6110
  var Card = forwardRef9(function PlanSelectorCard({ asChild, onClick, children, ...rest }, forwardedRef) {
@@ -5569,10 +6125,10 @@ var Card = forwardRef9(function PlanSelectorCard({ asChild, onClick, children, .
5569
6125
  if (asChild) {
5570
6126
  return (
5571
6127
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5572
- /* @__PURE__ */ jsx16(Slot, { ref: forwardedRef, ...commonProps, children })
6128
+ /* @__PURE__ */ jsx17(Slot, { ref: forwardedRef, ...commonProps, children })
5573
6129
  );
5574
6130
  }
5575
- return /* @__PURE__ */ jsx16(
6131
+ return /* @__PURE__ */ jsx17(
5576
6132
  "button",
5577
6133
  {
5578
6134
  ref: forwardedRef,
@@ -5589,13 +6145,13 @@ var CardName = forwardRef9(function PlanSelectorCardName({ asChild, children, ..
5589
6145
  const label = card.plan.name ?? fallback;
5590
6146
  if (!label && children == null) return null;
5591
6147
  const Comp = asChild ? Slot : "span";
5592
- return /* @__PURE__ */ jsx16(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-card-name": "", ...rest, children: children ?? label });
6148
+ return /* @__PURE__ */ jsx17(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-card-name": "", ...rest, children: children ?? label });
5593
6149
  });
5594
6150
  var CardPrice = forwardRef9(function PlanSelectorCardPrice({ asChild, children, ...rest }, forwardedRef) {
5595
6151
  const card = useCardContext("CardPrice");
5596
6152
  const locale = useLocale();
5597
6153
  const copy = useCopy();
5598
- const formatted = useMemo10(() => {
6154
+ const formatted = useMemo11(() => {
5599
6155
  if (card.isFree) return copy.planSelector.freeBadge;
5600
6156
  if (card.plan.type === "usage-based") {
5601
6157
  return copy.planSelector.usageRateLabel;
@@ -5615,7 +6171,7 @@ var CardPrice = forwardRef9(function PlanSelectorCardPrice({ asChild, children,
5615
6171
  copy.interval.free
5616
6172
  ]);
5617
6173
  const Comp = asChild ? Slot : "span";
5618
- return /* @__PURE__ */ jsx16(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-card-price": "", ...rest, children: children ?? formatted });
6174
+ return /* @__PURE__ */ jsx17(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-card-price": "", ...rest, children: children ?? formatted });
5619
6175
  });
5620
6176
  var CardInterval = forwardRef9(function PlanSelectorCardInterval({ asChild, children, ...rest }, forwardedRef) {
5621
6177
  const card = useCardContext("CardInterval");
@@ -5625,7 +6181,7 @@ var CardInterval = forwardRef9(function PlanSelectorCardInterval({ asChild, chil
5625
6181
  if (!rawInterval) return null;
5626
6182
  const text = interpolate(copy.planSelector.perIntervalShort, { interval: rawInterval });
5627
6183
  const Comp = asChild ? Slot : "span";
5628
- return /* @__PURE__ */ jsx16(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-card-interval": "", ...rest, children: children ?? text });
6184
+ return /* @__PURE__ */ jsx17(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-card-interval": "", ...rest, children: children ?? text });
5629
6185
  });
5630
6186
  function normalizeBillingCycle(cycle) {
5631
6187
  if (!cycle) return null;
@@ -5638,7 +6194,7 @@ function normalizeBillingCycle(cycle) {
5638
6194
  }
5639
6195
  var CurrencySwitcher = forwardRef9(function PlanSelectorCurrencySwitcher({ children, onChange, className, ...rest }, forwardedRef) {
5640
6196
  const ctx = usePlanSelectorContext("CurrencySwitcher");
5641
- const availableCurrencies = useMemo10(() => {
6197
+ const availableCurrencies = useMemo11(() => {
5642
6198
  const currencies = /* @__PURE__ */ new Set();
5643
6199
  for (const plan of ctx.plans) {
5644
6200
  const options = getPlanPricingOptions(plan);
@@ -5651,7 +6207,7 @@ var CurrencySwitcher = forwardRef9(function PlanSelectorCurrencySwitcher({ child
5651
6207
  }, [ctx.plans]);
5652
6208
  if (availableCurrencies.length < 2) return null;
5653
6209
  const effectiveCurrency = ctx.preferredCurrency ?? (ctx.selectedPlan ? ctx.getSelectedOption(ctx.selectedPlan).currency.toUpperCase() : null) ?? availableCurrencies[0];
5654
- return /* @__PURE__ */ jsx16(
6210
+ return /* @__PURE__ */ jsx17(
5655
6211
  "select",
5656
6212
  {
5657
6213
  ref: forwardedRef,
@@ -5663,7 +6219,7 @@ var CurrencySwitcher = forwardRef9(function PlanSelectorCurrencySwitcher({ child
5663
6219
  onChange?.(event);
5664
6220
  },
5665
6221
  ...rest,
5666
- children: children ?? availableCurrencies.map((currency) => /* @__PURE__ */ jsx16("option", { value: currency, children: currency }, currency))
6222
+ children: children ?? availableCurrencies.map((currency) => /* @__PURE__ */ jsx17("option", { value: currency, children: currency }, currency))
5667
6223
  }
5668
6224
  );
5669
6225
  });
@@ -5671,7 +6227,7 @@ var CardCurrency = forwardRef9(
5671
6227
  function PlanSelectorCardCurrency({ children, onChange, ...rest }, forwardedRef) {
5672
6228
  const card = useCardContext("CardCurrency");
5673
6229
  if (card.pricingOptions.length <= 1) return null;
5674
- return /* @__PURE__ */ jsx16(
6230
+ return /* @__PURE__ */ jsx17(
5675
6231
  "select",
5676
6232
  {
5677
6233
  ref: forwardedRef,
@@ -5682,7 +6238,7 @@ var CardCurrency = forwardRef9(
5682
6238
  onChange?.(event);
5683
6239
  },
5684
6240
  ...rest,
5685
- children: children ?? card.pricingOptions.map((option) => /* @__PURE__ */ jsx16("option", { value: option.currency, children: option.currency }, option.currency))
6241
+ children: children ?? card.pricingOptions.map((option) => /* @__PURE__ */ jsx17("option", { value: option.currency, children: option.currency }, option.currency))
5686
6242
  }
5687
6243
  );
5688
6244
  }
@@ -5701,7 +6257,7 @@ var CardBadge = forwardRef9(function PlanSelectorCardBadge({ asChild, children,
5701
6257
  }
5702
6258
  if (!variant) return null;
5703
6259
  const Comp = asChild ? Slot : "span";
5704
- return /* @__PURE__ */ jsx16(
6260
+ return /* @__PURE__ */ jsx17(
5705
6261
  Comp,
5706
6262
  {
5707
6263
  ref: forwardedRef,
@@ -5716,13 +6272,13 @@ var Loading3 = forwardRef9(function PlanSelectorLoading({ asChild, children, ...
5716
6272
  const ctx = usePlanSelectorContext("Loading");
5717
6273
  if (!ctx.loading || ctx.plans.length > 0) return null;
5718
6274
  const Comp = asChild ? Slot : "div";
5719
- return /* @__PURE__ */ jsx16(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-loading": "", ...rest, children });
6275
+ return /* @__PURE__ */ jsx17(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-loading": "", ...rest, children });
5720
6276
  });
5721
6277
  var ErrorSlot3 = forwardRef9(function PlanSelectorError({ asChild, children, ...rest }, forwardedRef) {
5722
6278
  const ctx = usePlanSelectorContext("Error");
5723
6279
  if (!ctx.error) return null;
5724
6280
  const Comp = asChild ? Slot : "div";
5725
- return /* @__PURE__ */ jsx16(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-error": "", role: "alert", ...rest, children: children ?? ctx.error.message });
6281
+ return /* @__PURE__ */ jsx17(Comp, { ref: forwardedRef, "data-solvapay-plan-selector-error": "", role: "alert", ...rest, children: children ?? ctx.error.message });
5726
6282
  });
5727
6283
  var PlanSelectorRoot2 = Root5;
5728
6284
  var PlanSelectorHeading2 = Heading;
@@ -5755,13 +6311,13 @@ function usePlanSelector() {
5755
6311
  }
5756
6312
 
5757
6313
  // src/hooks/usePurchaseActions.ts
5758
- import { useState as useState14, useCallback as useCallback15 } from "react";
6314
+ import { useState as useState15, useCallback as useCallback16 } from "react";
5759
6315
  function usePurchaseActions() {
5760
6316
  const ctx = useSolvaPay();
5761
- const [isCancelling, setIsCancelling] = useState14(false);
5762
- const [isReactivating, setIsReactivating] = useState14(false);
5763
- const [isActivating, setIsActivating] = useState14(false);
5764
- const cancelRenewal = useCallback15(
6317
+ const [isCancelling, setIsCancelling] = useState15(false);
6318
+ const [isReactivating, setIsReactivating] = useState15(false);
6319
+ const [isActivating, setIsActivating] = useState15(false);
6320
+ const cancelRenewal = useCallback16(
5765
6321
  async (params) => {
5766
6322
  setIsCancelling(true);
5767
6323
  try {
@@ -5772,7 +6328,7 @@ function usePurchaseActions() {
5772
6328
  },
5773
6329
  [ctx.cancelRenewal]
5774
6330
  );
5775
- const reactivateRenewal = useCallback15(
6331
+ const reactivateRenewal = useCallback16(
5776
6332
  async (params) => {
5777
6333
  setIsReactivating(true);
5778
6334
  try {
@@ -5783,7 +6339,7 @@ function usePurchaseActions() {
5783
6339
  },
5784
6340
  [ctx.reactivateRenewal]
5785
6341
  );
5786
- const activatePlan = useCallback15(
6342
+ const activatePlan = useCallback16(
5787
6343
  async (params) => {
5788
6344
  setIsActivating(true);
5789
6345
  try {
@@ -5804,88 +6360,15 @@ function usePurchaseActions() {
5804
6360
  };
5805
6361
  }
5806
6362
 
5807
- // src/primitives/CancelPlanButton.tsx
5808
- import { countsUsage } from "@solvapay/core";
5809
- import { forwardRef as forwardRef10, useCallback as useCallback16, useContext as useContext13 } from "react";
5810
- import { Fragment as Fragment9, jsx as jsx17 } from "react/jsx-runtime";
5811
- function resolveConfirmText(confirm, purchase, defaults) {
5812
- if (confirm === false) return null;
5813
- if (typeof confirm === "string") return confirm;
5814
- const isUsageBased = (countsUsage(purchase?.planSnapshot) || purchase?.planSnapshot?.isMetered === true) && purchase?.isRecurring !== true;
5815
- if (isUsageBased) return defaults.usageBased;
5816
- return defaults.recurring;
5817
- }
5818
- var CancelPlanButton = forwardRef10(
5819
- function CancelPlanButton2({
5820
- asChild,
5821
- purchaseRef,
5822
- reason,
5823
- confirm = true,
5824
- onCancelled,
5825
- onError,
5826
- onClick,
5827
- children,
5828
- ...rest
5829
- }, forwardedRef) {
5830
- const solva = useContext13(SolvaPayContext);
5831
- if (!solva) throw new MissingProviderError("CancelPlanButton");
5832
- const copy = useCopy();
5833
- const { activePurchase } = usePurchase();
5834
- const { cancelRenewal, isCancelling } = usePurchaseActions();
5835
- const effectivePurchase = purchaseRef ? activePurchase && activePurchase.reference === purchaseRef ? activePurchase : { reference: purchaseRef } : activePurchase;
5836
- const effectiveRef = purchaseRef ?? effectivePurchase?.reference;
5837
- const cancel = useCallback16(async () => {
5838
- if (!effectiveRef) return;
5839
- const confirmText = resolveConfirmText(confirm, effectivePurchase, {
5840
- recurring: copy.cancelPlan.confirmRecurring,
5841
- usageBased: copy.cancelPlan.confirmUsageBased
5842
- });
5843
- if (confirmText && typeof window !== "undefined" && !window.confirm(confirmText)) {
5844
- return;
5845
- }
5846
- try {
5847
- await cancelRenewal({ purchaseRef: effectiveRef, reason });
5848
- onCancelled?.();
5849
- } catch (err) {
5850
- onError?.(err instanceof Error ? err : new Error(String(err)));
5851
- }
5852
- }, [effectiveRef, effectivePurchase, confirm, copy, cancelRenewal, reason, onCancelled, onError]);
5853
- if (effectivePurchase?.status === "active" && effectivePurchase.cancelledAt) {
5854
- return null;
5855
- }
5856
- const disabled = isCancelling || !effectiveRef;
5857
- const state = isCancelling ? "cancelling" : "idle";
5858
- const commonProps = {
5859
- "data-solvapay-cancel-plan": "",
5860
- "data-state": state,
5861
- "aria-busy": isCancelling,
5862
- "aria-disabled": disabled || void 0,
5863
- disabled,
5864
- onClick: composeEventHandlers(onClick, () => {
5865
- void cancel();
5866
- }),
5867
- ...rest
5868
- };
5869
- const label = isCancelling ? copy.cancelPlan.buttonLoading : copy.cancelPlan.button;
5870
- if (asChild) {
5871
- return (
5872
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5873
- /* @__PURE__ */ jsx17(Slot, { ref: forwardedRef, ...commonProps, children: children ?? /* @__PURE__ */ jsx17(Fragment9, { children: label }) })
5874
- );
5875
- }
5876
- return /* @__PURE__ */ jsx17("button", { ref: forwardedRef, type: "button", ...commonProps, children: children ?? label });
5877
- }
5878
- );
5879
-
5880
6363
  // src/hooks/usePurchaseStatus.ts
5881
- import { useMemo as useMemo11, useCallback as useCallback17 } from "react";
6364
+ import { useMemo as useMemo12, useCallback as useCallback17 } from "react";
5882
6365
  function usePurchaseStatus() {
5883
6366
  const { purchases } = usePurchase();
5884
6367
  const locale = useLocale();
5885
6368
  const isPaidPurchase2 = useCallback17((p) => {
5886
6369
  return (p.amount ?? 0) > 0;
5887
6370
  }, []);
5888
- const purchaseData = useMemo11(() => {
6371
+ const purchaseData = useMemo12(() => {
5889
6372
  const cancelledPaidPurchases = purchases.filter((p) => {
5890
6373
  return p.status === "active" && p.cancelledAt && isPaidPurchase2(p) && isPlanPurchase(p);
5891
6374
  });
@@ -5927,14 +6410,14 @@ function usePurchaseStatus() {
5927
6410
 
5928
6411
  // src/primitives/CancelledPlanNotice.tsx
5929
6412
  import {
5930
- createContext as createContext9,
5931
- forwardRef as forwardRef11,
6413
+ createContext as createContext10,
6414
+ forwardRef as forwardRef10,
5932
6415
  useCallback as useCallback18,
5933
6416
  useContext as useContext14,
5934
- useMemo as useMemo12
6417
+ useMemo as useMemo13
5935
6418
  } from "react";
5936
- import { Fragment as Fragment10, jsx as jsx18 } from "react/jsx-runtime";
5937
- var CancelledPlanNoticeContext = createContext9(null);
6419
+ import { Fragment as Fragment9, jsx as jsx18 } from "react/jsx-runtime";
6420
+ var CancelledPlanNoticeContext = createContext10(null);
5938
6421
  function useNoticeCtx(part) {
5939
6422
  const ctx = useContext14(CancelledPlanNoticeContext);
5940
6423
  if (!ctx) {
@@ -5944,7 +6427,7 @@ function useNoticeCtx(part) {
5944
6427
  }
5945
6428
  return ctx;
5946
6429
  }
5947
- var Root6 = forwardRef11(function CancelledPlanNoticeRoot({ onReactivated, onError, asChild, children, ...rest }, forwardedRef) {
6430
+ var Root6 = forwardRef10(function CancelledPlanNoticeRoot({ onReactivated, onError, asChild, children, ...rest }, forwardedRef) {
5948
6431
  const solva = useContext14(SolvaPayContext);
5949
6432
  if (!solva) throw new MissingProviderError("CancelledPlanNotice");
5950
6433
  const {
@@ -5963,11 +6446,11 @@ var Root6 = forwardRef11(function CancelledPlanNoticeRoot({ onReactivated, onErr
5963
6446
  onError?.(err instanceof Error ? err : new Error(String(err)));
5964
6447
  }
5965
6448
  }, [cancelledPurchase, reactivateRenewal, onReactivated, onError]);
5966
- const daysRemaining = useMemo12(
6449
+ const daysRemaining = useMemo13(
5967
6450
  () => cancelledPurchase ? getDaysUntilExpiration(cancelledPurchase.endDate) : null,
5968
6451
  [cancelledPurchase, getDaysUntilExpiration]
5969
6452
  );
5970
- const ctx = useMemo12(() => {
6453
+ const ctx = useMemo13(() => {
5971
6454
  if (!cancelledPurchase) return null;
5972
6455
  const state = daysRemaining != null && daysRemaining > 0 ? "active" : "expired";
5973
6456
  return {
@@ -5994,13 +6477,13 @@ var Root6 = forwardRef11(function CancelledPlanNoticeRoot({ onReactivated, onErr
5994
6477
  }
5995
6478
  ) });
5996
6479
  });
5997
- var Heading2 = forwardRef11(function CancelledPlanNoticeHeading({ asChild, children, ...rest }, forwardedRef) {
6480
+ var Heading2 = forwardRef10(function CancelledPlanNoticeHeading({ asChild, children, ...rest }, forwardedRef) {
5998
6481
  useNoticeCtx("Heading");
5999
6482
  const copy = useCopy();
6000
6483
  const Comp = asChild ? Slot : "p";
6001
6484
  return /* @__PURE__ */ jsx18(Comp, { ref: forwardedRef, "data-solvapay-cancelled-notice-heading": "", ...rest, children: children ?? copy.cancelledNotice.heading });
6002
6485
  });
6003
- var Expires = forwardRef11(function CancelledPlanNoticeExpires({ asChild, children, ...rest }, forwardedRef) {
6486
+ var Expires = forwardRef10(function CancelledPlanNoticeExpires({ asChild, children, ...rest }, forwardedRef) {
6004
6487
  const ctx = useNoticeCtx("Expires");
6005
6488
  const copy = useCopy();
6006
6489
  const date = ctx.formatDate(ctx.purchase.endDate);
@@ -6011,7 +6494,7 @@ var Expires = forwardRef11(function CancelledPlanNoticeExpires({ asChild, childr
6011
6494
  const Comp = asChild ? Slot : "p";
6012
6495
  return /* @__PURE__ */ jsx18(Comp, { ref: forwardedRef, "data-solvapay-cancelled-notice-expires": "", ...rest, children: children ?? interpolate(copy.cancelledNotice.expiresLabel, { date: date ?? "" }) });
6013
6496
  });
6014
- var DaysRemaining = forwardRef11(
6497
+ var DaysRemaining = forwardRef10(
6015
6498
  function CancelledPlanNoticeDaysRemaining({ asChild, children, ...rest }, forwardedRef) {
6016
6499
  const ctx = useNoticeCtx("DaysRemaining");
6017
6500
  const copy = useCopy();
@@ -6021,7 +6504,7 @@ var DaysRemaining = forwardRef11(
6021
6504
  return /* @__PURE__ */ jsx18(Comp, { ref: forwardedRef, "data-solvapay-cancelled-notice-days-remaining": "", ...rest, children: children ?? interpolate(template, { days: ctx.daysRemaining }) });
6022
6505
  }
6023
6506
  );
6024
- var AccessUntil = forwardRef11(
6507
+ var AccessUntil = forwardRef10(
6025
6508
  function CancelledPlanNoticeAccessUntil({ asChild, children, ...rest }, forwardedRef) {
6026
6509
  const ctx = useNoticeCtx("AccessUntil");
6027
6510
  const copy = useCopy();
@@ -6032,7 +6515,7 @@ var AccessUntil = forwardRef11(
6032
6515
  }) });
6033
6516
  }
6034
6517
  );
6035
- var CancelledOn = forwardRef11(
6518
+ var CancelledOn = forwardRef10(
6036
6519
  function CancelledPlanNoticeCancelledOn({ asChild, children, ...rest }, forwardedRef) {
6037
6520
  const ctx = useNoticeCtx("CancelledOn");
6038
6521
  const copy = useCopy();
@@ -6042,13 +6525,13 @@ var CancelledOn = forwardRef11(
6042
6525
  return /* @__PURE__ */ jsx18(Comp, { ref: forwardedRef, "data-solvapay-cancelled-notice-cancelled-on": "", ...rest, children: children ?? interpolate(copy.cancelledNotice.cancelledOn, { date: date ?? "" }) });
6043
6526
  }
6044
6527
  );
6045
- var Reason = forwardRef11(function CancelledPlanNoticeReason({ asChild, children, ...rest }, forwardedRef) {
6528
+ var Reason = forwardRef10(function CancelledPlanNoticeReason({ asChild, children, ...rest }, forwardedRef) {
6046
6529
  const ctx = useNoticeCtx("Reason");
6047
6530
  if (!ctx.hasReason) return null;
6048
6531
  const Comp = asChild ? Slot : "span";
6049
6532
  return /* @__PURE__ */ jsx18(Comp, { ref: forwardedRef, "data-solvapay-cancelled-notice-reason": "", ...rest, children: children ?? ctx.purchase.cancellationReason });
6050
6533
  });
6051
- var ReactivateButton = forwardRef11(
6534
+ var ReactivateButton = forwardRef10(
6052
6535
  function CancelledPlanNoticeReactivateButton({ asChild, onClick, children, ...rest }, forwardedRef) {
6053
6536
  const ctx = useNoticeCtx("ReactivateButton");
6054
6537
  const copy = useCopy();
@@ -6068,7 +6551,7 @@ var ReactivateButton = forwardRef11(
6068
6551
  if (asChild) {
6069
6552
  return (
6070
6553
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
6071
- /* @__PURE__ */ jsx18(Slot, { ref: forwardedRef, ...commonProps, children: children ?? /* @__PURE__ */ jsx18(Fragment10, { children: label }) })
6554
+ /* @__PURE__ */ jsx18(Slot, { ref: forwardedRef, ...commonProps, children: children ?? /* @__PURE__ */ jsx18(Fragment9, { children: label }) })
6072
6555
  );
6073
6556
  }
6074
6557
  return /* @__PURE__ */ jsx18("button", { ref: forwardedRef, type: "button", ...commonProps, children: children ?? label });
@@ -6097,14 +6580,14 @@ function useCancelledPlanNotice() {
6097
6580
  }
6098
6581
 
6099
6582
  // src/hooks/useTransport.ts
6100
- import { useMemo as useMemo13 } from "react";
6583
+ import { useMemo as useMemo14 } from "react";
6101
6584
  function useTransport() {
6102
6585
  const { _config } = useSolvaPay();
6103
- return useMemo13(() => _config?.transport ?? createHttpTransport(_config), [_config]);
6586
+ return useMemo14(() => _config?.transport ?? createHttpTransport(_config), [_config]);
6104
6587
  }
6105
6588
 
6106
6589
  // src/hooks/useLimits.ts
6107
- import { useCallback as useCallback19, useEffect as useEffect12, useRef as useRef9, useState as useState15 } from "react";
6590
+ import { useCallback as useCallback19, useEffect as useEffect13, useRef as useRef9, useState as useState16 } from "react";
6108
6591
  var CACHE_TTL_MS = 1e4;
6109
6592
  var OPTIMISTIC_GRACE_MS = 8e3;
6110
6593
  var limitsCache = /* @__PURE__ */ new Map();
@@ -6121,13 +6604,13 @@ function useLimits(options) {
6121
6604
  const { customerRef } = useCustomer();
6122
6605
  const { purchase } = useSolvaPay();
6123
6606
  const currentKey = !productRef || !enabled ? null : cacheKey(customerRef, productRef, meterName2);
6124
- const [data, setData] = useState15(() => {
6607
+ const [data, setData] = useState16(() => {
6125
6608
  if (!currentKey) return null;
6126
6609
  const cached = limitsCache.get(currentKey);
6127
6610
  if (!cached || Date.now() - cached.timestamp >= CACHE_TTL_MS) return null;
6128
6611
  return cached.data;
6129
6612
  });
6130
- const [loading, setLoading] = useState15(() => {
6613
+ const [loading, setLoading] = useState16(() => {
6131
6614
  if (!currentKey) return false;
6132
6615
  const cached = limitsCache.get(currentKey);
6133
6616
  if (!cached) return true;
@@ -6135,8 +6618,8 @@ function useLimits(options) {
6135
6618
  if (Date.now() - cached.timestamp >= CACHE_TTL_MS) return true;
6136
6619
  return false;
6137
6620
  });
6138
- const [error, setError] = useState15(null);
6139
- const [trackedKey, setTrackedKey] = useState15(currentKey);
6621
+ const [error, setError] = useState16(null);
6622
+ const [trackedKey, setTrackedKey] = useState16(currentKey);
6140
6623
  if (trackedKey !== currentKey) {
6141
6624
  setTrackedKey(currentKey);
6142
6625
  setData(null);
@@ -6144,10 +6627,10 @@ function useLimits(options) {
6144
6627
  const unlimited = data === null ? null : isUnlimited(data.remaining);
6145
6628
  const optimisticTimerRef = useRef9(null);
6146
6629
  const unlimitedRef = useRef9(false);
6147
- useEffect12(() => {
6630
+ useEffect13(() => {
6148
6631
  unlimitedRef.current = unlimited === true;
6149
6632
  }, [unlimited]);
6150
- useEffect12(() => {
6633
+ useEffect13(() => {
6151
6634
  return () => {
6152
6635
  if (optimisticTimerRef.current) clearTimeout(optimisticTimerRef.current);
6153
6636
  };
@@ -6171,7 +6654,18 @@ function useLimits(options) {
6171
6654
  );
6172
6655
  const fetchLimits = useCallback19(
6173
6656
  async (force) => {
6174
- if (!productRef || !enabled || !transport.getLimits) {
6657
+ if (!productRef || !enabled) {
6658
+ setLoading(false);
6659
+ return;
6660
+ }
6661
+ if (!transport.getLimits) {
6662
+ const seeded = limitsCache.get(cacheKey(customerRef, productRef, meterName2));
6663
+ if (seeded?.data) {
6664
+ setData(seeded.data);
6665
+ setLoading(false);
6666
+ setError(null);
6667
+ return;
6668
+ }
6175
6669
  setLoading(false);
6176
6670
  return;
6177
6671
  }
@@ -6199,11 +6693,11 @@ function useLimits(options) {
6199
6693
  },
6200
6694
  [transport, customerRef, productRef, meterName2, enabled, attachToFetch]
6201
6695
  );
6202
- useEffect12(() => {
6696
+ useEffect13(() => {
6203
6697
  fetchLimits(false);
6204
6698
  }, [fetchLimits]);
6205
6699
  const prevPurchasesRef = useRef9(null);
6206
- useEffect12(() => {
6700
+ useEffect13(() => {
6207
6701
  if (prevPurchasesRef.current !== null && prevPurchasesRef.current !== purchase.purchases) {
6208
6702
  fetchLimits(true).catch(() => {
6209
6703
  });
@@ -6222,7 +6716,10 @@ function useLimits(options) {
6222
6716
  if (!baseline) return prev;
6223
6717
  const next = {
6224
6718
  ...baseline,
6225
- remaining: Math.max(0, baseline.remaining + delta)
6719
+ remaining: Math.max(0, baseline.remaining + delta),
6720
+ // Keep `used` self-consistent with the remaining nudge so
6721
+ // a meter that reads both does not flash a collapsing cap.
6722
+ ...typeof baseline.used === "number" ? { used: Math.max(0, baseline.used - delta) } : {}
6226
6723
  // Don't optimistically flip `withinLimits` — paywall gating
6227
6724
  // can hinge on factors beyond `remaining > 0`
6228
6725
  // (activationRequired, etc.). Let the trailing refetch
@@ -6255,6 +6752,8 @@ function useLimits(options) {
6255
6752
  needsTopUp: data?.needsTopUp ?? null,
6256
6753
  needsUpgrade: data?.needsUpgrade ?? null,
6257
6754
  upgraded: data?.upgraded ?? null,
6755
+ used: data?.used ?? null,
6756
+ limit: data?.limit ?? null,
6258
6757
  loading,
6259
6758
  error,
6260
6759
  refetch,
@@ -6263,18 +6762,24 @@ function useLimits(options) {
6263
6762
  }
6264
6763
 
6265
6764
  // src/hooks/useUsage.ts
6266
- import { countsUsage as countsUsage2 } from "@solvapay/core";
6267
- import { useCallback as useCallback20, useEffect as useEffect13, useMemo as useMemo14, useState as useState16 } from "react";
6765
+ import { countsUsage } from "@solvapay/core";
6766
+ import { useCallback as useCallback20, useEffect as useEffect14, useMemo as useMemo15, useState as useState17 } from "react";
6767
+ var seededUsage = null;
6768
+ var usageSeedGeneration = 0;
6769
+ function seedUsageSnapshot(usage) {
6770
+ seededUsage = usage;
6771
+ usageSeedGeneration += 1;
6772
+ }
6268
6773
  function deriveUsage(purchase, limits) {
6269
6774
  if (!purchase) return null;
6270
6775
  const usage = purchase.usage;
6271
- if (!countsUsage2(purchase.planSnapshot) && purchase.planSnapshot?.isMetered !== true && !usage) {
6776
+ if (!countsUsage(purchase.planSnapshot) && purchase.planSnapshot?.isMetered !== true && !usage) {
6272
6777
  return null;
6273
6778
  }
6274
- const used = typeof usage?.used === "number" ? usage.used : 0;
6275
6779
  const hasFiniteCap = limits.unlimited === false && limits.remaining !== null;
6276
6780
  const remaining = hasFiniteCap ? limits.remaining : null;
6277
- const total = remaining === null ? null : used + remaining;
6781
+ const total = typeof limits.limit === "number" && limits.limit > 0 ? limits.limit : null;
6782
+ const used = typeof limits.used === "number" ? limits.used : total !== null && remaining !== null ? Math.max(0, total - remaining) : typeof usage?.used === "number" ? usage.used : 0;
6278
6783
  const percentUsed = total !== null && total > 0 ? Math.min(100, Math.round(used / total * 1e4) / 100) : null;
6279
6784
  return {
6280
6785
  meterRef: limits.meterName,
@@ -6290,25 +6795,38 @@ function deriveUsage(purchase, limits) {
6290
6795
  function useUsage() {
6291
6796
  const { activePurchase, refetch: refetchPurchase, loading: purchaseLoading } = usePurchase();
6292
6797
  const transport = useTransport();
6293
- const [override, setOverride] = useState16(null);
6294
- const [error, setError] = useState16(null);
6295
- const [transportLoading, setTransportLoading] = useState16(false);
6296
- const usageCounted = countsUsage2(activePurchase?.planSnapshot) || activePurchase?.planSnapshot?.isMetered === true;
6798
+ const [override, setOverride] = useState17(() => seededUsage);
6799
+ const [seedGeneration, setSeedGeneration] = useState17(usageSeedGeneration);
6800
+ const [error, setError] = useState17(null);
6801
+ const [transportLoading, setTransportLoading] = useState17(false);
6802
+ if (seedGeneration !== usageSeedGeneration) {
6803
+ setSeedGeneration(usageSeedGeneration);
6804
+ setOverride(seededUsage);
6805
+ }
6806
+ const usageCounted = countsUsage(activePurchase?.planSnapshot) || activePurchase?.planSnapshot?.isMetered === true;
6297
6807
  const {
6298
6808
  remaining: limitRemaining,
6299
6809
  unlimited,
6300
- meterName: meterName2
6810
+ meterName: meterName2,
6811
+ used: limitUsed,
6812
+ limit
6301
6813
  } = useLimits({
6302
6814
  productRef: activePurchase?.productRef,
6303
6815
  enabled: usageCounted
6304
6816
  });
6305
- const derived = useMemo14(
6306
- () => deriveUsage(activePurchase ?? null, { remaining: limitRemaining, unlimited, meterName: meterName2 }),
6307
- [activePurchase, limitRemaining, unlimited, meterName2]
6817
+ const derived = useMemo15(
6818
+ () => deriveUsage(activePurchase ?? null, {
6819
+ remaining: limitRemaining,
6820
+ unlimited,
6821
+ meterName: meterName2,
6822
+ used: limitUsed,
6823
+ limit
6824
+ }),
6825
+ [activePurchase, limitRemaining, unlimited, meterName2, limitUsed, limit]
6308
6826
  );
6309
6827
  const activePurchaseRef = activePurchase?.reference ?? null;
6310
- useEffect13(() => {
6311
- setOverride(null);
6828
+ useEffect14(() => {
6829
+ setOverride(seededUsage);
6312
6830
  }, [activePurchaseRef]);
6313
6831
  const usage = override ?? derived;
6314
6832
  const refetch = useCallback20(async () => {
@@ -6349,9 +6867,9 @@ function useUsage() {
6349
6867
  }
6350
6868
 
6351
6869
  // src/primitives/UsageMeter.tsx
6352
- import { createContext as createContext10, forwardRef as forwardRef12, useContext as useContext15, useMemo as useMemo15 } from "react";
6870
+ import { createContext as createContext11, forwardRef as forwardRef11, useContext as useContext15, useMemo as useMemo16 } from "react";
6353
6871
  import { jsx as jsx19 } from "react/jsx-runtime";
6354
- var UsageMeterContext = createContext10(null);
6872
+ var UsageMeterContext = createContext11(null);
6355
6873
  function useUsageMeterCtx(part) {
6356
6874
  const ctx = useContext15(UsageMeterContext);
6357
6875
  if (!ctx) {
@@ -6359,7 +6877,7 @@ function useUsageMeterCtx(part) {
6359
6877
  }
6360
6878
  return ctx;
6361
6879
  }
6362
- var Root7 = forwardRef12(function UsageMeterRoot({
6880
+ var Root7 = forwardRef11(function UsageMeterRoot({
6363
6881
  warningAt = 75,
6364
6882
  criticalAt = 90,
6365
6883
  classNames,
@@ -6374,7 +6892,7 @@ var Root7 = forwardRef12(function UsageMeterRoot({
6374
6892
  const loading = hookResult.loading && !usage;
6375
6893
  const percentUsed = usage?.percentUsed ?? null;
6376
6894
  const state = loading ? "loading" : percentUsed === null ? "safe" : percentUsed >= criticalAt ? "critical" : percentUsed >= warningAt ? "warning" : "safe";
6377
- const ctx = useMemo15(
6895
+ const ctx = useMemo16(
6378
6896
  () => ({
6379
6897
  usage,
6380
6898
  loading,
@@ -6414,7 +6932,7 @@ var Root7 = forwardRef12(function UsageMeterRoot({
6414
6932
  }
6415
6933
  ) });
6416
6934
  });
6417
- var Bar = forwardRef12(function UsageMeterBar({ className, style, ...rest }, forwardedRef) {
6935
+ var Bar = forwardRef11(function UsageMeterBar({ className, style, ...rest }, forwardedRef) {
6418
6936
  const ctx = useUsageMeterCtx("Bar");
6419
6937
  const width = ctx.percentUsed === null ? 0 : Math.max(0, Math.min(100, ctx.percentUsed));
6420
6938
  return /* @__PURE__ */ jsx19(
@@ -6438,7 +6956,7 @@ var Bar = forwardRef12(function UsageMeterBar({ className, style, ...rest }, for
6438
6956
  }
6439
6957
  );
6440
6958
  });
6441
- var Label = forwardRef12(function UsageMeterLabel({ className, children, ...rest }, forwardedRef) {
6959
+ var Label = forwardRef11(function UsageMeterLabel({ className, children, ...rest }, forwardedRef) {
6442
6960
  const ctx = useUsageMeterCtx("Label");
6443
6961
  const copy = useCopy();
6444
6962
  if (!ctx.usage) return null;
@@ -6474,7 +6992,7 @@ var Label = forwardRef12(function UsageMeterLabel({ className, children, ...rest
6474
6992
  }
6475
6993
  );
6476
6994
  });
6477
- var Percentage = forwardRef12(function UsageMeterPercentage({ className, children, ...rest }, forwardedRef) {
6995
+ var Percentage = forwardRef11(function UsageMeterPercentage({ className, children, ...rest }, forwardedRef) {
6478
6996
  const ctx = useUsageMeterCtx("Percentage");
6479
6997
  const copy = useCopy();
6480
6998
  if (ctx.percentUsed === null) return null;
@@ -6495,7 +7013,7 @@ function formatResetsIn(copy, periodEnd, nowMs = typeof performance !== "undefin
6495
7013
  const daysLeft = Math.max(0, Math.ceil((end - nowMs) / (24 * 60 * 60 * 1e3)));
6496
7014
  return daysLeft > 0 ? interpolate(copy.usage.resetsInLabel, { days: String(daysLeft) }) : interpolate(copy.usage.resetsOnLabel, { date: new Date(periodEnd).toLocaleDateString() });
6497
7015
  }
6498
- var ResetsIn = forwardRef12(function UsageMeterResetsIn({ className, children, ...rest }, forwardedRef) {
7016
+ var ResetsIn = forwardRef11(function UsageMeterResetsIn({ className, children, ...rest }, forwardedRef) {
6499
7017
  const ctx = useUsageMeterCtx("ResetsIn");
6500
7018
  const copy = useCopy();
6501
7019
  const periodEnd = ctx.usage?.periodEnd;
@@ -6513,7 +7031,7 @@ var ResetsIn = forwardRef12(function UsageMeterResetsIn({ className, children, .
6513
7031
  }
6514
7032
  );
6515
7033
  });
6516
- var Loading4 = forwardRef12(function UsageMeterLoading({ className, children, ...rest }, forwardedRef) {
7034
+ var Loading4 = forwardRef11(function UsageMeterLoading({ className, children, ...rest }, forwardedRef) {
6517
7035
  const ctx = useUsageMeterCtx("Loading");
6518
7036
  const copy = useCopy();
6519
7037
  if (ctx.state !== "loading") return null;
@@ -6529,7 +7047,7 @@ var Loading4 = forwardRef12(function UsageMeterLoading({ className, children, ..
6529
7047
  }
6530
7048
  );
6531
7049
  });
6532
- var Empty = forwardRef12(function UsageMeterEmpty({ className, children, ...rest }, forwardedRef) {
7050
+ var Empty = forwardRef11(function UsageMeterEmpty({ className, children, ...rest }, forwardedRef) {
6533
7051
  const ctx = useUsageMeterCtx("Empty");
6534
7052
  const copy = useCopy();
6535
7053
  if (ctx.loading || ctx.usage !== null) return null;
@@ -6557,8 +7075,488 @@ function useUsageMeter() {
6557
7075
  return useUsageMeterCtx("useUsageMeter");
6558
7076
  }
6559
7077
 
7078
+ // src/hooks/useAutoRecharge.ts
7079
+ import { useCallback as useCallback21, useEffect as useEffect15, useRef as useRef10, useState as useState18 } from "react";
7080
+ function mergeAutoRechargeConfig(config, display) {
7081
+ return display ? { ...config, display } : config;
7082
+ }
7083
+ async function fetchAutoRecharge(config) {
7084
+ const transport = config?.transport ?? createHttpTransport(config);
7085
+ if (!transport.getAutoRecharge) return null;
7086
+ const response = await transport.getAutoRecharge();
7087
+ if (!response.config) return null;
7088
+ return mergeAutoRechargeConfig(response.config, response.display);
7089
+ }
7090
+ function useAutoRecharge() {
7091
+ const { _config } = useSolvaPay();
7092
+ const key = autoRechargeCacheKeyFor(_config);
7093
+ const [config, setConfig] = useState18(
7094
+ () => autoRechargeCache.get(key)?.config ?? null
7095
+ );
7096
+ const [loading, setLoading] = useState18(() => {
7097
+ const cached = autoRechargeCache.get(key);
7098
+ return !cached || !cached.config && !cached.promise;
7099
+ });
7100
+ const [saving, setSaving] = useState18(false);
7101
+ const [disabling, setDisabling] = useState18(false);
7102
+ const [error, setError] = useState18(null);
7103
+ const requestSeq = useRef10(0);
7104
+ const load = useCallback21(
7105
+ async (force = false) => {
7106
+ const seq = requestSeq.current;
7107
+ const cached = autoRechargeCache.get(key);
7108
+ const now = Date.now();
7109
+ if (!force && cached?.config && now - cached.timestamp < CACHE_DURATION2) {
7110
+ if (seq !== requestSeq.current) return;
7111
+ setConfig(cached.config);
7112
+ setLoading(false);
7113
+ setError(null);
7114
+ return;
7115
+ }
7116
+ if (!force && cached?.promise) {
7117
+ setLoading(true);
7118
+ try {
7119
+ const value = await cached.promise;
7120
+ if (seq !== requestSeq.current) return;
7121
+ setConfig(value);
7122
+ setError(null);
7123
+ } catch (caught) {
7124
+ if (seq !== requestSeq.current) return;
7125
+ setError(caught instanceof Error ? caught : new Error(String(caught)));
7126
+ } finally {
7127
+ if (seq === requestSeq.current) {
7128
+ setLoading(false);
7129
+ }
7130
+ }
7131
+ return;
7132
+ }
7133
+ setLoading(true);
7134
+ setError(null);
7135
+ const promise = fetchAutoRecharge(_config);
7136
+ writeAutoRechargeCache(key, {
7137
+ config: cached?.config ?? null,
7138
+ promise,
7139
+ timestamp: now
7140
+ });
7141
+ try {
7142
+ const value = await promise;
7143
+ if (seq !== requestSeq.current) return;
7144
+ writeAutoRechargeCache(key, { config: value, promise: null, timestamp: Date.now() });
7145
+ setConfig(value);
7146
+ } catch (caught) {
7147
+ if (seq !== requestSeq.current) return;
7148
+ const err = caught instanceof Error ? caught : new Error(String(caught));
7149
+ writeAutoRechargeCache(key, {
7150
+ config: cached?.config ?? null,
7151
+ promise: null,
7152
+ timestamp: Date.now()
7153
+ });
7154
+ setError(err);
7155
+ } finally {
7156
+ if (seq === requestSeq.current) {
7157
+ setLoading(false);
7158
+ }
7159
+ }
7160
+ },
7161
+ [_config, key]
7162
+ );
7163
+ useEffect15(() => {
7164
+ void load();
7165
+ }, [load]);
7166
+ useEffect15(() => {
7167
+ return subscribeAutoRecharge(key, () => {
7168
+ const cached = autoRechargeCache.get(key);
7169
+ if (cached) {
7170
+ setConfig(cached.config);
7171
+ return;
7172
+ }
7173
+ void load(true);
7174
+ });
7175
+ }, [key, load]);
7176
+ const refresh = useCallback21(
7177
+ async (force = true) => {
7178
+ await load(force);
7179
+ },
7180
+ [load]
7181
+ );
7182
+ const save = useCallback21(
7183
+ async (input) => {
7184
+ const transport = _config?.transport ?? createHttpTransport(_config);
7185
+ if (!transport.saveAutoRecharge) {
7186
+ throw new Error("saveAutoRecharge is not available on this transport");
7187
+ }
7188
+ setSaving(true);
7189
+ setError(null);
7190
+ const seq = ++requestSeq.current;
7191
+ try {
7192
+ const result = await transport.saveAutoRecharge(input);
7193
+ if (seq !== requestSeq.current) return result;
7194
+ const nextConfig = mergeAutoRechargeConfig(result.config, result.display);
7195
+ writeAutoRechargeCache(key, {
7196
+ config: nextConfig,
7197
+ promise: null,
7198
+ timestamp: Date.now()
7199
+ });
7200
+ setConfig(nextConfig);
7201
+ return result;
7202
+ } catch (caught) {
7203
+ const err = caught instanceof Error ? caught : new Error(String(caught));
7204
+ setError(err);
7205
+ throw err;
7206
+ } finally {
7207
+ setSaving(false);
7208
+ }
7209
+ },
7210
+ [_config, key]
7211
+ );
7212
+ const disable = useCallback21(async () => {
7213
+ const transport = _config?.transport ?? createHttpTransport(_config);
7214
+ if (!transport.disableAutoRecharge) {
7215
+ throw new Error("disableAutoRecharge is not available on this transport");
7216
+ }
7217
+ setDisabling(true);
7218
+ setError(null);
7219
+ const seq = ++requestSeq.current;
7220
+ try {
7221
+ const result = await transport.disableAutoRecharge();
7222
+ if (seq === requestSeq.current) {
7223
+ setConfig((current) => {
7224
+ const disabledConfig = current ? { ...current, enabled: false } : null;
7225
+ writeAutoRechargeCache(key, {
7226
+ config: disabledConfig,
7227
+ promise: null,
7228
+ timestamp: Date.now()
7229
+ });
7230
+ return disabledConfig;
7231
+ });
7232
+ await refresh(true);
7233
+ setConfig((current) => {
7234
+ const disabledConfig = current ? { ...current, enabled: false } : null;
7235
+ writeAutoRechargeCache(key, {
7236
+ config: disabledConfig,
7237
+ promise: null,
7238
+ timestamp: Date.now()
7239
+ });
7240
+ return disabledConfig;
7241
+ });
7242
+ }
7243
+ return result;
7244
+ } catch (caught) {
7245
+ const err = caught instanceof Error ? caught : new Error(String(caught));
7246
+ setError(err);
7247
+ throw err;
7248
+ } finally {
7249
+ setDisabling(false);
7250
+ }
7251
+ }, [_config, key, refresh]);
7252
+ return { config, loading, saving, disabling, error, refresh, save, disable };
7253
+ }
7254
+
7255
+ // src/utils/credit-estimation.ts
7256
+ import { creditsToDisplayMinorUnits as creditsToDisplayMinorUnits2 } from "@solvapay/core";
7257
+ function estimateCredits(resolvedAmountMajor, currency, creditsPerMinorUnit, displayExchangeRate) {
7258
+ if (resolvedAmountMajor == null || resolvedAmountMajor <= 0) return null;
7259
+ if (creditsPerMinorUnit == null || creditsPerMinorUnit <= 0) return null;
7260
+ const rate = displayExchangeRate ?? 1;
7261
+ const resolvedAmountMinor = Math.round(resolvedAmountMajor * getMinorUnitsPerMajor(currency));
7262
+ return Math.round(resolvedAmountMinor / rate * creditsPerMinorUnit);
7263
+ }
7264
+ function estimateCurrencyMajorFromCredits(credits, currency, creditsPerMinorUnit, displayExchangeRate) {
7265
+ if (credits == null || credits <= 0) return null;
7266
+ if (creditsPerMinorUnit == null || creditsPerMinorUnit <= 0) return null;
7267
+ const rate = displayExchangeRate ?? 1;
7268
+ const displayMinor = creditsToDisplayMinorUnits2({
7269
+ credits,
7270
+ creditsPerMinorUnit,
7271
+ displayExchangeRate: rate,
7272
+ displayCurrency: currency
7273
+ });
7274
+ if (displayMinor === null) return null;
7275
+ return displayMinor / getMinorUnitsPerMajor(currency);
7276
+ }
7277
+
7278
+ // src/helpers/stripe-minimums.ts
7279
+ var STRIPE_MINIMUM_MINOR = {
7280
+ USD: 50,
7281
+ EUR: 50,
7282
+ GBP: 30,
7283
+ SEK: 300,
7284
+ NOK: 300,
7285
+ DKK: 250,
7286
+ CHF: 50,
7287
+ CAD: 50,
7288
+ AUD: 50,
7289
+ JPY: 50,
7290
+ KRW: 500
7291
+ };
7292
+ var DEFAULT_MINIMUM_MINOR = 50;
7293
+ function getStripeMinimumMinor(currency) {
7294
+ return STRIPE_MINIMUM_MINOR[currency.toUpperCase()] ?? DEFAULT_MINIMUM_MINOR;
7295
+ }
7296
+
7297
+ // src/helpers/auto-recharge-form.ts
7298
+ var DEFAULT_VALIDATION_MESSAGES = {
7299
+ invalidThreshold: "Enter a valid balance threshold.",
7300
+ thresholdTooLow: "Balance threshold must be greater than zero.",
7301
+ minTopupAmount: "Top-up amount must be at least {amount}.",
7302
+ invalidMaxMonthlySpend: "Maximum monthly spend must be a positive amount.",
7303
+ maxMonthlySpendBelowTopup: "Maximum monthly spend must be at least your top-up amount ({amount})."
7304
+ };
7305
+ function amountAnchors(thresholdValue, topupValue, unit = "currency") {
7306
+ return {
7307
+ thresholdBaseValue: thresholdValue,
7308
+ thresholdBaseUnit: unit,
7309
+ topupBaseValue: topupValue,
7310
+ topupBaseUnit: unit
7311
+ };
7312
+ }
7313
+ function createDefaultAutoRechargeForm(currency, defaultTopupMajor, defaultThresholdMajor) {
7314
+ const topup = defaultTopupMajor != null && defaultTopupMajor > 0 ? String(defaultTopupMajor) : "10";
7315
+ const threshold = defaultThresholdMajor != null && defaultThresholdMajor >= 0 ? String(defaultThresholdMajor) : "5";
7316
+ return {
7317
+ enabled: false,
7318
+ thresholdAmountMajor: threshold,
7319
+ thresholdUnit: "currency",
7320
+ topupAmountMajor: topup,
7321
+ topupUnit: "currency",
7322
+ maxMonthlySpendMajor: "",
7323
+ ...amountAnchors(threshold, topup, "currency")
7324
+ };
7325
+ }
7326
+ function currentSpendPeriod(now = /* @__PURE__ */ new Date()) {
7327
+ const year = now.getUTCFullYear();
7328
+ const month = now.getUTCMonth() + 1;
7329
+ return `${year}-${String(month).padStart(2, "0")}`;
7330
+ }
7331
+ function effectiveMonthlySpend(config, now = /* @__PURE__ */ new Date()) {
7332
+ if (config.monthlySpendPeriod !== currentSpendPeriod(now)) {
7333
+ return 0;
7334
+ }
7335
+ return config.monthlySpendMinor ?? 0;
7336
+ }
7337
+ function isMonthlySpendCapReached(config, now = /* @__PURE__ */ new Date()) {
7338
+ if (config.maxMonthlySpendMinor == null) return false;
7339
+ const effectiveSpend = effectiveMonthlySpend(config, now);
7340
+ return effectiveSpend + config.topup.amountMinor > config.maxMonthlySpendMinor;
7341
+ }
7342
+ function maxMonthlySpendMajorFromConfig(config, currency) {
7343
+ if (config.maxMonthlySpendMinor == null) return "";
7344
+ const minorPerMajor = getMinorUnitsPerMajor(currency);
7345
+ return String(config.maxMonthlySpendMinor / minorPerMajor);
7346
+ }
7347
+ function parsePositiveNumber(value) {
7348
+ const trimmed = value.trim();
7349
+ if (trimmed.length === 0) return null;
7350
+ const parsed = Number(trimmed);
7351
+ if (!Number.isFinite(parsed) || parsed <= 0) return null;
7352
+ return parsed;
7353
+ }
7354
+ function parseNonNegativeNumber(value) {
7355
+ const trimmed = value.trim();
7356
+ if (trimmed.length === 0) return null;
7357
+ const parsed = Number(trimmed);
7358
+ if (!Number.isFinite(parsed) || parsed < 0) return null;
7359
+ return parsed;
7360
+ }
7361
+ function validateAutoRechargeForm(form, currency, conversion, messages = DEFAULT_VALIDATION_MESSAGES) {
7362
+ if (!form.enabled) {
7363
+ return {
7364
+ ok: true,
7365
+ payload: {
7366
+ enabled: false,
7367
+ triggerType: "balance",
7368
+ currency: currency.toUpperCase()
7369
+ }
7370
+ };
7371
+ }
7372
+ const payload = {
7373
+ enabled: true,
7374
+ triggerType: "balance",
7375
+ currency: currency.toUpperCase()
7376
+ };
7377
+ const minorPerMajor = getMinorUnitsPerMajor(currency);
7378
+ const minMinor = getStripeMinimumMinor(currency);
7379
+ const thresholdRaw = parseNonNegativeNumber(form.thresholdAmountMajor);
7380
+ if (thresholdRaw == null) {
7381
+ return { ok: false, error: messages.invalidThreshold };
7382
+ }
7383
+ if (form.thresholdUnit === "credits") {
7384
+ const major = estimateCurrencyMajorFromCredits(
7385
+ thresholdRaw,
7386
+ currency,
7387
+ conversion?.creditsPerMinorUnit,
7388
+ conversion?.displayExchangeRate
7389
+ );
7390
+ if (major == null) {
7391
+ return { ok: false, error: messages.invalidThreshold };
7392
+ }
7393
+ payload.thresholdAmountMajor = major;
7394
+ } else {
7395
+ payload.thresholdAmountMajor = thresholdRaw;
7396
+ }
7397
+ if (payload.thresholdAmountMajor == null || payload.thresholdAmountMajor <= 0) {
7398
+ return { ok: false, error: messages.thresholdTooLow };
7399
+ }
7400
+ const amountRaw = parsePositiveNumber(form.topupAmountMajor);
7401
+ if (amountRaw == null) {
7402
+ return {
7403
+ ok: false,
7404
+ error: interpolate(messages.minTopupAmount, {
7405
+ amount: formatPrice(minMinor, currency, { free: "" })
7406
+ })
7407
+ };
7408
+ }
7409
+ const amountMajor = form.topupUnit === "credits" ? estimateCurrencyMajorFromCredits(
7410
+ amountRaw,
7411
+ currency,
7412
+ conversion?.creditsPerMinorUnit,
7413
+ conversion?.displayExchangeRate
7414
+ ) : amountRaw;
7415
+ if (amountMajor == null || Math.round(amountMajor * minorPerMajor) < minMinor) {
7416
+ return {
7417
+ ok: false,
7418
+ error: interpolate(messages.minTopupAmount, {
7419
+ amount: formatPrice(minMinor, currency, { free: "" })
7420
+ })
7421
+ };
7422
+ }
7423
+ payload.topupAmountMajor = amountMajor;
7424
+ if (form.maxMonthlySpendMajor.trim().length > 0) {
7425
+ const max = parsePositiveNumber(form.maxMonthlySpendMajor);
7426
+ if (max == null) {
7427
+ return { ok: false, error: messages.invalidMaxMonthlySpend };
7428
+ }
7429
+ if (max < amountMajor) {
7430
+ return {
7431
+ ok: false,
7432
+ error: interpolate(messages.maxMonthlySpendBelowTopup, {
7433
+ amount: formatPrice(Math.round(amountMajor * minorPerMajor), currency, { free: "" })
7434
+ })
7435
+ };
7436
+ }
7437
+ payload.maxMonthlySpendMajor = max;
7438
+ }
7439
+ return { ok: true, payload };
7440
+ }
7441
+ function triggerAmountMajorFromConfig(config) {
7442
+ const topupCurrency = config.topup.currency.toUpperCase();
7443
+ const minorPerMajor = getMinorUnitsPerMajor(topupCurrency);
7444
+ const { thresholdAmountMinor } = config.trigger;
7445
+ if (!Number.isFinite(thresholdAmountMinor) || thresholdAmountMinor < 0) {
7446
+ return null;
7447
+ }
7448
+ return thresholdAmountMinor / minorPerMajor;
7449
+ }
7450
+ function configToAutoRechargeInput(config, options) {
7451
+ if (!config.enabled) return null;
7452
+ const display = options?.display ?? config.display;
7453
+ const currency = (display?.currency ?? config.topup.currency ?? options?.currency ?? "USD").toUpperCase();
7454
+ if (display?.thresholdAmountMajor != null && display.topupAmountMajor != null) {
7455
+ return {
7456
+ enabled: true,
7457
+ triggerType: "balance",
7458
+ thresholdAmountMajor: display.thresholdAmountMajor,
7459
+ topupAmountMajor: display.topupAmountMajor,
7460
+ currency,
7461
+ ...config.maxMonthlySpendMinor != null ? {
7462
+ maxMonthlySpendMajor: config.maxMonthlySpendMinor / getMinorUnitsPerMajor(currency)
7463
+ } : {}
7464
+ };
7465
+ }
7466
+ const thresholdMajor = triggerAmountMajorFromConfig(config);
7467
+ const topupAmountMajor = config.topup.amountMinor / getMinorUnitsPerMajor(currency);
7468
+ if (thresholdMajor == null || !Number.isFinite(topupAmountMajor) || topupAmountMajor <= 0) {
7469
+ return null;
7470
+ }
7471
+ return {
7472
+ enabled: true,
7473
+ triggerType: "balance",
7474
+ thresholdAmountMajor: Math.max(0, thresholdMajor),
7475
+ topupAmountMajor,
7476
+ currency,
7477
+ ...config.maxMonthlySpendMinor != null ? {
7478
+ maxMonthlySpendMajor: config.maxMonthlySpendMinor / getMinorUnitsPerMajor(currency)
7479
+ } : {}
7480
+ };
7481
+ }
7482
+ function configToForm(config, currency) {
7483
+ const base = createDefaultAutoRechargeForm(currency);
7484
+ const display = config.display;
7485
+ if (display?.thresholdAmountMajor != null && display.topupAmountMajor != null) {
7486
+ const thresholdStr2 = String(Math.max(0, display.thresholdAmountMajor));
7487
+ const topupStr2 = String(display.topupAmountMajor);
7488
+ return {
7489
+ ...base,
7490
+ enabled: config.enabled,
7491
+ thresholdAmountMajor: thresholdStr2,
7492
+ topupAmountMajor: topupStr2,
7493
+ maxMonthlySpendMajor: maxMonthlySpendMajorFromConfig(config, currency),
7494
+ ...amountAnchors(thresholdStr2, topupStr2, "currency")
7495
+ };
7496
+ }
7497
+ const thresholdMajor = triggerAmountMajorFromConfig(config);
7498
+ const thresholdStr = thresholdMajor != null ? String(Math.max(0, thresholdMajor)) : "0";
7499
+ const topupStr = String(config.topup.amountMinor / getMinorUnitsPerMajor(currency));
7500
+ return {
7501
+ ...base,
7502
+ enabled: config.enabled,
7503
+ thresholdAmountMajor: thresholdStr,
7504
+ topupAmountMajor: topupStr,
7505
+ maxMonthlySpendMajor: maxMonthlySpendMajorFromConfig(config, currency),
7506
+ ...amountAnchors(thresholdStr, topupStr, "currency")
7507
+ };
7508
+ }
7509
+ function formatAmountWithUnit(value, unit, currency) {
7510
+ const num = Number(value);
7511
+ if (!value || !Number.isFinite(num)) return "\u2014";
7512
+ if (unit === "credits") {
7513
+ return `${new Intl.NumberFormat().format(num)} credits`;
7514
+ }
7515
+ return formatPrice(Math.round(num * getMinorUnitsPerMajor(currency)), currency, { free: "" });
7516
+ }
7517
+ function buildSummaryLine(form, currency) {
7518
+ if (!form.enabled) return null;
7519
+ const thresholdDisplay = formatAmountWithUnit(
7520
+ form.thresholdAmountMajor,
7521
+ form.thresholdUnit,
7522
+ currency
7523
+ );
7524
+ const fixedDisplay = formatAmountWithUnit(form.topupAmountMajor, form.topupUnit, currency);
7525
+ return `When my balance falls below ${thresholdDisplay}, add ${fixedDisplay}.`;
7526
+ }
7527
+ function convertAmountForUnitFlip(value, fromUnit, toUnit, currency, creditsPerMinorUnit, displayExchangeRate) {
7528
+ if (fromUnit === toUnit) return value;
7529
+ const parsed = Number(value);
7530
+ if (!Number.isFinite(parsed) || parsed <= 0) return value;
7531
+ if (fromUnit === "currency" && toUnit === "credits") {
7532
+ const credits = estimateCredits(parsed, currency, creditsPerMinorUnit, displayExchangeRate);
7533
+ return credits != null ? String(credits) : value;
7534
+ }
7535
+ const major = estimateCurrencyMajorFromCredits(
7536
+ parsed,
7537
+ currency,
7538
+ creditsPerMinorUnit,
7539
+ displayExchangeRate
7540
+ );
7541
+ return major != null ? String(major) : value;
7542
+ }
7543
+ function flipUnitValue(anchor, targetUnit, currency, creditsPerMinorUnit, displayExchangeRate) {
7544
+ if (targetUnit === anchor.unit) {
7545
+ return { value: anchor.value, unit: anchor.unit };
7546
+ }
7547
+ const converted = convertAmountForUnitFlip(
7548
+ anchor.value,
7549
+ anchor.unit,
7550
+ targetUnit,
7551
+ currency,
7552
+ creditsPerMinorUnit,
7553
+ displayExchangeRate
7554
+ );
7555
+ return { value: converted, unit: targetUnit };
7556
+ }
7557
+
6560
7558
  // src/hooks/useCheckoutFlow.ts
6561
- import { useCallback as useCallback21, useMemo as useMemo16, useRef as useRef10, useState as useState17 } from "react";
7559
+ import { useCallback as useCallback22, useMemo as useMemo17, useRef as useRef11, useState as useState19 } from "react";
6562
7560
 
6563
7561
  // src/primitives/checkout/shared.ts
6564
7562
  import {
@@ -6566,6 +7564,7 @@ import {
6566
7564
  creditsPerUnitFromBalance,
6567
7565
  includedUnits,
6568
7566
  meterName,
7567
+ planPricingShape,
6569
7568
  usageRate
6570
7569
  } from "@solvapay/core";
6571
7570
  function isPayg(plan) {
@@ -6597,28 +7596,60 @@ function buildDefaultCheckoutPlanFilter(allPlans) {
6597
7596
  }
6598
7597
  function formatContinueLabel(plan, locale, pricingOption) {
6599
7598
  if (!plan) return "Continue";
6600
- if (isPayg(plan)) {
6601
- return `Continue with ${plan.name ?? "Pay as you go"}`;
6602
- }
6603
- const option = pricingOption ?? resolveBootstrapPlanPricing(plan);
6604
- const currency = option.currency.toUpperCase();
6605
- const priceLabel = formatPrice(option.price ?? 0, currency, { locale });
6606
- const interval = planBillingInterval(plan);
6607
- const cycle = interval ? `/${shortCycle(interval)}` : "";
6608
- return `Continue with ${plan.name ?? "Plan"} \u2014 ${priceLabel}${cycle}`;
7599
+ const priceLabel = formatPlanPriceLabel(plan, locale ?? "en-US", pricingOption);
7600
+ return `Continue with ${plan.name ?? "Plan"} \u2014 ${priceLabel}`;
7601
+ }
7602
+ function planBillingCycle(plan) {
7603
+ return readBillingCycle(plan);
6609
7604
  }
6610
7605
  function planBillingInterval(plan) {
6611
7606
  return readBillingCycle(plan)?.interval ?? null;
6612
7607
  }
7608
+ function formatCycleSuffix(cycle) {
7609
+ if (!cycle) return "";
7610
+ const short = shortCycle(cycle.interval);
7611
+ if (cycle.count && cycle.count > 1) return `/${cycle.count} ${short}`;
7612
+ return `/${short}`;
7613
+ }
7614
+ function formatPlanPriceLabel(plan, locale, pricingOption) {
7615
+ const shape = planPricingShape(plan);
7616
+ switch (shape.shape) {
7617
+ case "free":
7618
+ return "Free";
7619
+ case "usage":
7620
+ return "Pay per use";
7621
+ case "recurring":
7622
+ case "hybrid": {
7623
+ const option = pricingOption ?? resolveBootstrapPlanPricing(plan);
7624
+ const amount = option.price ?? shape.headlineMinor;
7625
+ const currency = option.currency.toUpperCase();
7626
+ const priceLabel = formatPrice(amount, currency, { locale, free: "" });
7627
+ return `${priceLabel}${formatCycleSuffix(shape.cycle)}`;
7628
+ }
7629
+ case "oneTime": {
7630
+ const option = pricingOption ?? resolveBootstrapPlanPricing(plan);
7631
+ const amount = option.price ?? shape.headlineMinor;
7632
+ const currency = option.currency.toUpperCase();
7633
+ const priceLabel = formatPrice(amount, currency, { locale, free: "" });
7634
+ return `${priceLabel} once`;
7635
+ }
7636
+ }
7637
+ }
7638
+ function singularMeterNoun(unit) {
7639
+ if (unit.endsWith("ies")) return `${unit.slice(0, -3)}y`;
7640
+ if (unit.endsWith("s") && !unit.endsWith("ss")) return unit.slice(0, -1);
7641
+ return unit;
7642
+ }
6613
7643
  function formatPaygRate(plan, locale, balance) {
6614
7644
  const rate = usageRate(plan);
6615
7645
  if (!rate || !(rate.amountMinor > 0)) return null;
6616
7646
  const prefix = rate.tiered ? "from " : "";
6617
7647
  const credits = creditsPerUnitFromBalance(plan, balance);
7648
+ const unit = singularMeterNoun(rate.meter ?? meterName(plan) ?? "unit");
6618
7649
  if (credits != null) {
6619
- return `${prefix}${credits.toLocaleString(locale)} ${credits === 1 ? "credit" : "credits"} / call`;
7650
+ return `${prefix}${credits.toLocaleString(locale)} ${credits === 1 ? "credit" : "credits"} / ${unit}`;
6620
7651
  }
6621
- return `${prefix}${formatPrice(rate.amountMinor, rate.currency.toUpperCase(), { locale })} / call`;
7652
+ return `${prefix}${formatPrice(rate.amountMinor, rate.currency.toUpperCase(), { locale })} / ${unit}`;
6622
7653
  }
6623
7654
  function inferIncludedUnits(plan) {
6624
7655
  const cap = includedUnits(plan);
@@ -6628,12 +7659,13 @@ function planMeterName(plan) {
6628
7659
  return meterName(plan);
6629
7660
  }
6630
7661
  function shortCycle(cycle) {
6631
- if (!cycle) return "mo";
7662
+ if (!cycle) return "";
6632
7663
  const lc = cycle.toLowerCase();
6633
- if (lc.startsWith("year") || lc === "annually" || lc === "annual") return "yr";
6634
- if (lc.startsWith("week")) return "wk";
6635
- if (lc.startsWith("day")) return "d";
6636
- return "mo";
7664
+ if (lc === "week" || lc.startsWith("week")) return "wk";
7665
+ if (lc === "year" || lc.startsWith("year") || lc === "annually" || lc === "annual") return "yr";
7666
+ if (lc === "month" || lc.startsWith("month")) return "mo";
7667
+ if (lc === "day" || lc.startsWith("day")) return "d";
7668
+ return cycle;
6637
7669
  }
6638
7670
 
6639
7671
  // src/hooks/useCheckoutFlow.ts
@@ -6643,10 +7675,10 @@ function resolveInitialCheckoutStep(initialStep) {
6643
7675
  }
6644
7676
  function useCheckoutFlow(opts) {
6645
7677
  const { productRef, initialStep = "plan", initialAmountMinor = null } = opts;
6646
- const onPlanSelectRef = useRef10(opts.onPlanSelect);
6647
- const onAmountSelectRef = useRef10(opts.onAmountSelect);
6648
- const onPurchaseSuccessRef = useRef10(opts.onPurchaseSuccess);
6649
- const onErrorRef = useRef10(opts.onError);
7678
+ const onPlanSelectRef = useRef11(opts.onPlanSelect);
7679
+ const onAmountSelectRef = useRef11(opts.onAmountSelect);
7680
+ const onPurchaseSuccessRef = useRef11(opts.onPurchaseSuccess);
7681
+ const onErrorRef = useRef11(opts.onError);
6650
7682
  onPlanSelectRef.current = opts.onPlanSelect;
6651
7683
  onAmountSelectRef.current = opts.onAmountSelect;
6652
7684
  onPurchaseSuccessRef.current = opts.onPurchaseSuccess;
@@ -6659,8 +7691,8 @@ function useCheckoutFlow(opts) {
6659
7691
  const { creditsPerMinorUnit, displayExchangeRate, adjustBalance } = balance;
6660
7692
  const { refetchPurchase } = useSolvaPay();
6661
7693
  const { merchant } = useMerchant();
6662
- const [topupCurrencyOverride, setTopupCurrencyOverride] = useState17(null);
6663
- const topupCurrencies = useMemo16(() => {
7694
+ const [topupCurrencyOverride, setTopupCurrencyOverride] = useState19(null);
7695
+ const topupCurrencies = useMemo17(() => {
6664
7696
  const fromMerchant = (merchant?.supportedTopupCurrencies ?? []).map((code) => code.toUpperCase()).filter(Boolean);
6665
7697
  const fallback = opts.topupCurrency?.toUpperCase() ?? merchant?.defaultCurrency?.toUpperCase() ?? null;
6666
7698
  const list = fromMerchant.length > 0 ? fromMerchant : fallback ? [fallback] : [];
@@ -6670,22 +7702,22 @@ function useCheckoutFlow(opts) {
6670
7702
  const planPickerCurrency = planCtx.preferredCurrency?.toUpperCase() ?? null;
6671
7703
  const topupCurrency = (topupCurrencyOverride && topupCurrencies.includes(topupCurrencyOverride) ? topupCurrencyOverride : null) ?? (planPickerCurrency && topupCurrencies.includes(planPickerCurrency) ? planPickerCurrency : null) ?? resolvedDefaultTopupCurrency;
6672
7704
  const topupCurrencyReady = topupCurrency != null;
6673
- const setTopupCurrency = useCallback21((code) => {
7705
+ const setTopupCurrency = useCallback22((code) => {
6674
7706
  const normalized = code.toUpperCase();
6675
7707
  setTopupCurrencyOverride(normalized);
6676
7708
  }, []);
6677
- const [step, setStep] = useState17(() => resolveInitialCheckoutStep(initialStep));
6678
- const [status, setStatus] = useState17("idle");
6679
- const [selectedAmountMinor, setSelectedAmountMinor] = useState17(initialAmountMinor);
6680
- const [successMeta, setSuccessMeta] = useState17(null);
6681
- const [error, setError] = useState17(null);
6682
- const selectedAmountMinorRef = useRef10(initialAmountMinor);
7709
+ const [step, setStep] = useState19(() => resolveInitialCheckoutStep(initialStep));
7710
+ const [status, setStatus] = useState19("idle");
7711
+ const [selectedAmountMinor, setSelectedAmountMinor] = useState19(initialAmountMinor);
7712
+ const [successMeta, setSuccessMeta] = useState19(null);
7713
+ const [error, setError] = useState19(null);
7714
+ const selectedAmountMinorRef = useRef11(initialAmountMinor);
6683
7715
  const selectedPlan = planCtx.selectedPlan;
6684
7716
  const selectedPlanRef = planCtx.selectedPlanRef;
6685
7717
  const selectedPlanShape = toBootstrapPlanLike(selectedPlan);
6686
7718
  const branch = selectedPlanShape ? isPayg(selectedPlanShape) ? "payg" : "recurring" : null;
6687
7719
  const canGoBack = step === "amount" || step === "payment";
6688
- const selectPlan = useCallback21(
7720
+ const selectPlan = useCallback22(
6689
7721
  (ref) => {
6690
7722
  planCtx.select(ref);
6691
7723
  const plan = planCtx.plans.find((p) => p.reference === ref) ?? null;
@@ -6695,7 +7727,7 @@ function useCheckoutFlow(opts) {
6695
7727
  },
6696
7728
  [planCtx]
6697
7729
  );
6698
- const selectAmount = useCallback21(
7730
+ const selectAmount = useCallback22(
6699
7731
  (amountMinor) => {
6700
7732
  selectedAmountMinorRef.current = amountMinor;
6701
7733
  setSelectedAmountMinor(amountMinor);
@@ -6704,7 +7736,7 @@ function useCheckoutFlow(opts) {
6704
7736
  },
6705
7737
  [topupCurrency]
6706
7738
  );
6707
- const runActivate = useCallback21(async () => {
7739
+ const runActivate = useCallback22(async () => {
6708
7740
  if (!selectedPlanShape || !selectedPlanRef) return false;
6709
7741
  setError(null);
6710
7742
  setStatus("activating");
@@ -6736,7 +7768,7 @@ function useCheckoutFlow(opts) {
6736
7768
  return false;
6737
7769
  }
6738
7770
  }, [balance, locale, productRef, selectedPlanRef, selectedPlanShape, transport]);
6739
- const advanceFromPlan = useCallback21(async () => {
7771
+ const advanceFromPlan = useCallback22(async () => {
6740
7772
  if (!selectedPlanShape || !selectedPlanRef) return;
6741
7773
  if (branch === "payg") {
6742
7774
  if (planCtx.currentPlanRef === selectedPlanRef) {
@@ -6774,7 +7806,7 @@ function useCheckoutFlow(opts) {
6774
7806
  setError(null);
6775
7807
  setStep("payment");
6776
7808
  }, [balance, branch, locale, planCtx.currentPlanRef, runActivate, selectedPlanRef, selectedPlanShape]);
6777
- const recordPaygSuccess = useCallback21(
7809
+ const recordPaygSuccess = useCallback22(
6778
7810
  (creditsAddedFromBackend) => {
6779
7811
  if (!selectedPlanShape || selectedAmountMinor == null) return;
6780
7812
  const currency = topupCurrency ?? "USD";
@@ -6818,7 +7850,7 @@ function useCheckoutFlow(opts) {
6818
7850
  transport
6819
7851
  ]
6820
7852
  );
6821
- const recordRecurringSuccess = useCallback21(() => {
7853
+ const recordRecurringSuccess = useCallback22(() => {
6822
7854
  if (!selectedPlanShape) return;
6823
7855
  const pricingOption = selectedPlan ? resolvePlanPricingOption(selectedPlan, planSelection?.selectedCurrency) : {
6824
7856
  currency: selectedPlanShape.currency ?? "USD",
@@ -6838,7 +7870,7 @@ function useCheckoutFlow(opts) {
6838
7870
  setStep("success");
6839
7871
  onPurchaseSuccessRef.current?.(meta);
6840
7872
  }, [selectedPlanShape, selectedPlan, planSelection?.selectedCurrency]);
6841
- const advance = useCallback21(async () => {
7873
+ const advance = useCallback22(async () => {
6842
7874
  if (step === "plan") {
6843
7875
  await advanceFromPlan();
6844
7876
  return;
@@ -6865,7 +7897,7 @@ function useCheckoutFlow(opts) {
6865
7897
  selectedAmountMinor,
6866
7898
  step
6867
7899
  ]);
6868
- const back = useCallback21(() => {
7900
+ const back = useCallback22(() => {
6869
7901
  setError(null);
6870
7902
  if (step === "amount") {
6871
7903
  setStep("plan");
@@ -6876,7 +7908,7 @@ function useCheckoutFlow(opts) {
6876
7908
  return;
6877
7909
  }
6878
7910
  }, [branch, step]);
6879
- const reset = useCallback21(() => {
7911
+ const reset = useCallback22(() => {
6880
7912
  setStep("plan");
6881
7913
  selectedAmountMinorRef.current = null;
6882
7914
  setSelectedAmountMinor(null);
@@ -6885,13 +7917,13 @@ function useCheckoutFlow(opts) {
6885
7917
  setStatus("idle");
6886
7918
  setTopupCurrencyOverride(null);
6887
7919
  }, []);
6888
- const retry = useCallback21(async () => {
7920
+ const retry = useCallback22(async () => {
6889
7921
  if (status !== "error") return;
6890
7922
  if (step === "plan" && branch === "payg") {
6891
7923
  await runActivate();
6892
7924
  }
6893
7925
  }, [branch, runActivate, status, step]);
6894
- const notifyPaymentSuccess = useCallback21(
7926
+ const notifyPaymentSuccess = useCallback22(
6895
7927
  (_intent, extras) => {
6896
7928
  if (branch === "payg") {
6897
7929
  recordPaygSuccess(extras?.creditsAdded);
@@ -6901,7 +7933,7 @@ function useCheckoutFlow(opts) {
6901
7933
  },
6902
7934
  [branch, recordPaygSuccess, recordRecurringSuccess]
6903
7935
  );
6904
- return useMemo16(
7936
+ return useMemo17(
6905
7937
  () => ({
6906
7938
  step,
6907
7939
  status,
@@ -6951,11 +7983,6 @@ function useCheckoutFlow(opts) {
6951
7983
 
6952
7984
  export {
6953
7985
  createTransportCacheKey,
6954
- autoRechargeCache,
6955
- CACHE_DURATION2 as CACHE_DURATION,
6956
- autoRechargeCacheKeyFor,
6957
- writeAutoRechargeCache,
6958
- subscribeAutoRecharge,
6959
7986
  filterPurchases,
6960
7987
  getActivePurchases,
6961
7988
  getCancelledPurchasesWithEndDate,
@@ -6979,6 +8006,9 @@ export {
6979
8006
  composeEventHandlers,
6980
8007
  useCopy,
6981
8008
  useLocale,
8009
+ ExternalLinkProvider,
8010
+ useOpenExternal,
8011
+ useExternalLinkClick,
6982
8012
  LegalFooter,
6983
8013
  withPaymentElementDefaults,
6984
8014
  MissingProviderError,
@@ -7076,7 +8106,6 @@ export {
7076
8106
  PlanSelector,
7077
8107
  usePlanSelector,
7078
8108
  usePurchaseActions,
7079
- CancelPlanButton,
7080
8109
  usePurchaseStatus,
7081
8110
  CancelledPlanNoticeRoot2 as CancelledPlanNoticeRoot,
7082
8111
  CancelledPlanNoticeHeading2 as CancelledPlanNoticeHeading,
@@ -7089,8 +8118,10 @@ export {
7089
8118
  CancelledPlanNotice,
7090
8119
  useCancelledPlanNotice,
7091
8120
  useTransport,
8121
+ limitsCache,
7092
8122
  isUnlimited,
7093
8123
  useLimits,
8124
+ seedUsageSnapshot,
7094
8125
  useUsage,
7095
8126
  Root7 as Root,
7096
8127
  Bar,
@@ -7105,9 +8136,24 @@ export {
7105
8136
  planSortByPaygFirstThenAsc,
7106
8137
  buildDefaultCheckoutPlanFilter,
7107
8138
  formatContinueLabel,
8139
+ planBillingCycle,
7108
8140
  planBillingInterval,
8141
+ formatCycleSuffix,
8142
+ formatPlanPriceLabel,
8143
+ formatPaygRate,
7109
8144
  inferIncludedUnits,
7110
8145
  planMeterName,
7111
- shortCycle,
8146
+ useAutoRecharge,
8147
+ estimateCredits,
8148
+ estimateCurrencyMajorFromCredits,
8149
+ createDefaultAutoRechargeForm,
8150
+ effectiveMonthlySpend,
8151
+ isMonthlySpendCapReached,
8152
+ validateAutoRechargeForm,
8153
+ configToAutoRechargeInput,
8154
+ configToForm,
8155
+ formatAmountWithUnit,
8156
+ buildSummaryLine,
8157
+ flipUnitValue,
7112
8158
  useCheckoutFlow
7113
8159
  };