@umituz/react-native-subscription 2.26.13 → 2.26.15
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 +1 -1
- package/src/domains/paywall/components/PaywallModal.tsx +36 -13
- package/src/domains/paywall/components/PlanCard.tsx +16 -3
- package/src/domains/paywall/entities/types.ts +4 -0
- package/src/domains/paywall/hooks/usePaywallTranslations.ts +8 -0
- package/src/presentation/hooks/index.ts +0 -15
- package/src/presentation/hooks/useFeatureGate.ts +41 -116
- package/src/revenuecat/domain/types/RevenueCatTypes.ts +32 -0
- package/src/revenuecat/index.ts +1 -0
- package/src/revenuecat/presentation/hooks/useRevenueCatTrialEligibility.ts +179 -0
- package/src/presentation/hooks/useAuthAwarePurchase.md +0 -92
- package/src/presentation/hooks/useAuthAwarePurchase.ts +0 -138
- package/src/presentation/hooks/useAuthGate.md +0 -89
- package/src/presentation/hooks/useAuthGate.ts +0 -65
- package/src/presentation/hooks/useCreditChecker.md +0 -102
- package/src/presentation/hooks/useCreditChecker.ts +0 -41
- package/src/presentation/hooks/useCreditsGate.md +0 -94
- package/src/presentation/hooks/useCreditsGate.ts +0 -81
- package/src/presentation/hooks/useDevTestCallbacks.md +0 -91
- package/src/presentation/hooks/useDevTestCallbacks.ts +0 -142
- package/src/presentation/hooks/useInitializeCredits.md +0 -92
- package/src/presentation/hooks/useInitializeCredits.ts +0 -57
- package/src/presentation/hooks/usePremiumGate.md +0 -88
- package/src/presentation/hooks/usePremiumGate.ts +0 -116
- package/src/presentation/hooks/usePremiumWithCredits.md +0 -92
- package/src/presentation/hooks/usePremiumWithCredits.ts +0 -48
- package/src/presentation/hooks/useSubscription.md +0 -94
- package/src/presentation/hooks/useSubscription.ts +0 -119
- package/src/presentation/hooks/useSubscriptionDetails.md +0 -93
- package/src/presentation/hooks/useSubscriptionDetails.ts +0 -85
- package/src/presentation/hooks/useSubscriptionGate.md +0 -84
- package/src/presentation/hooks/useSubscriptionGate.ts +0 -67
- package/src/presentation/hooks/useSubscriptionStatus.md +0 -94
- package/src/presentation/hooks/useSubscriptionStatus.ts +0 -64
- package/src/presentation/hooks/useTrialEligibility.ts +0 -66
- package/src/presentation/hooks/useUserTier.md +0 -91
- package/src/presentation/hooks/useUserTier.ts +0 -78
- package/src/presentation/hooks/useUserTierWithRepository.md +0 -92
- package/src/presentation/hooks/useUserTierWithRepository.ts +0 -151
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# useAuthAwarePurchase Hook
|
|
2
|
-
|
|
3
|
-
Security-focused purchase hook that requires authentication before any transaction.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useAuthAwarePurchase.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Auth-Gated Purchase Flow
|
|
16
|
-
|
|
17
|
-
1. **Auth Provider Validation**: Verify auth provider is configured at app startup
|
|
18
|
-
2. **Authentication Check**: Block purchases for unauthenticated users
|
|
19
|
-
3. **Auth Flow Trigger**: Show auth modal when guest attempts purchase
|
|
20
|
-
4. **Purchase Blocking**: Prevent all transactions without valid authentication
|
|
21
|
-
5. **Post-Auth Purchase**: Allow purchase after user completes authentication
|
|
22
|
-
6. **Security Enforcement**: Server-side validation required for final verification
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
26
|
-
- **Auth Provider Configuration**: Must be configured once at app initialization
|
|
27
|
-
- **Auth Context**: User authentication state
|
|
28
|
-
- **Paywall Domain**: For subscription upgrade flow
|
|
29
|
-
- **Auth UI**: For sign-in/sign-up flows
|
|
30
|
-
- **RevenueCat**: For purchase transactions
|
|
31
|
-
|
|
32
|
-
## Restrictions
|
|
33
|
-
|
|
34
|
-
### REQUIRED
|
|
35
|
-
|
|
36
|
-
- **Auth Provider Configuration**: MUST call `configureAuthProvider()` once at app startup
|
|
37
|
-
- **isAuthenticated Function**: MUST provide function to check auth status
|
|
38
|
-
- **showAuthModal Function**: MUST provide function to show auth UI
|
|
39
|
-
- **Error Handling**: MUST handle purchase failures appropriately
|
|
40
|
-
|
|
41
|
-
### PROHIBITED
|
|
42
|
-
|
|
43
|
-
- **NEVER** use without configuring auth provider first
|
|
44
|
-
- **NEVER** bypass auth checks for convenience
|
|
45
|
-
- **NEVER** allow anonymous/guest purchases
|
|
46
|
-
- **DO NOT** call handlePurchase/handleRestore without auth provider setup
|
|
47
|
-
|
|
48
|
-
### CRITICAL SAFETY
|
|
49
|
-
|
|
50
|
-
- **ALWAYS** configure auth provider at app initialization
|
|
51
|
-
- **NEVER** allow purchases for anonymous users
|
|
52
|
-
- **MUST** implement proper auth flow with pending purchase preservation
|
|
53
|
-
- **ALWAYS** verify auth status in production
|
|
54
|
-
|
|
55
|
-
## AI Agent Guidelines
|
|
56
|
-
|
|
57
|
-
### When Implementing Auth-Gated Purchases
|
|
58
|
-
|
|
59
|
-
1. **Always** configure auth provider at app startup
|
|
60
|
-
2. **Always** implement isAuthenticated function
|
|
61
|
-
3. **Always** implement showAuthModal function
|
|
62
|
-
4. **Never** bypass auth checks
|
|
63
|
-
5. **Always** test purchase flow with authenticated and unauthenticated users
|
|
64
|
-
|
|
65
|
-
### Integration Checklist
|
|
66
|
-
|
|
67
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
68
|
-
- [ ] Call `configureAuthProvider()` once at app startup
|
|
69
|
-
- [ ] Provide `isAuthenticated()` function
|
|
70
|
-
- [ ] Provide `showAuthModal()` function
|
|
71
|
-
- [ ] Test purchase flow with authenticated user
|
|
72
|
-
- [ ] Test purchase flow with unauthenticated user
|
|
73
|
-
- [ ] Verify auth modal appears for guests
|
|
74
|
-
- [ ] Verify purchase proceeds after authentication
|
|
75
|
-
- [ ] Check development logs for auth verification
|
|
76
|
-
- [ ] Verify purchases are blocked without auth provider
|
|
77
|
-
|
|
78
|
-
### Common Patterns
|
|
79
|
-
|
|
80
|
-
1. **App-Level Config**: Configure once in root App component
|
|
81
|
-
2. **Pending Purchase**: Store package for post-auth completion
|
|
82
|
-
3. **Auth Integration**: Use with Firebase, Auth0, or custom auth
|
|
83
|
-
4. **Error Handling**: Handle auth failures and purchase failures
|
|
84
|
-
5. **Development Testing**: Use dev logs to verify auth checks
|
|
85
|
-
|
|
86
|
-
## Related Documentation
|
|
87
|
-
|
|
88
|
-
- **usePremium**: For purchase and restore operations
|
|
89
|
-
- **usePaywallOperations**: For complete paywall purchase handling
|
|
90
|
-
- **useAuthGate**: For authentication gating
|
|
91
|
-
- **useAuthSubscriptionSync**: For syncing auth with subscription
|
|
92
|
-
- **Security Best Practices**: `src/docs/SECURITY.md`
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth-Aware Purchase Hook
|
|
3
|
-
* Handles purchase flow with authentication requirement
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useCallback } from "react";
|
|
7
|
-
import type { PurchasesPackage } from "react-native-purchases";
|
|
8
|
-
import { usePremium } from "./usePremium";
|
|
9
|
-
import type { PurchaseSource } from "../../domain/entities/Credits";
|
|
10
|
-
|
|
11
|
-
declare const __DEV__: boolean;
|
|
12
|
-
|
|
13
|
-
export interface PurchaseAuthProvider {
|
|
14
|
-
isAuthenticated: () => boolean;
|
|
15
|
-
showAuthModal: () => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let globalAuthProvider: PurchaseAuthProvider | null = null;
|
|
19
|
-
let savedPackage: PurchasesPackage | null = null;
|
|
20
|
-
let savedSource: PurchaseSource | null = null;
|
|
21
|
-
|
|
22
|
-
export const configureAuthProvider = (provider: PurchaseAuthProvider): void => {
|
|
23
|
-
globalAuthProvider = provider;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const getSavedPurchase = (): { pkg: PurchasesPackage; source: PurchaseSource } | null => {
|
|
27
|
-
if (savedPackage && savedSource) {
|
|
28
|
-
return { pkg: savedPackage, source: savedSource };
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export const clearSavedPurchase = (): void => {
|
|
34
|
-
savedPackage = null;
|
|
35
|
-
savedSource = null;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export interface UseAuthAwarePurchaseParams {
|
|
39
|
-
source?: PurchaseSource;
|
|
40
|
-
userId?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface UseAuthAwarePurchaseResult {
|
|
44
|
-
handlePurchase: (pkg: PurchasesPackage, source?: PurchaseSource) => Promise<boolean>;
|
|
45
|
-
handleRestore: () => Promise<boolean>;
|
|
46
|
-
executeSavedPurchase: () => Promise<boolean>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const useAuthAwarePurchase = (
|
|
50
|
-
params?: UseAuthAwarePurchaseParams
|
|
51
|
-
): UseAuthAwarePurchaseResult => {
|
|
52
|
-
const { purchasePackage, restorePurchase } = usePremium(params?.userId);
|
|
53
|
-
|
|
54
|
-
const handlePurchase = useCallback(
|
|
55
|
-
async (pkg: PurchasesPackage, source?: PurchaseSource): Promise<boolean> => {
|
|
56
|
-
if (__DEV__) {
|
|
57
|
-
console.log("[useAuthAwarePurchase] handlePurchase called:", {
|
|
58
|
-
productId: pkg.product.identifier,
|
|
59
|
-
hasAuthProvider: !!globalAuthProvider,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!globalAuthProvider) {
|
|
64
|
-
if (__DEV__) {
|
|
65
|
-
console.error("[useAuthAwarePurchase] Auth provider not configured");
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const isAuth = globalAuthProvider.isAuthenticated();
|
|
71
|
-
if (__DEV__) {
|
|
72
|
-
console.log("[useAuthAwarePurchase] Auth check:", { isAuthenticated: isAuth });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!isAuth) {
|
|
76
|
-
if (__DEV__) {
|
|
77
|
-
console.log("[useAuthAwarePurchase] Not authenticated, saving and showing auth");
|
|
78
|
-
}
|
|
79
|
-
savedPackage = pkg;
|
|
80
|
-
savedSource = source || params?.source || "settings";
|
|
81
|
-
globalAuthProvider.showAuthModal();
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (__DEV__) {
|
|
86
|
-
console.log("[useAuthAwarePurchase] Calling purchasePackage");
|
|
87
|
-
}
|
|
88
|
-
const result = await purchasePackage(pkg);
|
|
89
|
-
if (__DEV__) {
|
|
90
|
-
console.log("[useAuthAwarePurchase] purchasePackage returned:", result);
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
},
|
|
94
|
-
[purchasePackage, params?.source]
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const handleRestore = useCallback(async (): Promise<boolean> => {
|
|
98
|
-
if (!globalAuthProvider) {
|
|
99
|
-
if (__DEV__) {
|
|
100
|
-
console.error("[useAuthAwarePurchase] Auth provider not configured");
|
|
101
|
-
}
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!globalAuthProvider.isAuthenticated()) {
|
|
106
|
-
if (__DEV__) {
|
|
107
|
-
console.log("[useAuthAwarePurchase] Not authenticated for restore");
|
|
108
|
-
}
|
|
109
|
-
globalAuthProvider.showAuthModal();
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return restorePurchase();
|
|
114
|
-
}, [restorePurchase]);
|
|
115
|
-
|
|
116
|
-
const executeSavedPurchase = useCallback(async (): Promise<boolean> => {
|
|
117
|
-
const saved = getSavedPurchase();
|
|
118
|
-
if (!saved) {
|
|
119
|
-
if (__DEV__) {
|
|
120
|
-
console.log("[useAuthAwarePurchase] No saved purchase to execute");
|
|
121
|
-
}
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (__DEV__) {
|
|
126
|
-
console.log("[useAuthAwarePurchase] Executing saved purchase:", saved.pkg.product.identifier);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
clearSavedPurchase();
|
|
130
|
-
return purchasePackage(saved.pkg);
|
|
131
|
-
}, [purchasePackage]);
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
handlePurchase,
|
|
135
|
-
handleRestore,
|
|
136
|
-
executeSavedPurchase,
|
|
137
|
-
};
|
|
138
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# useAuthGate Hook
|
|
2
|
-
|
|
3
|
-
Hook for combining authentication and subscription gating.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useAuthGate.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Auth Gate Flow
|
|
16
|
-
|
|
17
|
-
1. **Authentication Check**: Verify if user is authenticated
|
|
18
|
-
2. **Subscription Check**: Verify if user has required subscription
|
|
19
|
-
3. **Authorization Evaluation**: Combine auth + subscription status
|
|
20
|
-
4. **Gate Actions**: Provide functions to gate features
|
|
21
|
-
5. **Sign In Flow**: Trigger authentication when required
|
|
22
|
-
6. **Upgrade Flow**: Trigger subscription when required
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
26
|
-
- **Auth Context**: User authentication state
|
|
27
|
-
- **Subscription Repository**: `src/infrastructure/repositories/SubscriptionRepository.ts`
|
|
28
|
-
- **Paywall Domain**: For subscription upgrade flow
|
|
29
|
-
- **Auth UI**: For sign-in/sign-up flows
|
|
30
|
-
|
|
31
|
-
## Restrictions
|
|
32
|
-
|
|
33
|
-
### REQUIRED
|
|
34
|
-
|
|
35
|
-
- **Loading State**: MUST handle loading state
|
|
36
|
-
- **Authorization Check**: MUST verify isAuthorized before showing protected content
|
|
37
|
-
- **Callback Implementation**: MUST implement onAuthRequired/onSubscriptionRequired callbacks
|
|
38
|
-
|
|
39
|
-
### PROHIBITED
|
|
40
|
-
|
|
41
|
-
- **NEVER** show protected content without checking isAuthorized
|
|
42
|
-
- **NEVER** use for security decisions without server validation
|
|
43
|
-
- **DO NOT** assume user is authenticated or subscribed
|
|
44
|
-
|
|
45
|
-
### CRITICAL SAFETY
|
|
46
|
-
|
|
47
|
-
- **ALWAYS** check isAuthorized before showing protected features
|
|
48
|
-
- **NEVER** trust client-side state for security
|
|
49
|
-
- **MUST** implement proper auth flow
|
|
50
|
-
- **ALWAYS** handle all states (loading, authorized, unauthorized)
|
|
51
|
-
|
|
52
|
-
## AI Agent Guidelines
|
|
53
|
-
|
|
54
|
-
### When Implementing Auth Gates
|
|
55
|
-
|
|
56
|
-
1. **Always** check loading state first
|
|
57
|
-
2. **Always** verify isAuthorized before showing content
|
|
58
|
-
3. **Always** provide sign-in option for unauthenticated users
|
|
59
|
-
4. **Always** provide upgrade option for non-subscribed users
|
|
60
|
-
5. **Never** bypass auth checks for convenience
|
|
61
|
-
|
|
62
|
-
### Integration Checklist
|
|
63
|
-
|
|
64
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
65
|
-
- [ ] Configure requireAuth and requireSubscription
|
|
66
|
-
- [ ] Handle loading state
|
|
67
|
-
- [ ] Check isAuthorized before showing content
|
|
68
|
-
- [ ] Implement sign-in callback
|
|
69
|
-
- [ ] Implement subscription upgrade callback
|
|
70
|
-
- [ ] Test with unauthenticated user
|
|
71
|
-
- [ ] Test with authenticated non-subscribed user
|
|
72
|
-
- [ ] Test with authenticated subscribed user
|
|
73
|
-
- [ ] Test loading states
|
|
74
|
-
|
|
75
|
-
### Common Patterns
|
|
76
|
-
|
|
77
|
-
1. **Auth Only**: Require authentication only
|
|
78
|
-
2. **Subscription Only**: Require subscription only (auth handled internally)
|
|
79
|
-
3. **Both**: Require both auth and subscription
|
|
80
|
-
4. **Conditional Gates**: Different requirements per feature
|
|
81
|
-
5. **Nested Gates**: Layer multiple gate conditions
|
|
82
|
-
|
|
83
|
-
## Related Documentation
|
|
84
|
-
|
|
85
|
-
- **useAuth**: Authentication state
|
|
86
|
-
- **usePremiumGate**: Premium-only gating
|
|
87
|
-
- **useSubscriptionGate**: Subscription-only gating
|
|
88
|
-
- **useFeatureGate**: Unified feature gating
|
|
89
|
-
- **Auth Domain**: `src/domains/config/README.md`
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useAuthGate Hook
|
|
3
|
-
*
|
|
4
|
-
* Single responsibility: Authentication gating
|
|
5
|
-
* Checks if user is authenticated before allowing actions.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* const { requireAuth, isAuthenticated } = useAuthGate({
|
|
10
|
-
* isAuthenticated: !!user && !user.isAnonymous,
|
|
11
|
-
* onAuthRequired: (callback) => showAuthModal(callback),
|
|
12
|
-
* });
|
|
13
|
-
*
|
|
14
|
-
* const handleAction = () => {
|
|
15
|
-
* requireAuth(() => doSomething());
|
|
16
|
-
* };
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { useCallback } from "react";
|
|
21
|
-
|
|
22
|
-
declare const __DEV__: boolean;
|
|
23
|
-
|
|
24
|
-
export interface UseAuthGateParams {
|
|
25
|
-
/** Whether user is authenticated (not guest/anonymous) */
|
|
26
|
-
isAuthenticated: boolean;
|
|
27
|
-
/** Callback when auth is required - receives pending action callback */
|
|
28
|
-
onAuthRequired: (pendingCallback: () => void | Promise<void>) => void;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface UseAuthGateResult {
|
|
32
|
-
/** Whether user is authenticated */
|
|
33
|
-
isAuthenticated: boolean;
|
|
34
|
-
/** Gate action behind auth - executes if authenticated, else shows auth modal */
|
|
35
|
-
requireAuth: (action: () => void | Promise<void>) => boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function useAuthGate(params: UseAuthGateParams): UseAuthGateResult {
|
|
39
|
-
const { isAuthenticated, onAuthRequired } = params;
|
|
40
|
-
|
|
41
|
-
const requireAuth = useCallback(
|
|
42
|
-
(action: () => void | Promise<void>): boolean => {
|
|
43
|
-
if (!isAuthenticated) {
|
|
44
|
-
if (__DEV__) {
|
|
45
|
-
|
|
46
|
-
console.log("[useAuthGate] Not authenticated, showing auth modal");
|
|
47
|
-
}
|
|
48
|
-
onAuthRequired(action);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (__DEV__) {
|
|
53
|
-
|
|
54
|
-
console.log("[useAuthGate] Authenticated, proceeding");
|
|
55
|
-
}
|
|
56
|
-
return true;
|
|
57
|
-
},
|
|
58
|
-
[isAuthenticated, onAuthRequired]
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
isAuthenticated,
|
|
63
|
-
requireAuth,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# useCreditChecker Hook
|
|
2
|
-
|
|
3
|
-
Simple hook for checking if user has sufficient credits before operations.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useCreditChecker.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Credit Validation Flow
|
|
16
|
-
|
|
17
|
-
1. **Initial Check**
|
|
18
|
-
- Compare current balance against required amount
|
|
19
|
-
- Return boolean result immediately
|
|
20
|
-
- No network calls required (uses cached balance)
|
|
21
|
-
|
|
22
|
-
2. **Real-time Updates**
|
|
23
|
-
- Automatically re-checks when credits change
|
|
24
|
-
- Updates result via `useCredits` hook
|
|
25
|
-
- Triggers re-renders on balance changes
|
|
26
|
-
|
|
27
|
-
3. **Manual Refresh**
|
|
28
|
-
- Optionally trigger manual credit check
|
|
29
|
-
- Useful before expensive operations
|
|
30
|
-
- Validates current state before action
|
|
31
|
-
|
|
32
|
-
### Integration Points
|
|
33
|
-
|
|
34
|
-
- **useCredits**: For fetching current credit balance
|
|
35
|
-
- **useDeductCredit**: For deducting credits after check
|
|
36
|
-
- **Credit Checking UI**: Pre-action validation displays
|
|
37
|
-
- **Purchase Flows**: Redirect to credit packages
|
|
38
|
-
|
|
39
|
-
## Restrictions
|
|
40
|
-
|
|
41
|
-
### REQUIRED
|
|
42
|
-
|
|
43
|
-
- **Required Amount**: MUST specify positive credit cost
|
|
44
|
-
- **Balance Check**: MUST verify `hasEnoughCredits` before action
|
|
45
|
-
- **User Feedback**: MUST show credit cost to user
|
|
46
|
-
|
|
47
|
-
### PROHIBITED
|
|
48
|
-
|
|
49
|
-
- **NEVER** assume credits are sufficient without checking
|
|
50
|
-
- **NEVER** use for security decisions (server-side validation required)
|
|
51
|
-
- **DO NOT** deduct credits with this hook (use `useDeductCredit`)
|
|
52
|
-
- **NEVER** hardcode credit costs (should be configurable)
|
|
53
|
-
|
|
54
|
-
### CRITICAL SAFETY
|
|
55
|
-
|
|
56
|
-
- **ALWAYS** check return value before proceeding with action
|
|
57
|
-
- **ALWAYS** show credit cost to user before execution
|
|
58
|
-
- **NEVER** trust client-side check for security (server must validate)
|
|
59
|
-
- **MUST** handle case where credits become insufficient between check and action
|
|
60
|
-
|
|
61
|
-
## AI Agent Guidelines
|
|
62
|
-
|
|
63
|
-
### When Implementing Credit Checks
|
|
64
|
-
|
|
65
|
-
1. **Always** specify positive credit cost
|
|
66
|
-
2. **Always** check `hasEnoughCredits` before action
|
|
67
|
-
3. **Always** show credit cost to user in UI
|
|
68
|
-
4. **Always** provide upgrade path when insufficient
|
|
69
|
-
5. **Never** use for security decisions (server validation required)
|
|
70
|
-
|
|
71
|
-
### Integration Checklist
|
|
72
|
-
|
|
73
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
74
|
-
- [ ] Specify positive credit cost
|
|
75
|
-
- [ ] Check `hasEnoughCredits` before action
|
|
76
|
-
- [ ] Display credit cost to user
|
|
77
|
-
- [ ] Show upgrade/purchase option when insufficient
|
|
78
|
-
- [ ] Validate credit cost is positive number
|
|
79
|
-
- [ ] Test with sufficient credits
|
|
80
|
-
- [ ] Test with insufficient credits
|
|
81
|
-
- [ ] Test with zero credits
|
|
82
|
-
- [ ] Test credit changes between check and action
|
|
83
|
-
|
|
84
|
-
### Common Patterns to Implement
|
|
85
|
-
|
|
86
|
-
1. **Pre-action Validation**: Check before showing button
|
|
87
|
-
2. **Cost Display**: Always show credit cost to user
|
|
88
|
-
3. **Upgrade Prompts**: Link to credit packages when low
|
|
89
|
-
4. **Manual Refresh**: Check again before expensive operations
|
|
90
|
-
5. **Credit Deduction**: Use with `useDeductCredit` for complete flow
|
|
91
|
-
6. **Visual Feedback**: Disable buttons when insufficient
|
|
92
|
-
7. **Warning Messages**: Alert user before high-cost operations
|
|
93
|
-
8. **Balance Display**: Show current balance alongside cost
|
|
94
|
-
|
|
95
|
-
## Related Documentation
|
|
96
|
-
|
|
97
|
-
- **useCredits**: Access current credit balance
|
|
98
|
-
- **useDeductCredit**: Deduct credits after check passes
|
|
99
|
-
- **useCreditsGate**: Complete credit gating with deduction
|
|
100
|
-
- **useFeatureGate**: Unified feature gating with credits
|
|
101
|
-
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
102
|
-
- **Wallet Domain**: `src/domains/wallet/README.md`
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useCreditChecker Hook
|
|
3
|
-
*
|
|
4
|
-
* Provides credit checking utilities using module-level repository.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useMemo } from "react";
|
|
8
|
-
import { getCreditsRepository } from "../../infrastructure/repositories/CreditsRepositoryProvider";
|
|
9
|
-
import {
|
|
10
|
-
createCreditChecker,
|
|
11
|
-
type CreditCheckResult,
|
|
12
|
-
} from "../../utils/creditChecker";
|
|
13
|
-
|
|
14
|
-
export interface UseCreditCheckerParams {
|
|
15
|
-
onCreditDeducted?: (userId: string, cost: number) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface UseCreditCheckerResult {
|
|
19
|
-
checkCreditsAvailable: (
|
|
20
|
-
userId: string | undefined,
|
|
21
|
-
cost?: number
|
|
22
|
-
) => Promise<CreditCheckResult>;
|
|
23
|
-
deductCreditsAfterSuccess: (
|
|
24
|
-
userId: string | undefined,
|
|
25
|
-
cost?: number
|
|
26
|
-
) => Promise<void>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const useCreditChecker = (
|
|
30
|
-
params?: UseCreditCheckerParams
|
|
31
|
-
): UseCreditCheckerResult => {
|
|
32
|
-
const repository = getCreditsRepository();
|
|
33
|
-
const onCreditDeducted = params?.onCreditDeducted;
|
|
34
|
-
|
|
35
|
-
const checker = useMemo(
|
|
36
|
-
() => createCreditChecker({ repository, onCreditDeducted }),
|
|
37
|
-
[repository, onCreditDeducted]
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
return checker;
|
|
41
|
-
};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# useCreditsGate Hook
|
|
2
|
-
|
|
3
|
-
Hook for gating features behind credit requirements.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useCreditsGate.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Credit Gating Flow
|
|
16
|
-
|
|
17
|
-
1. **Credit Check**: Verify user has sufficient credits for feature
|
|
18
|
-
2. **Balance Display**: Show current credit balance to user
|
|
19
|
-
3. **Feature Access Control**: Enable/disable features based on credit availability
|
|
20
|
-
4. **Consumption**: Deduct credits when feature is used
|
|
21
|
-
5. **Purchase Prompt**: Guide users to purchase when insufficient
|
|
22
|
-
6. **Transaction Tracking**: Record all credit transactions
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
26
|
-
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/CreditsRepository.ts`
|
|
27
|
-
- **Credits Entity**: `src/domains/wallet/domain/entities/UserCredits.ts`
|
|
28
|
-
- **TanStack Query**: For optimistic updates and caching
|
|
29
|
-
- **Paywall Domain**: For purchase flow integration
|
|
30
|
-
|
|
31
|
-
## Restrictions
|
|
32
|
-
|
|
33
|
-
### REQUIRED
|
|
34
|
-
|
|
35
|
-
- **Credit Cost**: MUST specify credit cost for feature
|
|
36
|
-
- **Feature ID**: MUST provide unique feature identifier
|
|
37
|
-
- **Balance Check**: MUST verify hasCredits before allowing action
|
|
38
|
-
- **Error Handling**: MUST handle consumption failures
|
|
39
|
-
|
|
40
|
-
### PROHIBITED
|
|
41
|
-
|
|
42
|
-
- **NEVER** allow feature access when hasCredits is false
|
|
43
|
-
- **NEVER** consume credits without checking balance first
|
|
44
|
-
- **NEVER** assume credits will be sufficient (always check)
|
|
45
|
-
- **DO NOT** use for security decisions without server validation
|
|
46
|
-
|
|
47
|
-
### CRITICAL SAFETY
|
|
48
|
-
|
|
49
|
-
- **ALWAYS** check return value from consumeCredit
|
|
50
|
-
- **NEVER** allow negative credit balance
|
|
51
|
-
- **MUST** handle insufficient credits gracefully
|
|
52
|
-
- **ALWAYS** show credit cost to user before action
|
|
53
|
-
|
|
54
|
-
## AI Agent Guidelines
|
|
55
|
-
|
|
56
|
-
### When Implementing Credit Gates
|
|
57
|
-
|
|
58
|
-
1. **Always** display credit cost to user before action
|
|
59
|
-
2. **Always** check hasCredits before enabling buttons
|
|
60
|
-
3. **Always** handle consumeCredit result
|
|
61
|
-
4. **Never** allow action when credits are insufficient
|
|
62
|
-
5. **Always** provide purchase path for credits
|
|
63
|
-
|
|
64
|
-
### Integration Checklist
|
|
65
|
-
|
|
66
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
67
|
-
- [ ] Specify credit cost in config
|
|
68
|
-
- [ ] Provide unique feature ID
|
|
69
|
-
- [ ] Check hasCredits before enabling feature
|
|
70
|
-
- [ ] Handle consumeCredit result
|
|
71
|
-
- [ ] Show credit cost in UI
|
|
72
|
-
- [ ] Display current balance
|
|
73
|
-
- [ ] Implement purchase prompt for insufficient credits
|
|
74
|
-
- [ ] Test with zero balance
|
|
75
|
-
- [ ] Test with insufficient credits
|
|
76
|
-
- [ ] Test with sufficient credits
|
|
77
|
-
|
|
78
|
-
### Common Patterns
|
|
79
|
-
|
|
80
|
-
1. **Pre-check**: Verify balance before showing feature
|
|
81
|
-
2. **Confirmation**: Ask user before expensive operations
|
|
82
|
-
3. **Success Feedback**: Show message after successful consumption
|
|
83
|
-
4. **Failure Handling**: Display appropriate error on failure
|
|
84
|
-
5. **Purchase Flow**: Navigate to purchase screen on exhaustion
|
|
85
|
-
|
|
86
|
-
## Related Documentation
|
|
87
|
-
|
|
88
|
-
- **useCredits**: Access current credit balance
|
|
89
|
-
- **useDeductCredit**: Manual credit deduction with optimistic updates
|
|
90
|
-
- **useCreditChecker**: Simple credit validation
|
|
91
|
-
- **usePremiumWithCredits**: Hybrid premium/credits access
|
|
92
|
-
- **useFeatureGate**: Unified feature gating
|
|
93
|
-
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
94
|
-
- **Wallet Domain**: `src/domains/wallet/README.md`
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useCreditsGate Hook
|
|
3
|
-
*
|
|
4
|
-
* Single responsibility: Credits gating
|
|
5
|
-
* Checks if user has enough credits before allowing actions.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* const { requireCredits, hasCredits } = useCreditsGate({
|
|
10
|
-
* hasCredits: canAfford(cost),
|
|
11
|
-
* creditBalance: credits,
|
|
12
|
-
* requiredCredits: cost,
|
|
13
|
-
* onCreditsRequired: (required) => showPaywall(required),
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* const handleGenerate = () => {
|
|
17
|
-
* requireCredits(() => generate());
|
|
18
|
-
* };
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { useCallback } from "react";
|
|
23
|
-
|
|
24
|
-
export interface UseCreditsGateParams {
|
|
25
|
-
/** Whether user has enough credits for the action */
|
|
26
|
-
hasCredits: boolean;
|
|
27
|
-
/** Current credit balance */
|
|
28
|
-
creditBalance: number;
|
|
29
|
-
/** Credits required for this action (optional, for display) */
|
|
30
|
-
requiredCredits?: number;
|
|
31
|
-
/** Callback when credits are required - receives required amount */
|
|
32
|
-
onCreditsRequired: (requiredCredits?: number) => void;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface UseCreditsGateResult {
|
|
36
|
-
/** Whether user has enough credits */
|
|
37
|
-
hasCredits: boolean;
|
|
38
|
-
/** Current credit balance */
|
|
39
|
-
creditBalance: number;
|
|
40
|
-
/** Gate action behind credits - executes if has credits, else shows paywall */
|
|
41
|
-
requireCredits: (action: () => void | Promise<void>) => boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
declare const __DEV__: boolean;
|
|
45
|
-
|
|
46
|
-
export function useCreditsGate(
|
|
47
|
-
params: UseCreditsGateParams
|
|
48
|
-
): UseCreditsGateResult {
|
|
49
|
-
const { hasCredits, creditBalance, requiredCredits, onCreditsRequired } =
|
|
50
|
-
params;
|
|
51
|
-
|
|
52
|
-
const requireCredits = useCallback(
|
|
53
|
-
(_action: () => void | Promise<void>): boolean => {
|
|
54
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
55
|
-
console.log("[useCreditsGate] requireCredits called", {
|
|
56
|
-
hasCredits,
|
|
57
|
-
creditBalance,
|
|
58
|
-
requiredCredits,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
if (!hasCredits) {
|
|
62
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
63
|
-
console.log("[useCreditsGate] No credits, showing paywall");
|
|
64
|
-
}
|
|
65
|
-
onCreditsRequired(requiredCredits);
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
69
|
-
console.log("[useCreditsGate] Has credits, allowing action");
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
},
|
|
73
|
-
[hasCredits, creditBalance, requiredCredits, onCreditsRequired]
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
hasCredits,
|
|
78
|
-
creditBalance,
|
|
79
|
-
requireCredits,
|
|
80
|
-
};
|
|
81
|
-
}
|