@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,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePremiumWithCredits Hook
|
|
3
|
-
*
|
|
4
|
-
* Combined hook for premium subscription with credits system.
|
|
5
|
-
* Ensures premium users always have credits initialized.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useEffect, useCallback } from "react";
|
|
9
|
-
import { useCredits, type UseCreditsResult } from "./useCredits";
|
|
10
|
-
import { useInitializeCredits } from "./useInitializeCredits";
|
|
11
|
-
|
|
12
|
-
export interface UsePremiumWithCreditsParams {
|
|
13
|
-
userId: string | undefined;
|
|
14
|
-
isPremium: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface UsePremiumWithCreditsResult extends UseCreditsResult {
|
|
18
|
-
ensureCreditsInitialized: () => Promise<void>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const usePremiumWithCredits = ({
|
|
22
|
-
userId,
|
|
23
|
-
isPremium,
|
|
24
|
-
}: UsePremiumWithCreditsParams): UsePremiumWithCreditsResult => {
|
|
25
|
-
const creditsResult = useCredits({ userId });
|
|
26
|
-
const { initializeCredits, isInitializing } = useInitializeCredits({
|
|
27
|
-
userId,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const ensureCreditsInitialized = useCallback(async () => {
|
|
31
|
-
if (!userId || !isPremium) return;
|
|
32
|
-
if (creditsResult.credits) return;
|
|
33
|
-
if (isInitializing) return;
|
|
34
|
-
|
|
35
|
-
await initializeCredits();
|
|
36
|
-
}, [userId, isPremium, creditsResult.credits, isInitializing, initializeCredits]);
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (isPremium && userId && !creditsResult.credits && !creditsResult.isLoading) {
|
|
40
|
-
ensureCreditsInitialized();
|
|
41
|
-
}
|
|
42
|
-
}, [isPremium, userId, creditsResult.credits, creditsResult.isLoading, ensureCreditsInitialized]);
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
...creditsResult,
|
|
46
|
-
ensureCreditsInitialized,
|
|
47
|
-
};
|
|
48
|
-
};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# useSubscription Hook
|
|
2
|
-
|
|
3
|
-
Core hook for subscription status management and operations.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useSubscription.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Subscription Management Flow
|
|
16
|
-
|
|
17
|
-
1. **Status Retrieval**: Fetch current subscription status from repository
|
|
18
|
-
2. **State Caching**: Cache status for 5 minutes using TanStack Query
|
|
19
|
-
3. **Real-time Updates**: Auto-refresh when user auth state changes
|
|
20
|
-
4. **Manual Operations**: Provide functions for manual activation/deactivation
|
|
21
|
-
5. **Loading States**: Handle loading and error states properly
|
|
22
|
-
6. **Refresh Mechanisms**: Support both load and refresh operations
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
26
|
-
- **Subscription Repository**: `src/infrastructure/repositories/SubscriptionRepository.ts`
|
|
27
|
-
- **TanStack Query**: For caching and background updates
|
|
28
|
-
- **Auth Context**: Sync with user authentication state
|
|
29
|
-
- **RevenueCat**: For subscription data source
|
|
30
|
-
|
|
31
|
-
## Restrictions
|
|
32
|
-
|
|
33
|
-
### REQUIRED
|
|
34
|
-
|
|
35
|
-
- **User ID**: MUST provide valid userId for operations
|
|
36
|
-
- **Loading State**: MUST handle loading state in UI
|
|
37
|
-
- **Error Handling**: MUST handle error state
|
|
38
|
-
- **Status Check**: MUST verify isPremium before showing premium features
|
|
39
|
-
|
|
40
|
-
### PROHIBITED
|
|
41
|
-
|
|
42
|
-
- **NEVER** assume instant data availability (always check loading state)
|
|
43
|
-
- **NEVER** use for security decisions without server validation
|
|
44
|
-
- **DO NOT** call loadStatus/refreshStatus without userId
|
|
45
|
-
- **DO NOT** activate/deactivate subscriptions without proper validation
|
|
46
|
-
|
|
47
|
-
### CRITICAL SAFETY
|
|
48
|
-
|
|
49
|
-
- **ALWAYS** check loading state before accessing status
|
|
50
|
-
- **NEVER** trust client-side state for security enforcement
|
|
51
|
-
- **MUST** validate userId before all operations
|
|
52
|
-
- **ALWAYS** handle errors gracefully
|
|
53
|
-
|
|
54
|
-
## AI Agent Guidelines
|
|
55
|
-
|
|
56
|
-
### When Implementing Subscription Features
|
|
57
|
-
|
|
58
|
-
1. **Always** check loading state before rendering subscription data
|
|
59
|
-
2. **Always** handle error state
|
|
60
|
-
3. **Always** verify isPremium before showing premium features
|
|
61
|
-
4. **Always** validate userId before operations
|
|
62
|
-
5. **Never** use for security decisions without server validation
|
|
63
|
-
|
|
64
|
-
### Integration Checklist
|
|
65
|
-
|
|
66
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
67
|
-
- [ ] Handle loading state
|
|
68
|
-
- [ ] Handle error state
|
|
69
|
-
- [ ] Check isPremium before rendering premium content
|
|
70
|
-
- [ ] Validate userId before operations
|
|
71
|
-
- [ ] Implement refresh mechanism
|
|
72
|
-
- [ ] Test with premium user
|
|
73
|
-
- [ ] Test with free user
|
|
74
|
-
- [ ] Test with guest user
|
|
75
|
-
- [ ] Test offline scenario
|
|
76
|
-
|
|
77
|
-
### Common Patterns
|
|
78
|
-
|
|
79
|
-
1. **Status Display**: Show current subscription status
|
|
80
|
-
2. **Conditional Rendering**: Show/hide features based on isPremium
|
|
81
|
-
3. **Manual Refresh**: Provide refresh button for users
|
|
82
|
-
4. **Auto-refresh**: Refresh on screen focus or app foreground
|
|
83
|
-
5. **Status Polling**: Periodic refresh for real-time updates
|
|
84
|
-
6. **Purchase Completion**: Refresh after successful purchase
|
|
85
|
-
7. **Error Recovery**: Provide retry mechanism on failure
|
|
86
|
-
|
|
87
|
-
## Related Documentation
|
|
88
|
-
|
|
89
|
-
- **usePremium**: Simplified premium checking
|
|
90
|
-
- **useSubscriptionStatus**: Detailed subscription status
|
|
91
|
-
- **useSubscriptionDetails**: Package and pricing info
|
|
92
|
-
- **useUserTier**: Tier information
|
|
93
|
-
- **Subscription Repository**: `src/infrastructure/repositories/README.md`
|
|
94
|
-
- **Domain Layer**: `src/domain/README.md`
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useSubscription Hook
|
|
3
|
-
* React hook for subscription management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useState, useCallback } from 'react';
|
|
7
|
-
import type { SubscriptionStatus } from '../../domain/entities/SubscriptionStatus';
|
|
8
|
-
import { isSubscriptionValid } from '../../domain/entities/SubscriptionStatus';
|
|
9
|
-
import {
|
|
10
|
-
checkSubscriptionService,
|
|
11
|
-
validateUserId,
|
|
12
|
-
executeSubscriptionOperation,
|
|
13
|
-
} from './useSubscription.utils';
|
|
14
|
-
|
|
15
|
-
export interface UseSubscriptionResult {
|
|
16
|
-
/** Current subscription status */
|
|
17
|
-
status: SubscriptionStatus | null;
|
|
18
|
-
/** Whether subscription is loading */
|
|
19
|
-
loading: boolean;
|
|
20
|
-
/** Error if any */
|
|
21
|
-
error: string | null;
|
|
22
|
-
/** Whether user has active subscription */
|
|
23
|
-
isPremium: boolean;
|
|
24
|
-
/** Load subscription status */
|
|
25
|
-
loadStatus: (userId: string) => Promise<void>;
|
|
26
|
-
/** Refresh subscription status */
|
|
27
|
-
refreshStatus: (userId: string) => Promise<void>;
|
|
28
|
-
/** Activate subscription */
|
|
29
|
-
activateSubscription: (
|
|
30
|
-
userId: string,
|
|
31
|
-
productId: string,
|
|
32
|
-
expiresAt: string | null,
|
|
33
|
-
) => Promise<void>;
|
|
34
|
-
/** Deactivate subscription */
|
|
35
|
-
deactivateSubscription: (userId: string) => Promise<void>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Hook for subscription operations
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* const { status, isPremium, loadStatus } = useSubscription();
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export function useSubscription(): UseSubscriptionResult {
|
|
47
|
-
const [status, setStatus] = useState<SubscriptionStatus | null>(null);
|
|
48
|
-
const [loading, setLoading] = useState(false);
|
|
49
|
-
const [error, setError] = useState<string | null>(null);
|
|
50
|
-
|
|
51
|
-
const performOperation = useCallback(async (
|
|
52
|
-
userId: string,
|
|
53
|
-
operation: () => Promise<SubscriptionStatus | null | void>
|
|
54
|
-
) => {
|
|
55
|
-
const errorMsg = validateUserId(userId);
|
|
56
|
-
if (errorMsg) {
|
|
57
|
-
setError(errorMsg);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const check = checkSubscriptionService();
|
|
62
|
-
if (!check.success) {
|
|
63
|
-
setError(check.error || "Service error");
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
await executeSubscriptionOperation(
|
|
68
|
-
operation,
|
|
69
|
-
setLoading,
|
|
70
|
-
setError,
|
|
71
|
-
(result) => { if (result) setStatus(result as SubscriptionStatus); }
|
|
72
|
-
);
|
|
73
|
-
}, []);
|
|
74
|
-
|
|
75
|
-
const loadStatus = useCallback((userId: string) =>
|
|
76
|
-
performOperation(userId, () => {
|
|
77
|
-
const { service } = checkSubscriptionService();
|
|
78
|
-
|
|
79
|
-
return service!.getSubscriptionStatus(userId);
|
|
80
|
-
}), [performOperation]);
|
|
81
|
-
|
|
82
|
-
const refreshStatus = loadStatus;
|
|
83
|
-
|
|
84
|
-
const activateSubscription = useCallback(
|
|
85
|
-
(userId: string, productId: string, expiresAt: string | null) => {
|
|
86
|
-
if (!productId) {
|
|
87
|
-
setError("Product ID is required");
|
|
88
|
-
return Promise.resolve();
|
|
89
|
-
}
|
|
90
|
-
return performOperation(userId, () => {
|
|
91
|
-
const { service } = checkSubscriptionService();
|
|
92
|
-
|
|
93
|
-
return service!.activateSubscription(userId, productId, expiresAt).then(res => res ?? undefined);
|
|
94
|
-
});
|
|
95
|
-
},
|
|
96
|
-
[performOperation]
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const deactivateSubscription = useCallback((userId: string) =>
|
|
100
|
-
performOperation(userId, () => {
|
|
101
|
-
const { service } = checkSubscriptionService();
|
|
102
|
-
|
|
103
|
-
return service!.deactivateSubscription(userId).then(res => res ?? undefined);
|
|
104
|
-
}), [performOperation]);
|
|
105
|
-
|
|
106
|
-
const isPremium = isSubscriptionValid(status);
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
status,
|
|
110
|
-
loading,
|
|
111
|
-
error,
|
|
112
|
-
isPremium,
|
|
113
|
-
loadStatus,
|
|
114
|
-
refreshStatus,
|
|
115
|
-
activateSubscription,
|
|
116
|
-
deactivateSubscription,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# useSubscriptionDetails Hook
|
|
2
|
-
|
|
3
|
-
Hook for accessing detailed subscription information including package details.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useSubscriptionDetails.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Detailed Subscription Information
|
|
16
|
-
|
|
17
|
-
1. **Subscription Status**: Fetch current subscription status object
|
|
18
|
-
2. **Package Details**: Retrieve RevenueCat package information
|
|
19
|
-
3. **Period Detection**: Determine subscription period (monthly/annual/lifetime)
|
|
20
|
-
4. **Price Calculation**: Calculate current price and monthly equivalent
|
|
21
|
-
5. **Feature List**: Extract feature list from package configuration
|
|
22
|
-
6. **Real-time Updates**: Refresh when subscription changes
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
26
|
-
- **Subscription Repository**: `src/infrastructure/repositories/SubscriptionRepository.ts`
|
|
27
|
-
- **Package Configuration**: RevenueCat package definitions
|
|
28
|
-
- **useSubscriptionStatus**: For detailed status information
|
|
29
|
-
- **TanStack Query**: For caching and background updates
|
|
30
|
-
|
|
31
|
-
## Restrictions
|
|
32
|
-
|
|
33
|
-
### REQUIRED
|
|
34
|
-
|
|
35
|
-
- **Loading State**: MUST handle loading state
|
|
36
|
-
- **Null Handling**: MUST handle null subscription and package
|
|
37
|
-
- **Error Handling**: MUST handle error state
|
|
38
|
-
- **Price Formatting**: MUST format prices appropriately
|
|
39
|
-
|
|
40
|
-
### PROHIBITED
|
|
41
|
-
|
|
42
|
-
- **NEVER** assume subscription exists (check for null)
|
|
43
|
-
- **NEVER** assume package exists (check for null)
|
|
44
|
-
- **DO NOT** use prices without formatting
|
|
45
|
-
- **DO NOT** hardcode feature lists
|
|
46
|
-
|
|
47
|
-
### CRITICAL SAFETY
|
|
48
|
-
|
|
49
|
-
- **ALWAYS** check for null subscription and package
|
|
50
|
-
- **MUST** handle loading state before accessing details
|
|
51
|
-
- **ALWAYS** format prices with currency codes
|
|
52
|
-
- **NEVER** trust client-side details for security
|
|
53
|
-
|
|
54
|
-
## AI Agent Guidelines
|
|
55
|
-
|
|
56
|
-
### When Implementing Subscription Details
|
|
57
|
-
|
|
58
|
-
1. **Always** check for null subscription and package
|
|
59
|
-
2. **Always** handle loading state
|
|
60
|
-
3. **Always** format prices properly
|
|
61
|
-
4. **Always** handle all period types (monthly, annual, lifetime)
|
|
62
|
-
5. **Never** use for security decisions without server validation
|
|
63
|
-
|
|
64
|
-
### Integration Checklist
|
|
65
|
-
|
|
66
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
67
|
-
- [ ] Handle loading state
|
|
68
|
-
- [ ] Check subscription for null
|
|
69
|
-
- [ ] Check package for null
|
|
70
|
-
- [ ] Format prices with currency codes
|
|
71
|
-
- [ ] Handle all period types
|
|
72
|
-
- [ ] Display features properly
|
|
73
|
-
- [ ] Test with monthly subscription
|
|
74
|
-
- [ ] Test with annual subscription
|
|
75
|
-
- [ ] Test with lifetime subscription
|
|
76
|
-
- [ ] Test with no subscription
|
|
77
|
-
|
|
78
|
-
### Common Patterns
|
|
79
|
-
|
|
80
|
-
1. **Detailed Card**: Display comprehensive subscription information
|
|
81
|
-
2. **Price Comparison**: Show monthly vs annual pricing
|
|
82
|
-
3. **Upgrade Suggestion**: Suggest upgrades based on current plan
|
|
83
|
-
4. **Feature List**: Display included features
|
|
84
|
-
5. **Billing History**: Show subscription history
|
|
85
|
-
6. **Cancellation Flow**: Handle subscription cancellation
|
|
86
|
-
|
|
87
|
-
## Related Documentation
|
|
88
|
-
|
|
89
|
-
- **useSubscription**: Basic subscription status
|
|
90
|
-
- **useSubscriptionStatus**: Detailed status
|
|
91
|
-
- **usePremium**: Simple premium check
|
|
92
|
-
- **useSubscriptionPackages**: Available packages
|
|
93
|
-
- **Package Utilities**: `src/utils/README.md`
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import {
|
|
3
|
-
type SubscriptionStatus,
|
|
4
|
-
SUBSCRIPTION_STATUS,
|
|
5
|
-
type SubscriptionStatusType,
|
|
6
|
-
isSubscriptionValid,
|
|
7
|
-
calculateDaysRemaining
|
|
8
|
-
} from "../../domain/entities/SubscriptionStatus";
|
|
9
|
-
import { formatDate } from "../utils/subscriptionDateUtils";
|
|
10
|
-
|
|
11
|
-
export interface SubscriptionDetails {
|
|
12
|
-
/** Raw subscription status */
|
|
13
|
-
status: SubscriptionStatus | null;
|
|
14
|
-
/** Whether user has active premium */
|
|
15
|
-
isPremium: boolean;
|
|
16
|
-
/** Whether subscription is expired */
|
|
17
|
-
isExpired: boolean;
|
|
18
|
-
/** Whether this is a lifetime subscription */
|
|
19
|
-
isLifetime: boolean;
|
|
20
|
-
/** Days remaining until expiration (null for lifetime) */
|
|
21
|
-
daysRemaining: number | null;
|
|
22
|
-
/** Formatted expiration date (null if lifetime) */
|
|
23
|
-
formattedExpirationDate: string | null;
|
|
24
|
-
/** Formatted purchase date */
|
|
25
|
-
formattedPurchaseDate: string | null;
|
|
26
|
-
/** Status text key for localization */
|
|
27
|
-
statusKey: SubscriptionStatusType;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface UseSubscriptionDetailsParams {
|
|
31
|
-
status: SubscriptionStatus | null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Hook for formatted subscription details
|
|
36
|
-
*/
|
|
37
|
-
export function useSubscriptionDetails(
|
|
38
|
-
params: UseSubscriptionDetailsParams,
|
|
39
|
-
): SubscriptionDetails {
|
|
40
|
-
const { status } = params;
|
|
41
|
-
|
|
42
|
-
return useMemo(() => {
|
|
43
|
-
if (!status) {
|
|
44
|
-
return {
|
|
45
|
-
status: null,
|
|
46
|
-
isPremium: false,
|
|
47
|
-
isExpired: false,
|
|
48
|
-
isLifetime: false,
|
|
49
|
-
daysRemaining: null,
|
|
50
|
-
formattedExpirationDate: null,
|
|
51
|
-
formattedPurchaseDate: null,
|
|
52
|
-
statusKey: SUBSCRIPTION_STATUS.NONE,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const isValid = isSubscriptionValid(status);
|
|
57
|
-
const isExpired = status.isPremium && !isValid;
|
|
58
|
-
const isLifetime = status.isPremium && !status.expiresAt;
|
|
59
|
-
const daysRemainingValue = calculateDaysRemaining(status.expiresAt ?? null);
|
|
60
|
-
const isPremium = status.isPremium && isValid;
|
|
61
|
-
|
|
62
|
-
let statusKey: SubscriptionStatusType = status.status || SUBSCRIPTION_STATUS.NONE;
|
|
63
|
-
|
|
64
|
-
// Override status key based on current calculation for active/expired
|
|
65
|
-
if (status.isPremium) {
|
|
66
|
-
statusKey = isExpired ? SUBSCRIPTION_STATUS.EXPIRED : SUBSCRIPTION_STATUS.ACTIVE;
|
|
67
|
-
} else if (status.status === SUBSCRIPTION_STATUS.CANCELED) {
|
|
68
|
-
statusKey = SUBSCRIPTION_STATUS.CANCELED;
|
|
69
|
-
} else {
|
|
70
|
-
statusKey = SUBSCRIPTION_STATUS.NONE;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
status,
|
|
75
|
-
isPremium,
|
|
76
|
-
isExpired,
|
|
77
|
-
isLifetime,
|
|
78
|
-
daysRemaining: daysRemainingValue,
|
|
79
|
-
formattedExpirationDate: formatDate(status.expiresAt ?? null),
|
|
80
|
-
formattedPurchaseDate: formatDate(status.purchasedAt ?? null),
|
|
81
|
-
statusKey,
|
|
82
|
-
};
|
|
83
|
-
}, [status]);
|
|
84
|
-
}
|
|
85
|
-
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# useSubscriptionGate Hook
|
|
2
|
-
|
|
3
|
-
Subscription-only feature gating with simple, focused API.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useSubscriptionGate.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Subscription Gating Flow
|
|
16
|
-
|
|
17
|
-
1. **Subscription Check**: Verify if user has active subscription
|
|
18
|
-
2. **Gate Function**: Provide requireSubscription function for gating
|
|
19
|
-
3. **Action Wrapping**: Wrap actions behind subscription check
|
|
20
|
-
4. **Callback Trigger**: Execute callback when subscription is required
|
|
21
|
-
5. **Boolean Access**: Provide simple hasSubscription boolean for conditional rendering
|
|
22
|
-
|
|
23
|
-
### Integration Points
|
|
24
|
-
|
|
25
|
-
- **usePremium**: For subscription status check
|
|
26
|
-
- **Paywall Domain**: For subscription upgrade flow
|
|
27
|
-
- **Feature Gates**: For unified feature access control
|
|
28
|
-
|
|
29
|
-
## Restrictions
|
|
30
|
-
|
|
31
|
-
### REQUIRED
|
|
32
|
-
|
|
33
|
-
- **Subscription Status**: MUST provide hasSubscription parameter
|
|
34
|
-
- **Callback**: MUST implement onSubscriptionRequired callback
|
|
35
|
-
- **Return Check**: SHOULD check return value from requireSubscription
|
|
36
|
-
|
|
37
|
-
### PROHIBITED
|
|
38
|
-
|
|
39
|
-
- **NEVER** show protected content without checking hasSubscription
|
|
40
|
-
- **NEVER** use for security decisions without server validation
|
|
41
|
-
- **DO NOT** assume subscription status (always verify)
|
|
42
|
-
|
|
43
|
-
### CRITICAL SAFETY
|
|
44
|
-
|
|
45
|
-
- **ALWAYS** verify hasSubscription before allowing access
|
|
46
|
-
- **NEVER** trust client-side state for security
|
|
47
|
-
- **MUST** implement proper upgrade flow
|
|
48
|
-
- **ALWAYS** handle both subscribed and non-subscribed states
|
|
49
|
-
|
|
50
|
-
## AI Agent Guidelines
|
|
51
|
-
|
|
52
|
-
### When Implementing Subscription Gates
|
|
53
|
-
|
|
54
|
-
1. **Always** check hasSubscription before showing protected content
|
|
55
|
-
2. **Always** provide clear upgrade path in callback
|
|
56
|
-
3. **Always** use requireSubscription for action gating
|
|
57
|
-
4. **Never** bypass subscription checks
|
|
58
|
-
5. **Always** test with both subscribed and non-subscribed users
|
|
59
|
-
|
|
60
|
-
### Integration Checklist
|
|
61
|
-
|
|
62
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
63
|
-
- [ ] Provide hasSubscription from usePremium
|
|
64
|
-
- [ ] Implement onSubscriptionRequired callback
|
|
65
|
-
- [ ] Use requireSubscription for action gating
|
|
66
|
-
- [ ] Check hasSubscription for conditional rendering
|
|
67
|
-
- [ ] Test with subscribed user
|
|
68
|
-
- [ ] Test with non-subscribed user
|
|
69
|
-
- [ ] Verify upgrade flow triggers correctly
|
|
70
|
-
|
|
71
|
-
### Common Patterns
|
|
72
|
-
|
|
73
|
-
1. **Action Gating**: Use requireSubscription to wrap actions
|
|
74
|
-
2. **Conditional Rendering**: Check hasSubscription for UI
|
|
75
|
-
3. **Button States**: Disable buttons when not subscribed
|
|
76
|
-
4. **Upgrade Prompts**: Show in callback when not subscribed
|
|
77
|
-
|
|
78
|
-
## Related Documentation
|
|
79
|
-
|
|
80
|
-
- **usePremium**: Subscription status check
|
|
81
|
-
- **usePremiumGate**: Premium feature gating
|
|
82
|
-
- **useAuthGate**: Auth + subscription gating
|
|
83
|
-
- **useFeatureGate**: Unified feature gating
|
|
84
|
-
- **Paywall Domain**: `src/domains/paywall/README.md`
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useSubscriptionGate Hook
|
|
3
|
-
*
|
|
4
|
-
* Single responsibility: Subscription/Premium gating
|
|
5
|
-
* Checks if user has active subscription before allowing actions.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* const { requireSubscription, hasSubscription } = useSubscriptionGate({
|
|
10
|
-
* hasSubscription: isPremium,
|
|
11
|
-
* onSubscriptionRequired: () => showPaywall(),
|
|
12
|
-
* });
|
|
13
|
-
*
|
|
14
|
-
* const handlePremiumAction = () => {
|
|
15
|
-
* requireSubscription(() => doPremiumThing());
|
|
16
|
-
* };
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { useCallback } from "react";
|
|
21
|
-
|
|
22
|
-
declare const __DEV__: boolean;
|
|
23
|
-
|
|
24
|
-
export interface UseSubscriptionGateParams {
|
|
25
|
-
/** Whether user has active subscription */
|
|
26
|
-
hasSubscription: boolean;
|
|
27
|
-
/** Callback when subscription is required */
|
|
28
|
-
onSubscriptionRequired: () => void;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface UseSubscriptionGateResult {
|
|
32
|
-
/** Whether user has active subscription */
|
|
33
|
-
hasSubscription: boolean;
|
|
34
|
-
/** Gate action behind subscription - executes if subscribed, else shows paywall */
|
|
35
|
-
requireSubscription: (action: () => void | Promise<void>) => boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function useSubscriptionGate(
|
|
39
|
-
params: UseSubscriptionGateParams
|
|
40
|
-
): UseSubscriptionGateResult {
|
|
41
|
-
const { hasSubscription, onSubscriptionRequired } = params;
|
|
42
|
-
|
|
43
|
-
const requireSubscription = useCallback(
|
|
44
|
-
(_action: () => void | Promise<void>): boolean => {
|
|
45
|
-
if (!hasSubscription) {
|
|
46
|
-
if (__DEV__) {
|
|
47
|
-
|
|
48
|
-
console.log("[useSubscriptionGate] No subscription, showing paywall");
|
|
49
|
-
}
|
|
50
|
-
onSubscriptionRequired();
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (__DEV__) {
|
|
55
|
-
|
|
56
|
-
console.log("[useSubscriptionGate] Has subscription, proceeding");
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
},
|
|
60
|
-
[hasSubscription, onSubscriptionRequired]
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
hasSubscription,
|
|
65
|
-
requireSubscription,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# useSubscriptionStatus Hook
|
|
2
|
-
|
|
3
|
-
Hook for accessing detailed subscription status information.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useSubscriptionStatus.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Subscription Status Flow
|
|
16
|
-
|
|
17
|
-
1. **Status Fetch**: Retrieve current subscription status from repository
|
|
18
|
-
2. **Status Parsing**: Extract tier, active state, expiration info
|
|
19
|
-
3. **Real-time Updates**: Update when subscription changes
|
|
20
|
-
4. **Expiration Tracking**: Calculate days until expiration
|
|
21
|
-
5. **Renewal Status**: Determine if subscription will auto-renew
|
|
22
|
-
6. **Caching**: Cache status for performance (5 min default)
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
26
|
-
- **Subscription Repository**: `src/infrastructure/repositories/SubscriptionRepository.ts`
|
|
27
|
-
- **SubscriptionStatus Entity**: `src/domain/entities/README.md`
|
|
28
|
-
- **TanStack Query**: For caching and real-time updates
|
|
29
|
-
- **RevenueCat**: For subscription data source
|
|
30
|
-
|
|
31
|
-
## Restrictions
|
|
32
|
-
|
|
33
|
-
### REQUIRED
|
|
34
|
-
|
|
35
|
-
- **Loading State**: MUST handle loading state
|
|
36
|
-
- **Error Handling**: MUST handle error state
|
|
37
|
-
- **Null Check**: MUST handle null status (guest users)
|
|
38
|
-
- **Expiration**: SHOULD warn users before expiration
|
|
39
|
-
|
|
40
|
-
### PROHIBITED
|
|
41
|
-
|
|
42
|
-
- **NEVER** assume status exists (check for null)
|
|
43
|
-
- **NEVER** use for security decisions without server validation
|
|
44
|
-
- **DO NOT** show expiration for lifetime subscriptions
|
|
45
|
-
- **DO NOT** cache beyond configured time
|
|
46
|
-
|
|
47
|
-
### CRITICAL SAFETY
|
|
48
|
-
|
|
49
|
-
- **ALWAYS** check if status is null before accessing properties
|
|
50
|
-
- **NEVER** trust client-side status for security
|
|
51
|
-
- **MUST** implement error boundaries
|
|
52
|
-
- **ALWAYS** refresh status when app comes to foreground
|
|
53
|
-
|
|
54
|
-
## AI Agent Guidelines
|
|
55
|
-
|
|
56
|
-
### When Displaying Subscription Info
|
|
57
|
-
|
|
58
|
-
1. **Always** check for null status (guest users)
|
|
59
|
-
2. **Always** handle loading and error states
|
|
60
|
-
3. **Always** show expiration warnings
|
|
61
|
-
4. **Always** display auto-renewal status
|
|
62
|
-
5. **Never** show technical details to end users
|
|
63
|
-
|
|
64
|
-
### Integration Checklist
|
|
65
|
-
|
|
66
|
-
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
67
|
-
- [ ] Handle loading state
|
|
68
|
-
- [ ] Handle error state
|
|
69
|
-
- [ ] Check for null status (guest users)
|
|
70
|
-
- [ ] Display subscription tier
|
|
71
|
-
- [ ] Show expiration date
|
|
72
|
-
- [ ] Show days until expiration
|
|
73
|
-
- [ ] Show auto-renewal status
|
|
74
|
-
- [ ] Implement expiration warnings
|
|
75
|
-
- [ ] Test with active subscription
|
|
76
|
-
- [ ] Test with expired subscription
|
|
77
|
-
- [ ] Test with guest user (null status)
|
|
78
|
-
|
|
79
|
-
### Common Patterns
|
|
80
|
-
|
|
81
|
-
1. **Status Badge**: Visual indicator of subscription status
|
|
82
|
-
2. **Expiration Countdown**: Days remaining until expiration
|
|
83
|
-
3. **Warning System**: Notify users before expiration
|
|
84
|
-
4. **Detail Card**: Comprehensive subscription information
|
|
85
|
-
5. **Lifecycle Management**: Handle subscription events
|
|
86
|
-
|
|
87
|
-
## Related Documentation
|
|
88
|
-
|
|
89
|
-
- **usePremium**: Simple premium status check
|
|
90
|
-
- **useSubscription**: Detailed subscription information
|
|
91
|
-
- **useSubscriptionDetails**: Package and feature details
|
|
92
|
-
- **useUserTier**: Tier information
|
|
93
|
-
- **SubscriptionStatus Entity**: `src/domain/entities/README.md`
|
|
94
|
-
- **Subscription Repository**: `src/infrastructure/repositories/README.md`
|