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

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @solvapay/react changelog
2
2
 
3
- ## 2.0.1-preview-7d806cc7af767dec26b099517bc6ededc85adf9a
3
+ ## 2.0.1
4
4
 
5
5
  ### Patch Changes
6
6
 
@@ -16,9 +16,12 @@
16
16
  on the plan row. Usage surfaces (`useUsage`, `getUsageCore`, plan cards) derive
17
17
  from `options[]` instead of trusting `planSnapshot.isMetered` alone.
18
18
 
19
+ - 30f7021: PAYG embedded checkout now activates first, then shows the credit top-up picker only when the wallet is empty.
20
+
21
+ A funded wallet skips the amount and payment steps. `ActivationFlow` follows the same contract: API `activated` at zero credits opens the amount picker instead of firing `onSuccess`. Once the customer is on the amount or payment step, a later zero-credit balance refetch no longer rewinds them back to the picker.
22
+
19
23
  - Updated dependencies [a47adff]
20
- - @solvapay/core@1.5.0-preview-7d806cc7af767dec26b099517bc6ededc85adf9a
21
- - @solvapay/mcp-core@0.3.2-preview-7d806cc7af767dec26b099517bc6ededc85adf9a
24
+ - @solvapay/core@1.5.0
22
25
 
23
26
  ## 2.0.0
24
27
 
@@ -14,7 +14,7 @@ import {
14
14
  usePurchaseStatus,
15
15
  useSolvaPay,
16
16
  useTransport
17
- } from "./chunk-JN5BJ5MI.js";
17
+ } from "./chunk-GMZU5M6R.js";
18
18
 
19
19
  // src/components/CancelledPlanNotice.tsx
20
20
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -483,7 +483,7 @@ var enCopy = {
483
483
  activateButton: "Activate",
484
484
  activatingLabel: "Activating...",
485
485
  topupHeading: "Add credits",
486
- topupSubheading: "Top up your credits to activate this plan.",
486
+ topupSubheading: "Top up your credits to start using this plan.",
487
487
  continueToPayment: "Continue to payment",
488
488
  changeAmountButton: "Change amount",
489
489
  retryingHeading: "Activating your plan...",
@@ -816,7 +816,7 @@ var SolvaPayProvider = ({ config, children }) => {
816
816
  setHasAttachBusinessDetails(!!transportRef.current.attachBusinessDetails);
817
817
  }, [config]);
818
818
  const fetchBalanceImpl = useCallback(async () => {
819
- if (optimisticUntilRef.current > Date.now()) return;
819
+ if (optimisticUntilRef.current > Date.now()) return creditsValueRef.current;
820
820
  if (!isAuthenticated && !internalCustomerRef) {
821
821
  creditsValueRef.current = null;
822
822
  setCreditsValue(null);
@@ -826,18 +826,16 @@ var SolvaPayProvider = ({ config, children }) => {
826
826
  setDisplayBlockValue(null);
827
827
  setBalanceLoading(false);
828
828
  balanceLoadedRef.current = false;
829
- return;
829
+ return null;
830
830
  }
831
- if (balanceInFlightRef.current) return;
831
+ if (balanceInFlightRef.current) return creditsValueRef.current;
832
832
  balanceInFlightRef.current = true;
833
833
  if (!balanceLoadedRef.current) {
834
834
  setBalanceLoading(true);
835
835
  }
836
836
  try {
837
837
  if (!transportRef.current.getBalance) {
838
- setBalanceLoading(false);
839
- balanceInFlightRef.current = false;
840
- return;
838
+ return creditsValueRef.current;
841
839
  }
842
840
  const data = await transportRef.current.getBalance();
843
841
  creditsValueRef.current = data.credits ?? null;
@@ -847,8 +845,10 @@ var SolvaPayProvider = ({ config, children }) => {
847
845
  setDisplayExchangeRateValue(data.displayExchangeRate ?? null);
848
846
  setDisplayBlockValue(data.display ?? null);
849
847
  balanceLoadedRef.current = true;
848
+ return creditsValueRef.current;
850
849
  } catch (error) {
851
850
  console.error("[SolvaPayProvider] Failed to fetch balance:", error);
851
+ return creditsValueRef.current;
852
852
  } finally {
853
853
  setBalanceLoading(false);
854
854
  balanceInFlightRef.current = false;
@@ -6698,8 +6698,23 @@ function useCheckoutFlow(opts) {
6698
6698
  setStatus("activating");
6699
6699
  try {
6700
6700
  await transport.activatePlan({ productRef, planRef: selectedPlanRef });
6701
+ const credits = await balance.refetch() ?? balance.credits;
6702
+ if (credits == null) {
6703
+ throw new Error("Credit balance is unavailable");
6704
+ }
6701
6705
  setStatus("idle");
6702
- setStep("amount");
6706
+ if (credits === 0) {
6707
+ setStep("amount");
6708
+ } else {
6709
+ const meta = {
6710
+ branch: "payg",
6711
+ plan: selectedPlanShape,
6712
+ rateLabel: formatPaygRate(selectedPlanShape, locale, balance)
6713
+ };
6714
+ setSuccessMeta(meta);
6715
+ setStep("success");
6716
+ onPurchaseSuccessRef.current?.(meta);
6717
+ }
6703
6718
  return true;
6704
6719
  } catch (err) {
6705
6720
  const wrapped = err instanceof Error ? err : new Error("Activation failed");
@@ -6708,13 +6723,37 @@ function useCheckoutFlow(opts) {
6708
6723
  onErrorRef.current?.(wrapped, "activate");
6709
6724
  return false;
6710
6725
  }
6711
- }, [productRef, selectedPlanRef, selectedPlanShape, transport]);
6726
+ }, [balance, locale, productRef, selectedPlanRef, selectedPlanShape, transport]);
6712
6727
  const advanceFromPlan = useCallback21(async () => {
6713
6728
  if (!selectedPlanShape || !selectedPlanRef) return;
6714
6729
  if (branch === "payg") {
6715
6730
  if (planCtx.currentPlanRef === selectedPlanRef) {
6716
6731
  setError(null);
6717
- setStep("amount");
6732
+ setStatus("activating");
6733
+ try {
6734
+ const credits = await balance.refetch() ?? balance.credits;
6735
+ if (credits == null) {
6736
+ throw new Error("Credit balance is unavailable");
6737
+ }
6738
+ setStatus("idle");
6739
+ if (credits === 0) {
6740
+ setStep("amount");
6741
+ } else {
6742
+ const meta = {
6743
+ branch: "payg",
6744
+ plan: selectedPlanShape,
6745
+ rateLabel: formatPaygRate(selectedPlanShape, locale, balance)
6746
+ };
6747
+ setSuccessMeta(meta);
6748
+ setStep("success");
6749
+ onPurchaseSuccessRef.current?.(meta);
6750
+ }
6751
+ } catch (err) {
6752
+ const wrapped = err instanceof Error ? err : new Error("Activation failed");
6753
+ setError(wrapped.message);
6754
+ setStatus("error");
6755
+ onErrorRef.current?.(wrapped, "activate");
6756
+ }
6718
6757
  return;
6719
6758
  }
6720
6759
  await runActivate();
@@ -6722,7 +6761,7 @@ function useCheckoutFlow(opts) {
6722
6761
  }
6723
6762
  setError(null);
6724
6763
  setStep("payment");
6725
- }, [branch, planCtx.currentPlanRef, runActivate, selectedPlanRef, selectedPlanShape]);
6764
+ }, [balance, branch, locale, planCtx.currentPlanRef, runActivate, selectedPlanRef, selectedPlanShape]);
6726
6765
  const recordPaygSuccess = useCallback21(
6727
6766
  (creditsAddedFromBackend) => {
6728
6767
  if (!selectedPlanShape || selectedAmountMinor == null) return;
@@ -40,7 +40,7 @@ import {
40
40
  useTopupAmountSelector,
41
41
  withPaymentElementDefaults,
42
42
  writeAutoRechargeCache
43
- } from "./chunk-JN5BJ5MI.js";
43
+ } from "./chunk-GMZU5M6R.js";
44
44
 
45
45
  // src/TopupForm.tsx
46
46
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -216,12 +216,29 @@ var Root2 = forwardRef3(function ActivationFlowRoot({
216
216
  const { activate, state, error, result, reset } = useActivation();
217
217
  const currency = plan?.currency ?? "USD";
218
218
  const amountSelector = useTopupAmountSelector({ currency });
219
- const { adjustBalance, creditsPerMinorUnit, displayExchangeRate } = useBalance();
219
+ const {
220
+ credits,
221
+ adjustBalance,
222
+ creditsPerMinorUnit,
223
+ displayExchangeRate,
224
+ refetch,
225
+ loading: balanceLoading
226
+ } = useBalance();
220
227
  const [step, setStep] = useState2("summary");
221
228
  const calledSuccessRef = useRef(false);
222
229
  const retryTimeoutRef = useRef(null);
223
230
  useEffect2(() => {
224
- if (state === "activated" && !calledSuccessRef.current) {
231
+ if (state !== "activated") return;
232
+ if (step === "selectAmount" || step === "topupPayment") return;
233
+ const isUsagePlan = plan?.type === "usage-based";
234
+ if (isUsagePlan && (credits === null || balanceLoading)) {
235
+ return;
236
+ }
237
+ if (isUsagePlan && credits === 0) {
238
+ setStep("selectAmount");
239
+ return;
240
+ }
241
+ if (!calledSuccessRef.current) {
225
242
  calledSuccessRef.current = true;
226
243
  setStep("activated");
227
244
  if (result) {
@@ -229,7 +246,7 @@ var Root2 = forwardRef3(function ActivationFlowRoot({
229
246
  onSuccess?.(activationResult);
230
247
  }
231
248
  }
232
- }, [state, result, onSuccess]);
249
+ }, [state, result, onSuccess, credits, plan, balanceLoading, step]);
233
250
  useEffect2(() => {
234
251
  if (state === "topup_required" && (step === "summary" || step === "activating")) {
235
252
  setStep("selectAmount");
@@ -245,7 +262,8 @@ var Root2 = forwardRef3(function ActivationFlowRoot({
245
262
  if (!resolvedPlanRef) return;
246
263
  setStep("activating");
247
264
  await activate({ productRef, planRef: resolvedPlanRef });
248
- }, [activate, productRef, resolvedPlanRef]);
265
+ await refetch();
266
+ }, [activate, productRef, resolvedPlanRef, refetch]);
249
267
  const goToTopupPayment = useCallback(() => {
250
268
  if (amountSelector.validate()) setStep("topupPayment");
251
269
  }, [amountSelector]);
@@ -336,16 +354,7 @@ var Root2 = forwardRef3(function ActivationFlowRoot({
336
354
  ]
337
355
  );
338
356
  const Comp = asChild ? Slot : "div";
339
- return /* @__PURE__ */ jsx4(ActivationFlowContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx4(
340
- Comp,
341
- {
342
- ref: forwardedRef,
343
- "data-solvapay-activation-flow": "",
344
- "data-state": step,
345
- ...rest,
346
- children
347
- }
348
- ) });
357
+ return /* @__PURE__ */ jsx4(ActivationFlowContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx4(Comp, { ref: forwardedRef, "data-solvapay-activation-flow": "", "data-state": step, ...rest, children }) });
349
358
  });
350
359
  function matchStep(step, allowed) {
351
360
  return allowed.includes(step);
@@ -435,16 +444,7 @@ var ErrorSlot2 = forwardRef3(function ActivationFlowError({ asChild, children, .
435
444
  const ctx = useFlowCtx("Error");
436
445
  if (ctx.step !== "error") return null;
437
446
  const Comp = asChild ? Slot : "div";
438
- return /* @__PURE__ */ jsx4(
439
- Comp,
440
- {
441
- ref: forwardedRef,
442
- role: "alert",
443
- "data-solvapay-activation-flow-error": "",
444
- ...rest,
445
- children: children ?? ctx.error
446
- }
447
- );
447
+ return /* @__PURE__ */ jsx4(Comp, { ref: forwardedRef, role: "alert", "data-solvapay-activation-flow-error": "", ...rest, children: children ?? ctx.error });
448
448
  });
449
449
  var ActivationFlowRoot2 = Root2;
450
450
  var ActivationFlowSummary2 = Summary;
@@ -2885,22 +2885,27 @@ function Success({ className, children }) {
2885
2885
  }
2886
2886
  const meta = flow.successMeta;
2887
2887
  if (meta.branch === "payg") {
2888
+ const chargedAmount = meta.amountMinor;
2889
+ const chargedCurrency = meta.currency;
2890
+ const charged = chargedAmount != null && chargedCurrency != null;
2888
2891
  return /* @__PURE__ */ jsxs3("div", { className: className ?? "solvapay-checkout-success", "data-branch": "payg", children: [
2889
2892
  /* @__PURE__ */ jsx7("div", { className: "solvapay-checkout-success-check", "aria-hidden": "true", children: "\u2713" }),
2890
- /* @__PURE__ */ jsx7("h2", { className: "solvapay-checkout-success-heading", children: "Credits added" }),
2893
+ /* @__PURE__ */ jsx7("h2", { className: "solvapay-checkout-success-heading", children: charged ? "Credits added" : "Plan activated" }),
2891
2894
  /* @__PURE__ */ jsx7("p", { className: "solvapay-checkout-success-subheading", children: "Pay as you go plan is active." }),
2892
2895
  /* @__PURE__ */ jsxs3("dl", { className: "solvapay-checkout-receipt", "data-variant": "payg", children: [
2893
- /* @__PURE__ */ jsxs3("div", { className: "solvapay-checkout-receipt-row", children: [
2894
- /* @__PURE__ */ jsx7("dt", { children: "Amount" }),
2895
- /* @__PURE__ */ jsx7("dd", { children: formatPrice(meta.amountMinor, meta.currency, { locale }) })
2896
- ] }),
2897
- /* @__PURE__ */ jsxs3("div", { className: "solvapay-checkout-receipt-row", children: [
2898
- /* @__PURE__ */ jsx7("dt", { children: "Credits" }),
2899
- /* @__PURE__ */ jsxs3("dd", { children: [
2900
- "+",
2901
- meta.creditsAdded.toLocaleString(locale)
2896
+ charged ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
2897
+ /* @__PURE__ */ jsxs3("div", { className: "solvapay-checkout-receipt-row", children: [
2898
+ /* @__PURE__ */ jsx7("dt", { children: "Amount" }),
2899
+ /* @__PURE__ */ jsx7("dd", { children: formatPrice(chargedAmount, chargedCurrency, { locale }) })
2900
+ ] }),
2901
+ /* @__PURE__ */ jsxs3("div", { className: "solvapay-checkout-receipt-row", children: [
2902
+ /* @__PURE__ */ jsx7("dt", { children: "Credits" }),
2903
+ /* @__PURE__ */ jsxs3("dd", { children: [
2904
+ "+",
2905
+ (meta.creditsAdded ?? 0).toLocaleString(locale)
2906
+ ] })
2902
2907
  ] })
2903
- ] }),
2908
+ ] }) : null,
2904
2909
  /* @__PURE__ */ jsxs3("div", { className: "solvapay-checkout-receipt-row", children: [
2905
2910
  /* @__PURE__ */ jsx7("dt", { children: "Plan" }),
2906
2911
  /* @__PURE__ */ jsx7("dd", { children: meta.plan.name ?? "Pay as you go" })
@@ -1,4 +1,4 @@
1
- import { $ as components, b as Plan, P as PaymentFormProps, a as PrefillCustomer, A as ActivationResult, r as UseTopupAmountSelectorReturn, a0 as AutoRechargeInput, t as AutoRechargeConfig, a1 as AutoRechargeDisplayBlock, E as CheckoutStep, O as SuccessMeta } from './shared-CAxz5wWs.js';
1
+ import { $ as components, b as Plan, P as PaymentFormProps, a as PrefillCustomer, A as ActivationResult, r as UseTopupAmountSelectorReturn, a0 as AutoRechargeInput, t as AutoRechargeConfig, a1 as AutoRechargeDisplayBlock, E as CheckoutStep, O as SuccessMeta } from './shared-BI78qUom.js';
2
2
  import React from 'react';
3
3
  import { PaymentElement, CardElement } from '@stripe/react-stripe-js';
4
4
  import { TaxBreakdown } from '@solvapay/core';
@@ -411,8 +411,11 @@ declare const BalanceBadge: React.ForwardRefExoticComponent<React.HTMLAttributes
411
411
  * ActivationFlow compound primitive.
412
412
  *
413
413
  * Drives the usage-based plan activation state machine:
414
- * summary → activating → (topup_required selectAmounttopupPayment
415
- * retrying) → activated | error.
414
+ * summary → activating → (activated + empty wallet selectAmount
415
+ * topupPayment → retrying) → activated | error.
416
+ *
417
+ * `topup_required` remains a tolerated legacy activate status that also
418
+ * opens the amount picker.
416
419
  *
417
420
  * `Root` exposes `data-state` set to the current step and publishes the
418
421
  * shared context consumed by leaves. Leaves render only during their
@@ -1,4 +1,4 @@
1
- import { $ as components, b as Plan, P as PaymentFormProps, a as PrefillCustomer, A as ActivationResult, r as UseTopupAmountSelectorReturn, a0 as AutoRechargeInput, t as AutoRechargeConfig, a1 as AutoRechargeDisplayBlock, E as CheckoutStep, O as SuccessMeta } from './shared-A_Sg-fJE.cjs';
1
+ import { $ as components, b as Plan, P as PaymentFormProps, a as PrefillCustomer, A as ActivationResult, r as UseTopupAmountSelectorReturn, a0 as AutoRechargeInput, t as AutoRechargeConfig, a1 as AutoRechargeDisplayBlock, E as CheckoutStep, O as SuccessMeta } from './shared-BgyNW-D3.cjs';
2
2
  import React from 'react';
3
3
  import { PaymentElement, CardElement } from '@stripe/react-stripe-js';
4
4
  import { TaxBreakdown } from '@solvapay/core';
@@ -411,8 +411,11 @@ declare const BalanceBadge: React.ForwardRefExoticComponent<React.HTMLAttributes
411
411
  * ActivationFlow compound primitive.
412
412
  *
413
413
  * Drives the usage-based plan activation state machine:
414
- * summary → activating → (topup_required selectAmounttopupPayment
415
- * retrying) → activated | error.
414
+ * summary → activating → (activated + empty wallet selectAmount
415
+ * topupPayment → retrying) → activated | error.
416
+ *
417
+ * `topup_required` remains a tolerated legacy activate status that also
418
+ * opens the amount picker.
416
419
  *
417
420
  * `Root` exposes `data-state` set to the current step and publishes the
418
421
  * shared context consumed by leaves. Leaves render only during their
package/dist/index.cjs CHANGED
@@ -727,7 +727,7 @@ var enCopy = {
727
727
  activateButton: "Activate",
728
728
  activatingLabel: "Activating...",
729
729
  topupHeading: "Add credits",
730
- topupSubheading: "Top up your credits to activate this plan.",
730
+ topupSubheading: "Top up your credits to start using this plan.",
731
731
  continueToPayment: "Continue to payment",
732
732
  changeAmountButton: "Change amount",
733
733
  retryingHeading: "Activating your plan...",
@@ -980,7 +980,7 @@ var SolvaPayProvider = ({ config, children }) => {
980
980
  setHasAttachBusinessDetails(!!transportRef.current.attachBusinessDetails);
981
981
  }, [config]);
982
982
  const fetchBalanceImpl = (0, import_react2.useCallback)(async () => {
983
- if (optimisticUntilRef.current > Date.now()) return;
983
+ if (optimisticUntilRef.current > Date.now()) return creditsValueRef.current;
984
984
  if (!isAuthenticated && !internalCustomerRef) {
985
985
  creditsValueRef.current = null;
986
986
  setCreditsValue(null);
@@ -990,18 +990,16 @@ var SolvaPayProvider = ({ config, children }) => {
990
990
  setDisplayBlockValue(null);
991
991
  setBalanceLoading(false);
992
992
  balanceLoadedRef.current = false;
993
- return;
993
+ return null;
994
994
  }
995
- if (balanceInFlightRef.current) return;
995
+ if (balanceInFlightRef.current) return creditsValueRef.current;
996
996
  balanceInFlightRef.current = true;
997
997
  if (!balanceLoadedRef.current) {
998
998
  setBalanceLoading(true);
999
999
  }
1000
1000
  try {
1001
1001
  if (!transportRef.current.getBalance) {
1002
- setBalanceLoading(false);
1003
- balanceInFlightRef.current = false;
1004
- return;
1002
+ return creditsValueRef.current;
1005
1003
  }
1006
1004
  const data = await transportRef.current.getBalance();
1007
1005
  creditsValueRef.current = data.credits ?? null;
@@ -1011,8 +1009,10 @@ var SolvaPayProvider = ({ config, children }) => {
1011
1009
  setDisplayExchangeRateValue(data.displayExchangeRate ?? null);
1012
1010
  setDisplayBlockValue(data.display ?? null);
1013
1011
  balanceLoadedRef.current = true;
1012
+ return creditsValueRef.current;
1014
1013
  } catch (error) {
1015
1014
  console.error("[SolvaPayProvider] Failed to fetch balance:", error);
1015
+ return creditsValueRef.current;
1016
1016
  } finally {
1017
1017
  setBalanceLoading(false);
1018
1018
  balanceInFlightRef.current = false;
@@ -6202,12 +6202,29 @@ var Root7 = (0, import_react29.forwardRef)(function ActivationFlowRoot({
6202
6202
  const { activate, state, error, result, reset } = useActivation();
6203
6203
  const currency = plan?.currency ?? "USD";
6204
6204
  const amountSelector = useTopupAmountSelector({ currency });
6205
- const { adjustBalance, creditsPerMinorUnit, displayExchangeRate } = useBalance();
6205
+ const {
6206
+ credits,
6207
+ adjustBalance,
6208
+ creditsPerMinorUnit,
6209
+ displayExchangeRate,
6210
+ refetch,
6211
+ loading: balanceLoading
6212
+ } = useBalance();
6206
6213
  const [step, setStep] = (0, import_react29.useState)("summary");
6207
6214
  const calledSuccessRef = (0, import_react29.useRef)(false);
6208
6215
  const retryTimeoutRef = (0, import_react29.useRef)(null);
6209
6216
  (0, import_react29.useEffect)(() => {
6210
- if (state === "activated" && !calledSuccessRef.current) {
6217
+ if (state !== "activated") return;
6218
+ if (step === "selectAmount" || step === "topupPayment") return;
6219
+ const isUsagePlan = plan?.type === "usage-based";
6220
+ if (isUsagePlan && (credits === null || balanceLoading)) {
6221
+ return;
6222
+ }
6223
+ if (isUsagePlan && credits === 0) {
6224
+ setStep("selectAmount");
6225
+ return;
6226
+ }
6227
+ if (!calledSuccessRef.current) {
6211
6228
  calledSuccessRef.current = true;
6212
6229
  setStep("activated");
6213
6230
  if (result) {
@@ -6215,7 +6232,7 @@ var Root7 = (0, import_react29.forwardRef)(function ActivationFlowRoot({
6215
6232
  onSuccess?.(activationResult);
6216
6233
  }
6217
6234
  }
6218
- }, [state, result, onSuccess]);
6235
+ }, [state, result, onSuccess, credits, plan, balanceLoading, step]);
6219
6236
  (0, import_react29.useEffect)(() => {
6220
6237
  if (state === "topup_required" && (step === "summary" || step === "activating")) {
6221
6238
  setStep("selectAmount");
@@ -6231,7 +6248,8 @@ var Root7 = (0, import_react29.forwardRef)(function ActivationFlowRoot({
6231
6248
  if (!resolvedPlanRef) return;
6232
6249
  setStep("activating");
6233
6250
  await activate({ productRef, planRef: resolvedPlanRef });
6234
- }, [activate, productRef, resolvedPlanRef]);
6251
+ await refetch();
6252
+ }, [activate, productRef, resolvedPlanRef, refetch]);
6235
6253
  const goToTopupPayment = (0, import_react29.useCallback)(() => {
6236
6254
  if (amountSelector.validate()) setStep("topupPayment");
6237
6255
  }, [amountSelector]);
@@ -6322,16 +6340,7 @@ var Root7 = (0, import_react29.forwardRef)(function ActivationFlowRoot({
6322
6340
  ]
6323
6341
  );
6324
6342
  const Comp = asChild ? Slot : "div";
6325
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ActivationFlowContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6326
- Comp,
6327
- {
6328
- ref: forwardedRef,
6329
- "data-solvapay-activation-flow": "",
6330
- "data-state": step,
6331
- ...rest,
6332
- children
6333
- }
6334
- ) });
6343
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ActivationFlowContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Comp, { ref: forwardedRef, "data-solvapay-activation-flow": "", "data-state": step, ...rest, children }) });
6335
6344
  });
6336
6345
  function matchStep(step, allowed) {
6337
6346
  return allowed.includes(step);
@@ -6421,16 +6430,7 @@ var ErrorSlot5 = (0, import_react29.forwardRef)(function ActivationFlowError({ a
6421
6430
  const ctx = useFlowCtx("Error");
6422
6431
  if (ctx.step !== "error") return null;
6423
6432
  const Comp = asChild ? Slot : "div";
6424
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6425
- Comp,
6426
- {
6427
- ref: forwardedRef,
6428
- role: "alert",
6429
- "data-solvapay-activation-flow-error": "",
6430
- ...rest,
6431
- children: children ?? ctx.error
6432
- }
6433
- );
6433
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Comp, { ref: forwardedRef, role: "alert", "data-solvapay-activation-flow-error": "", ...rest, children: children ?? ctx.error });
6434
6434
  });
6435
6435
  var ActivationFlow = {
6436
6436
  Root: Root7,
@@ -10318,8 +10318,23 @@ function useCheckoutFlow(opts) {
10318
10318
  setStatus("activating");
10319
10319
  try {
10320
10320
  await transport.activatePlan({ productRef, planRef: selectedPlanRef });
10321
+ const credits = await balance.refetch() ?? balance.credits;
10322
+ if (credits == null) {
10323
+ throw new Error("Credit balance is unavailable");
10324
+ }
10321
10325
  setStatus("idle");
10322
- setStep("amount");
10326
+ if (credits === 0) {
10327
+ setStep("amount");
10328
+ } else {
10329
+ const meta = {
10330
+ branch: "payg",
10331
+ plan: selectedPlanShape,
10332
+ rateLabel: formatPaygRate(selectedPlanShape, locale, balance)
10333
+ };
10334
+ setSuccessMeta(meta);
10335
+ setStep("success");
10336
+ onPurchaseSuccessRef.current?.(meta);
10337
+ }
10323
10338
  return true;
10324
10339
  } catch (err) {
10325
10340
  const wrapped = err instanceof Error ? err : new Error("Activation failed");
@@ -10328,13 +10343,37 @@ function useCheckoutFlow(opts) {
10328
10343
  onErrorRef.current?.(wrapped, "activate");
10329
10344
  return false;
10330
10345
  }
10331
- }, [productRef, selectedPlanRef, selectedPlanShape, transport]);
10346
+ }, [balance, locale, productRef, selectedPlanRef, selectedPlanShape, transport]);
10332
10347
  const advanceFromPlan = (0, import_react48.useCallback)(async () => {
10333
10348
  if (!selectedPlanShape || !selectedPlanRef) return;
10334
10349
  if (branch === "payg") {
10335
10350
  if (planCtx.currentPlanRef === selectedPlanRef) {
10336
10351
  setError(null);
10337
- setStep("amount");
10352
+ setStatus("activating");
10353
+ try {
10354
+ const credits = await balance.refetch() ?? balance.credits;
10355
+ if (credits == null) {
10356
+ throw new Error("Credit balance is unavailable");
10357
+ }
10358
+ setStatus("idle");
10359
+ if (credits === 0) {
10360
+ setStep("amount");
10361
+ } else {
10362
+ const meta = {
10363
+ branch: "payg",
10364
+ plan: selectedPlanShape,
10365
+ rateLabel: formatPaygRate(selectedPlanShape, locale, balance)
10366
+ };
10367
+ setSuccessMeta(meta);
10368
+ setStep("success");
10369
+ onPurchaseSuccessRef.current?.(meta);
10370
+ }
10371
+ } catch (err) {
10372
+ const wrapped = err instanceof Error ? err : new Error("Activation failed");
10373
+ setError(wrapped.message);
10374
+ setStatus("error");
10375
+ onErrorRef.current?.(wrapped, "activate");
10376
+ }
10338
10377
  return;
10339
10378
  }
10340
10379
  await runActivate();
@@ -10342,7 +10381,7 @@ function useCheckoutFlow(opts) {
10342
10381
  }
10343
10382
  setError(null);
10344
10383
  setStep("payment");
10345
- }, [branch, planCtx.currentPlanRef, runActivate, selectedPlanRef, selectedPlanShape]);
10384
+ }, [balance, branch, locale, planCtx.currentPlanRef, runActivate, selectedPlanRef, selectedPlanShape]);
10346
10385
  const recordPaygSuccess = (0, import_react48.useCallback)(
10347
10386
  (creditsAddedFromBackend) => {
10348
10387
  if (!selectedPlanShape || selectedAmountMinor == null) return;
@@ -11279,22 +11318,27 @@ function Success({ className, children }) {
11279
11318
  }
11280
11319
  const meta = flow.successMeta;
11281
11320
  if (meta.branch === "payg") {
11321
+ const chargedAmount = meta.amountMinor;
11322
+ const chargedCurrency = meta.currency;
11323
+ const charged = chargedAmount != null && chargedCurrency != null;
11282
11324
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: className ?? "solvapay-checkout-success", "data-branch": "payg", children: [
11283
11325
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "solvapay-checkout-success-check", "aria-hidden": "true", children: "\u2713" }),
11284
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h2", { className: "solvapay-checkout-success-heading", children: "Credits added" }),
11326
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h2", { className: "solvapay-checkout-success-heading", children: charged ? "Credits added" : "Plan activated" }),
11285
11327
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "solvapay-checkout-success-subheading", children: "Pay as you go plan is active." }),
11286
11328
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("dl", { className: "solvapay-checkout-receipt", "data-variant": "payg", children: [
11287
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "solvapay-checkout-receipt-row", children: [
11288
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { children: "Amount" }),
11289
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dd", { children: formatPrice(meta.amountMinor, meta.currency, { locale }) })
11290
- ] }),
11291
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "solvapay-checkout-receipt-row", children: [
11292
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { children: "Credits" }),
11293
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("dd", { children: [
11294
- "+",
11295
- meta.creditsAdded.toLocaleString(locale)
11329
+ charged ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
11330
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "solvapay-checkout-receipt-row", children: [
11331
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { children: "Amount" }),
11332
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dd", { children: formatPrice(chargedAmount, chargedCurrency, { locale }) })
11333
+ ] }),
11334
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "solvapay-checkout-receipt-row", children: [
11335
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { children: "Credits" }),
11336
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("dd", { children: [
11337
+ "+",
11338
+ (meta.creditsAdded ?? 0).toLocaleString(locale)
11339
+ ] })
11296
11340
  ] })
11297
- ] }),
11341
+ ] }) : null,
11298
11342
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "solvapay-checkout-receipt-row", children: [
11299
11343
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { children: "Plan" }),
11300
11344
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dd", { children: meta.plan.name ?? "Pay as you go" })
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, T as TopupFormProps, C as CheckoutResult, b as Plan, A as ActivationResult, c as SaveAutoRechargeResponse, d as PurchaseStatus, e as SolvaPayContextValue, U as UsePlansOptions, f as UsePlansReturn, g as UsePlanOptions, h as UsePlanReturn, i as UseProductReturn, j as UseMerchantReturn, k as SolvaPayCopy, l as PurchaseStatusReturn, m as CancelResult, R as ReactivateResult, n as ActivatePlanResult, o as UseTopupOptions, p as UseTopupReturn, B as BalanceStatus, q as UseTopupAmountSelectorOptions, r as UseTopupAmountSelectorReturn, s as UsePaymentMethodReturn, t as AutoRechargeConfig, u as SaveAutoRechargeInput, v as SolvaPayTransport, w as PartialSolvaPayCopy, x as PurchaseInfo, y as Product, z as SolvaPayConfig } from './shared-A_Sg-fJE.cjs';
3
- export { D as BootstrapPlanLike, E as CheckoutStep, F as CustomerPurchaseData, M as MandateContext, G as MandateTemplate, H as Merchant, I as PaymentError, J as PaymentIntentResult, K as PaymentMethodInfo, L as PaymentResult, N as PurchaseStatusValue, O as SuccessMeta, Q as TopupPaymentResult, V as TransportBalanceResult, W as TransportCheckoutSessionResult, X as TransportCustomerSessionResult, Y as TransportLimitsResult, Z as UnsupportedTransportMethodError } from './shared-A_Sg-fJE.cjs';
4
- import { P as PaymentFormSummary, a as PaymentFormCustomerFields, b as PaymentFormPaymentElement, c as PaymentFormCardElement, d as PaymentFormMandateText, e as PaymentFormTermsCheckbox, f as PaymentFormSubmitButton, g as PaymentFormLoading, h as PaymentFormError, i as PaymentFormLegalFooter, j as PaymentFormBusinessDetails, k as PaymentFormTaxSummary, A as AutoRechargeInputPayload, l as PaywallStructuredContent, C as CheckoutVariant } from './index-BuVbt1j1.cjs';
5
- export { m as ActivationFlowStep, B as BalanceBadge, n as CancelPlanButton, o as CheckoutErrorPhase, p as CheckoutStatus, q as CheckoutSteps, r as CheckoutSummary, s as CheckoutSummaryProps, M as MandateText, t as MandateTextProps, u as PlanBadge, v as ProductBadge, w as PurchaseGate, U as UsageSnapshot, x as UseCheckoutFlowOptions, y as UseCheckoutFlowReturn, z as UseUsageReturn, D as configToAutoRechargeInput, E as deriveVariant, F as useCheckoutFlow, G as useCheckoutStepsContext, H as useUsage } from './index-BuVbt1j1.cjs';
2
+ import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, T as TopupFormProps, C as CheckoutResult, b as Plan, A as ActivationResult, c as SaveAutoRechargeResponse, d as PurchaseStatus, e as SolvaPayContextValue, U as UsePlansOptions, f as UsePlansReturn, g as UsePlanOptions, h as UsePlanReturn, i as UseProductReturn, j as UseMerchantReturn, k as SolvaPayCopy, l as PurchaseStatusReturn, m as CancelResult, R as ReactivateResult, n as ActivatePlanResult, o as UseTopupOptions, p as UseTopupReturn, B as BalanceStatus, q as UseTopupAmountSelectorOptions, r as UseTopupAmountSelectorReturn, s as UsePaymentMethodReturn, t as AutoRechargeConfig, u as SaveAutoRechargeInput, v as SolvaPayTransport, w as PartialSolvaPayCopy, x as PurchaseInfo, y as Product, z as SolvaPayConfig } from './shared-BgyNW-D3.cjs';
3
+ export { D as BootstrapPlanLike, E as CheckoutStep, F as CustomerPurchaseData, M as MandateContext, G as MandateTemplate, H as Merchant, I as PaymentError, J as PaymentIntentResult, K as PaymentMethodInfo, L as PaymentResult, N as PurchaseStatusValue, O as SuccessMeta, Q as TopupPaymentResult, V as TransportBalanceResult, W as TransportCheckoutSessionResult, X as TransportCustomerSessionResult, Y as TransportLimitsResult, Z as UnsupportedTransportMethodError } from './shared-BgyNW-D3.cjs';
4
+ import { P as PaymentFormSummary, a as PaymentFormCustomerFields, b as PaymentFormPaymentElement, c as PaymentFormCardElement, d as PaymentFormMandateText, e as PaymentFormTermsCheckbox, f as PaymentFormSubmitButton, g as PaymentFormLoading, h as PaymentFormError, i as PaymentFormLegalFooter, j as PaymentFormBusinessDetails, k as PaymentFormTaxSummary, A as AutoRechargeInputPayload, l as PaywallStructuredContent, C as CheckoutVariant } from './index-jmmZYs2K.cjs';
5
+ export { m as ActivationFlowStep, B as BalanceBadge, n as CancelPlanButton, o as CheckoutErrorPhase, p as CheckoutStatus, q as CheckoutSteps, r as CheckoutSummary, s as CheckoutSummaryProps, M as MandateText, t as MandateTextProps, u as PlanBadge, v as ProductBadge, w as PurchaseGate, U as UsageSnapshot, x as UseCheckoutFlowOptions, y as UseCheckoutFlowReturn, z as UseUsageReturn, D as configToAutoRechargeInput, E as deriveVariant, F as useCheckoutFlow, G as useCheckoutStepsContext, H as useUsage } from './index-jmmZYs2K.cjs';
6
6
  import { PaymentIntent, Stripe, StripeElements } from '@stripe/stripe-js';
7
7
  import { BusinessDetailsInput, TaxBreakdown } from '@solvapay/core';
8
8
  export { AuthAdapter, createAnonymousAuthAdapter, defaultAuthAdapter, getOrCreateAnonymousCustomerRef, resetAnonymousCustomerRef } from './adapters/auth.cjs';
@@ -237,8 +237,8 @@ declare const AmountPicker: React.FC<AmountPickerProps>;
237
237
  * Renders the full usage-based activation state machine (summary →
238
238
  * activating → selectAmount → topupPayment → retrying → activated | error)
239
239
  * with the golden-path copy, the embedded `<CheckoutSummary>` + `<TopupForm>`,
240
- * and an optional back button. Full control is available by composing the
241
- * primitive at `@solvapay/react/primitives`.
240
+ * and an optional back button. After activate, an empty wallet opens the
241
+ * amount picker; a funded wallet completes immediately.
242
242
  */
243
243
 
244
244
  interface ActivationFlowProps {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, T as TopupFormProps, C as CheckoutResult, b as Plan, A as ActivationResult, c as SaveAutoRechargeResponse, d as PurchaseStatus, e as SolvaPayContextValue, U as UsePlansOptions, f as UsePlansReturn, g as UsePlanOptions, h as UsePlanReturn, i as UseProductReturn, j as UseMerchantReturn, k as SolvaPayCopy, l as PurchaseStatusReturn, m as CancelResult, R as ReactivateResult, n as ActivatePlanResult, o as UseTopupOptions, p as UseTopupReturn, B as BalanceStatus, q as UseTopupAmountSelectorOptions, r as UseTopupAmountSelectorReturn, s as UsePaymentMethodReturn, t as AutoRechargeConfig, u as SaveAutoRechargeInput, v as SolvaPayTransport, w as PartialSolvaPayCopy, x as PurchaseInfo, y as Product, z as SolvaPayConfig } from './shared-CAxz5wWs.js';
3
- export { D as BootstrapPlanLike, E as CheckoutStep, F as CustomerPurchaseData, M as MandateContext, G as MandateTemplate, H as Merchant, I as PaymentError, J as PaymentIntentResult, K as PaymentMethodInfo, L as PaymentResult, N as PurchaseStatusValue, O as SuccessMeta, Q as TopupPaymentResult, V as TransportBalanceResult, W as TransportCheckoutSessionResult, X as TransportCustomerSessionResult, Y as TransportLimitsResult, Z as UnsupportedTransportMethodError } from './shared-CAxz5wWs.js';
4
- import { P as PaymentFormSummary, a as PaymentFormCustomerFields, b as PaymentFormPaymentElement, c as PaymentFormCardElement, d as PaymentFormMandateText, e as PaymentFormTermsCheckbox, f as PaymentFormSubmitButton, g as PaymentFormLoading, h as PaymentFormError, i as PaymentFormLegalFooter, j as PaymentFormBusinessDetails, k as PaymentFormTaxSummary, A as AutoRechargeInputPayload, l as PaywallStructuredContent, C as CheckoutVariant } from './index-DHK9Op-i.js';
5
- export { m as ActivationFlowStep, B as BalanceBadge, n as CancelPlanButton, o as CheckoutErrorPhase, p as CheckoutStatus, q as CheckoutSteps, r as CheckoutSummary, s as CheckoutSummaryProps, M as MandateText, t as MandateTextProps, u as PlanBadge, v as ProductBadge, w as PurchaseGate, U as UsageSnapshot, x as UseCheckoutFlowOptions, y as UseCheckoutFlowReturn, z as UseUsageReturn, D as configToAutoRechargeInput, E as deriveVariant, F as useCheckoutFlow, G as useCheckoutStepsContext, H as useUsage } from './index-DHK9Op-i.js';
2
+ import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, T as TopupFormProps, C as CheckoutResult, b as Plan, A as ActivationResult, c as SaveAutoRechargeResponse, d as PurchaseStatus, e as SolvaPayContextValue, U as UsePlansOptions, f as UsePlansReturn, g as UsePlanOptions, h as UsePlanReturn, i as UseProductReturn, j as UseMerchantReturn, k as SolvaPayCopy, l as PurchaseStatusReturn, m as CancelResult, R as ReactivateResult, n as ActivatePlanResult, o as UseTopupOptions, p as UseTopupReturn, B as BalanceStatus, q as UseTopupAmountSelectorOptions, r as UseTopupAmountSelectorReturn, s as UsePaymentMethodReturn, t as AutoRechargeConfig, u as SaveAutoRechargeInput, v as SolvaPayTransport, w as PartialSolvaPayCopy, x as PurchaseInfo, y as Product, z as SolvaPayConfig } from './shared-BI78qUom.js';
3
+ export { D as BootstrapPlanLike, E as CheckoutStep, F as CustomerPurchaseData, M as MandateContext, G as MandateTemplate, H as Merchant, I as PaymentError, J as PaymentIntentResult, K as PaymentMethodInfo, L as PaymentResult, N as PurchaseStatusValue, O as SuccessMeta, Q as TopupPaymentResult, V as TransportBalanceResult, W as TransportCheckoutSessionResult, X as TransportCustomerSessionResult, Y as TransportLimitsResult, Z as UnsupportedTransportMethodError } from './shared-BI78qUom.js';
4
+ import { P as PaymentFormSummary, a as PaymentFormCustomerFields, b as PaymentFormPaymentElement, c as PaymentFormCardElement, d as PaymentFormMandateText, e as PaymentFormTermsCheckbox, f as PaymentFormSubmitButton, g as PaymentFormLoading, h as PaymentFormError, i as PaymentFormLegalFooter, j as PaymentFormBusinessDetails, k as PaymentFormTaxSummary, A as AutoRechargeInputPayload, l as PaywallStructuredContent, C as CheckoutVariant } from './index-DHcgeDBy.js';
5
+ export { m as ActivationFlowStep, B as BalanceBadge, n as CancelPlanButton, o as CheckoutErrorPhase, p as CheckoutStatus, q as CheckoutSteps, r as CheckoutSummary, s as CheckoutSummaryProps, M as MandateText, t as MandateTextProps, u as PlanBadge, v as ProductBadge, w as PurchaseGate, U as UsageSnapshot, x as UseCheckoutFlowOptions, y as UseCheckoutFlowReturn, z as UseUsageReturn, D as configToAutoRechargeInput, E as deriveVariant, F as useCheckoutFlow, G as useCheckoutStepsContext, H as useUsage } from './index-DHcgeDBy.js';
6
6
  import { PaymentIntent, Stripe, StripeElements } from '@stripe/stripe-js';
7
7
  import { BusinessDetailsInput, TaxBreakdown } from '@solvapay/core';
8
8
  export { AuthAdapter, createAnonymousAuthAdapter, defaultAuthAdapter, getOrCreateAnonymousCustomerRef, resetAnonymousCustomerRef } from './adapters/auth.js';
@@ -237,8 +237,8 @@ declare const AmountPicker: React.FC<AmountPickerProps>;
237
237
  * Renders the full usage-based activation state machine (summary →
238
238
  * activating → selectAmount → topupPayment → retrying → activated | error)
239
239
  * with the golden-path copy, the embedded `<CheckoutSummary>` + `<TopupForm>`,
240
- * and an optional back button. Full control is available by composing the
241
- * primitive at `@solvapay/react/primitives`.
240
+ * and an optional back button. After activate, an empty wallet opens the
241
+ * amount picker; a funded wallet completes immediately.
242
242
  */
243
243
 
244
244
  interface ActivationFlowProps {