@solvapay/react 1.0.8-preview.4 → 1.0.8-preview.6

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/README.md CHANGED
@@ -13,6 +13,57 @@ pnpm add @solvapay/react
13
13
  - `react` ^18.2.0 || ^19.0.0
14
14
  - `react-dom` ^18.2.0 || ^19.0.0
15
15
 
16
+ ## Tailwind setup
17
+
18
+ SolvaPay primitives ship plain CSS. They work with Tailwind v3 and v4
19
+ identically. Import `@solvapay/react/styles.css` **after** your Tailwind
20
+ entry so the SolvaPay rules resolve above preflight.
21
+
22
+ ### Tailwind v4 (recommended for new projects)
23
+
24
+ `src/index.css`:
25
+
26
+ ```css
27
+ @import "tailwindcss";
28
+ @import "@solvapay/react/styles.css";
29
+ ```
30
+
31
+ No `tailwind.config.ts` needed. Theme via `@theme` block if desired.
32
+
33
+ ### Tailwind v3 (Lovable, older projects)
34
+
35
+ `tailwind.config.ts`:
36
+
37
+ ```ts
38
+ import type { Config } from 'tailwindcss'
39
+
40
+ export default {
41
+ content: ['./src/**/*.{ts,tsx}'],
42
+ theme: { extend: {} },
43
+ plugins: [],
44
+ } satisfies Config
45
+ ```
46
+
47
+ `src/index.css`:
48
+
49
+ ```css
50
+ @tailwind base;
51
+ @tailwind components;
52
+ @tailwind utilities;
53
+ @import "@solvapay/react/styles.css";
54
+ ```
55
+
56
+ No `content[]` entry for `@solvapay/react` is needed — primitives don't ship
57
+ utility classes. `data-[state=*]:` variants work natively in v3.3+.
58
+
59
+ ### CSS loading order
60
+
61
+ Always import `@solvapay/react/styles.css` **after** `@tailwind utilities`
62
+ (v3) or `@import "tailwindcss"` (v4). SolvaPay rules are unlayered; they beat
63
+ Tailwind's preflight when loaded last. See
64
+ [`examples/spa-checkout/src/index.css`](../../examples/spa-checkout/src/index.css)
65
+ for the canonical demonstration.
66
+
16
67
  ## Quick Start
17
68
 
18
69
  ### Zero-Config Usage (Recommended)
@@ -344,23 +395,48 @@ Displays current product subscription with render props or className pattern.
344
395
 
345
396
  ### PurchaseGate
346
397
 
347
- Controls access to content based on purchase status.
398
+ Controls access to content based on purchase status. Uses a compound
399
+ primitive API (`PurchaseGate.Root`, `.Allowed`, `.Blocked`, `.Loading`,
400
+ `.Error`). Matching is by stable reference — `productRef` (`prd_*`) and/or
401
+ `planRef` (`pln_*`) — never by product or plan name.
348
402
 
349
- **Props:**
403
+ **Root props:**
350
404
 
351
- - `requireProduct?: string` - Optional product name to check for an active purchase
352
- - `children: (props) => React.ReactNode` - Render prop function
405
+ - `productRef?: string` - Require an active purchase for this product
406
+ - `planRef?: string` - Require an active purchase for this specific plan
407
+ - `asChild?: boolean` - Merge onto the consumer element via `Slot`
353
408
 
354
- **Example:**
409
+ Matching precedence:
410
+
411
+ - If neither is set, any `status === 'active'` purchase allows access
412
+ - If only one is set, that field must match on an active purchase
413
+ - If both are set, both must match on the **same** active purchase (AND)
414
+
415
+ **Example — product-level gating:**
355
416
 
356
417
  ```tsx
357
- <PurchaseGate requireProduct="Pro Plan">
358
- {({ hasAccess, loading, purchases }) => {
359
- if (loading) return <Loading />
360
- if (!hasAccess) return <Paywall />
361
- return <PremiumContent />
362
- }}
363
- </PurchaseGate>
418
+ <PurchaseGate.Root productRef="prd_widget">
419
+ <PurchaseGate.Loading>Loading…</PurchaseGate.Loading>
420
+ <PurchaseGate.Allowed>
421
+ <PremiumContent />
422
+ </PurchaseGate.Allowed>
423
+ <PurchaseGate.Blocked>
424
+ <Paywall />
425
+ </PurchaseGate.Blocked>
426
+ </PurchaseGate.Root>
427
+ ```
428
+
429
+ **Example — plan-level gating:**
430
+
431
+ ```tsx
432
+ <PurchaseGate.Root planRef="pln_premium">
433
+ <PurchaseGate.Allowed>
434
+ <PremiumOnlyFeature />
435
+ </PurchaseGate.Allowed>
436
+ <PurchaseGate.Blocked>
437
+ <UpgradePrompt />
438
+ </PurchaseGate.Blocked>
439
+ </PurchaseGate.Root>
364
440
  ```
365
441
 
366
442
  ## Hooks
@@ -375,8 +451,15 @@ const {
375
451
  loading, // Loading state
376
452
  hasPaidPurchase, // Boolean: has any paid purchase
377
453
  activePurchase, // Most recent active purchase
454
+ hasPurchase, // (criteria?: { productRef?, planRef? }) => boolean — predicate matching the PurchaseGate.Root shape
378
455
  refetch, // Function to refetch purchases
379
456
  } = usePurchase()
457
+
458
+ // Examples — criteria-based checks:
459
+ hasPurchase() // any active purchase
460
+ hasPurchase({ productRef: 'prd_widget' }) // any active purchase for this product
461
+ hasPurchase({ planRef: 'pln_premium' }) // any active purchase for this specific plan
462
+ hasPurchase({ productRef: 'prd_widget', planRef: 'pln_premium' }) // both must match the same active purchase
380
463
  ```
381
464
 
382
465
  ### usePlans
@@ -315,9 +315,18 @@ interface PurchaseStatus {
315
315
  email?: string;
316
316
  name?: string;
317
317
  purchases: PurchaseInfo[];
318
- hasProduct: (productName: string) => boolean;
319
- /** @deprecated Use hasProduct instead */
320
- hasPlan: (productName: string) => boolean;
318
+ /**
319
+ * Check if the user has an active purchase matching the given criteria.
320
+ *
321
+ * - No criteria: any active purchase
322
+ * - `productRef` only: any active purchase for that product (any plan)
323
+ * - `planRef` only: any active purchase for that specific plan
324
+ * - Both: both must match on the same active purchase (AND)
325
+ */
326
+ hasPurchase: (criteria?: {
327
+ productRef?: string;
328
+ planRef?: string;
329
+ }) => boolean;
321
330
  /**
322
331
  * Primary active purchase (paid or free) - most recent purchase with status === 'active'
323
332
  * Backend keeps purchases as 'active' until expiration, even when cancelled.
@@ -908,9 +917,10 @@ type PurchaseGateContextValue = {
908
917
  error: Error | null;
909
918
  };
910
919
  declare const PurchaseGateRoot: React$1.ForwardRefExoticComponent<{
911
- /** @deprecated Use `requireProduct`. */
912
- requirePlan?: string;
913
- requireProduct?: string;
920
+ /** Require an active purchase for this product (e.g. "prd_abc"). */
921
+ productRef?: string;
922
+ /** Require an active purchase for this specific plan (e.g. "pln_premium"). */
923
+ planRef?: string;
914
924
  asChild?: boolean;
915
925
  children?: React$1.ReactNode;
916
926
  } & Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> & React$1.RefAttributes<HTMLDivElement>>;
@@ -928,9 +938,10 @@ declare const PurchaseGateError: React$1.ForwardRefExoticComponent<React$1.HTMLA
928
938
  } & React$1.RefAttributes<HTMLDivElement>>;
929
939
  declare const PurchaseGate: {
930
940
  readonly Root: React$1.ForwardRefExoticComponent<{
931
- /** @deprecated Use `requireProduct`. */
932
- requirePlan?: string;
933
- requireProduct?: string;
941
+ /** Require an active purchase for this product (e.g. "prd_abc"). */
942
+ productRef?: string;
943
+ /** Require an active purchase for this specific plan (e.g. "pln_premium"). */
944
+ planRef?: string;
934
945
  asChild?: boolean;
935
946
  children?: React$1.ReactNode;
936
947
  } & Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> & React$1.RefAttributes<HTMLDivElement>>;
@@ -315,9 +315,18 @@ interface PurchaseStatus {
315
315
  email?: string;
316
316
  name?: string;
317
317
  purchases: PurchaseInfo[];
318
- hasProduct: (productName: string) => boolean;
319
- /** @deprecated Use hasProduct instead */
320
- hasPlan: (productName: string) => boolean;
318
+ /**
319
+ * Check if the user has an active purchase matching the given criteria.
320
+ *
321
+ * - No criteria: any active purchase
322
+ * - `productRef` only: any active purchase for that product (any plan)
323
+ * - `planRef` only: any active purchase for that specific plan
324
+ * - Both: both must match on the same active purchase (AND)
325
+ */
326
+ hasPurchase: (criteria?: {
327
+ productRef?: string;
328
+ planRef?: string;
329
+ }) => boolean;
321
330
  /**
322
331
  * Primary active purchase (paid or free) - most recent purchase with status === 'active'
323
332
  * Backend keeps purchases as 'active' until expiration, even when cancelled.
@@ -908,9 +917,10 @@ type PurchaseGateContextValue = {
908
917
  error: Error | null;
909
918
  };
910
919
  declare const PurchaseGateRoot: React$1.ForwardRefExoticComponent<{
911
- /** @deprecated Use `requireProduct`. */
912
- requirePlan?: string;
913
- requireProduct?: string;
920
+ /** Require an active purchase for this product (e.g. "prd_abc"). */
921
+ productRef?: string;
922
+ /** Require an active purchase for this specific plan (e.g. "pln_premium"). */
923
+ planRef?: string;
914
924
  asChild?: boolean;
915
925
  children?: React$1.ReactNode;
916
926
  } & Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> & React$1.RefAttributes<HTMLDivElement>>;
@@ -928,9 +938,10 @@ declare const PurchaseGateError: React$1.ForwardRefExoticComponent<React$1.HTMLA
928
938
  } & React$1.RefAttributes<HTMLDivElement>>;
929
939
  declare const PurchaseGate: {
930
940
  readonly Root: React$1.ForwardRefExoticComponent<{
931
- /** @deprecated Use `requireProduct`. */
932
- requirePlan?: string;
933
- requireProduct?: string;
941
+ /** Require an active purchase for this product (e.g. "prd_abc"). */
942
+ productRef?: string;
943
+ /** Require an active purchase for this specific plan (e.g. "pln_premium"). */
944
+ planRef?: string;
934
945
  asChild?: boolean;
935
946
  children?: React$1.ReactNode;
936
947
  } & Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> & React$1.RefAttributes<HTMLDivElement>>;
@@ -748,14 +748,11 @@ var SolvaPayProvider = ({
748
748
  email: purchaseData.email,
749
749
  name: purchaseData.name,
750
750
  purchases: purchaseData.purchases,
751
- hasProduct: (productName) => {
751
+ hasPurchase: (criteria) => {
752
+ const productRef = criteria?.productRef;
753
+ const planRef = criteria?.planRef;
752
754
  return purchaseData.purchases.some(
753
- (p) => p.productName.toLowerCase() === productName.toLowerCase() && p.status === "active"
754
- );
755
- },
756
- hasPlan: (productName) => {
757
- return purchaseData.purchases.some(
758
- (p) => p.productName.toLowerCase() === productName.toLowerCase() && p.status === "active"
755
+ (p) => p.status === "active" && (!productRef || p.productRef === productRef) && (!planRef || p.planRef === planRef)
759
756
  );
760
757
  },
761
758
  activePurchase,
@@ -1969,6 +1966,13 @@ async function confirmPayment(input) {
1969
1966
  if (!paymentElement) {
1970
1967
  return { status: "error", message: copy.errors.cardElementMissing };
1971
1968
  }
1969
+ const { error: submitError } = await elements.submit();
1970
+ if (submitError) {
1971
+ return {
1972
+ status: "error",
1973
+ message: submitError.message || copy.errors.paymentUnexpected
1974
+ };
1975
+ }
1972
1976
  const { error: error2, paymentIntent: paymentIntent2 } = await stripe.confirmPayment({
1973
1977
  elements,
1974
1978
  clientSecret,
@@ -3214,6 +3218,14 @@ var Inner = ({
3214
3218
  }
3215
3219
  setError(null);
3216
3220
  setIsProcessing(true);
3221
+ const { error: submitError } = await elements.submit();
3222
+ if (submitError) {
3223
+ const msg = submitError.message || copy.errors.paymentUnexpected;
3224
+ setError(msg);
3225
+ setIsProcessing(false);
3226
+ onError?.(new Error(msg));
3227
+ return;
3228
+ }
3217
3229
  const { error: confirmError, paymentIntent } = await stripe.confirmPayment({
3218
3230
  elements,
3219
3231
  clientSecret,
@@ -3457,12 +3469,14 @@ function useGateCtx(part) {
3457
3469
  }
3458
3470
  return ctx;
3459
3471
  }
3460
- var Root5 = forwardRef7(function PurchaseGateRoot({ requirePlan, requireProduct, asChild, children, ...rest }, forwardedRef) {
3472
+ var Root5 = forwardRef7(function PurchaseGateRoot({ productRef, planRef, asChild, children, ...rest }, forwardedRef) {
3461
3473
  const solva = useContext11(SolvaPayContext);
3462
3474
  if (!solva) throw new MissingProviderError("PurchaseGate");
3463
- const { purchases, loading, hasProduct, error } = usePurchase();
3464
- const productToCheck = requireProduct || requirePlan;
3465
- const hasAccess = productToCheck ? hasProduct(productToCheck) : purchases.some((p) => p.status === "active");
3475
+ const { loading, hasPurchase, error } = usePurchase();
3476
+ const hasAccess = useMemo10(
3477
+ () => hasPurchase({ productRef, planRef }),
3478
+ [hasPurchase, productRef, planRef]
3479
+ );
3466
3480
  const state = loading ? "loading" : hasAccess ? "allowed" : "blocked";
3467
3481
  const ctx = useMemo10(
3468
3482
  () => ({ state, loading, hasAccess, error }),
package/dist/index.cjs CHANGED
@@ -856,14 +856,11 @@ var SolvaPayProvider = ({
856
856
  email: purchaseData.email,
857
857
  name: purchaseData.name,
858
858
  purchases: purchaseData.purchases,
859
- hasProduct: (productName) => {
859
+ hasPurchase: (criteria) => {
860
+ const productRef = criteria?.productRef;
861
+ const planRef = criteria?.planRef;
860
862
  return purchaseData.purchases.some(
861
- (p) => p.productName.toLowerCase() === productName.toLowerCase() && p.status === "active"
862
- );
863
- },
864
- hasPlan: (productName) => {
865
- return purchaseData.purchases.some(
866
- (p) => p.productName.toLowerCase() === productName.toLowerCase() && p.status === "active"
863
+ (p) => p.status === "active" && (!productRef || p.productRef === productRef) && (!planRef || p.planRef === planRef)
867
864
  );
868
865
  },
869
866
  activePurchase,
@@ -2080,6 +2077,13 @@ async function confirmPayment(input) {
2080
2077
  if (!paymentElement) {
2081
2078
  return { status: "error", message: copy.errors.cardElementMissing };
2082
2079
  }
2080
+ const { error: submitError } = await elements.submit();
2081
+ if (submitError) {
2082
+ return {
2083
+ status: "error",
2084
+ message: submitError.message || copy.errors.paymentUnexpected
2085
+ };
2086
+ }
2083
2087
  const { error: error2, paymentIntent: paymentIntent2 } = await stripe.confirmPayment({
2084
2088
  elements,
2085
2089
  clientSecret,
@@ -3314,6 +3318,14 @@ var Inner = ({
3314
3318
  }
3315
3319
  setError(null);
3316
3320
  setIsProcessing(true);
3321
+ const { error: submitError } = await elements.submit();
3322
+ if (submitError) {
3323
+ const msg = submitError.message || copy.errors.paymentUnexpected;
3324
+ setError(msg);
3325
+ setIsProcessing(false);
3326
+ onError?.(new Error(msg));
3327
+ return;
3328
+ }
3317
3329
  const { error: confirmError, paymentIntent } = await stripe.confirmPayment({
3318
3330
  elements,
3319
3331
  clientSecret,
@@ -3544,12 +3556,14 @@ function useGateCtx(part) {
3544
3556
  }
3545
3557
  return ctx;
3546
3558
  }
3547
- var Root5 = (0, import_react22.forwardRef)(function PurchaseGateRoot({ requirePlan, requireProduct, asChild, children, ...rest }, forwardedRef) {
3559
+ var Root5 = (0, import_react22.forwardRef)(function PurchaseGateRoot({ productRef, planRef, asChild, children, ...rest }, forwardedRef) {
3548
3560
  const solva = (0, import_react22.useContext)(SolvaPayContext);
3549
3561
  if (!solva) throw new MissingProviderError("PurchaseGate");
3550
- const { purchases, loading, hasProduct, error } = usePurchase();
3551
- const productToCheck = requireProduct || requirePlan;
3552
- const hasAccess = productToCheck ? hasProduct(productToCheck) : purchases.some((p) => p.status === "active");
3562
+ const { loading, hasPurchase, error } = usePurchase();
3563
+ const hasAccess = (0, import_react22.useMemo)(
3564
+ () => hasPurchase({ productRef, planRef }),
3565
+ [hasPurchase, productRef, planRef]
3566
+ );
3553
3567
  const state = loading ? "loading" : hasAccess ? "allowed" : "blocked";
3554
3568
  const ctx = (0, import_react22.useMemo)(
3555
3569
  () => ({ state, loading, hasAccess, error }),
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, b as PaymentFormSummary, c as PaymentFormCustomerFields, d as PaymentFormPaymentElement, e as PaymentFormCardElement, f as PaymentFormMandateText, g as PaymentFormTermsCheckbox, h as PaymentFormSubmitButton, i as PaymentFormLoading, j as PaymentFormError, T as TopupFormProps, C as CheckoutResult, k as Plan, A as ActivationResult, l as PurchaseStatus, m as SolvaPayContextValue, U as UsePlansOptions, n as UsePlansReturn, o as UsePlanOptions, p as UsePlanReturn, q as UseProductReturn, r as UseMerchantReturn, s as SolvaPayCopy, t as PurchaseStatusReturn, u as CancelResult, R as ReactivateResult, v as UseTopupOptions, w as UseTopupReturn, B as BalanceStatus, x as UseTopupAmountSelectorOptions, y as UseTopupAmountSelectorReturn, z as PartialSolvaPayCopy, D as PurchaseInfo, E as CheckoutVariant, F as Product } from './CancelPlanButton-CieT9swn.cjs';
3
- export { G as ActivationFlowStep, H as BalanceBadge, I as CancelPlanButton, J as CheckoutSummary, K as CheckoutSummaryProps, L as CustomerPurchaseData, M as MandateContext, N as MandateTemplate, O as MandateText, Q as MandateTextProps, V as Merchant, W as PaymentError, X as PaymentIntentResult, Y as PaymentResult, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, a0 as PurchaseStatusValue, a1 as SolvaPayConfig, a2 as TopupPaymentResult, a3 as deriveVariant } from './CancelPlanButton-CieT9swn.cjs';
2
+ import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, b as PaymentFormSummary, c as PaymentFormCustomerFields, d as PaymentFormPaymentElement, e as PaymentFormCardElement, f as PaymentFormMandateText, g as PaymentFormTermsCheckbox, h as PaymentFormSubmitButton, i as PaymentFormLoading, j as PaymentFormError, T as TopupFormProps, C as CheckoutResult, k as Plan, A as ActivationResult, l as PurchaseStatus, m as SolvaPayContextValue, U as UsePlansOptions, n as UsePlansReturn, o as UsePlanOptions, p as UsePlanReturn, q as UseProductReturn, r as UseMerchantReturn, s as SolvaPayCopy, t as PurchaseStatusReturn, u as CancelResult, R as ReactivateResult, v as UseTopupOptions, w as UseTopupReturn, B as BalanceStatus, x as UseTopupAmountSelectorOptions, y as UseTopupAmountSelectorReturn, z as PartialSolvaPayCopy, D as PurchaseInfo, E as CheckoutVariant, F as Product } from './CancelPlanButton-DQXUTHmF.cjs';
3
+ export { G as ActivationFlowStep, H as BalanceBadge, I as CancelPlanButton, J as CheckoutSummary, K as CheckoutSummaryProps, L as CustomerPurchaseData, M as MandateContext, N as MandateTemplate, O as MandateText, Q as MandateTextProps, V as Merchant, W as PaymentError, X as PaymentIntentResult, Y as PaymentResult, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, a0 as PurchaseStatusValue, a1 as SolvaPayConfig, a2 as TopupPaymentResult, a3 as deriveVariant } from './CancelPlanButton-DQXUTHmF.cjs';
4
4
  import { PaymentIntent, Stripe, StripeElements } from '@stripe/stripe-js';
5
5
  import { ActivatePlanResult } from '@solvapay/server';
6
6
  export { ActivatePlanResult } from '@solvapay/server';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, b as PaymentFormSummary, c as PaymentFormCustomerFields, d as PaymentFormPaymentElement, e as PaymentFormCardElement, f as PaymentFormMandateText, g as PaymentFormTermsCheckbox, h as PaymentFormSubmitButton, i as PaymentFormLoading, j as PaymentFormError, T as TopupFormProps, C as CheckoutResult, k as Plan, A as ActivationResult, l as PurchaseStatus, m as SolvaPayContextValue, U as UsePlansOptions, n as UsePlansReturn, o as UsePlanOptions, p as UsePlanReturn, q as UseProductReturn, r as UseMerchantReturn, s as SolvaPayCopy, t as PurchaseStatusReturn, u as CancelResult, R as ReactivateResult, v as UseTopupOptions, w as UseTopupReturn, B as BalanceStatus, x as UseTopupAmountSelectorOptions, y as UseTopupAmountSelectorReturn, z as PartialSolvaPayCopy, D as PurchaseInfo, E as CheckoutVariant, F as Product } from './CancelPlanButton-f56UlQN-.js';
3
- export { G as ActivationFlowStep, H as BalanceBadge, I as CancelPlanButton, J as CheckoutSummary, K as CheckoutSummaryProps, L as CustomerPurchaseData, M as MandateContext, N as MandateTemplate, O as MandateText, Q as MandateTextProps, V as Merchant, W as PaymentError, X as PaymentIntentResult, Y as PaymentResult, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, a0 as PurchaseStatusValue, a1 as SolvaPayConfig, a2 as TopupPaymentResult, a3 as deriveVariant } from './CancelPlanButton-f56UlQN-.js';
2
+ import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, b as PaymentFormSummary, c as PaymentFormCustomerFields, d as PaymentFormPaymentElement, e as PaymentFormCardElement, f as PaymentFormMandateText, g as PaymentFormTermsCheckbox, h as PaymentFormSubmitButton, i as PaymentFormLoading, j as PaymentFormError, T as TopupFormProps, C as CheckoutResult, k as Plan, A as ActivationResult, l as PurchaseStatus, m as SolvaPayContextValue, U as UsePlansOptions, n as UsePlansReturn, o as UsePlanOptions, p as UsePlanReturn, q as UseProductReturn, r as UseMerchantReturn, s as SolvaPayCopy, t as PurchaseStatusReturn, u as CancelResult, R as ReactivateResult, v as UseTopupOptions, w as UseTopupReturn, B as BalanceStatus, x as UseTopupAmountSelectorOptions, y as UseTopupAmountSelectorReturn, z as PartialSolvaPayCopy, D as PurchaseInfo, E as CheckoutVariant, F as Product } from './CancelPlanButton-BKH_yv1d.js';
3
+ export { G as ActivationFlowStep, H as BalanceBadge, I as CancelPlanButton, J as CheckoutSummary, K as CheckoutSummaryProps, L as CustomerPurchaseData, M as MandateContext, N as MandateTemplate, O as MandateText, Q as MandateTextProps, V as Merchant, W as PaymentError, X as PaymentIntentResult, Y as PaymentResult, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, a0 as PurchaseStatusValue, a1 as SolvaPayConfig, a2 as TopupPaymentResult, a3 as deriveVariant } from './CancelPlanButton-BKH_yv1d.js';
4
4
  import { PaymentIntent, Stripe, StripeElements } from '@stripe/stripe-js';
5
5
  import { ActivatePlanResult } from '@solvapay/server';
6
6
  export { ActivatePlanResult } from '@solvapay/server';
package/dist/index.js CHANGED
@@ -64,7 +64,7 @@ import {
64
64
  useSolvaPay,
65
65
  useTopup,
66
66
  useTopupAmountSelector
67
- } from "./chunk-MOP3ZBGC.js";
67
+ } from "./chunk-5FZBOPIW.js";
68
68
  import {
69
69
  defaultAuthAdapter
70
70
  } from "./chunk-OUSEQRCT.js";
@@ -1866,6 +1866,13 @@ async function confirmPayment(input) {
1866
1866
  if (!paymentElement) {
1867
1867
  return { status: "error", message: copy.errors.cardElementMissing };
1868
1868
  }
1869
+ const { error: submitError } = await elements.submit();
1870
+ if (submitError) {
1871
+ return {
1872
+ status: "error",
1873
+ message: submitError.message || copy.errors.paymentUnexpected
1874
+ };
1875
+ }
1869
1876
  const { error: error2, paymentIntent: paymentIntent2 } = await stripe.confirmPayment({
1870
1877
  elements,
1871
1878
  clientSecret,
@@ -3746,6 +3753,14 @@ var Inner = ({
3746
3753
  }
3747
3754
  setError(null);
3748
3755
  setIsProcessing(true);
3756
+ const { error: submitError } = await elements.submit();
3757
+ if (submitError) {
3758
+ const msg = submitError.message || copy.errors.paymentUnexpected;
3759
+ setError(msg);
3760
+ setIsProcessing(false);
3761
+ onError?.(new Error(msg));
3762
+ return;
3763
+ }
3749
3764
  const { error: confirmError, paymentIntent } = await stripe.confirmPayment({
3750
3765
  elements,
3751
3766
  clientSecret,
@@ -4055,12 +4070,14 @@ function useGateCtx2(part) {
4055
4070
  }
4056
4071
  return ctx;
4057
4072
  }
4058
- var Root9 = (0, import_react29.forwardRef)(function PurchaseGateRoot({ requirePlan, requireProduct, asChild, children, ...rest }, forwardedRef) {
4073
+ var Root9 = (0, import_react29.forwardRef)(function PurchaseGateRoot({ productRef, planRef, asChild, children, ...rest }, forwardedRef) {
4059
4074
  const solva = (0, import_react29.useContext)(SolvaPayContext);
4060
4075
  if (!solva) throw new MissingProviderError("PurchaseGate");
4061
- const { purchases, loading, hasProduct, error } = usePurchase();
4062
- const productToCheck = requireProduct || requirePlan;
4063
- const hasAccess = productToCheck ? hasProduct(productToCheck) : purchases.some((p) => p.status === "active");
4076
+ const { loading, hasPurchase, error } = usePurchase();
4077
+ const hasAccess = (0, import_react29.useMemo)(
4078
+ () => hasPurchase({ productRef, planRef }),
4079
+ [hasPurchase, productRef, planRef]
4080
+ );
4064
4081
  const state = loading ? "loading" : hasAccess ? "allowed" : "blocked";
4065
4082
  const ctx = (0, import_react29.useMemo)(
4066
4083
  () => ({ state, loading, hasAccess, error }),
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React, { Ref } from 'react';
3
- import { k as Plan, F as Product, y as UseTopupAmountSelectorReturn, D as PurchaseInfo, T as TopupFormProps } from '../CancelPlanButton-CieT9swn.cjs';
4
- export { a4 as ActivationFlow, a5 as ActivationFlowActivateButton, a6 as ActivationFlowActivated, a7 as ActivationFlowAmountPicker, a8 as ActivationFlowContinueButton, a9 as ActivationFlowError, aa as ActivationFlowLoading, ab as ActivationFlowRetrying, ac as ActivationFlowRoot, G as ActivationFlowStep, ad as ActivationFlowSummary, H as BalanceBadge, I as CancelPlanButton, O as MandateText, Q as MandateTextProps, ae as PaymentForm, e as PaymentFormCardElement, c as PaymentFormCustomerFields, j as PaymentFormError, i as PaymentFormLoading, f as PaymentFormMandateText, d as PaymentFormPaymentElement, af as PaymentFormRoot, h as PaymentFormSubmitButton, b as PaymentFormSummary, g as PaymentFormTermsCheckbox, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, ag as PurchaseGateAllowed, ah as PurchaseGateBlocked, ai as PurchaseGateError, aj as PurchaseGateLoading, ak as PurchaseGateRoot, al as useActivationFlow, am as usePurchaseGate } from '../CancelPlanButton-CieT9swn.cjs';
3
+ import { k as Plan, F as Product, y as UseTopupAmountSelectorReturn, D as PurchaseInfo, T as TopupFormProps } from '../CancelPlanButton-DQXUTHmF.cjs';
4
+ export { a4 as ActivationFlow, a5 as ActivationFlowActivateButton, a6 as ActivationFlowActivated, a7 as ActivationFlowAmountPicker, a8 as ActivationFlowContinueButton, a9 as ActivationFlowError, aa as ActivationFlowLoading, ab as ActivationFlowRetrying, ac as ActivationFlowRoot, G as ActivationFlowStep, ad as ActivationFlowSummary, H as BalanceBadge, I as CancelPlanButton, O as MandateText, Q as MandateTextProps, ae as PaymentForm, e as PaymentFormCardElement, c as PaymentFormCustomerFields, j as PaymentFormError, i as PaymentFormLoading, f as PaymentFormMandateText, d as PaymentFormPaymentElement, af as PaymentFormRoot, h as PaymentFormSubmitButton, b as PaymentFormSummary, g as PaymentFormTermsCheckbox, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, ag as PurchaseGateAllowed, ah as PurchaseGateBlocked, ai as PurchaseGateError, aj as PurchaseGateLoading, ak as PurchaseGateRoot, al as useActivationFlow, am as usePurchaseGate } from '../CancelPlanButton-DQXUTHmF.cjs';
5
5
  import { PaymentElement } from '@stripe/react-stripe-js';
6
6
  import { Stripe, StripeElements } from '@stripe/stripe-js';
7
7
  import '@solvapay/server';
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React, { Ref } from 'react';
3
- import { k as Plan, F as Product, y as UseTopupAmountSelectorReturn, D as PurchaseInfo, T as TopupFormProps } from '../CancelPlanButton-f56UlQN-.js';
4
- export { a4 as ActivationFlow, a5 as ActivationFlowActivateButton, a6 as ActivationFlowActivated, a7 as ActivationFlowAmountPicker, a8 as ActivationFlowContinueButton, a9 as ActivationFlowError, aa as ActivationFlowLoading, ab as ActivationFlowRetrying, ac as ActivationFlowRoot, G as ActivationFlowStep, ad as ActivationFlowSummary, H as BalanceBadge, I as CancelPlanButton, O as MandateText, Q as MandateTextProps, ae as PaymentForm, e as PaymentFormCardElement, c as PaymentFormCustomerFields, j as PaymentFormError, i as PaymentFormLoading, f as PaymentFormMandateText, d as PaymentFormPaymentElement, af as PaymentFormRoot, h as PaymentFormSubmitButton, b as PaymentFormSummary, g as PaymentFormTermsCheckbox, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, ag as PurchaseGateAllowed, ah as PurchaseGateBlocked, ai as PurchaseGateError, aj as PurchaseGateLoading, ak as PurchaseGateRoot, al as useActivationFlow, am as usePurchaseGate } from '../CancelPlanButton-f56UlQN-.js';
3
+ import { k as Plan, F as Product, y as UseTopupAmountSelectorReturn, D as PurchaseInfo, T as TopupFormProps } from '../CancelPlanButton-BKH_yv1d.js';
4
+ export { a4 as ActivationFlow, a5 as ActivationFlowActivateButton, a6 as ActivationFlowActivated, a7 as ActivationFlowAmountPicker, a8 as ActivationFlowContinueButton, a9 as ActivationFlowError, aa as ActivationFlowLoading, ab as ActivationFlowRetrying, ac as ActivationFlowRoot, G as ActivationFlowStep, ad as ActivationFlowSummary, H as BalanceBadge, I as CancelPlanButton, O as MandateText, Q as MandateTextProps, ae as PaymentForm, e as PaymentFormCardElement, c as PaymentFormCustomerFields, j as PaymentFormError, i as PaymentFormLoading, f as PaymentFormMandateText, d as PaymentFormPaymentElement, af as PaymentFormRoot, h as PaymentFormSubmitButton, b as PaymentFormSummary, g as PaymentFormTermsCheckbox, Z as PlanBadge, _ as ProductBadge, $ as PurchaseGate, ag as PurchaseGateAllowed, ah as PurchaseGateBlocked, ai as PurchaseGateError, aj as PurchaseGateLoading, ak as PurchaseGateRoot, al as useActivationFlow, am as usePurchaseGate } from '../CancelPlanButton-BKH_yv1d.js';
5
5
  import { PaymentElement } from '@stripe/react-stripe-js';
6
6
  import { Stripe, StripeElements } from '@stripe/stripe-js';
7
7
  import '@solvapay/server';
@@ -90,7 +90,7 @@ import {
90
90
  usePlanSelector,
91
91
  usePurchaseGate,
92
92
  useTopupForm
93
- } from "../chunk-MOP3ZBGC.js";
93
+ } from "../chunk-5FZBOPIW.js";
94
94
  import "../chunk-OUSEQRCT.js";
95
95
  export {
96
96
  ActivationFlow,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solvapay/react",
3
- "version": "1.0.8-preview.4",
3
+ "version": "1.0.8-preview.6",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -65,8 +65,8 @@
65
65
  "react-dom": "^19.2.4",
66
66
  "typescript": "^5.9.3",
67
67
  "vitest": "^4.1.2",
68
- "@solvapay/test-utils": "0.0.0",
69
- "@solvapay/server": "1.0.8-preview.4"
68
+ "@solvapay/server": "1.0.8-preview.6",
69
+ "@solvapay/test-utils": "0.0.0"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "tsup src/index.tsx src/primitives/index.ts src/adapters/auth.ts --format esm,cjs --dts --tsconfig tsconfig.build.json && cp src/styles.css dist/styles.css",