@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.
- package/CHANGELOG.md +11 -0
- package/dist/adapters/auth0.cjs +60 -0
- package/dist/adapters/auth0.d.cts +16 -0
- package/dist/adapters/auth0.d.ts +16 -0
- package/dist/adapters/auth0.js +15 -0
- package/dist/adapters/session-auth.cjs +49 -0
- package/dist/adapters/session-auth.d.cts +20 -0
- package/dist/adapters/session-auth.d.ts +20 -0
- package/dist/adapters/session-auth.js +6 -0
- package/dist/{chunk-XRNCDI2T.js → chunk-3UR4QQ5P.js} +1 -1
- package/dist/chunk-JTW665JR.js +25 -0
- package/dist/{chunk-Y2UAJG67.js → chunk-ORMHGGOU.js} +244 -73
- package/dist/{chunk-MYD6KTR2.js → chunk-WGDHMTDO.js} +1 -1
- package/dist/{index-DPoqku6R.d.cts → index-BwwdMBdt.d.ts} +20 -5
- package/dist/{index-Cs-QqpkJ.d.ts → index-kXLvp7EU.d.cts} +20 -5
- package/dist/index.cjs +294 -106
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +8 -3
- package/dist/mcp/index.cjs +328 -70
- package/dist/mcp/index.d.cts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +131 -34
- package/dist/mcp/styles.css +39 -0
- package/dist/primitives/index.cjs +207 -38
- package/dist/primitives/index.d.cts +22 -5
- package/dist/primitives/index.d.ts +22 -5
- package/dist/primitives/index.js +6 -2
- package/dist/{shared-DpS2yEah.d.cts → shared-BNZAIWkU.d.cts} +196 -7
- package/dist/{shared-CXRbrNFw.d.ts → shared-lIhc8DS3.d.ts} +196 -7
- package/dist/styles.css +25 -0
- package/package.json +12 -7
package/dist/mcp/index.cjs
CHANGED
|
@@ -342,7 +342,7 @@ function toMajorUnits(amountMinor, currency) {
|
|
|
342
342
|
return fractionDigits === 0 ? amountMinor : amountMinor / 100;
|
|
343
343
|
}
|
|
344
344
|
function formatPrice(amountMinor, currency, opts = {}) {
|
|
345
|
-
const { locale, interval, intervalCount = 1, free = "Free" } = opts;
|
|
345
|
+
const { locale, interval, intervalCount = 1, free = "Free", currencyDisplay } = opts;
|
|
346
346
|
if (amountMinor === 0 && free !== "") {
|
|
347
347
|
return free;
|
|
348
348
|
}
|
|
@@ -351,12 +351,16 @@ function formatPrice(amountMinor, currency, opts = {}) {
|
|
|
351
351
|
const isWhole = amountMinor % minorPerMajor === 0;
|
|
352
352
|
const fractionDigits = isWhole ? 0 : naturalFractionDigits;
|
|
353
353
|
const major = toMajorUnits(amountMinor, currency);
|
|
354
|
-
const
|
|
354
|
+
const formatterOptions = {
|
|
355
355
|
style: "currency",
|
|
356
356
|
currency: currency.toUpperCase(),
|
|
357
357
|
minimumFractionDigits: fractionDigits,
|
|
358
358
|
maximumFractionDigits: fractionDigits
|
|
359
|
-
}
|
|
359
|
+
};
|
|
360
|
+
if (currencyDisplay) {
|
|
361
|
+
formatterOptions.currencyDisplay = currencyDisplay;
|
|
362
|
+
}
|
|
363
|
+
const formatter = new Intl.NumberFormat(locale, formatterOptions);
|
|
360
364
|
const formatted = formatter.format(major);
|
|
361
365
|
if (!interval) return formatted;
|
|
362
366
|
const suffix = intervalCount > 1 ? `${intervalCount} ${interval}s` : interval;
|
|
@@ -682,6 +686,17 @@ async function buildRequestHeaders(config) {
|
|
|
682
686
|
return { headers, userId };
|
|
683
687
|
}
|
|
684
688
|
|
|
689
|
+
// src/utils/readErrorMessage.ts
|
|
690
|
+
async function readErrorMessage(res, fallbackPrefix) {
|
|
691
|
+
let serverMessage;
|
|
692
|
+
try {
|
|
693
|
+
const data = await res.clone().json();
|
|
694
|
+
serverMessage = data?.error;
|
|
695
|
+
} catch {
|
|
696
|
+
}
|
|
697
|
+
return serverMessage || `${fallbackPrefix}: ${res.statusText || res.status}`;
|
|
698
|
+
}
|
|
699
|
+
|
|
685
700
|
// src/transport/http.ts
|
|
686
701
|
async function request(config, url, opts) {
|
|
687
702
|
const { headers } = await buildRequestHeaders(config);
|
|
@@ -692,13 +707,8 @@ async function request(config, url, opts) {
|
|
|
692
707
|
}
|
|
693
708
|
const res = await fetchFn(url, init);
|
|
694
709
|
if (!res.ok) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const data = await res.clone().json();
|
|
698
|
-
serverMessage = data?.error;
|
|
699
|
-
} catch {
|
|
700
|
-
}
|
|
701
|
-
const error = new Error(serverMessage || `${opts.errorPrefix}: ${res.statusText || res.status}`);
|
|
710
|
+
const message = await readErrorMessage(res, opts.errorPrefix);
|
|
711
|
+
const error = new Error(message);
|
|
702
712
|
config?.onError?.(error, opts.onErrorContext);
|
|
703
713
|
throw error;
|
|
704
714
|
}
|
|
@@ -738,6 +748,7 @@ function createHttpTransport(config) {
|
|
|
738
748
|
const body = {};
|
|
739
749
|
if (params.planRef) body.planRef = params.planRef;
|
|
740
750
|
if (params.productRef) body.productRef = params.productRef;
|
|
751
|
+
if (params.currency) body.currency = params.currency;
|
|
741
752
|
if (params.customer && (params.customer.name || params.customer.email)) {
|
|
742
753
|
body.customer = params.customer;
|
|
743
754
|
}
|
|
@@ -970,6 +981,7 @@ var enCopy = {
|
|
|
970
981
|
currentBadge: "Current",
|
|
971
982
|
popularBadge: "Popular",
|
|
972
983
|
freeBadge: "Free",
|
|
984
|
+
usageRateLabel: "Usage-based",
|
|
973
985
|
perIntervalShort: "/{interval}",
|
|
974
986
|
continueButton: "Continue",
|
|
975
987
|
backButton: "\u2190 Back to plans",
|
|
@@ -1838,7 +1850,8 @@ async function defaultListPlans(productRef, config) {
|
|
|
1838
1850
|
const { headers } = await buildRequestHeaders(config);
|
|
1839
1851
|
const res = await fetchFn(url, { method: "GET", headers });
|
|
1840
1852
|
if (!res.ok) {
|
|
1841
|
-
const
|
|
1853
|
+
const message = await readErrorMessage(res, "Failed to fetch plans");
|
|
1854
|
+
const error = new Error(message);
|
|
1842
1855
|
config?.onError?.(error, "listPlans");
|
|
1843
1856
|
throw error;
|
|
1844
1857
|
}
|
|
@@ -3778,6 +3791,28 @@ function isPaygPlan(plan) {
|
|
|
3778
3791
|
return type === "usage-based" || type === "hybrid";
|
|
3779
3792
|
}
|
|
3780
3793
|
|
|
3794
|
+
// src/utils/planPricing.ts
|
|
3795
|
+
function getPlanPricingOptions(plan) {
|
|
3796
|
+
if (plan.pricingOptions && plan.pricingOptions.length > 0) {
|
|
3797
|
+
return plan.pricingOptions;
|
|
3798
|
+
}
|
|
3799
|
+
return [
|
|
3800
|
+
{
|
|
3801
|
+
currency: plan.currency ?? "USD",
|
|
3802
|
+
price: plan.price ?? 0,
|
|
3803
|
+
default: true
|
|
3804
|
+
}
|
|
3805
|
+
];
|
|
3806
|
+
}
|
|
3807
|
+
function resolvePlanPricingOption(plan, currency) {
|
|
3808
|
+
const options = getPlanPricingOptions(plan);
|
|
3809
|
+
if (currency) {
|
|
3810
|
+
const match = options.find((option) => option.currency.toUpperCase() === currency.toUpperCase());
|
|
3811
|
+
if (match) return match;
|
|
3812
|
+
}
|
|
3813
|
+
return options.find((option) => option.default) ?? options[0];
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3781
3816
|
// src/primitives/PlanSelector.tsx
|
|
3782
3817
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3783
3818
|
var PlanSelectorContext = (0, import_react26.createContext)(null);
|
|
@@ -3858,6 +3893,23 @@ var Root3 = (0, import_react26.forwardRef)(function PlanSelectorRoot(props, forw
|
|
|
3858
3893
|
setSelectedPlanIndex(-1);
|
|
3859
3894
|
}, [setSelectedPlanIndex]);
|
|
3860
3895
|
const selectedPlanRef = selectedPlan?.reference ?? null;
|
|
3896
|
+
const [preferredCurrency, setPreferredCurrencyState] = (0, import_react26.useState)(null);
|
|
3897
|
+
const [selectedCurrencies, setSelectedCurrencies] = (0, import_react26.useState)({});
|
|
3898
|
+
const getSelectedOption = (0, import_react26.useCallback)(
|
|
3899
|
+
(plan) => resolvePlanPricingOption(
|
|
3900
|
+
plan,
|
|
3901
|
+
selectedCurrencies[plan.reference] ?? preferredCurrency ?? null
|
|
3902
|
+
),
|
|
3903
|
+
[selectedCurrencies, preferredCurrency]
|
|
3904
|
+
);
|
|
3905
|
+
const setPreferredCurrency = (0, import_react26.useCallback)((currency) => {
|
|
3906
|
+
setPreferredCurrencyState(currency.toUpperCase());
|
|
3907
|
+
setSelectedCurrencies({});
|
|
3908
|
+
}, []);
|
|
3909
|
+
const setPlanCurrency = (0, import_react26.useCallback)((planRef, currency) => {
|
|
3910
|
+
setSelectedCurrencies((current) => ({ ...current, [planRef]: currency }));
|
|
3911
|
+
}, []);
|
|
3912
|
+
const selectedCurrency = selectedPlan ? getSelectedOption(selectedPlan).currency : null;
|
|
3861
3913
|
const autoCurrentAppliedKeyRef = (0, import_react26.useRef)(null);
|
|
3862
3914
|
(0, import_react26.useEffect)(() => {
|
|
3863
3915
|
if (autoCurrentAppliedKeyRef.current === productRef) return;
|
|
@@ -3887,7 +3939,12 @@ var Root3 = (0, import_react26.forwardRef)(function PlanSelectorRoot(props, forw
|
|
|
3887
3939
|
isFree,
|
|
3888
3940
|
isPopular,
|
|
3889
3941
|
select,
|
|
3890
|
-
clearSelection
|
|
3942
|
+
clearSelection,
|
|
3943
|
+
preferredCurrency,
|
|
3944
|
+
setPreferredCurrency,
|
|
3945
|
+
selectedCurrencies,
|
|
3946
|
+
setPlanCurrency,
|
|
3947
|
+
getSelectedOption
|
|
3891
3948
|
}),
|
|
3892
3949
|
[
|
|
3893
3950
|
plans,
|
|
@@ -3901,7 +3958,12 @@ var Root3 = (0, import_react26.forwardRef)(function PlanSelectorRoot(props, forw
|
|
|
3901
3958
|
isFree,
|
|
3902
3959
|
isPopular,
|
|
3903
3960
|
select,
|
|
3904
|
-
clearSelection
|
|
3961
|
+
clearSelection,
|
|
3962
|
+
preferredCurrency,
|
|
3963
|
+
setPreferredCurrency,
|
|
3964
|
+
selectedCurrencies,
|
|
3965
|
+
setPlanCurrency,
|
|
3966
|
+
getSelectedOption
|
|
3905
3967
|
]
|
|
3906
3968
|
);
|
|
3907
3969
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
@@ -3913,6 +3975,12 @@ var Root3 = (0, import_react26.forwardRef)(function PlanSelectorRoot(props, forw
|
|
|
3913
3975
|
setSelectedPlanRef: (ref) => {
|
|
3914
3976
|
if (ref) select(ref);
|
|
3915
3977
|
},
|
|
3978
|
+
selectedCurrency,
|
|
3979
|
+
setSelectedCurrency: (currency) => {
|
|
3980
|
+
if (currency) {
|
|
3981
|
+
setPreferredCurrency(currency);
|
|
3982
|
+
}
|
|
3983
|
+
},
|
|
3916
3984
|
plans,
|
|
3917
3985
|
loading,
|
|
3918
3986
|
error
|
|
@@ -3937,14 +4005,19 @@ var Grid = (0, import_react26.forwardRef)(function PlanSelectorGrid({ children,
|
|
|
3937
4005
|
const isPaygCurrent = isCurrent && isPaygPlan(plan);
|
|
3938
4006
|
const disabled = isFree || isCurrent && !isPaygCurrent;
|
|
3939
4007
|
const state = isCurrent && !isPaygCurrent ? "current" : selected ? "selected" : isCurrent ? "current" : isFree ? "disabled" : "idle";
|
|
4008
|
+
const pricingOptions = getPlanPricingOptions(plan);
|
|
4009
|
+
const selectedOption = ctx.getSelectedOption(plan);
|
|
3940
4010
|
const cardCtx = {
|
|
3941
4011
|
plan,
|
|
4012
|
+
selectedOption,
|
|
4013
|
+
pricingOptions,
|
|
3942
4014
|
state,
|
|
3943
4015
|
isCurrent,
|
|
3944
4016
|
isFree,
|
|
3945
4017
|
isPopular,
|
|
3946
4018
|
disabled,
|
|
3947
|
-
select: () => ctx.select(plan.reference)
|
|
4019
|
+
select: () => ctx.select(plan.reference),
|
|
4020
|
+
setCurrency: (currency) => ctx.setPlanCurrency(plan.reference, currency)
|
|
3948
4021
|
};
|
|
3949
4022
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CardContext.Provider, { value: cardCtx, children }, plan.reference);
|
|
3950
4023
|
}) });
|
|
@@ -3996,26 +4069,20 @@ var CardPrice = (0, import_react26.forwardRef)(function PlanSelectorCardPrice({
|
|
|
3996
4069
|
const formatted = (0, import_react26.useMemo)(() => {
|
|
3997
4070
|
if (card.isFree) return copy.planSelector.freeBadge;
|
|
3998
4071
|
if (card.plan.type === "usage-based") {
|
|
3999
|
-
|
|
4000
|
-
const perCallMinor = Math.max(1, Math.round(1 / creditsPerUnit));
|
|
4001
|
-
const rate = formatPrice(perCallMinor, card.plan.currency ?? "usd", {
|
|
4002
|
-
locale,
|
|
4003
|
-
free: ""
|
|
4004
|
-
});
|
|
4005
|
-
return `${rate} / call`;
|
|
4072
|
+
return copy.planSelector.usageRateLabel;
|
|
4006
4073
|
}
|
|
4007
|
-
return formatPrice(card.
|
|
4074
|
+
return formatPrice(card.selectedOption.price ?? 0, card.selectedOption.currency ?? "usd", {
|
|
4008
4075
|
locale,
|
|
4009
4076
|
free: copy.interval.free
|
|
4010
4077
|
});
|
|
4011
4078
|
}, [
|
|
4012
4079
|
card.isFree,
|
|
4013
4080
|
card.plan.type,
|
|
4014
|
-
card.
|
|
4015
|
-
card.
|
|
4016
|
-
card.plan.creditsPerUnit,
|
|
4081
|
+
card.selectedOption.price,
|
|
4082
|
+
card.selectedOption.currency,
|
|
4017
4083
|
locale,
|
|
4018
4084
|
copy.planSelector.freeBadge,
|
|
4085
|
+
copy.planSelector.usageRateLabel,
|
|
4019
4086
|
copy.interval.free
|
|
4020
4087
|
]);
|
|
4021
4088
|
const Comp = asChild ? Slot : "span";
|
|
@@ -4040,6 +4107,57 @@ function normalizeBillingCycle(cycle) {
|
|
|
4040
4107
|
if (lc === "daily" || lc === "day") return "day";
|
|
4041
4108
|
return cycle;
|
|
4042
4109
|
}
|
|
4110
|
+
var CurrencySwitcher = (0, import_react26.forwardRef)(function PlanSelectorCurrencySwitcher({ children, onChange, className, ...rest }, forwardedRef) {
|
|
4111
|
+
const ctx = usePlanSelectorContext("CurrencySwitcher");
|
|
4112
|
+
const availableCurrencies = (0, import_react26.useMemo)(() => {
|
|
4113
|
+
const currencies = /* @__PURE__ */ new Set();
|
|
4114
|
+
for (const plan of ctx.plans) {
|
|
4115
|
+
const options = getPlanPricingOptions(plan);
|
|
4116
|
+
if (options.length <= 1) continue;
|
|
4117
|
+
for (const option of options) {
|
|
4118
|
+
currencies.add(option.currency.toUpperCase());
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
return [...currencies].sort();
|
|
4122
|
+
}, [ctx.plans]);
|
|
4123
|
+
if (availableCurrencies.length < 2) return null;
|
|
4124
|
+
const effectiveCurrency = ctx.preferredCurrency ?? (ctx.selectedPlan ? ctx.getSelectedOption(ctx.selectedPlan).currency.toUpperCase() : null) ?? availableCurrencies[0];
|
|
4125
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4126
|
+
"select",
|
|
4127
|
+
{
|
|
4128
|
+
ref: forwardedRef,
|
|
4129
|
+
"data-solvapay-plan-selector-currency-switcher": "",
|
|
4130
|
+
className,
|
|
4131
|
+
value: effectiveCurrency,
|
|
4132
|
+
onChange: (event) => {
|
|
4133
|
+
ctx.setPreferredCurrency(event.target.value);
|
|
4134
|
+
onChange?.(event);
|
|
4135
|
+
},
|
|
4136
|
+
...rest,
|
|
4137
|
+
children: children ?? availableCurrencies.map((currency) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: currency, children: currency }, currency))
|
|
4138
|
+
}
|
|
4139
|
+
);
|
|
4140
|
+
});
|
|
4141
|
+
var CardCurrency = (0, import_react26.forwardRef)(
|
|
4142
|
+
function PlanSelectorCardCurrency({ children, onChange, ...rest }, forwardedRef) {
|
|
4143
|
+
const card = useCardContext("CardCurrency");
|
|
4144
|
+
if (card.pricingOptions.length <= 1) return null;
|
|
4145
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4146
|
+
"select",
|
|
4147
|
+
{
|
|
4148
|
+
ref: forwardedRef,
|
|
4149
|
+
"data-solvapay-plan-selector-card-currency": "",
|
|
4150
|
+
value: card.selectedOption.currency,
|
|
4151
|
+
onChange: (event) => {
|
|
4152
|
+
card.setCurrency(event.target.value);
|
|
4153
|
+
onChange?.(event);
|
|
4154
|
+
},
|
|
4155
|
+
...rest,
|
|
4156
|
+
children: children ?? card.pricingOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: option.currency, children: option.currency }, option.currency))
|
|
4157
|
+
}
|
|
4158
|
+
);
|
|
4159
|
+
}
|
|
4160
|
+
);
|
|
4043
4161
|
var CardBadge = (0, import_react26.forwardRef)(function PlanSelectorCardBadge({ asChild, children, ...rest }, forwardedRef) {
|
|
4044
4162
|
const card = useCardContext("CardBadge");
|
|
4045
4163
|
const copy = useCopy();
|
|
@@ -4085,6 +4203,8 @@ var PlanSelector = {
|
|
|
4085
4203
|
CardName,
|
|
4086
4204
|
CardPrice,
|
|
4087
4205
|
CardInterval,
|
|
4206
|
+
CurrencySwitcher,
|
|
4207
|
+
CardCurrency,
|
|
4088
4208
|
CardBadge,
|
|
4089
4209
|
Loading: Loading2,
|
|
4090
4210
|
Error: ErrorSlot
|
|
@@ -4107,21 +4227,21 @@ function planSortByPaygFirstThenAsc(a, b) {
|
|
|
4107
4227
|
if (!aPayg && bPayg) return 1;
|
|
4108
4228
|
return (a.price ?? 0) - (b.price ?? 0);
|
|
4109
4229
|
}
|
|
4110
|
-
function formatContinueLabel(plan, locale) {
|
|
4230
|
+
function formatContinueLabel(plan, locale, pricingOption) {
|
|
4111
4231
|
if (!plan) return "Continue";
|
|
4112
4232
|
if (isPayg(plan)) {
|
|
4113
4233
|
return `Continue with ${plan.name ?? "Pay as you go"}`;
|
|
4114
4234
|
}
|
|
4115
|
-
const
|
|
4116
|
-
const
|
|
4235
|
+
const option = pricingOption ?? resolvePlanPricingOption(plan, null);
|
|
4236
|
+
const currency = option.currency.toUpperCase();
|
|
4237
|
+
const priceLabel = formatPrice(option.price ?? 0, currency, { locale });
|
|
4117
4238
|
const cycle = plan.billingCycle ? `/${shortCycle(plan.billingCycle)}` : "";
|
|
4118
4239
|
return `Continue with ${plan.name ?? "Plan"} \u2014 ${priceLabel}${cycle}`;
|
|
4119
4240
|
}
|
|
4120
4241
|
function formatPaygRate(plan, locale) {
|
|
4121
|
-
const currency = (plan.currency ?? "USD").toUpperCase();
|
|
4122
4242
|
const creditsPerUnit = plan.creditsPerUnit ?? 1;
|
|
4123
|
-
const
|
|
4124
|
-
return `${
|
|
4243
|
+
const creditLabel = creditsPerUnit === 1 ? "credit" : "credits";
|
|
4244
|
+
return `${creditsPerUnit.toLocaleString(locale)} ${creditLabel} / call`;
|
|
4125
4245
|
}
|
|
4126
4246
|
function inferIncludedCredits(plan) {
|
|
4127
4247
|
const price = plan.price ?? 0;
|
|
@@ -4156,14 +4276,31 @@ function useCheckoutFlow(opts) {
|
|
|
4156
4276
|
onPurchaseSuccessRef.current = opts.onPurchaseSuccess;
|
|
4157
4277
|
onErrorRef.current = opts.onError;
|
|
4158
4278
|
const planCtx = usePlanSelector();
|
|
4279
|
+
const planSelection = usePlanSelection();
|
|
4159
4280
|
const transport = useTransport();
|
|
4160
4281
|
const locale = useLocale();
|
|
4161
4282
|
const balance = useBalance();
|
|
4162
4283
|
const { creditsPerMinorUnit, displayExchangeRate, adjustBalance } = balance;
|
|
4163
4284
|
const { refetchPurchase } = useSolvaPay();
|
|
4164
4285
|
const { merchant } = useMerchant();
|
|
4165
|
-
const
|
|
4286
|
+
const [topupCurrencyOverride, setTopupCurrencyOverride] = (0, import_react27.useState)(null);
|
|
4287
|
+
const topupCurrencies = (0, import_react27.useMemo)(() => {
|
|
4288
|
+
const fromMerchant = (merchant?.supportedTopupCurrencies ?? []).map((code) => code.toUpperCase()).filter(Boolean);
|
|
4289
|
+
const fallback = opts.topupCurrency?.toUpperCase() ?? merchant?.defaultCurrency?.toUpperCase() ?? null;
|
|
4290
|
+
const list = fromMerchant.length > 0 ? fromMerchant : fallback ? [fallback] : [];
|
|
4291
|
+
return Array.from(new Set(list));
|
|
4292
|
+
}, [merchant?.supportedTopupCurrencies, merchant?.defaultCurrency, opts.topupCurrency]);
|
|
4293
|
+
const resolvedDefaultTopupCurrency = opts.topupCurrency?.toUpperCase() ?? merchant?.defaultCurrency?.toUpperCase() ?? null;
|
|
4294
|
+
const planPickerCurrency = planCtx.preferredCurrency?.toUpperCase() ?? null;
|
|
4295
|
+
const topupCurrency = (topupCurrencyOverride && topupCurrencies.includes(topupCurrencyOverride) ? topupCurrencyOverride : null) ?? (planPickerCurrency && topupCurrencies.includes(planPickerCurrency) ? planPickerCurrency : null) ?? resolvedDefaultTopupCurrency;
|
|
4166
4296
|
const topupCurrencyReady = topupCurrency != null;
|
|
4297
|
+
const setTopupCurrency = (0, import_react27.useCallback)(
|
|
4298
|
+
(code) => {
|
|
4299
|
+
const normalized = code.toUpperCase();
|
|
4300
|
+
setTopupCurrencyOverride(normalized);
|
|
4301
|
+
},
|
|
4302
|
+
[]
|
|
4303
|
+
);
|
|
4167
4304
|
const [step, setStep] = (0, import_react27.useState)(initialStep);
|
|
4168
4305
|
const [status, setStatus] = (0, import_react27.useState)("idle");
|
|
4169
4306
|
const [selectedAmountMinor, setSelectedAmountMinor] = (0, import_react27.useState)(initialAmountMinor);
|
|
@@ -4261,19 +4398,23 @@ function useCheckoutFlow(opts) {
|
|
|
4261
4398
|
);
|
|
4262
4399
|
const recordRecurringSuccess = (0, import_react27.useCallback)(() => {
|
|
4263
4400
|
if (!selectedPlanShape) return;
|
|
4264
|
-
const
|
|
4401
|
+
const pricingOption = selectedPlan ? resolvePlanPricingOption(selectedPlan, planSelection?.selectedCurrency) : {
|
|
4402
|
+
currency: selectedPlanShape.currency ?? "USD",
|
|
4403
|
+
price: selectedPlanShape.price ?? 0
|
|
4404
|
+
};
|
|
4405
|
+
const currency = pricingOption.currency.toUpperCase();
|
|
4265
4406
|
const meta = {
|
|
4266
4407
|
branch: "recurring",
|
|
4267
4408
|
plan: selectedPlanShape,
|
|
4268
4409
|
creditsIncluded: inferIncludedCredits(selectedPlanShape),
|
|
4269
|
-
chargedTodayMinor:
|
|
4410
|
+
chargedTodayMinor: pricingOption.price ?? 0,
|
|
4270
4411
|
currency,
|
|
4271
4412
|
nextRenewalLabel: null
|
|
4272
4413
|
};
|
|
4273
4414
|
setSuccessMeta(meta);
|
|
4274
4415
|
setStep("success");
|
|
4275
4416
|
onPurchaseSuccessRef.current?.(meta);
|
|
4276
|
-
}, [selectedPlanShape]);
|
|
4417
|
+
}, [selectedPlanShape, selectedPlan, planSelection?.selectedCurrency]);
|
|
4277
4418
|
const advance = (0, import_react27.useCallback)(async () => {
|
|
4278
4419
|
if (step === "plan") {
|
|
4279
4420
|
await advanceFromPlan();
|
|
@@ -4319,6 +4460,7 @@ function useCheckoutFlow(opts) {
|
|
|
4319
4460
|
setSuccessMeta(null);
|
|
4320
4461
|
setError(null);
|
|
4321
4462
|
setStatus("idle");
|
|
4463
|
+
setTopupCurrencyOverride(null);
|
|
4322
4464
|
}, []);
|
|
4323
4465
|
const retry = (0, import_react27.useCallback)(async () => {
|
|
4324
4466
|
if (status !== "error") return;
|
|
@@ -4348,6 +4490,8 @@ function useCheckoutFlow(opts) {
|
|
|
4348
4490
|
branch,
|
|
4349
4491
|
topupCurrency,
|
|
4350
4492
|
topupCurrencyReady,
|
|
4493
|
+
topupCurrencies,
|
|
4494
|
+
setTopupCurrency,
|
|
4351
4495
|
canGoBack,
|
|
4352
4496
|
selectPlan,
|
|
4353
4497
|
selectAmount,
|
|
@@ -4368,6 +4512,8 @@ function useCheckoutFlow(opts) {
|
|
|
4368
4512
|
branch,
|
|
4369
4513
|
topupCurrency,
|
|
4370
4514
|
topupCurrencyReady,
|
|
4515
|
+
topupCurrencies,
|
|
4516
|
+
setTopupCurrency,
|
|
4371
4517
|
canGoBack,
|
|
4372
4518
|
selectPlan,
|
|
4373
4519
|
selectAmount,
|
|
@@ -4414,16 +4560,20 @@ var PlanStep = (0, import_react28.memo)(function PlanStep2({
|
|
|
4414
4560
|
activationError,
|
|
4415
4561
|
cx
|
|
4416
4562
|
}) {
|
|
4417
|
-
const { selectedPlan, selectedPlanRef } = usePlanSelector();
|
|
4563
|
+
const { selectedPlan, selectedPlanRef, getSelectedOption } = usePlanSelector();
|
|
4418
4564
|
const locale = useHostLocale();
|
|
4419
4565
|
const copy = useCopy();
|
|
4420
4566
|
const selectedPlanShape = selectedPlan;
|
|
4421
|
-
const
|
|
4567
|
+
const pricingOption = selectedPlan ? getSelectedOption(selectedPlan) : void 0;
|
|
4568
|
+
const ctaLabel = formatContinueLabel(selectedPlanShape, locale, pricingOption);
|
|
4422
4569
|
const showBanner = fromPaywall && !hideUpgradeBanner;
|
|
4423
4570
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
4424
4571
|
onBack ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(BackLink, { label: copy.checkout.backToAccount, onClick: onBack }) : null,
|
|
4425
4572
|
showBanner ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(UpgradeBanner, { kind: paywallKind, cx }) : null,
|
|
4426
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.
|
|
4573
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "solvapay-mcp-plan-step-header", children: [
|
|
4574
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: cx.heading, children: "Choose a plan" }),
|
|
4575
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PlanSelector.CurrencySwitcher, { className: "solvapay-plan-selector-currency-switcher" })
|
|
4576
|
+
] }),
|
|
4427
4577
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PlanSelector.Grid, { className: "solvapay-plan-selector-grid", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(PlanSelector.Card, { className: "solvapay-plan-selector-card", children: [
|
|
4428
4578
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PlanSelector.CardBadge, { className: "solvapay-plan-selector-card-badge" }),
|
|
4429
4579
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PlanSelector.CardName, { className: "solvapay-plan-selector-card-name" }),
|
|
@@ -4748,17 +4898,34 @@ function useAmountPicker() {
|
|
|
4748
4898
|
// src/mcp/views/checkout/steps/AmountStep.tsx
|
|
4749
4899
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
4750
4900
|
var AmountStep = (0, import_react31.memo)(function AmountStep2({
|
|
4751
|
-
|
|
4901
|
+
topupCurrency,
|
|
4902
|
+
topupCurrencies,
|
|
4903
|
+
onCurrencyChange,
|
|
4752
4904
|
onBack,
|
|
4753
4905
|
onContinue,
|
|
4754
4906
|
cx
|
|
4755
4907
|
}) {
|
|
4756
|
-
const currency = (
|
|
4908
|
+
const currency = (topupCurrency ?? "USD").toUpperCase();
|
|
4757
4909
|
const locale = useHostLocale();
|
|
4758
4910
|
const [stagedAmountMinor, setStagedAmountMinor] = (0, import_react31.useState)(null);
|
|
4911
|
+
const currencies = topupCurrencies ?? [];
|
|
4912
|
+
const showCurrencySwitch = currencies.length > 1 && !!onCurrencyChange;
|
|
4913
|
+
const currencyDisplay = showCurrencySwitch ? "code" : "symbol";
|
|
4759
4914
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
4760
4915
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BackLink, { label: "Back", onClick: onBack }),
|
|
4761
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.
|
|
4916
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "solvapay-mcp-step-header", children: [
|
|
4917
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { className: cx.heading, children: "How many credits?" }),
|
|
4918
|
+
showCurrencySwitch && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4919
|
+
"select",
|
|
4920
|
+
{
|
|
4921
|
+
className: "solvapay-mcp-currency-switch",
|
|
4922
|
+
value: currency,
|
|
4923
|
+
onChange: (event) => onCurrencyChange?.(event.target.value),
|
|
4924
|
+
"aria-label": "Topup currency",
|
|
4925
|
+
children: currencies.map((code) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: code, children: code }, code))
|
|
4926
|
+
}
|
|
4927
|
+
)
|
|
4928
|
+
] }),
|
|
4762
4929
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: cx.muted, children: "Top up to start using the tool." }),
|
|
4763
4930
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4764
4931
|
AmountPicker.Root,
|
|
@@ -4768,14 +4935,14 @@ var AmountStep = (0, import_react31.memo)(function AmountStep2({
|
|
|
4768
4935
|
className: cx.amountPicker,
|
|
4769
4936
|
onChange: (value) => setStagedAmountMinor(value),
|
|
4770
4937
|
children: [
|
|
4771
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PresetAmountRow, { cx }),
|
|
4772
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomAmountRow, { rowClassName: cx.amountCustom }),
|
|
4938
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PresetAmountRow, { cx, currencyDisplay }),
|
|
4939
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomAmountRow, { rowClassName: cx.amountCustom, currencyDisplay }),
|
|
4773
4940
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4774
4941
|
AmountPicker.Confirm,
|
|
4775
4942
|
{
|
|
4776
4943
|
className: cx.button,
|
|
4777
4944
|
onConfirm: (amountMinor) => onContinue(amountMinor),
|
|
4778
|
-
children: stagedAmountMinor ? `Continue \u2014 ${formatPrice(stagedAmountMinor, currency, { locale })}` : "Continue"
|
|
4945
|
+
children: stagedAmountMinor ? `Continue \u2014 ${formatPrice(stagedAmountMinor, currency, { locale, currencyDisplay })}` : "Continue"
|
|
4779
4946
|
}
|
|
4780
4947
|
)
|
|
4781
4948
|
]
|
|
@@ -4783,14 +4950,18 @@ var AmountStep = (0, import_react31.memo)(function AmountStep2({
|
|
|
4783
4950
|
)
|
|
4784
4951
|
] });
|
|
4785
4952
|
});
|
|
4786
|
-
function PresetAmountRow({
|
|
4953
|
+
function PresetAmountRow({
|
|
4954
|
+
cx,
|
|
4955
|
+
currencyDisplay
|
|
4956
|
+
}) {
|
|
4787
4957
|
const { quickAmounts, currency } = useAmountPicker();
|
|
4788
4958
|
const locale = useHostLocale();
|
|
4789
4959
|
const popularIndex = Math.min(1, quickAmounts.length - 1);
|
|
4790
4960
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cx.amountOptions, children: quickAmounts.map((amount, i) => {
|
|
4791
4961
|
const label = formatPrice(amount * getMinorUnitsPerMajor(currency), currency, {
|
|
4792
4962
|
locale,
|
|
4793
|
-
free: ""
|
|
4963
|
+
free: "",
|
|
4964
|
+
currencyDisplay
|
|
4794
4965
|
});
|
|
4795
4966
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4796
4967
|
AmountPicker.Option,
|
|
@@ -4798,16 +4969,21 @@ function PresetAmountRow({ cx }) {
|
|
|
4798
4969
|
amount,
|
|
4799
4970
|
className: cx.amountOption,
|
|
4800
4971
|
"data-popular": i === popularIndex ? "" : void 0,
|
|
4801
|
-
"aria-label": `${label}${i === popularIndex ? " (popular)" : ""}
|
|
4972
|
+
"aria-label": `${label}${i === popularIndex ? " (popular)" : ""}`,
|
|
4973
|
+
children: label
|
|
4802
4974
|
},
|
|
4803
4975
|
amount
|
|
4804
4976
|
);
|
|
4805
4977
|
}) });
|
|
4806
4978
|
}
|
|
4807
|
-
function CustomAmountRow({
|
|
4808
|
-
|
|
4979
|
+
function CustomAmountRow({
|
|
4980
|
+
rowClassName,
|
|
4981
|
+
currencyDisplay
|
|
4982
|
+
}) {
|
|
4983
|
+
const { currencySymbol, currency } = useAmountPicker();
|
|
4984
|
+
const prefix = currencyDisplay === "code" ? currency.toUpperCase() : currencySymbol;
|
|
4809
4985
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: rowClassName, children: [
|
|
4810
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "solvapay-mcp-amount-currency-symbol", children:
|
|
4986
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "solvapay-mcp-amount-currency-symbol", children: prefix }),
|
|
4811
4987
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AmountPicker.Custom, { className: "solvapay-mcp-amount-custom-input", placeholder: "0.00" })
|
|
4812
4988
|
] });
|
|
4813
4989
|
}
|
|
@@ -5543,14 +5719,15 @@ var TopupForm = {
|
|
|
5543
5719
|
// src/mcp/views/checkout/steps/PaygPaymentStep.tsx
|
|
5544
5720
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
5545
5721
|
var PaygPaymentStep = (0, import_react37.memo)(function PaygPaymentStep2({
|
|
5546
|
-
plan,
|
|
5722
|
+
plan: _plan,
|
|
5547
5723
|
amountMinor,
|
|
5724
|
+
topupCurrency,
|
|
5548
5725
|
returnUrl,
|
|
5549
5726
|
onBack,
|
|
5550
5727
|
onSuccess,
|
|
5551
5728
|
cx
|
|
5552
5729
|
}) {
|
|
5553
|
-
const currency = (
|
|
5730
|
+
const currency = (topupCurrency ?? "USD").toUpperCase();
|
|
5554
5731
|
const locale = useHostLocale();
|
|
5555
5732
|
const { creditsPerMinorUnit, displayExchangeRate } = useBalance();
|
|
5556
5733
|
const creditsAdded = creditsPerMinorUnit != null && creditsPerMinorUnit > 0 ? Math.floor(amountMinor / (displayExchangeRate ?? 1) * creditsPerMinorUnit) : null;
|
|
@@ -5612,7 +5789,11 @@ async function resolvePlanRef(productRef, config) {
|
|
|
5612
5789
|
const { headers } = await buildRequestHeaders(config);
|
|
5613
5790
|
const res = await fetchFn(url, { method: "GET", headers });
|
|
5614
5791
|
if (!res.ok) {
|
|
5615
|
-
|
|
5792
|
+
const message = await readErrorMessage(
|
|
5793
|
+
res,
|
|
5794
|
+
`Failed to fetch plans for product "${productRef}"`
|
|
5795
|
+
);
|
|
5796
|
+
throw new Error(message);
|
|
5616
5797
|
}
|
|
5617
5798
|
const data = await res.json();
|
|
5618
5799
|
const allPlans = data.plans ?? [];
|
|
@@ -5634,7 +5815,8 @@ async function resolvePlanRef(productRef, config) {
|
|
|
5634
5815
|
);
|
|
5635
5816
|
}
|
|
5636
5817
|
function useCheckout(options) {
|
|
5637
|
-
const { planRef, productRef, customer } = options;
|
|
5818
|
+
const { planRef, productRef, currency: currencyOverride, customer } = options;
|
|
5819
|
+
const planSelection = usePlanSelection();
|
|
5638
5820
|
const { createPayment, customerRef, updateCustomerRef, _config } = useSolvaPay();
|
|
5639
5821
|
const [loading, setLoading] = (0, import_react38.useState)(false);
|
|
5640
5822
|
const [error, setError] = (0, import_react38.useState)(null);
|
|
@@ -5666,9 +5848,11 @@ function useCheckout(options) {
|
|
|
5666
5848
|
if (!effectivePlanRef) {
|
|
5667
5849
|
throw new Error("Could not determine plan reference for checkout");
|
|
5668
5850
|
}
|
|
5851
|
+
const selectedCurrency = currencyOverride ?? planSelection?.selectedCurrency ?? void 0;
|
|
5669
5852
|
const result = await createPayment({
|
|
5670
5853
|
planRef: effectivePlanRef,
|
|
5671
5854
|
productRef,
|
|
5855
|
+
...selectedCurrency && { currency: selectedCurrency },
|
|
5672
5856
|
customer
|
|
5673
5857
|
});
|
|
5674
5858
|
if (!result || typeof result !== "object") {
|
|
@@ -5702,7 +5886,17 @@ function useCheckout(options) {
|
|
|
5702
5886
|
setLoading(false);
|
|
5703
5887
|
isStartingRef.current = false;
|
|
5704
5888
|
}
|
|
5705
|
-
}, [
|
|
5889
|
+
}, [
|
|
5890
|
+
planRef,
|
|
5891
|
+
productRef,
|
|
5892
|
+
currencyOverride,
|
|
5893
|
+
planSelection?.selectedCurrency,
|
|
5894
|
+
customer,
|
|
5895
|
+
createPayment,
|
|
5896
|
+
updateCustomerRef,
|
|
5897
|
+
loading,
|
|
5898
|
+
_config
|
|
5899
|
+
]);
|
|
5706
5900
|
const reset = (0, import_react38.useCallback)(() => {
|
|
5707
5901
|
isStartingRef.current = false;
|
|
5708
5902
|
setLoading(false);
|
|
@@ -6701,9 +6895,14 @@ var RecurringPaymentStep = (0, import_react43.memo)(function RecurringPaymentSte
|
|
|
6701
6895
|
onSuccess,
|
|
6702
6896
|
cx
|
|
6703
6897
|
}) {
|
|
6704
|
-
const
|
|
6898
|
+
const planSelection = usePlanSelection();
|
|
6899
|
+
const pricingOption = resolvePlanPricingOption(
|
|
6900
|
+
plan,
|
|
6901
|
+
planSelection?.selectedCurrency
|
|
6902
|
+
);
|
|
6903
|
+
const currency = pricingOption.currency.toUpperCase();
|
|
6705
6904
|
const locale = useHostLocale();
|
|
6706
|
-
const amountMinor =
|
|
6905
|
+
const amountMinor = pricingOption.price ?? 0;
|
|
6707
6906
|
const cycle = plan.billingCycle ?? "monthly";
|
|
6708
6907
|
const credits = inferIncludedCredits(plan);
|
|
6709
6908
|
const planName = plan.name ?? "Plan";
|
|
@@ -6960,6 +7159,9 @@ function McpCheckoutBody({
|
|
|
6960
7159
|
AmountStep,
|
|
6961
7160
|
{
|
|
6962
7161
|
plan: selectedPlanShape,
|
|
7162
|
+
topupCurrency: flow.topupCurrency,
|
|
7163
|
+
topupCurrencies: flow.topupCurrencies,
|
|
7164
|
+
onCurrencyChange: flow.setTopupCurrency,
|
|
6963
7165
|
onBack: () => flow.back(),
|
|
6964
7166
|
onContinue: (amountMinor) => {
|
|
6965
7167
|
flow.selectAmount(amountMinor);
|
|
@@ -6976,6 +7178,7 @@ function McpCheckoutBody({
|
|
|
6976
7178
|
{
|
|
6977
7179
|
plan: selectedPlanShape,
|
|
6978
7180
|
amountMinor: flow.selectedAmountMinor,
|
|
7181
|
+
topupCurrency: flow.topupCurrency,
|
|
6979
7182
|
returnUrl,
|
|
6980
7183
|
onBack: () => flow.back(),
|
|
6981
7184
|
onSuccess: (extras) => flow.notifyPaymentSuccess(void 0, extras),
|
|
@@ -7358,12 +7561,17 @@ function McpTopupView({
|
|
|
7358
7561
|
}
|
|
7359
7562
|
if (probe === "blocked")
|
|
7360
7563
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(HostedTopupFallback, { cx, onBack });
|
|
7361
|
-
const
|
|
7564
|
+
const defaultCurrency = merchant?.defaultCurrency?.toUpperCase() ?? FALLBACK_TOPUP_CURRENCY;
|
|
7565
|
+
const fromMerchant = (merchant?.supportedTopupCurrencies ?? []).map((code) => code.toUpperCase()).filter(Boolean);
|
|
7566
|
+
const topupCurrencies = Array.from(
|
|
7567
|
+
new Set(fromMerchant.length > 0 ? fromMerchant : [defaultCurrency])
|
|
7568
|
+
);
|
|
7362
7569
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
7363
7570
|
EmbeddedTopup,
|
|
7364
7571
|
{
|
|
7365
7572
|
returnUrl,
|
|
7366
|
-
|
|
7573
|
+
defaultCurrency,
|
|
7574
|
+
topupCurrencies,
|
|
7367
7575
|
onTopupSuccess,
|
|
7368
7576
|
onBack,
|
|
7369
7577
|
cx
|
|
@@ -7372,16 +7580,25 @@ function McpTopupView({
|
|
|
7372
7580
|
}
|
|
7373
7581
|
function EmbeddedTopup({
|
|
7374
7582
|
returnUrl,
|
|
7375
|
-
|
|
7583
|
+
defaultCurrency,
|
|
7584
|
+
topupCurrencies,
|
|
7376
7585
|
onTopupSuccess,
|
|
7377
7586
|
onBack,
|
|
7378
7587
|
cx
|
|
7379
7588
|
}) {
|
|
7380
7589
|
const [committedAmountMinor, setCommittedAmountMinor] = (0, import_react47.useState)(null);
|
|
7381
7590
|
const [justPaidMinor, setJustPaidMinor] = (0, import_react47.useState)(null);
|
|
7382
|
-
const
|
|
7591
|
+
const [selectedCurrency, setSelectedCurrency] = (0, import_react47.useState)(defaultCurrency);
|
|
7592
|
+
const currency = selectedCurrency;
|
|
7593
|
+
const showCurrencySwitch = topupCurrencies.length > 1;
|
|
7594
|
+
const { adjustBalance, credits, creditsPerMinorUnit, displayCurrency, displayExchangeRate } = useBalance();
|
|
7383
7595
|
const locale = useHostLocale();
|
|
7384
7596
|
const { notifyModelContext, notifySuccess } = useMcpBridge();
|
|
7597
|
+
const handleCurrencyChange = (code) => {
|
|
7598
|
+
setSelectedCurrency(code.toUpperCase());
|
|
7599
|
+
setCommittedAmountMinor(null);
|
|
7600
|
+
setJustPaidMinor(null);
|
|
7601
|
+
};
|
|
7385
7602
|
if (justPaidMinor != null) {
|
|
7386
7603
|
const displayAmount = formatPrice(justPaidMinor, currency, { locale, free: "" });
|
|
7387
7604
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cx.card, children: [
|
|
@@ -7415,7 +7632,8 @@ function EmbeddedTopup({
|
|
|
7415
7632
|
}
|
|
7416
7633
|
if (committedAmountMinor != null && committedAmountMinor > 0) {
|
|
7417
7634
|
const displayAmount = formatPrice(committedAmountMinor, currency, { locale, free: "" });
|
|
7418
|
-
const
|
|
7635
|
+
const rateAppliesToCurrency = displayCurrency != null && currency.toUpperCase() === displayCurrency.toUpperCase();
|
|
7636
|
+
const creditsAdded = rateAppliesToCurrency && creditsPerMinorUnit != null && creditsPerMinorUnit > 0 ? Math.floor(
|
|
7419
7637
|
committedAmountMinor / (displayExchangeRate ?? 1) * creditsPerMinorUnit
|
|
7420
7638
|
) : null;
|
|
7421
7639
|
const formattedBalance = credits != null ? new Intl.NumberFormat(locale).format(credits) : null;
|
|
@@ -7481,9 +7699,36 @@ function EmbeddedTopup({
|
|
|
7481
7699
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h2", { className: cx.heading, children: "Add credits" }),
|
|
7482
7700
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(BalanceBadge, {})
|
|
7483
7701
|
] }),
|
|
7702
|
+
showCurrencySwitch ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "solvapay-mcp-step-header", children: [
|
|
7703
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: cx.muted, children: "Pay in" }),
|
|
7704
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
7705
|
+
"select",
|
|
7706
|
+
{
|
|
7707
|
+
className: "solvapay-mcp-currency-switch",
|
|
7708
|
+
value: currency,
|
|
7709
|
+
onChange: (event) => handleCurrencyChange(event.target.value),
|
|
7710
|
+
"aria-label": "Topup currency",
|
|
7711
|
+
children: topupCurrencies.map((code) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("option", { value: code, children: code }, code))
|
|
7712
|
+
}
|
|
7713
|
+
)
|
|
7714
|
+
] }) : null,
|
|
7484
7715
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(AmountPicker.Root, { currency, emit: "minor", className: cx.amountPicker, children: [
|
|
7485
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
7486
|
-
|
|
7716
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
7717
|
+
QuickAmountOptions,
|
|
7718
|
+
{
|
|
7719
|
+
className: cx.amountOptions,
|
|
7720
|
+
optionClassName: cx.amountOption,
|
|
7721
|
+
currencyDisplay: showCurrencySwitch ? "code" : "symbol",
|
|
7722
|
+
locale
|
|
7723
|
+
}
|
|
7724
|
+
),
|
|
7725
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
7726
|
+
CustomAmountRow2,
|
|
7727
|
+
{
|
|
7728
|
+
rowClassName: cx.amountCustom,
|
|
7729
|
+
currencyDisplay: showCurrencySwitch ? "code" : "symbol"
|
|
7730
|
+
}
|
|
7731
|
+
),
|
|
7487
7732
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
7488
7733
|
AmountPicker.Confirm,
|
|
7489
7734
|
{
|
|
@@ -7505,15 +7750,28 @@ function EmbeddedTopup({
|
|
|
7505
7750
|
}
|
|
7506
7751
|
function QuickAmountOptions({
|
|
7507
7752
|
className,
|
|
7508
|
-
optionClassName
|
|
7753
|
+
optionClassName,
|
|
7754
|
+
currencyDisplay,
|
|
7755
|
+
locale
|
|
7509
7756
|
}) {
|
|
7510
|
-
const { quickAmounts } = useAmountPicker();
|
|
7511
|
-
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className, children: quickAmounts.map((amount) =>
|
|
7757
|
+
const { quickAmounts, currency } = useAmountPicker();
|
|
7758
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className, children: quickAmounts.map((amount) => {
|
|
7759
|
+
const label = formatPrice(amount * getMinorUnitsPerMajor(currency), currency, {
|
|
7760
|
+
locale,
|
|
7761
|
+
free: "",
|
|
7762
|
+
currencyDisplay
|
|
7763
|
+
});
|
|
7764
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AmountPicker.Option, { amount, className: optionClassName, children: label }, amount);
|
|
7765
|
+
}) });
|
|
7512
7766
|
}
|
|
7513
|
-
function CustomAmountRow2({
|
|
7514
|
-
|
|
7767
|
+
function CustomAmountRow2({
|
|
7768
|
+
rowClassName,
|
|
7769
|
+
currencyDisplay
|
|
7770
|
+
}) {
|
|
7771
|
+
const { currencySymbol, currency } = useAmountPicker();
|
|
7772
|
+
const prefix = currencyDisplay === "code" ? currency.toUpperCase() : currencySymbol;
|
|
7515
7773
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: rowClassName, children: [
|
|
7516
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "solvapay-mcp-amount-currency-symbol", children:
|
|
7774
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "solvapay-mcp-amount-currency-symbol", children: prefix }),
|
|
7517
7775
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AmountPicker.Custom, { className: "solvapay-mcp-amount-custom-input", placeholder: "0.00" })
|
|
7518
7776
|
] });
|
|
7519
7777
|
}
|