@solvapay/react 1.2.1 → 1.3.0-preview-cb8ad16e16ce8f8f357bd7f2e7068d85159df587

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.
@@ -1,4 +1,4 @@
1
- import { s as SolvaPayTransport, X as SolvaPayProviderInitial, w as SolvaPayConfig, x as BootstrapPlanLike, E as Merchant } from '../shared-DpS2yEah.cjs';
1
+ import { s as SolvaPayTransport, X as SolvaPayProviderInitial, w as SolvaPayConfig, x as BootstrapPlanLike, E as Merchant } from '../shared-BNZAIWkU.cjs';
2
2
  import { TOOL_FOR_VIEW, SolvaPayMcpViewKind, BootstrapMerchant, BootstrapProduct, BootstrapPlan, BootstrapCustomer } from '@solvapay/mcp-core';
3
3
  export { MCP_TOOL_NAMES, McpToolName } from '@solvapay/mcp-core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { s as SolvaPayTransport, X as SolvaPayProviderInitial, w as SolvaPayConfig, x as BootstrapPlanLike, E as Merchant } from '../shared-CXRbrNFw.js';
1
+ import { s as SolvaPayTransport, X as SolvaPayProviderInitial, w as SolvaPayConfig, x as BootstrapPlanLike, E as Merchant } from '../shared-lIhc8DS3.js';
2
2
  import { TOOL_FOR_VIEW, SolvaPayMcpViewKind, BootstrapMerchant, BootstrapProduct, BootstrapPlan, BootstrapCustomer } from '@solvapay/mcp-core';
3
3
  export { MCP_TOOL_NAMES, McpToolName } from '@solvapay/mcp-core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
package/dist/mcp/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  ExternalLinkGlyph,
4
4
  LaunchCustomerPortalButton,
5
5
  paymentMethodCache
6
- } from "../chunk-XRNCDI2T.js";
6
+ } from "../chunk-3UR4QQ5P.js";
7
7
  import {
8
8
  AmountPicker,
9
9
  BalanceBadge,
@@ -25,6 +25,7 @@ import {
25
25
  planSortByPaygFirstThenAsc,
26
26
  plansCache,
27
27
  productCache,
28
+ resolvePlanPricingOption,
28
29
  shortCycle,
29
30
  useAmountPicker,
30
31
  useBalance,
@@ -32,11 +33,12 @@ import {
32
33
  useCopy,
33
34
  useCustomer,
34
35
  useMerchant,
36
+ usePlanSelection,
35
37
  usePlanSelector,
36
38
  usePurchase,
37
39
  usePurchaseStatus,
38
40
  useTransport
39
- } from "../chunk-Y2UAJG67.js";
41
+ } from "../chunk-ORMHGGOU.js";
40
42
  import "../chunk-UMXOAUW7.js";
41
43
 
42
44
  // src/mcp/adapter.ts
@@ -813,16 +815,20 @@ var PlanStep = memo(function PlanStep2({
813
815
  activationError,
814
816
  cx
815
817
  }) {
816
- const { selectedPlan, selectedPlanRef } = usePlanSelector();
818
+ const { selectedPlan, selectedPlanRef, getSelectedOption } = usePlanSelector();
817
819
  const locale = useHostLocale();
818
820
  const copy = useCopy();
819
821
  const selectedPlanShape = selectedPlan;
820
- const ctaLabel = formatContinueLabel(selectedPlanShape, locale);
822
+ const pricingOption = selectedPlan ? getSelectedOption(selectedPlan) : void 0;
823
+ const ctaLabel = formatContinueLabel(selectedPlanShape, locale, pricingOption);
821
824
  const showBanner = fromPaywall && !hideUpgradeBanner;
822
825
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
823
826
  onBack ? /* @__PURE__ */ jsx5(BackLink, { label: copy.checkout.backToAccount, onClick: onBack }) : null,
824
827
  showBanner ? /* @__PURE__ */ jsx5(UpgradeBanner, { kind: paywallKind, cx }) : null,
825
- /* @__PURE__ */ jsx5("h2", { className: cx.heading, children: "Choose a plan" }),
828
+ /* @__PURE__ */ jsxs4("div", { className: "solvapay-mcp-plan-step-header", children: [
829
+ /* @__PURE__ */ jsx5("h2", { className: cx.heading, children: "Choose a plan" }),
830
+ /* @__PURE__ */ jsx5(PlanSelector.CurrencySwitcher, { className: "solvapay-plan-selector-currency-switcher" })
831
+ ] }),
826
832
  /* @__PURE__ */ jsx5(PlanSelector.Grid, { className: "solvapay-plan-selector-grid", children: /* @__PURE__ */ jsxs4(PlanSelector.Card, { className: "solvapay-plan-selector-card", children: [
827
833
  /* @__PURE__ */ jsx5(PlanSelector.CardBadge, { className: "solvapay-plan-selector-card-badge" }),
828
834
  /* @__PURE__ */ jsx5(PlanSelector.CardName, { className: "solvapay-plan-selector-card-name" }),
@@ -878,17 +884,34 @@ function UpgradeBanner({
878
884
  import { memo as memo2, useState as useState3 } from "react";
879
885
  import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
880
886
  var AmountStep = memo2(function AmountStep2({
881
- plan,
887
+ topupCurrency,
888
+ topupCurrencies,
889
+ onCurrencyChange,
882
890
  onBack,
883
891
  onContinue,
884
892
  cx
885
893
  }) {
886
- const currency = (plan.currency ?? "USD").toUpperCase();
894
+ const currency = (topupCurrency ?? "USD").toUpperCase();
887
895
  const locale = useHostLocale();
888
896
  const [stagedAmountMinor, setStagedAmountMinor] = useState3(null);
897
+ const currencies = topupCurrencies ?? [];
898
+ const showCurrencySwitch = currencies.length > 1 && !!onCurrencyChange;
899
+ const currencyDisplay = showCurrencySwitch ? "code" : "symbol";
889
900
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
890
901
  /* @__PURE__ */ jsx6(BackLink, { label: "Back", onClick: onBack }),
891
- /* @__PURE__ */ jsx6("h2", { className: cx.heading, children: "How many credits?" }),
902
+ /* @__PURE__ */ jsxs5("div", { className: "solvapay-mcp-step-header", children: [
903
+ /* @__PURE__ */ jsx6("h2", { className: cx.heading, children: "How many credits?" }),
904
+ showCurrencySwitch && /* @__PURE__ */ jsx6(
905
+ "select",
906
+ {
907
+ className: "solvapay-mcp-currency-switch",
908
+ value: currency,
909
+ onChange: (event) => onCurrencyChange?.(event.target.value),
910
+ "aria-label": "Topup currency",
911
+ children: currencies.map((code) => /* @__PURE__ */ jsx6("option", { value: code, children: code }, code))
912
+ }
913
+ )
914
+ ] }),
892
915
  /* @__PURE__ */ jsx6("p", { className: cx.muted, children: "Top up to start using the tool." }),
893
916
  /* @__PURE__ */ jsxs5(
894
917
  AmountPicker.Root,
@@ -898,14 +921,14 @@ var AmountStep = memo2(function AmountStep2({
898
921
  className: cx.amountPicker,
899
922
  onChange: (value) => setStagedAmountMinor(value),
900
923
  children: [
901
- /* @__PURE__ */ jsx6(PresetAmountRow, { cx }),
902
- /* @__PURE__ */ jsx6(CustomAmountRow, { rowClassName: cx.amountCustom }),
924
+ /* @__PURE__ */ jsx6(PresetAmountRow, { cx, currencyDisplay }),
925
+ /* @__PURE__ */ jsx6(CustomAmountRow, { rowClassName: cx.amountCustom, currencyDisplay }),
903
926
  /* @__PURE__ */ jsx6(
904
927
  AmountPicker.Confirm,
905
928
  {
906
929
  className: cx.button,
907
930
  onConfirm: (amountMinor) => onContinue(amountMinor),
908
- children: stagedAmountMinor ? `Continue \u2014 ${formatPrice(stagedAmountMinor, currency, { locale })}` : "Continue"
931
+ children: stagedAmountMinor ? `Continue \u2014 ${formatPrice(stagedAmountMinor, currency, { locale, currencyDisplay })}` : "Continue"
909
932
  }
910
933
  )
911
934
  ]
@@ -913,14 +936,18 @@ var AmountStep = memo2(function AmountStep2({
913
936
  )
914
937
  ] });
915
938
  });
916
- function PresetAmountRow({ cx }) {
939
+ function PresetAmountRow({
940
+ cx,
941
+ currencyDisplay
942
+ }) {
917
943
  const { quickAmounts, currency } = useAmountPicker();
918
944
  const locale = useHostLocale();
919
945
  const popularIndex = Math.min(1, quickAmounts.length - 1);
920
946
  return /* @__PURE__ */ jsx6("div", { className: cx.amountOptions, children: quickAmounts.map((amount, i) => {
921
947
  const label = formatPrice(amount * getMinorUnitsPerMajor(currency), currency, {
922
948
  locale,
923
- free: ""
949
+ free: "",
950
+ currencyDisplay
924
951
  });
925
952
  return /* @__PURE__ */ jsx6(
926
953
  AmountPicker.Option,
@@ -928,16 +955,21 @@ function PresetAmountRow({ cx }) {
928
955
  amount,
929
956
  className: cx.amountOption,
930
957
  "data-popular": i === popularIndex ? "" : void 0,
931
- "aria-label": `${label}${i === popularIndex ? " (popular)" : ""}`
958
+ "aria-label": `${label}${i === popularIndex ? " (popular)" : ""}`,
959
+ children: label
932
960
  },
933
961
  amount
934
962
  );
935
963
  }) });
936
964
  }
937
- function CustomAmountRow({ rowClassName }) {
938
- const { currencySymbol } = useAmountPicker();
965
+ function CustomAmountRow({
966
+ rowClassName,
967
+ currencyDisplay
968
+ }) {
969
+ const { currencySymbol, currency } = useAmountPicker();
970
+ const prefix = currencyDisplay === "code" ? currency.toUpperCase() : currencySymbol;
939
971
  return /* @__PURE__ */ jsxs5("div", { className: rowClassName, children: [
940
- /* @__PURE__ */ jsx6("span", { className: "solvapay-mcp-amount-currency-symbol", children: currencySymbol }),
972
+ /* @__PURE__ */ jsx6("span", { className: "solvapay-mcp-amount-currency-symbol", children: prefix }),
941
973
  /* @__PURE__ */ jsx6(AmountPicker.Custom, { className: "solvapay-mcp-amount-custom-input", placeholder: "0.00" })
942
974
  ] });
943
975
  }
@@ -946,14 +978,15 @@ function CustomAmountRow({ rowClassName }) {
946
978
  import { memo as memo3 } from "react";
947
979
  import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
948
980
  var PaygPaymentStep = memo3(function PaygPaymentStep2({
949
- plan,
981
+ plan: _plan,
950
982
  amountMinor,
983
+ topupCurrency,
951
984
  returnUrl,
952
985
  onBack,
953
986
  onSuccess,
954
987
  cx
955
988
  }) {
956
- const currency = (plan.currency ?? "USD").toUpperCase();
989
+ const currency = (topupCurrency ?? "USD").toUpperCase();
957
990
  const locale = useHostLocale();
958
991
  const { creditsPerMinorUnit, displayExchangeRate } = useBalance();
959
992
  const creditsAdded = creditsPerMinorUnit != null && creditsPerMinorUnit > 0 ? Math.floor(amountMinor / (displayExchangeRate ?? 1) * creditsPerMinorUnit) : null;
@@ -1006,9 +1039,14 @@ var RecurringPaymentStep = memo4(function RecurringPaymentStep2({
1006
1039
  onSuccess,
1007
1040
  cx
1008
1041
  }) {
1009
- const currency = (plan.currency ?? "USD").toUpperCase();
1042
+ const planSelection = usePlanSelection();
1043
+ const pricingOption = resolvePlanPricingOption(
1044
+ plan,
1045
+ planSelection?.selectedCurrency
1046
+ );
1047
+ const currency = pricingOption.currency.toUpperCase();
1010
1048
  const locale = useHostLocale();
1011
- const amountMinor = plan.price ?? 0;
1049
+ const amountMinor = pricingOption.price ?? 0;
1012
1050
  const cycle = plan.billingCycle ?? "monthly";
1013
1051
  const credits = inferIncludedCredits(plan);
1014
1052
  const planName = plan.name ?? "Plan";
@@ -1265,6 +1303,9 @@ function McpCheckoutBody({
1265
1303
  AmountStep,
1266
1304
  {
1267
1305
  plan: selectedPlanShape,
1306
+ topupCurrency: flow.topupCurrency,
1307
+ topupCurrencies: flow.topupCurrencies,
1308
+ onCurrencyChange: flow.setTopupCurrency,
1268
1309
  onBack: () => flow.back(),
1269
1310
  onContinue: (amountMinor) => {
1270
1311
  flow.selectAmount(amountMinor);
@@ -1281,6 +1322,7 @@ function McpCheckoutBody({
1281
1322
  {
1282
1323
  plan: selectedPlanShape,
1283
1324
  amountMinor: flow.selectedAmountMinor,
1325
+ topupCurrency: flow.topupCurrency,
1284
1326
  returnUrl,
1285
1327
  onBack: () => flow.back(),
1286
1328
  onSuccess: (extras) => flow.notifyPaymentSuccess(void 0, extras),
@@ -1663,12 +1705,17 @@ function McpTopupView({
1663
1705
  }
1664
1706
  if (probe === "blocked")
1665
1707
  return /* @__PURE__ */ jsx12(HostedTopupFallback, { cx, onBack });
1666
- const currency = merchant?.defaultCurrency?.toUpperCase() ?? FALLBACK_TOPUP_CURRENCY;
1708
+ const defaultCurrency = merchant?.defaultCurrency?.toUpperCase() ?? FALLBACK_TOPUP_CURRENCY;
1709
+ const fromMerchant = (merchant?.supportedTopupCurrencies ?? []).map((code) => code.toUpperCase()).filter(Boolean);
1710
+ const topupCurrencies = Array.from(
1711
+ new Set(fromMerchant.length > 0 ? fromMerchant : [defaultCurrency])
1712
+ );
1667
1713
  return /* @__PURE__ */ jsx12(
1668
1714
  EmbeddedTopup,
1669
1715
  {
1670
1716
  returnUrl,
1671
- currency,
1717
+ defaultCurrency,
1718
+ topupCurrencies,
1672
1719
  onTopupSuccess,
1673
1720
  onBack,
1674
1721
  cx
@@ -1677,16 +1724,25 @@ function McpTopupView({
1677
1724
  }
1678
1725
  function EmbeddedTopup({
1679
1726
  returnUrl,
1680
- currency,
1727
+ defaultCurrency,
1728
+ topupCurrencies,
1681
1729
  onTopupSuccess,
1682
1730
  onBack,
1683
1731
  cx
1684
1732
  }) {
1685
1733
  const [committedAmountMinor, setCommittedAmountMinor] = useState5(null);
1686
1734
  const [justPaidMinor, setJustPaidMinor] = useState5(null);
1687
- const { adjustBalance, credits, creditsPerMinorUnit, displayExchangeRate } = useBalance();
1735
+ const [selectedCurrency, setSelectedCurrency] = useState5(defaultCurrency);
1736
+ const currency = selectedCurrency;
1737
+ const showCurrencySwitch = topupCurrencies.length > 1;
1738
+ const { adjustBalance, credits, creditsPerMinorUnit, displayCurrency, displayExchangeRate } = useBalance();
1688
1739
  const locale = useHostLocale();
1689
1740
  const { notifyModelContext, notifySuccess } = useMcpBridge();
1741
+ const handleCurrencyChange = (code) => {
1742
+ setSelectedCurrency(code.toUpperCase());
1743
+ setCommittedAmountMinor(null);
1744
+ setJustPaidMinor(null);
1745
+ };
1690
1746
  if (justPaidMinor != null) {
1691
1747
  const displayAmount = formatPrice(justPaidMinor, currency, { locale, free: "" });
1692
1748
  return /* @__PURE__ */ jsxs11("div", { className: cx.card, children: [
@@ -1720,7 +1776,8 @@ function EmbeddedTopup({
1720
1776
  }
1721
1777
  if (committedAmountMinor != null && committedAmountMinor > 0) {
1722
1778
  const displayAmount = formatPrice(committedAmountMinor, currency, { locale, free: "" });
1723
- const creditsAdded = creditsPerMinorUnit != null && creditsPerMinorUnit > 0 ? Math.floor(
1779
+ const rateAppliesToCurrency = displayCurrency != null && currency.toUpperCase() === displayCurrency.toUpperCase();
1780
+ const creditsAdded = rateAppliesToCurrency && creditsPerMinorUnit != null && creditsPerMinorUnit > 0 ? Math.floor(
1724
1781
  committedAmountMinor / (displayExchangeRate ?? 1) * creditsPerMinorUnit
1725
1782
  ) : null;
1726
1783
  const formattedBalance = credits != null ? new Intl.NumberFormat(locale).format(credits) : null;
@@ -1786,9 +1843,36 @@ function EmbeddedTopup({
1786
1843
  /* @__PURE__ */ jsx12("h2", { className: cx.heading, children: "Add credits" }),
1787
1844
  /* @__PURE__ */ jsx12(BalanceBadge, {})
1788
1845
  ] }),
1846
+ showCurrencySwitch ? /* @__PURE__ */ jsxs11("div", { className: "solvapay-mcp-step-header", children: [
1847
+ /* @__PURE__ */ jsx12("span", { className: cx.muted, children: "Pay in" }),
1848
+ /* @__PURE__ */ jsx12(
1849
+ "select",
1850
+ {
1851
+ className: "solvapay-mcp-currency-switch",
1852
+ value: currency,
1853
+ onChange: (event) => handleCurrencyChange(event.target.value),
1854
+ "aria-label": "Topup currency",
1855
+ children: topupCurrencies.map((code) => /* @__PURE__ */ jsx12("option", { value: code, children: code }, code))
1856
+ }
1857
+ )
1858
+ ] }) : null,
1789
1859
  /* @__PURE__ */ jsxs11(AmountPicker.Root, { currency, emit: "minor", className: cx.amountPicker, children: [
1790
- /* @__PURE__ */ jsx12(QuickAmountOptions, { className: cx.amountOptions, optionClassName: cx.amountOption }),
1791
- /* @__PURE__ */ jsx12(CustomAmountRow2, { rowClassName: cx.amountCustom }),
1860
+ /* @__PURE__ */ jsx12(
1861
+ QuickAmountOptions,
1862
+ {
1863
+ className: cx.amountOptions,
1864
+ optionClassName: cx.amountOption,
1865
+ currencyDisplay: showCurrencySwitch ? "code" : "symbol",
1866
+ locale
1867
+ }
1868
+ ),
1869
+ /* @__PURE__ */ jsx12(
1870
+ CustomAmountRow2,
1871
+ {
1872
+ rowClassName: cx.amountCustom,
1873
+ currencyDisplay: showCurrencySwitch ? "code" : "symbol"
1874
+ }
1875
+ ),
1792
1876
  /* @__PURE__ */ jsx12(
1793
1877
  AmountPicker.Confirm,
1794
1878
  {
@@ -1810,15 +1894,28 @@ function EmbeddedTopup({
1810
1894
  }
1811
1895
  function QuickAmountOptions({
1812
1896
  className,
1813
- optionClassName
1897
+ optionClassName,
1898
+ currencyDisplay,
1899
+ locale
1814
1900
  }) {
1815
- const { quickAmounts } = useAmountPicker();
1816
- return /* @__PURE__ */ jsx12("div", { className, children: quickAmounts.map((amount) => /* @__PURE__ */ jsx12(AmountPicker.Option, { amount, className: optionClassName }, amount)) });
1901
+ const { quickAmounts, currency } = useAmountPicker();
1902
+ return /* @__PURE__ */ jsx12("div", { className, children: quickAmounts.map((amount) => {
1903
+ const label = formatPrice(amount * getMinorUnitsPerMajor(currency), currency, {
1904
+ locale,
1905
+ free: "",
1906
+ currencyDisplay
1907
+ });
1908
+ return /* @__PURE__ */ jsx12(AmountPicker.Option, { amount, className: optionClassName, children: label }, amount);
1909
+ }) });
1817
1910
  }
1818
- function CustomAmountRow2({ rowClassName }) {
1819
- const { currencySymbol } = useAmountPicker();
1911
+ function CustomAmountRow2({
1912
+ rowClassName,
1913
+ currencyDisplay
1914
+ }) {
1915
+ const { currencySymbol, currency } = useAmountPicker();
1916
+ const prefix = currencyDisplay === "code" ? currency.toUpperCase() : currencySymbol;
1820
1917
  return /* @__PURE__ */ jsxs11("div", { className: rowClassName, children: [
1821
- /* @__PURE__ */ jsx12("span", { className: "solvapay-mcp-amount-currency-symbol", children: currencySymbol }),
1918
+ /* @__PURE__ */ jsx12("span", { className: "solvapay-mcp-amount-currency-symbol", children: prefix }),
1822
1919
  /* @__PURE__ */ jsx12(AmountPicker.Custom, { className: "solvapay-mcp-amount-custom-input", placeholder: "0.00" })
1823
1920
  ] });
1824
1921
  }
@@ -225,6 +225,28 @@ body {
225
225
  margin-bottom: 4px;
226
226
  }
227
227
 
228
+ /* AmountStep header: title left, compact topup-currency switcher right.
229
+ * Only present for multi-currency merchants; single-currency merchants
230
+ * render the bare heading exactly as before. */
231
+ .solvapay-mcp-step-header {
232
+ display: flex;
233
+ align-items: center;
234
+ justify-content: space-between;
235
+ gap: 12px;
236
+ }
237
+
238
+ .solvapay-mcp-currency-switch {
239
+ flex: 0 0 auto;
240
+ padding: 4px 8px;
241
+ border: 1px solid var(--color-border, #e2e8f0);
242
+ border-radius: 6px;
243
+ background: var(--color-background, #ffffff);
244
+ color: var(--color-text, #0f172a);
245
+ font-size: 13px;
246
+ font-weight: 600;
247
+ cursor: pointer;
248
+ }
249
+
228
250
  .solvapay-mcp-muted {
229
251
  margin: 0;
230
252
  color: var(--color-text-secondary, #475569);
@@ -978,6 +1000,23 @@ body {
978
1000
  font-size: 13px;
979
1001
  }
980
1002
 
1003
+ .solvapay-mcp-plan-step-header {
1004
+ display: flex;
1005
+ align-items: center;
1006
+ justify-content: space-between;
1007
+ gap: 12px;
1008
+ }
1009
+
1010
+ .solvapay-mcp-plan-step-header .solvapay-mcp-heading,
1011
+ .solvapay-mcp-plan-step-header > .solvapay-mcp-heading {
1012
+ margin: 0;
1013
+ }
1014
+
1015
+ .solvapay-mcp-plan-step-header .solvapay-plan-selector-currency-switcher {
1016
+ flex: 0 0 auto;
1017
+ width: auto;
1018
+ }
1019
+
981
1020
  .solvapay-mcp-checkout-dismiss {
982
1021
  align-self: center;
983
1022
  }