@umituz/react-native-subscription 2.13.10 → 2.13.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.13.10",
3
+ "version": "2.13.12",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -270,7 +270,7 @@ export {
270
270
 
271
271
  export {
272
272
  useAuthAwarePurchase,
273
- type UseAuthAwarePurchaseParams,
273
+ configureAuthProvider,
274
274
  type UseAuthAwarePurchaseResult,
275
275
  type PurchaseAuthProvider,
276
276
  } from "./presentation/hooks/useAuthAwarePurchase";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Auth-Aware Purchase Hook
3
- * Generic hook that works with any auth provider
4
- * Ensures user is authenticated before allowing purchases
3
+ * Uses globally configured auth provider
4
+ * Configure once at app start with configureAuthProvider()
5
5
  */
6
6
 
7
7
  import { useCallback } from "react";
@@ -13,52 +13,78 @@ export interface PurchaseAuthProvider {
13
13
  showAuthModal: () => void;
14
14
  }
15
15
 
16
- export interface UseAuthAwarePurchaseParams {
17
- authProvider: PurchaseAuthProvider;
18
- }
16
+ // Global auth provider - configured once at app start
17
+ let globalAuthProvider: PurchaseAuthProvider | null = null;
18
+
19
+ /**
20
+ * Configure auth provider for purchases
21
+ * Call this once at app initialization
22
+ */
23
+ export const configureAuthProvider = (provider: PurchaseAuthProvider): void => {
24
+ globalAuthProvider = provider;
25
+ if (__DEV__) {
26
+ console.log("[useAuthAwarePurchase] Auth provider configured");
27
+ }
28
+ };
19
29
 
20
30
  export interface UseAuthAwarePurchaseResult {
21
31
  handlePurchase: (pkg: PurchasesPackage) => Promise<boolean>;
22
32
  handleRestore: () => Promise<boolean>;
23
33
  }
24
34
 
25
- export const useAuthAwarePurchase = ({
26
- authProvider,
27
- }: UseAuthAwarePurchaseParams): UseAuthAwarePurchaseResult => {
35
+ export const useAuthAwarePurchase = (): UseAuthAwarePurchaseResult => {
28
36
  const { purchasePackage, restorePurchase, closePaywall } = usePremium();
29
37
 
30
38
  const handlePurchase = useCallback(
31
39
  async (pkg: PurchasesPackage): Promise<boolean> => {
32
- if (!authProvider.isAuthenticated()) {
40
+ if (!globalAuthProvider) {
41
+ if (__DEV__) {
42
+ console.warn(
43
+ "[useAuthAwarePurchase] Auth provider not configured. Call configureAuthProvider() at app start.",
44
+ );
45
+ }
46
+ return purchasePackage(pkg);
47
+ }
48
+
49
+ if (!globalAuthProvider.isAuthenticated()) {
33
50
  if (__DEV__) {
34
51
  console.log(
35
52
  "[useAuthAwarePurchase] User not authenticated, opening auth modal",
36
53
  );
37
54
  }
38
55
  closePaywall();
39
- authProvider.showAuthModal();
56
+ globalAuthProvider.showAuthModal();
40
57
  return false;
41
58
  }
42
59
 
43
60
  return purchasePackage(pkg);
44
61
  },
45
- [purchasePackage, closePaywall, authProvider],
62
+ [purchasePackage, closePaywall],
46
63
  );
47
64
 
48
65
  const handleRestore = useCallback(async (): Promise<boolean> => {
49
- if (!authProvider.isAuthenticated()) {
66
+ if (!globalAuthProvider) {
67
+ if (__DEV__) {
68
+ console.warn(
69
+ "[useAuthAwarePurchase] Auth provider not configured. Call configureAuthProvider() at app start.",
70
+ );
71
+ }
72
+ return restorePurchase();
73
+ }
74
+
75
+ if (!globalAuthProvider.isAuthenticated()) {
50
76
  if (__DEV__) {
51
77
  console.log(
52
78
  "[useAuthAwarePurchase] User not authenticated, opening auth modal",
53
79
  );
54
80
  }
55
81
  closePaywall();
56
- authProvider.showAuthModal();
82
+ globalAuthProvider.showAuthModal();
57
83
  return false;
58
84
  }
59
85
 
60
86
  return restorePurchase();
61
- }, [restorePurchase, closePaywall, authProvider]);
87
+ }, [restorePurchase, closePaywall]);
62
88
 
63
89
  return {
64
90
  handlePurchase,
@@ -2,12 +2,12 @@
2
2
  * useFeatureGate Hook
3
3
  *
4
4
  * Feature gating with TanStack Query for server state.
5
- * Checks auth and premium status before allowing actions.
5
+ * Checks auth, premium status, AND credit balance before allowing actions.
6
6
  *
7
7
  * Flow:
8
8
  * 1. NOT authenticated → onShowAuthModal(callback)
9
- * 2. Authenticated but not premium → onShowPaywall()
10
- * 3. Authenticated and premium → Execute action
9
+ * 2. Authenticated but no credits → onShowPaywall()
10
+ * 3. Authenticated with credits → Execute action
11
11
  *
12
12
  * @example
13
13
  * ```typescript
@@ -16,6 +16,7 @@
16
16
  * isAuthenticated: !!user,
17
17
  * onShowAuthModal: (cb) => authModal.show(cb),
18
18
  * onShowPaywall: () => setShowPaywall(true),
19
+ * creditType: 'image', // or 'text'
19
20
  * });
20
21
  *
21
22
  * // Gate a premium feature
@@ -27,6 +28,7 @@
27
28
 
28
29
  import { useCallback } from "react";
29
30
  import { useCredits } from "./useCredits";
31
+ import type { CreditType } from "../../domain/entities/Credits";
30
32
 
31
33
  declare const __DEV__: boolean;
32
34
 
@@ -39,67 +41,107 @@ export interface UseFeatureGateParams {
39
41
  onShowAuthModal: (pendingCallback: () => void | Promise<void>) => void;
40
42
  /** Callback to show paywall */
41
43
  onShowPaywall: () => void;
44
+ /** Credit type to check (default: 'image') */
45
+ creditType?: CreditType;
42
46
  }
43
47
 
44
48
  export interface UseFeatureGateResult {
45
- /** Gate a feature - checks auth first, then premium (credits) */
49
+ /** Gate a feature - checks auth first, then credits balance */
46
50
  requireFeature: (action: () => void | Promise<void>) => void;
47
51
  /** Whether user is authenticated */
48
52
  isAuthenticated: boolean;
49
- /** Whether user has premium access (has credits) */
50
- isPremium: boolean;
53
+ /** Whether user has credits remaining */
54
+ hasCredits: boolean;
51
55
  /** Whether feature access is allowed */
52
56
  canAccess: boolean;
53
57
  /** Loading state */
54
58
  isLoading: boolean;
59
+ /** Current credit balance for the specified type */
60
+ creditBalance: number;
55
61
  }
56
62
 
57
63
  export function useFeatureGate(
58
64
  params: UseFeatureGateParams
59
65
  ): UseFeatureGateResult {
60
- const { userId, isAuthenticated, onShowAuthModal, onShowPaywall } = params;
66
+ const {
67
+ userId,
68
+ isAuthenticated,
69
+ onShowAuthModal,
70
+ onShowPaywall,
71
+ creditType = "image",
72
+ } = params;
61
73
 
62
74
  // Use TanStack Query to get credits (server state)
63
- const { credits, isLoading } = useCredits({
75
+ const { credits, isLoading, hasImageCredits, hasTextCredits } = useCredits({
64
76
  userId,
65
77
  enabled: isAuthenticated && !!userId,
66
78
  });
67
79
 
68
- // User is premium if they have credits
69
- // NOTE: This assumes credits system = premium subscription
70
- // If your app uses CustomerInfo for premium status, use useCustomerInfo() instead
71
- const isPremium = credits !== null;
80
+ // Check actual credit balance, not just existence
81
+ const hasCredits = creditType === "image" ? hasImageCredits : hasTextCredits;
82
+ const creditBalance =
83
+ creditType === "image"
84
+ ? credits?.imageCredits ?? 0
85
+ : credits?.textCredits ?? 0;
72
86
 
73
87
  const requireFeature = useCallback(
74
88
  (action: () => void | Promise<void>) => {
89
+ if (__DEV__) {
90
+ console.log("[useFeatureGate] requireFeature called", {
91
+ isAuthenticated,
92
+ hasCredits,
93
+ creditBalance,
94
+ creditType,
95
+ });
96
+ }
97
+
75
98
  // Step 1: Check authentication
76
99
  if (!isAuthenticated) {
100
+ if (__DEV__) {
101
+ console.log("[useFeatureGate] Not authenticated, showing auth modal");
102
+ }
77
103
  onShowAuthModal(() => {
78
104
  // We NO LONGER call action() blindly here.
79
105
  // The component will re-render with the new auth state,
80
106
  // and the user should be allowed to try the action again.
81
- // This avoids executing actions before credits are loaded or verified.
82
107
  });
83
108
  return;
84
109
  }
85
110
 
86
- // Step 2: Check premium (has credits from TanStack Query)
87
- if (!isPremium) {
111
+ // Step 2: Check credit balance (not just existence)
112
+ if (!hasCredits) {
113
+ if (__DEV__) {
114
+ console.log("[useFeatureGate] No credits, showing paywall", {
115
+ creditBalance,
116
+ creditType,
117
+ });
118
+ }
88
119
  onShowPaywall();
89
120
  return;
90
121
  }
91
122
 
92
- // Step 3: User is authenticated and premium - execute action
123
+ // Step 3: User is authenticated with credits - execute action
124
+ if (__DEV__) {
125
+ console.log("[useFeatureGate] Access granted, executing action");
126
+ }
93
127
  action();
94
128
  },
95
- [isAuthenticated, isPremium, onShowAuthModal, onShowPaywall]
129
+ [
130
+ isAuthenticated,
131
+ hasCredits,
132
+ creditBalance,
133
+ creditType,
134
+ onShowAuthModal,
135
+ onShowPaywall,
136
+ ]
96
137
  );
97
138
 
98
139
  return {
99
140
  requireFeature,
100
141
  isAuthenticated,
101
- isPremium,
102
- canAccess: isAuthenticated && isPremium,
142
+ hasCredits,
143
+ canAccess: isAuthenticated && hasCredits,
103
144
  isLoading,
145
+ creditBalance,
104
146
  };
105
147
  }