@solvapay/react 2.0.0 → 2.0.1-preview-7d806cc7af767dec26b099517bc6ededc85adf9a

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.
@@ -967,10 +967,10 @@ function createHttpTransport(config) {
967
967
  onErrorContext: "getUsage",
968
968
  errorPrefix: "Failed to load usage"
969
969
  }),
970
- getLimits: async ({ productRef, meterName }) => {
970
+ getLimits: async ({ productRef, meterName: meterName2 }) => {
971
971
  const base = routeFor(config, "getLimits");
972
972
  const params = new URLSearchParams({ productRef });
973
- if (meterName) params.set("meterName", meterName);
973
+ if (meterName2) params.set("meterName", meterName2);
974
974
  const url = `${base}?${params.toString()}`;
975
975
  const data = await request(config, url, {
976
976
  method: "GET",
@@ -2447,6 +2447,9 @@ var import_mcp_core3 = require("@solvapay/mcp-core");
2447
2447
  // src/mcp/McpAppShell.tsx
2448
2448
  var import_react51 = require("react");
2449
2449
 
2450
+ // src/components/CurrentPlanCard.tsx
2451
+ var import_core5 = require("@solvapay/core");
2452
+
2450
2453
  // src/hooks/usePurchase.ts
2451
2454
  function usePurchase() {
2452
2455
  const { purchase, refetchPurchase } = useSolvaPay();
@@ -2983,12 +2986,13 @@ var BalanceBadge = (0, import_react17.forwardRef)(function BalanceBadge2({ asChi
2983
2986
  });
2984
2987
 
2985
2988
  // src/primitives/CancelPlanButton.tsx
2989
+ var import_core3 = require("@solvapay/core");
2986
2990
  var import_react18 = require("react");
2987
2991
  var import_jsx_runtime8 = require("react/jsx-runtime");
2988
2992
  function resolveConfirmText(confirm, purchase, defaults) {
2989
2993
  if (confirm === false) return null;
2990
2994
  if (typeof confirm === "string") return confirm;
2991
- const isUsageBased = purchase?.planSnapshot?.isMetered === true && purchase.isRecurring !== true;
2995
+ const isUsageBased = ((0, import_core3.countsUsage)(purchase?.planSnapshot) || purchase?.planSnapshot?.isMetered === true) && purchase?.isRecurring !== true;
2992
2996
  if (isUsageBased) return defaults.usageBased;
2993
2997
  return defaults.recurring;
2994
2998
  }
@@ -3291,6 +3295,7 @@ var UpdatePaymentMethodButton = (0, import_react22.forwardRef)(function UpdatePa
3291
3295
  var import_react25 = require("react");
3292
3296
 
3293
3297
  // src/hooks/useUsage.ts
3298
+ var import_core4 = require("@solvapay/core");
3294
3299
  var import_react24 = require("react");
3295
3300
 
3296
3301
  // src/hooks/useLimits.ts
@@ -3311,19 +3316,19 @@ function useCustomer() {
3311
3316
  var CACHE_TTL_MS = 1e4;
3312
3317
  var OPTIMISTIC_GRACE_MS = 8e3;
3313
3318
  var limitsCache = /* @__PURE__ */ new Map();
3314
- function cacheKey(customerRef, productRef, meterName) {
3315
- return `${customerRef ?? "anonymous"}:${productRef}:${meterName}`;
3319
+ function cacheKey(customerRef, productRef, meterName2) {
3320
+ return `${customerRef ?? "anonymous"}:${productRef}:${meterName2}`;
3316
3321
  }
3317
3322
  function isUnlimited(remaining) {
3318
3323
  return remaining < 0;
3319
3324
  }
3320
3325
  function useLimits(options) {
3321
3326
  const { productRef, meterName: rawMeterName, enabled = true } = options;
3322
- const meterName = rawMeterName || "requests";
3327
+ const meterName2 = rawMeterName || "requests";
3323
3328
  const transport = useTransport();
3324
3329
  const { customerRef } = useCustomer();
3325
3330
  const { purchase } = useSolvaPay();
3326
- const currentKey = !productRef || !enabled ? null : cacheKey(customerRef, productRef, meterName);
3331
+ const currentKey = !productRef || !enabled ? null : cacheKey(customerRef, productRef, meterName2);
3327
3332
  const [data, setData] = (0, import_react23.useState)(() => {
3328
3333
  if (!currentKey) return null;
3329
3334
  const cached = limitsCache.get(currentKey);
@@ -3378,7 +3383,7 @@ function useLimits(options) {
3378
3383
  setLoading(false);
3379
3384
  return;
3380
3385
  }
3381
- const key = cacheKey(customerRef, productRef, meterName);
3386
+ const key = cacheKey(customerRef, productRef, meterName2);
3382
3387
  const cached = limitsCache.get(key);
3383
3388
  const now = Date.now();
3384
3389
  if (cached?.promise) {
@@ -3391,7 +3396,7 @@ function useLimits(options) {
3391
3396
  setError(null);
3392
3397
  return;
3393
3398
  }
3394
- const promise = transport.getLimits({ productRef, meterName });
3399
+ const promise = transport.getLimits({ productRef, meterName: meterName2 });
3395
3400
  limitsCache.set(key, { data: cached?.data ?? null, timestamp: now, promise });
3396
3401
  const fresh = await attachToFetch(promise);
3397
3402
  if (fresh !== null) {
@@ -3400,7 +3405,7 @@ function useLimits(options) {
3400
3405
  limitsCache.delete(key);
3401
3406
  }
3402
3407
  },
3403
- [transport, customerRef, productRef, meterName, enabled, attachToFetch]
3408
+ [transport, customerRef, productRef, meterName2, enabled, attachToFetch]
3404
3409
  );
3405
3410
  (0, import_react23.useEffect)(() => {
3406
3411
  fetchLimits(false);
@@ -3418,7 +3423,7 @@ function useLimits(options) {
3418
3423
  (delta) => {
3419
3424
  if (!productRef || !enabled) return;
3420
3425
  if (unlimitedRef.current) return;
3421
- const key = cacheKey(customerRef, productRef, meterName);
3426
+ const key = cacheKey(customerRef, productRef, meterName2);
3422
3427
  setData((prev) => {
3423
3428
  const cached = limitsCache.get(key);
3424
3429
  const baseline = prev ?? cached?.data ?? null;
@@ -3445,7 +3450,7 @@ function useLimits(options) {
3445
3450
  });
3446
3451
  }, OPTIMISTIC_GRACE_MS);
3447
3452
  },
3448
- [customerRef, productRef, meterName, enabled, fetchLimits]
3453
+ [customerRef, productRef, meterName2, enabled, fetchLimits]
3449
3454
  );
3450
3455
  return {
3451
3456
  remaining: data?.remaining ?? null,
@@ -3464,7 +3469,9 @@ function useLimits(options) {
3464
3469
  function deriveUsage(purchase, limits) {
3465
3470
  if (!purchase) return null;
3466
3471
  const usage = purchase.usage;
3467
- if (purchase.planSnapshot?.isMetered !== true && !usage) return null;
3472
+ if (!(0, import_core4.countsUsage)(purchase.planSnapshot) && purchase.planSnapshot?.isMetered !== true && !usage) {
3473
+ return null;
3474
+ }
3468
3475
  const used = typeof usage?.used === "number" ? usage.used : 0;
3469
3476
  const hasFiniteCap = limits.unlimited === false && limits.remaining !== null;
3470
3477
  const remaining = hasFiniteCap ? limits.remaining : null;
@@ -3487,18 +3494,18 @@ function useUsage() {
3487
3494
  const [override, setOverride] = (0, import_react24.useState)(null);
3488
3495
  const [error, setError] = (0, import_react24.useState)(null);
3489
3496
  const [transportLoading, setTransportLoading] = (0, import_react24.useState)(false);
3490
- const isMetered = activePurchase?.planSnapshot?.isMetered === true;
3497
+ const usageCounted = (0, import_core4.countsUsage)(activePurchase?.planSnapshot) || activePurchase?.planSnapshot?.isMetered === true;
3491
3498
  const {
3492
3499
  remaining: limitRemaining,
3493
3500
  unlimited,
3494
- meterName
3501
+ meterName: meterName2
3495
3502
  } = useLimits({
3496
3503
  productRef: activePurchase?.productRef,
3497
- enabled: isMetered
3504
+ enabled: usageCounted
3498
3505
  });
3499
3506
  const derived = (0, import_react24.useMemo)(
3500
- () => deriveUsage(activePurchase ?? null, { remaining: limitRemaining, unlimited, meterName }),
3501
- [activePurchase, limitRemaining, unlimited, meterName]
3507
+ () => deriveUsage(activePurchase ?? null, { remaining: limitRemaining, unlimited, meterName: meterName2 }),
3508
+ [activePurchase, limitRemaining, unlimited, meterName2]
3502
3509
  );
3503
3510
  const activePurchaseRef = activePurchase?.reference ?? null;
3504
3511
  (0, import_react24.useEffect)(() => {
@@ -3755,7 +3762,7 @@ function PlanTypeLine({
3755
3762
  className
3756
3763
  }) {
3757
3764
  const copy = useCopy();
3758
- const isMetered = purchase.planSnapshot?.isMetered === true;
3765
+ const isMetered = (0, import_core5.countsUsage)(purchase.planSnapshot) || purchase.planSnapshot?.isMetered === true;
3759
3766
  if (purchase.isRecurring) {
3760
3767
  const date2 = formatDate(purchase.nextBillingDate);
3761
3768
  if (!date2) return null;
@@ -3815,7 +3822,7 @@ var CurrentPlanCard = ({
3815
3822
  const { formatDate, shouldShowCancelledNotice } = usePurchaseStatus();
3816
3823
  const { paymentMethod } = usePaymentMethod();
3817
3824
  if (!activePurchase) return null;
3818
- const isUsageBased = activePurchase.planSnapshot?.isMetered === true;
3825
+ const isUsageBased = (0, import_core5.countsUsage)(activePurchase.planSnapshot) || activePurchase.planSnapshot?.isMetered === true;
3819
3826
  const planType = activePurchase.isRecurring ? "recurring" : isUsageBased ? "usage-based" : "one-time";
3820
3827
  const amount = activePurchase.originalAmount ?? activePurchase.amount ?? 0;
3821
3828
  const currency = activePurchase.currency ?? "usd";
@@ -3954,7 +3961,7 @@ var CurrentPlanCard = ({
3954
3961
  };
3955
3962
 
3956
3963
  // src/mcp/views/detail-cards.tsx
3957
- var import_core3 = require("@solvapay/core");
3964
+ var import_core6 = require("@solvapay/core");
3958
3965
 
3959
3966
  // src/mcp/views/types.ts
3960
3967
  var DEFAULT_MCP_CLASS_NAMES = {
@@ -4036,7 +4043,7 @@ function McpCustomerDetailsCard({
4036
4043
  }
4037
4044
  const displayName = name?.trim() || "Customer";
4038
4045
  const showBalance = !hideBalance && typeof credits === "number" && credits > 0;
4039
- const displayMinor = showBalance && typeof creditsPerMinorUnit === "number" && creditsPerMinorUnit > 0 && displayCurrency ? (0, import_core3.creditsToDisplayMinorUnits)({
4046
+ const displayMinor = showBalance && typeof creditsPerMinorUnit === "number" && creditsPerMinorUnit > 0 && displayCurrency ? (0, import_core6.creditsToDisplayMinorUnits)({
4040
4047
  credits: credits ?? 0,
4041
4048
  creditsPerMinorUnit,
4042
4049
  displayExchangeRate: displayExchangeRate ?? 1,
@@ -4099,7 +4106,7 @@ function McpSellerDetailsCard({
4099
4106
  }
4100
4107
  const supportEmail = merchant.supportEmail;
4101
4108
  const supportUrl = merchant.supportUrl;
4102
- const sellerIdentity = (0, import_core3.resolveSellerIdentityDisplay)({
4109
+ const sellerIdentity = (0, import_core6.resolveSellerIdentityDisplay)({
4103
4110
  country: merchant.country,
4104
4111
  vatNumber: merchant.vatNumber,
4105
4112
  taxId: merchant.taxId,
@@ -4252,13 +4259,13 @@ function isPaygPlan(plan) {
4252
4259
  }
4253
4260
 
4254
4261
  // src/utils/planPricing.ts
4255
- var import_core4 = require("@solvapay/core");
4262
+ var import_core7 = require("@solvapay/core");
4256
4263
  function getPlanPricingOptions(plan) {
4257
4264
  if (plan.pricingOptions && plan.pricingOptions.length > 0) {
4258
4265
  return plan.pricingOptions;
4259
4266
  }
4260
4267
  const defaultCurrency = (plan.currency ?? "USD").toUpperCase();
4261
- const charges2 = (0, import_core4.headlineCharges)(plan);
4268
+ const charges2 = (0, import_core7.headlineCharges)(plan);
4262
4269
  if (charges2.length > 0) {
4263
4270
  return charges2.map((charge) => ({
4264
4271
  currency: charge.currency,
@@ -4687,7 +4694,7 @@ function usePlanSelector() {
4687
4694
  var import_react28 = require("react");
4688
4695
 
4689
4696
  // src/primitives/checkout/shared.ts
4690
- var import_core5 = require("@solvapay/core");
4697
+ var import_core8 = require("@solvapay/core");
4691
4698
  function isPayg(plan) {
4692
4699
  return isPaygPlan(plan ?? null);
4693
4700
  }
@@ -4718,25 +4725,25 @@ function formatContinueLabel(plan, locale, pricingOption) {
4718
4725
  return `Continue with ${plan.name ?? "Plan"} \u2014 ${priceLabel}${cycle}`;
4719
4726
  }
4720
4727
  function planBillingInterval(plan) {
4721
- return (0, import_core5.billingCycle)(plan)?.interval ?? null;
4728
+ return (0, import_core8.billingCycle)(plan)?.interval ?? null;
4722
4729
  }
4723
4730
  function formatPaygRate(plan, locale, balance) {
4724
- const credits = (0, import_core5.creditsPerUnitFromBalance)(plan, balance);
4731
+ const credits = (0, import_core8.creditsPerUnitFromBalance)(plan, balance);
4725
4732
  if (credits != null) {
4726
4733
  return `${credits.toLocaleString(locale)} ${credits === 1 ? "credit" : "credits"} / call`;
4727
4734
  }
4728
- const charge = (0, import_core5.perUnitCharge)(plan);
4735
+ const charge = (0, import_core8.perUnitCharge)(plan);
4729
4736
  if (charge && charge.amountMinor > 0) {
4730
4737
  return `${formatPrice(charge.amountMinor, charge.currency.toUpperCase(), { locale })} / call`;
4731
4738
  }
4732
4739
  return null;
4733
4740
  }
4734
4741
  function inferIncludedUnits(plan) {
4735
- const cap = (0, import_core5.includedUnits)(plan);
4742
+ const cap = (0, import_core8.includedUnits)(plan);
4736
4743
  return cap != null && cap > 0 ? cap : null;
4737
4744
  }
4738
4745
  function planMeterName(plan) {
4739
- return (0, import_core5.perUnitCharge)(plan)?.meter ?? null;
4746
+ return (0, import_core8.meterName)(plan);
4740
4747
  }
4741
4748
  function shortCycle(cycle) {
4742
4749
  if (!cycle) return "mo";
@@ -5988,14 +5995,14 @@ var Spinner = ({ className, size = "md" }) => {
5988
5995
 
5989
5996
  // src/hooks/useBusinessDetailsAttach.ts
5990
5997
  var import_react38 = require("react");
5991
- var import_core7 = require("@solvapay/core");
5998
+ var import_core10 = require("@solvapay/core");
5992
5999
 
5993
6000
  // src/components/businessCheckoutParts.tsx
5994
6001
  var import_react37 = require("react");
5995
- var import_core6 = require("@solvapay/core");
6002
+ var import_core9 = require("@solvapay/core");
5996
6003
  var import_jsx_runtime25 = require("react/jsx-runtime");
5997
6004
  function mapBusinessFieldErrors(input) {
5998
- const result = (0, import_core6.validateBusinessDetails)(input);
6005
+ const result = (0, import_core9.validateBusinessDetails)(input);
5999
6006
  if (result.success) return {};
6000
6007
  const errors = {};
6001
6008
  for (const issue of result.error.issues) {
@@ -6012,25 +6019,25 @@ function isBusinessDetailsKey(key) {
6012
6019
  function attr(prefix, suffix) {
6013
6020
  return `data-solvapay-${prefix}-${suffix}`;
6014
6021
  }
6015
- var SUPPORTED_BUSINESS_COUNTRIES_SET = new Set(import_core6.SUPPORTED_BUSINESS_COUNTRIES);
6022
+ var SUPPORTED_BUSINESS_COUNTRIES_SET = new Set(import_core9.SUPPORTED_BUSINESS_COUNTRIES);
6016
6023
  function isSupportedBusinessCountry(value) {
6017
6024
  return SUPPORTED_BUSINESS_COUNTRIES_SET.has(value);
6018
6025
  }
6019
6026
  function resolveTaxIdLabel(country) {
6020
6027
  if (country && isSupportedBusinessCountry(country)) {
6021
- return (0, import_core6.getTaxIdFieldLabel)(country);
6028
+ return (0, import_core9.getTaxIdFieldLabel)(country);
6022
6029
  }
6023
6030
  return "Tax ID";
6024
6031
  }
6025
6032
  function resolveTaxIdPlaceholder(country) {
6026
6033
  if (country && isSupportedBusinessCountry(country)) {
6027
- return (0, import_core6.getTaxIdExample)(country);
6034
+ return (0, import_core9.getTaxIdExample)(country);
6028
6035
  }
6029
6036
  return "Enter tax ID";
6030
6037
  }
6031
6038
  function resolveTaxIdHelperText(country) {
6032
6039
  if (country && isSupportedBusinessCountry(country)) {
6033
- return (0, import_core6.getTaxIdHelperText)(country);
6040
+ return (0, import_core9.getTaxIdHelperText)(country);
6034
6041
  }
6035
6042
  return "";
6036
6043
  }
@@ -6105,7 +6112,7 @@ function createBusinessDetailsParts(useCtx, prefix) {
6105
6112
  };
6106
6113
  const defaultOptions = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
6107
6114
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "", children: "Select country" }),
6108
- import_core6.BUSINESS_COUNTRY_OPTIONS.map(({ value, label }) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value, children: label }, value))
6115
+ import_core9.BUSINESS_COUNTRY_OPTIONS.map(({ value, label }) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value, children: label }, value))
6109
6116
  ] });
6110
6117
  if (asChild) {
6111
6118
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Slot, { ref: forwardedRef, ...commonProps, children: children ?? defaultOptions });
@@ -6310,7 +6317,7 @@ function useBusinessDetailsAttach(options) {
6310
6317
  if (!processorPaymentId || !attachBusinessDetails) {
6311
6318
  return !attachBusinessDetails;
6312
6319
  }
6313
- const validation = (0, import_core7.validateBusinessDetails)(input);
6320
+ const validation = (0, import_core10.validateBusinessDetails)(input);
6314
6321
  if (!validation.success) {
6315
6322
  setFieldErrors(mapBusinessFieldErrors(input));
6316
6323
  return false;
@@ -6349,7 +6356,7 @@ function useBusinessDetailsAttach(options) {
6349
6356
  );
6350
6357
  (0, import_react38.useEffect)(() => {
6351
6358
  if (!processorPaymentId || !attachBusinessDetails) return;
6352
- const validation = (0, import_core7.validateBusinessDetails)(businessDetails);
6359
+ const validation = (0, import_core10.validateBusinessDetails)(businessDetails);
6353
6360
  if (!validation.success) {
6354
6361
  setFieldErrors(mapBusinessFieldErrors(businessDetails));
6355
6362
  setBusinessDetailsAttached(false);
@@ -7555,7 +7562,8 @@ function normalizeOneTimePurchase(input) {
7555
7562
  planSnapshot: {
7556
7563
  price: input.amount,
7557
7564
  currency: input.currency,
7558
- isMetered: false
7565
+ isMetered: false,
7566
+ options: []
7559
7567
  }
7560
7568
  };
7561
7569
  }
@@ -8463,7 +8471,7 @@ var RecurringPaymentStep = (0, import_react46.memo)(function RecurringPaymentSte
8463
8471
  const amountMinor = pricingOption.price ?? 0;
8464
8472
  const cycle = planBillingInterval(plan) ?? "month";
8465
8473
  const included = inferIncludedUnits(plan);
8466
- const meterName = planMeterName(plan) ?? "units";
8474
+ const meterName2 = planMeterName(plan) ?? "units";
8467
8475
  const planName = plan.name ?? "Plan";
8468
8476
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
8469
8477
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(BackLink, { label: "Change plan", onClick: onBack }),
@@ -8480,7 +8488,7 @@ var RecurringPaymentStep = (0, import_react46.memo)(function RecurringPaymentSte
8480
8488
  included != null ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "solvapay-mcp-checkout-order-summary-row", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: cx.muted, children: [
8481
8489
  included.toLocaleString(locale),
8482
8490
  " ",
8483
- meterName,
8491
+ meterName2,
8484
8492
  " included"
8485
8493
  ] }) }) : null
8486
8494
  ] }),
@@ -9898,22 +9906,21 @@ function McpApp({
9898
9906
  }
9899
9907
 
9900
9908
  // src/mcp/plan-actions.ts
9901
- var import_core8 = require("@solvapay/core");
9909
+ var import_core11 = require("@solvapay/core");
9902
9910
  function isPaidPlan(plan) {
9903
9911
  if (plan.requiresPayment === false) return false;
9904
- if ((0, import_core8.charges)(plan).some((charge) => charge.amountMinor > 0)) return true;
9912
+ if ((0, import_core11.charges)(plan).some((charge) => charge.amountMinor > 0)) return true;
9905
9913
  return (plan.price ?? 0) > 0;
9906
9914
  }
9907
9915
  function isMeteredPlan(plan) {
9908
- if (plan.isMetered === true) return true;
9909
- return (0, import_core8.perUnitCharge)(plan) != null || (0, import_core8.includedUnits)(plan) != null;
9916
+ return (0, import_core11.countsUsage)(plan);
9910
9917
  }
9911
9918
  function resolvePlanShape(plan) {
9912
9919
  if (!plan) return null;
9913
- if (((0, import_core8.trialDays)(plan) ?? 0) > 0) return "trial";
9920
+ if (((0, import_core11.trialDays)(plan) ?? 0) > 0) return "trial";
9914
9921
  if (!isPaidPlan(plan)) return "free";
9915
9922
  const metered = isMeteredPlan(plan);
9916
- if ((0, import_core8.billingCycle)(plan)) return metered ? "recurring-metered" : "recurring-unlimited";
9923
+ if ((0, import_core11.billingCycle)(plan)) return metered ? "recurring-metered" : "recurring-unlimited";
9917
9924
  return metered ? "usage-based" : "recurring-unlimited";
9918
9925
  }
9919
9926
  function resolveActivationStrategy(plan) {
@@ -897,17 +897,14 @@ type PlanShape = 'free' | 'trial' | 'usage-based' | 'recurring-unlimited' | 'rec
897
897
  type ActivationStrategy = 'activate' | 'topup-first' | 'paid-checkout';
898
898
  /**
899
899
  * The subset of a plan — or of the plan snapshot frozen onto a purchase
900
- * — that the shape derivation reads. The two differ: a plan carries
901
- * `requiresPayment`, a snapshot carries `isMetered`, and neither
902
- * carries both. Everything else comes out of `options[]`.
900
+ * — that the shape derivation reads. A plan carries `requiresPayment`;
901
+ * a snapshot may not. Usage-counting and the rest come out of `options[]`.
903
902
  */
904
903
  interface PlanLike {
905
904
  /** Composable pricing options: charges, billing cycle, limit, trial. */
906
905
  options?: PricingOptionLike[] | null;
907
906
  /** `false` marks a free plan. Only present on a plan, not a snapshot. */
908
907
  requiresPayment?: boolean | null;
909
- /** Set on a frozen purchase snapshot when the plan meters usage. */
910
- isMetered?: boolean | null;
911
908
  /** Derived headline amount; only a fallback for snapshots frozen before `options[]`. */
912
909
  price?: number | null;
913
910
  currency?: string | null;
@@ -897,17 +897,14 @@ type PlanShape = 'free' | 'trial' | 'usage-based' | 'recurring-unlimited' | 'rec
897
897
  type ActivationStrategy = 'activate' | 'topup-first' | 'paid-checkout';
898
898
  /**
899
899
  * The subset of a plan — or of the plan snapshot frozen onto a purchase
900
- * — that the shape derivation reads. The two differ: a plan carries
901
- * `requiresPayment`, a snapshot carries `isMetered`, and neither
902
- * carries both. Everything else comes out of `options[]`.
900
+ * — that the shape derivation reads. A plan carries `requiresPayment`;
901
+ * a snapshot may not. Usage-counting and the rest come out of `options[]`.
903
902
  */
904
903
  interface PlanLike {
905
904
  /** Composable pricing options: charges, billing cycle, limit, trial. */
906
905
  options?: PricingOptionLike[] | null;
907
906
  /** `false` marks a free plan. Only present on a plan, not a snapshot. */
908
907
  requiresPayment?: boolean | null;
909
- /** Set on a frozen purchase snapshot when the plan meters usage. */
910
- isMetered?: boolean | null;
911
908
  /** Derived headline amount; only a fallback for snapshots frozen before `options[]`. */
912
909
  price?: number | null;
913
910
  currency?: string | null;
package/dist/mcp/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  ExternalLinkGlyph,
4
4
  LaunchCustomerPortalButton,
5
5
  paymentMethodCache
6
- } from "../chunk-D6TOFFNL.js";
6
+ } from "../chunk-VUGPLAS4.js";
7
7
  import {
8
8
  AmountPicker,
9
9
  BalanceBadge,
@@ -41,7 +41,7 @@ import {
41
41
  usePurchaseStatus,
42
42
  useTopupAmountSelector,
43
43
  useTransport
44
- } from "../chunk-6ABOJOEV.js";
44
+ } from "../chunk-JN5BJ5MI.js";
45
45
  import "../chunk-UMXOAUW7.js";
46
46
  import "../chunk-MLKGABMK.js";
47
47
 
@@ -2568,8 +2568,7 @@ function McpApp({
2568
2568
  import {
2569
2569
  billingCycle,
2570
2570
  charges,
2571
- includedUnits,
2572
- perUnitCharge,
2571
+ countsUsage,
2573
2572
  trialDays
2574
2573
  } from "@solvapay/core";
2575
2574
  function isPaidPlan(plan) {
@@ -2578,8 +2577,7 @@ function isPaidPlan(plan) {
2578
2577
  return (plan.price ?? 0) > 0;
2579
2578
  }
2580
2579
  function isMeteredPlan(plan) {
2581
- if (plan.isMetered === true) return true;
2582
- return perUnitCharge(plan) != null || includedUnits(plan) != null;
2580
+ return countsUsage(plan);
2583
2581
  }
2584
2582
  function resolvePlanShape(plan) {
2585
2583
  if (!plan) return null;
@@ -665,10 +665,10 @@ function createHttpTransport(config) {
665
665
  onErrorContext: "getUsage",
666
666
  errorPrefix: "Failed to load usage"
667
667
  }),
668
- getLimits: async ({ productRef, meterName }) => {
668
+ getLimits: async ({ productRef, meterName: meterName2 }) => {
669
669
  const base = routeFor(config, "getLimits");
670
670
  const params = new URLSearchParams({ productRef });
671
- if (meterName) params.set("meterName", meterName);
671
+ if (meterName2) params.set("meterName", meterName2);
672
672
  const url = `${base}?${params.toString()}`;
673
673
  const data = await request(config, url, {
674
674
  method: "GET",
@@ -2998,7 +2998,8 @@ function normalizeOneTimePurchase(input) {
2998
2998
  planSnapshot: {
2999
2999
  price: input.amount,
3000
3000
  currency: input.currency,
3001
- isMetered: false
3001
+ isMetered: false,
3002
+ options: []
3002
3003
  }
3003
3004
  };
3004
3005
  }
@@ -4929,6 +4930,7 @@ var BalanceBadge = (0, import_react24.forwardRef)(function BalanceBadge2({ asChi
4929
4930
  });
4930
4931
 
4931
4932
  // src/primitives/CancelPlanButton.tsx
4933
+ var import_core5 = require("@solvapay/core");
4932
4934
  var import_react26 = require("react");
4933
4935
 
4934
4936
  // src/hooks/usePurchaseActions.ts
@@ -4986,7 +4988,7 @@ var import_jsx_runtime17 = require("react/jsx-runtime");
4986
4988
  function resolveConfirmText(confirm, purchase, defaults) {
4987
4989
  if (confirm === false) return null;
4988
4990
  if (typeof confirm === "string") return confirm;
4989
- const isUsageBased = purchase?.planSnapshot?.isMetered === true && purchase.isRecurring !== true;
4991
+ const isUsageBased = ((0, import_core5.countsUsage)(purchase?.planSnapshot) || purchase?.planSnapshot?.isMetered === true) && purchase?.isRecurring !== true;
4990
4992
  if (isUsageBased) return defaults.usageBased;
4991
4993
  return defaults.recurring;
4992
4994
  }
@@ -6319,7 +6321,7 @@ function stripSetupIntentParams() {
6319
6321
  }
6320
6322
 
6321
6323
  // src/utils/credit-estimation.ts
6322
- var import_core5 = require("@solvapay/core");
6324
+ var import_core6 = require("@solvapay/core");
6323
6325
  function estimateCredits(resolvedAmountMajor, currency, creditsPerMinorUnit, displayExchangeRate) {
6324
6326
  if (resolvedAmountMajor == null || resolvedAmountMajor <= 0) return null;
6325
6327
  if (creditsPerMinorUnit == null || creditsPerMinorUnit <= 0) return null;
@@ -6331,7 +6333,7 @@ function estimateCurrencyMajorFromCredits(credits, currency, creditsPerMinorUnit
6331
6333
  if (credits == null || credits <= 0) return null;
6332
6334
  if (creditsPerMinorUnit == null || creditsPerMinorUnit <= 0) return null;
6333
6335
  const rate = displayExchangeRate ?? 1;
6334
- const displayMinor = (0, import_core5.creditsToDisplayMinorUnits)({
6336
+ const displayMinor = (0, import_core6.creditsToDisplayMinorUnits)({
6335
6337
  credits,
6336
6338
  creditsPerMinorUnit,
6337
6339
  displayExchangeRate: rate,
@@ -7882,7 +7884,7 @@ function useTransport() {
7882
7884
  }
7883
7885
 
7884
7886
  // src/primitives/checkout/shared.ts
7885
- var import_core6 = require("@solvapay/core");
7887
+ var import_core7 = require("@solvapay/core");
7886
7888
  function isPayg(plan) {
7887
7889
  return isPaygPlan(plan ?? null);
7888
7890
  }
@@ -7923,25 +7925,25 @@ function formatContinueLabel(plan, locale, pricingOption) {
7923
7925
  return `Continue with ${plan.name ?? "Plan"} \u2014 ${priceLabel}${cycle}`;
7924
7926
  }
7925
7927
  function planBillingInterval(plan) {
7926
- return (0, import_core6.billingCycle)(plan)?.interval ?? null;
7928
+ return (0, import_core7.billingCycle)(plan)?.interval ?? null;
7927
7929
  }
7928
7930
  function formatPaygRate(plan, locale, balance) {
7929
- const credits = (0, import_core6.creditsPerUnitFromBalance)(plan, balance);
7931
+ const credits = (0, import_core7.creditsPerUnitFromBalance)(plan, balance);
7930
7932
  if (credits != null) {
7931
7933
  return `${credits.toLocaleString(locale)} ${credits === 1 ? "credit" : "credits"} / call`;
7932
7934
  }
7933
- const charge = (0, import_core6.perUnitCharge)(plan);
7935
+ const charge = (0, import_core7.perUnitCharge)(plan);
7934
7936
  if (charge && charge.amountMinor > 0) {
7935
7937
  return `${formatPrice(charge.amountMinor, charge.currency.toUpperCase(), { locale })} / call`;
7936
7938
  }
7937
7939
  return null;
7938
7940
  }
7939
7941
  function inferIncludedUnits(plan) {
7940
- const cap = (0, import_core6.includedUnits)(plan);
7942
+ const cap = (0, import_core7.includedUnits)(plan);
7941
7943
  return cap != null && cap > 0 ? cap : null;
7942
7944
  }
7943
7945
  function planMeterName(plan) {
7944
- return (0, import_core6.perUnitCharge)(plan)?.meter ?? null;
7946
+ return (0, import_core7.meterName)(plan);
7945
7947
  }
7946
7948
  function shortCycle(cycle) {
7947
7949
  if (!cycle) return "mo";
@@ -9116,6 +9118,7 @@ function usePaywallNoticeOptional() {
9116
9118
  var import_react43 = require("react");
9117
9119
 
9118
9120
  // src/hooks/useUsage.ts
9121
+ var import_core8 = require("@solvapay/core");
9119
9122
  var import_react42 = require("react");
9120
9123
 
9121
9124
  // src/hooks/useLimits.ts
@@ -9123,19 +9126,19 @@ var import_react41 = require("react");
9123
9126
  var CACHE_TTL_MS = 1e4;
9124
9127
  var OPTIMISTIC_GRACE_MS = 8e3;
9125
9128
  var limitsCache = /* @__PURE__ */ new Map();
9126
- function cacheKey(customerRef, productRef, meterName) {
9127
- return `${customerRef ?? "anonymous"}:${productRef}:${meterName}`;
9129
+ function cacheKey(customerRef, productRef, meterName2) {
9130
+ return `${customerRef ?? "anonymous"}:${productRef}:${meterName2}`;
9128
9131
  }
9129
9132
  function isUnlimited(remaining) {
9130
9133
  return remaining < 0;
9131
9134
  }
9132
9135
  function useLimits(options) {
9133
9136
  const { productRef, meterName: rawMeterName, enabled = true } = options;
9134
- const meterName = rawMeterName || "requests";
9137
+ const meterName2 = rawMeterName || "requests";
9135
9138
  const transport = useTransport();
9136
9139
  const { customerRef } = useCustomer();
9137
9140
  const { purchase } = useSolvaPay();
9138
- const currentKey = !productRef || !enabled ? null : cacheKey(customerRef, productRef, meterName);
9141
+ const currentKey = !productRef || !enabled ? null : cacheKey(customerRef, productRef, meterName2);
9139
9142
  const [data, setData] = (0, import_react41.useState)(() => {
9140
9143
  if (!currentKey) return null;
9141
9144
  const cached = limitsCache.get(currentKey);
@@ -9190,7 +9193,7 @@ function useLimits(options) {
9190
9193
  setLoading(false);
9191
9194
  return;
9192
9195
  }
9193
- const key = cacheKey(customerRef, productRef, meterName);
9196
+ const key = cacheKey(customerRef, productRef, meterName2);
9194
9197
  const cached = limitsCache.get(key);
9195
9198
  const now = Date.now();
9196
9199
  if (cached?.promise) {
@@ -9203,7 +9206,7 @@ function useLimits(options) {
9203
9206
  setError(null);
9204
9207
  return;
9205
9208
  }
9206
- const promise = transport.getLimits({ productRef, meterName });
9209
+ const promise = transport.getLimits({ productRef, meterName: meterName2 });
9207
9210
  limitsCache.set(key, { data: cached?.data ?? null, timestamp: now, promise });
9208
9211
  const fresh = await attachToFetch(promise);
9209
9212
  if (fresh !== null) {
@@ -9212,7 +9215,7 @@ function useLimits(options) {
9212
9215
  limitsCache.delete(key);
9213
9216
  }
9214
9217
  },
9215
- [transport, customerRef, productRef, meterName, enabled, attachToFetch]
9218
+ [transport, customerRef, productRef, meterName2, enabled, attachToFetch]
9216
9219
  );
9217
9220
  (0, import_react41.useEffect)(() => {
9218
9221
  fetchLimits(false);
@@ -9230,7 +9233,7 @@ function useLimits(options) {
9230
9233
  (delta) => {
9231
9234
  if (!productRef || !enabled) return;
9232
9235
  if (unlimitedRef.current) return;
9233
- const key = cacheKey(customerRef, productRef, meterName);
9236
+ const key = cacheKey(customerRef, productRef, meterName2);
9234
9237
  setData((prev) => {
9235
9238
  const cached = limitsCache.get(key);
9236
9239
  const baseline = prev ?? cached?.data ?? null;
@@ -9257,7 +9260,7 @@ function useLimits(options) {
9257
9260
  });
9258
9261
  }, OPTIMISTIC_GRACE_MS);
9259
9262
  },
9260
- [customerRef, productRef, meterName, enabled, fetchLimits]
9263
+ [customerRef, productRef, meterName2, enabled, fetchLimits]
9261
9264
  );
9262
9265
  return {
9263
9266
  remaining: data?.remaining ?? null,
@@ -9276,7 +9279,9 @@ function useLimits(options) {
9276
9279
  function deriveUsage(purchase, limits) {
9277
9280
  if (!purchase) return null;
9278
9281
  const usage = purchase.usage;
9279
- if (purchase.planSnapshot?.isMetered !== true && !usage) return null;
9282
+ if (!(0, import_core8.countsUsage)(purchase.planSnapshot) && purchase.planSnapshot?.isMetered !== true && !usage) {
9283
+ return null;
9284
+ }
9280
9285
  const used = typeof usage?.used === "number" ? usage.used : 0;
9281
9286
  const hasFiniteCap = limits.unlimited === false && limits.remaining !== null;
9282
9287
  const remaining = hasFiniteCap ? limits.remaining : null;
@@ -9299,18 +9304,18 @@ function useUsage() {
9299
9304
  const [override, setOverride] = (0, import_react42.useState)(null);
9300
9305
  const [error, setError] = (0, import_react42.useState)(null);
9301
9306
  const [transportLoading, setTransportLoading] = (0, import_react42.useState)(false);
9302
- const isMetered = activePurchase?.planSnapshot?.isMetered === true;
9307
+ const usageCounted = (0, import_core8.countsUsage)(activePurchase?.planSnapshot) || activePurchase?.planSnapshot?.isMetered === true;
9303
9308
  const {
9304
9309
  remaining: limitRemaining,
9305
9310
  unlimited,
9306
- meterName
9311
+ meterName: meterName2
9307
9312
  } = useLimits({
9308
9313
  productRef: activePurchase?.productRef,
9309
- enabled: isMetered
9314
+ enabled: usageCounted
9310
9315
  });
9311
9316
  const derived = (0, import_react42.useMemo)(
9312
- () => deriveUsage(activePurchase ?? null, { remaining: limitRemaining, unlimited, meterName }),
9313
- [activePurchase, limitRemaining, unlimited, meterName]
9317
+ () => deriveUsage(activePurchase ?? null, { remaining: limitRemaining, unlimited, meterName: meterName2 }),
9318
+ [activePurchase, limitRemaining, unlimited, meterName2]
9314
9319
  );
9315
9320
  const activePurchaseRef = activePurchase?.reference ?? null;
9316
9321
  (0, import_react42.useEffect)(() => {