@umituz/react-native-subscription 2.14.97 โ 2.14.99
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/LICENSE +21 -0
- package/README.md +461 -0
- package/package.json +1 -3
- package/src/application/README.md +229 -0
- package/src/application/ports/README.md +103 -0
- package/src/domain/README.md +402 -0
- package/src/domain/constants/README.md +80 -0
- package/src/domain/entities/README.md +176 -0
- package/src/domain/errors/README.md +307 -0
- package/src/domain/value-objects/README.md +186 -0
- package/src/domains/README.md +240 -0
- package/src/domains/config/README.md +390 -0
- package/src/domains/config/domain/README.md +390 -0
- package/src/domains/config/domain/entities/README.md +350 -0
- package/src/domains/paywall/README.md +371 -0
- package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
- package/src/domains/paywall/components/README.md +185 -0
- package/src/domains/paywall/entities/README.md +199 -0
- package/src/domains/paywall/hooks/README.md +129 -0
- package/src/domains/wallet/README.md +292 -0
- package/src/domains/wallet/domain/README.md +108 -0
- package/src/domains/wallet/domain/entities/README.md +122 -0
- package/src/domains/wallet/domain/errors/README.md +157 -0
- package/src/domains/wallet/infrastructure/README.md +96 -0
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
- package/src/domains/wallet/presentation/components/README.md +231 -0
- package/src/domains/wallet/presentation/hooks/README.md +255 -0
- package/src/infrastructure/README.md +514 -0
- package/src/infrastructure/mappers/README.md +34 -0
- package/src/infrastructure/models/README.md +26 -0
- package/src/infrastructure/repositories/README.md +385 -0
- package/src/infrastructure/services/README.md +374 -0
- package/src/presentation/README.md +410 -0
- package/src/presentation/components/README.md +183 -0
- package/src/presentation/components/details/CreditRow.md +337 -0
- package/src/presentation/components/details/DetailRow.md +283 -0
- package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
- package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
- package/src/presentation/components/details/README.md +449 -0
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
- package/src/presentation/components/feedback/README.md +447 -0
- package/src/presentation/components/paywall/PaywallModal.md +444 -0
- package/src/presentation/components/paywall/README.md +190 -0
- package/src/presentation/components/sections/README.md +468 -0
- package/src/presentation/components/sections/SubscriptionSection.md +246 -0
- package/src/presentation/hooks/README.md +743 -0
- package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
- package/src/presentation/hooks/useAuthGate.md +403 -0
- package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
- package/src/presentation/hooks/useCreditChecker.md +407 -0
- package/src/presentation/hooks/useCredits.md +342 -0
- package/src/presentation/hooks/useCreditsGate.md +346 -0
- package/src/presentation/hooks/useDeductCredit.md +176 -0
- package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
- package/src/presentation/hooks/useFeatureGate.md +157 -0
- package/src/presentation/hooks/useInitializeCredits.md +458 -0
- package/src/presentation/hooks/usePaywall.md +334 -0
- package/src/presentation/hooks/usePaywallOperations.md +486 -0
- package/src/presentation/hooks/usePaywallVisibility.md +344 -0
- package/src/presentation/hooks/usePremium.md +230 -0
- package/src/presentation/hooks/usePremiumGate.md +423 -0
- package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
- package/src/presentation/hooks/useSubscription.md +450 -0
- package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
- package/src/presentation/hooks/useSubscriptionGate.md +168 -0
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
- package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
- package/src/presentation/hooks/useUserTier.md +356 -0
- package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
- package/src/presentation/screens/README.md +194 -0
- package/src/presentation/types/README.md +38 -0
- package/src/presentation/utils/README.md +52 -0
- package/src/revenuecat/README.md +523 -0
- package/src/revenuecat/application/README.md +158 -0
- package/src/revenuecat/application/ports/README.md +169 -0
- package/src/revenuecat/domain/README.md +147 -0
- package/src/revenuecat/domain/constants/README.md +183 -0
- package/src/revenuecat/domain/entities/README.md +382 -0
- package/src/revenuecat/domain/errors/README.md +197 -0
- package/src/revenuecat/domain/types/README.md +373 -0
- package/src/revenuecat/domain/value-objects/README.md +441 -0
- package/src/revenuecat/infrastructure/README.md +50 -0
- package/src/revenuecat/infrastructure/config/README.md +40 -0
- package/src/revenuecat/infrastructure/handlers/README.md +218 -0
- package/src/revenuecat/infrastructure/managers/README.md +49 -0
- package/src/revenuecat/infrastructure/services/README.md +325 -0
- package/src/revenuecat/infrastructure/utils/README.md +382 -0
- package/src/revenuecat/presentation/README.md +184 -0
- package/src/revenuecat/presentation/hooks/README.md +56 -0
- package/src/utils/README.md +529 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# useDeductCredit Hook
|
|
2
|
+
|
|
3
|
+
Hook for deducting credits from user balance with optimistic updates.
|
|
4
|
+
|
|
5
|
+
## Location
|
|
6
|
+
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
+
|
|
9
|
+
**File**: `src/presentation/hooks/useDeductCredit.ts`
|
|
10
|
+
|
|
11
|
+
## Strategy
|
|
12
|
+
|
|
13
|
+
### Credit Deduction Flow
|
|
14
|
+
|
|
15
|
+
1. **Pre-deduction Validation**
|
|
16
|
+
- Verify user is authenticated (userId must be defined)
|
|
17
|
+
- Check if sufficient credits exist
|
|
18
|
+
- Validate deduction amount is positive
|
|
19
|
+
|
|
20
|
+
2. **Optimistic Update**
|
|
21
|
+
- Immediately update UI with new balance
|
|
22
|
+
- Store previous state for potential rollback
|
|
23
|
+
- Update TanStack Query cache
|
|
24
|
+
|
|
25
|
+
3. **Server Synchronization**
|
|
26
|
+
- Send deduction request to backend
|
|
27
|
+
- Handle success/failure responses
|
|
28
|
+
- Rollback on failure
|
|
29
|
+
|
|
30
|
+
4. **Post-deduction Handling**
|
|
31
|
+
- Trigger `onCreditsExhausted` callback if balance reaches zero
|
|
32
|
+
- Return success/failure boolean
|
|
33
|
+
- Reset loading state
|
|
34
|
+
|
|
35
|
+
### Integration Points
|
|
36
|
+
|
|
37
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/CreditsRepository.ts`
|
|
38
|
+
- **Credits Entity**: `src/domains/wallet/domain/entities/UserCredits.ts`
|
|
39
|
+
- **TanStack Query**: For cache management and optimistic updates
|
|
40
|
+
|
|
41
|
+
## Restrictions
|
|
42
|
+
|
|
43
|
+
### REQUIRED
|
|
44
|
+
|
|
45
|
+
- **User Authentication**: `userId` parameter MUST be provided and cannot be undefined
|
|
46
|
+
- **Positive Amount**: Credit cost MUST be greater than zero
|
|
47
|
+
- **Callback Implementation**: `onCreditsExhausted` callback SHOULD be implemented to handle zero balance scenarios
|
|
48
|
+
|
|
49
|
+
### PROHIBITED
|
|
50
|
+
|
|
51
|
+
- **NEVER** call `deductCredit` or `deductCredits` without checking `isDeducting` state first
|
|
52
|
+
- **NEVER** allow multiple simultaneous deduction calls for the same user
|
|
53
|
+
- **NEVER** deduce credits when balance is insufficient (should check with `useCreditChecker` first)
|
|
54
|
+
- **DO NOT** use this hook for guest users (userId undefined)
|
|
55
|
+
|
|
56
|
+
### CRITICAL SAFETY
|
|
57
|
+
|
|
58
|
+
- **ALWAYS** check return value before proceeding with feature execution
|
|
59
|
+
- **ALWAYS** handle the case where deduction returns `false`
|
|
60
|
+
- **NEVER** assume deduction succeeded without checking return value
|
|
61
|
+
- **MUST** implement error boundaries when using this hook
|
|
62
|
+
|
|
63
|
+
## Rules
|
|
64
|
+
|
|
65
|
+
### Initialization
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// CORRECT
|
|
69
|
+
const { deductCredit, isDeducting } = useDeductCredit({
|
|
70
|
+
userId: user?.uid,
|
|
71
|
+
onCreditsExhausted: () => showPaywall(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// INCORRECT - Missing callback
|
|
75
|
+
const { deductCredit } = useDeductCredit({
|
|
76
|
+
userId: user?.uid,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// INCORRECT - userId undefined
|
|
80
|
+
const { deductCredit } = useDeductCredit({
|
|
81
|
+
userId: undefined,
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Deduction Execution
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// CORRECT - Check return value
|
|
89
|
+
const success = await deductCredit(5);
|
|
90
|
+
if (success) {
|
|
91
|
+
executeFeature();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// INCORRECT - No return value check
|
|
95
|
+
await deductCredit(5);
|
|
96
|
+
executeFeature(); // May execute even if deduction failed
|
|
97
|
+
|
|
98
|
+
// INCORRECT - Multiple simultaneous calls
|
|
99
|
+
Promise.all([
|
|
100
|
+
deductCredit(5),
|
|
101
|
+
deductCredit(3), // Race condition!
|
|
102
|
+
]);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Loading States
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// CORRECT - Respect loading state
|
|
109
|
+
<Button onPress={handleDeduct} disabled={isDeducting}>
|
|
110
|
+
{isDeducting ? 'Deducting...' : 'Use Feature'}
|
|
111
|
+
</Button>
|
|
112
|
+
|
|
113
|
+
// INCORRECT - Ignore loading state
|
|
114
|
+
<Button onPress={handleDeduct}>
|
|
115
|
+
Use Feature
|
|
116
|
+
</Button>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Credit Exhaustion Handling
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// CORRECT - Implement callback
|
|
123
|
+
const { deductCredit } = useDeductCredit({
|
|
124
|
+
userId: user?.uid,
|
|
125
|
+
onCreditsExhausted: () => {
|
|
126
|
+
navigation.navigate('CreditPackages');
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// MINIMUM - Show alert
|
|
131
|
+
const { deductCredit } = useDeductCredit({
|
|
132
|
+
userId: user?.uid,
|
|
133
|
+
onCreditsExhausted: () => {
|
|
134
|
+
Alert.alert('No Credits', 'Please purchase more credits');
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## AI Agent Guidelines
|
|
140
|
+
|
|
141
|
+
### When Implementing Features
|
|
142
|
+
|
|
143
|
+
1. **Always** check if user has sufficient credits BEFORE allowing action
|
|
144
|
+
2. **Always** show the credit cost to user before deducting
|
|
145
|
+
3. **Always** disable buttons while `isDeducting` is true
|
|
146
|
+
4. **Always** handle the case where deduction returns false
|
|
147
|
+
5. **Never** allow zero or negative credit costs
|
|
148
|
+
|
|
149
|
+
### Integration Checklist
|
|
150
|
+
|
|
151
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
152
|
+
- [ ] Provide valid `userId` from authentication context
|
|
153
|
+
- [ ] Implement `onCreditsExhausted` callback
|
|
154
|
+
- [ ] Check `isDeducting` before calling deduct functions
|
|
155
|
+
- [ ] Validate return value before proceeding
|
|
156
|
+
- [ ] Show credit cost to user before action
|
|
157
|
+
- [ ] Handle error cases gracefully
|
|
158
|
+
- [ ] Test with insufficient credits
|
|
159
|
+
- [ ] Test with zero balance
|
|
160
|
+
|
|
161
|
+
### Common Patterns to Implement
|
|
162
|
+
|
|
163
|
+
1. **Pre-check**: Use `useCreditChecker` before showing feature button
|
|
164
|
+
2. **Confirmation Dialog**: Ask user before expensive operations (>5 credits)
|
|
165
|
+
3. **Success Feedback**: Show success message after deduction
|
|
166
|
+
4. **Failure Handling**: Show appropriate error message on failure
|
|
167
|
+
5. **Purchase Flow**: Navigate to purchase screen on exhaustion
|
|
168
|
+
|
|
169
|
+
## Related Documentation
|
|
170
|
+
|
|
171
|
+
- **useCredits**: Access current credit balance
|
|
172
|
+
- **useCreditChecker**: Check credit availability before operations
|
|
173
|
+
- **useInitializeCredits**: Initialize credits after purchase
|
|
174
|
+
- **useFeatureGate**: Unified feature gating with credits
|
|
175
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
176
|
+
- **Wallet Domain**: `src/domains/wallet/README.md`
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# useDevTestCallbacks Hook
|
|
2
|
+
|
|
3
|
+
**Development-only** hook for testing subscription renewal and credit operations.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { useDevTestCallbacks } from '@umituz/react-native-subscription';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Signature
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
function useDevTestCallbacks(): {
|
|
15
|
+
onTestRenewal: () => void;
|
|
16
|
+
onCheckCredits: () => void;
|
|
17
|
+
onTestDuplicate: () => void;
|
|
18
|
+
} | undefined
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Returns
|
|
22
|
+
|
|
23
|
+
Returns `undefined` in production. In development, returns:
|
|
24
|
+
|
|
25
|
+
| Property | Type | Description |
|
|
26
|
+
|----------|------|-------------|
|
|
27
|
+
| `onTestRenewal` | `() => void` | Simulate subscription renewal |
|
|
28
|
+
| `onCheckCredits` | `() => void` | Show current credits in alert |
|
|
29
|
+
| `onTestDuplicate` | `() => void` | Test duplicate renewal protection |
|
|
30
|
+
|
|
31
|
+
## Important Notes
|
|
32
|
+
|
|
33
|
+
โ ๏ธ **Development Only**
|
|
34
|
+
- This hook only works in `__DEV__` mode
|
|
35
|
+
- Returns `undefined` in production builds
|
|
36
|
+
- Use for testing and development purposes only
|
|
37
|
+
|
|
38
|
+
## Basic Usage
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
function DevTestSection() {
|
|
42
|
+
const devCallbacks = useDevTestCallbacks();
|
|
43
|
+
|
|
44
|
+
// Don't render in production
|
|
45
|
+
if (!devCallbacks) return null;
|
|
46
|
+
|
|
47
|
+
const { onTestRenewal, onCheckCredits, onTestDuplicate } = devCallbacks;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<View style={styles.devSection}>
|
|
51
|
+
<Text style={styles.title}>Developer Tools</Text>
|
|
52
|
+
|
|
53
|
+
<Button onPress={onTestRenewal} title="Test Renewal" />
|
|
54
|
+
<Button onPress={onCheckCredits} title="Check Credits" />
|
|
55
|
+
<Button onPress={onTestDuplicate} title="Test Duplicate Protection" />
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Advanced Usage
|
|
62
|
+
|
|
63
|
+
### Complete Dev Test Panel
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
function DevTestPanel() {
|
|
67
|
+
const devCallbacks = useDevTestCallbacks();
|
|
68
|
+
|
|
69
|
+
if (!__DEV__ || !devCallbacks) return null;
|
|
70
|
+
|
|
71
|
+
const { onTestRenewal, onCheckCredits, onTestDuplicate } = devCallbacks;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<ScrollView style={styles.devPanel}>
|
|
75
|
+
<Text style={styles.header}>Developer Test Panel</Text>
|
|
76
|
+
|
|
77
|
+
<View style={styles.section}>
|
|
78
|
+
<Text style={styles.sectionTitle}>Renewal Testing</Text>
|
|
79
|
+
|
|
80
|
+
<TouchableOpacity
|
|
81
|
+
style={styles.button}
|
|
82
|
+
onPress={onTestRenewal}
|
|
83
|
+
>
|
|
84
|
+
<Text>Test Renewal (Add Credits)</Text>
|
|
85
|
+
</TouchableOpacity>
|
|
86
|
+
|
|
87
|
+
<Text style={styles.help}>
|
|
88
|
+
Simulates a subscription renewal and adds credits using ACCUMULATE mode
|
|
89
|
+
</Text>
|
|
90
|
+
</View>
|
|
91
|
+
|
|
92
|
+
<View style={styles.section}>
|
|
93
|
+
<Text style={styles.sectionTitle}>Credits Inspection</Text>
|
|
94
|
+
|
|
95
|
+
<TouchableOpacity
|
|
96
|
+
style={styles.button}
|
|
97
|
+
onPress={onCheckCredits}
|
|
98
|
+
>
|
|
99
|
+
<Text>Check Current Credits</Text>
|
|
100
|
+
</TouchableOpacity>
|
|
101
|
+
|
|
102
|
+
<Text style={styles.help}>
|
|
103
|
+
Display current credit balance and purchase date
|
|
104
|
+
</Text>
|
|
105
|
+
</View>
|
|
106
|
+
|
|
107
|
+
<View style={styles.section}>
|
|
108
|
+
<Text style={styles.sectionTitle}>Duplicate Protection</Text>
|
|
109
|
+
|
|
110
|
+
<TouchableOpacity
|
|
111
|
+
style={styles.button}
|
|
112
|
+
onPress={onTestDuplicate}
|
|
113
|
+
>
|
|
114
|
+
<Text>Test Duplicate Protection</Text>
|
|
115
|
+
</TouchableOpacity>
|
|
116
|
+
|
|
117
|
+
<Text style={styles.help}>
|
|
118
|
+
Tests that duplicate renewals with the same ID are prevented
|
|
119
|
+
</Text>
|
|
120
|
+
</View>
|
|
121
|
+
</ScrollView>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### With Settings Screen Integration
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
function SettingsScreen() {
|
|
130
|
+
const devCallbacks = useDevTestCallbacks();
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<ScrollView>
|
|
134
|
+
<Section title="Account">
|
|
135
|
+
<SettingsItem label="Email" value={user?.email} />
|
|
136
|
+
<SettingsItem label="Subscription" value={isPremium ? 'Premium' : 'Free'} />
|
|
137
|
+
</Section>
|
|
138
|
+
|
|
139
|
+
{/* Only show in development */}
|
|
140
|
+
{__DEV__ && devCallbacks && (
|
|
141
|
+
<Section title="Developer Tools">
|
|
142
|
+
<SettingsItem
|
|
143
|
+
label="Test Renewal"
|
|
144
|
+
onPress={devCallbacks.onTestRenewal}
|
|
145
|
+
/>
|
|
146
|
+
<SettingsItem
|
|
147
|
+
label="Check Credits"
|
|
148
|
+
onPress={devCallbacks.onCheckCredits}
|
|
149
|
+
/>
|
|
150
|
+
<SettingsItem
|
|
151
|
+
label="Test Duplicate Protection"
|
|
152
|
+
onPress={devCallbacks.onTestDuplicate}
|
|
153
|
+
/>
|
|
154
|
+
</Section>
|
|
155
|
+
)}
|
|
156
|
+
</ScrollView>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### With Debug Menu
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
function DebugMenu() {
|
|
165
|
+
const devCallbacks = useDevTestCallbacks();
|
|
166
|
+
|
|
167
|
+
if (!__DEV__) return null;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<Menu>
|
|
171
|
+
{devCallbacks && (
|
|
172
|
+
<>
|
|
173
|
+
<MenuItem onPress={devCallbacks.onTestRenewal}>
|
|
174
|
+
Test Renewal
|
|
175
|
+
</MenuItem>
|
|
176
|
+
<MenuItem onPress={devCallbacks.onCheckCredits}>
|
|
177
|
+
Check Credits
|
|
178
|
+
</MenuItem>
|
|
179
|
+
<MenuItem onPress={devCallbacks.onTestDuplicate}>
|
|
180
|
+
Test Duplicate Protection
|
|
181
|
+
</MenuItem>
|
|
182
|
+
</>
|
|
183
|
+
)}
|
|
184
|
+
</Menu>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Test Functions
|
|
190
|
+
|
|
191
|
+
### onTestRenewal
|
|
192
|
+
|
|
193
|
+
Simulates a subscription renewal event:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const onTestRenewal = () => {
|
|
197
|
+
// Simulates renewal with:
|
|
198
|
+
// - renewalId: `dev_renewal_${Date.now()}`
|
|
199
|
+
// - productId: 'test_yearly_subscription'
|
|
200
|
+
// - Mode: ACCUMULATE (adds to existing credits)
|
|
201
|
+
|
|
202
|
+
// Shows alert with:
|
|
203
|
+
// - New credit balance
|
|
204
|
+
// - Confirmation of ACCUMULATE mode
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Example output:**
|
|
209
|
+
```
|
|
210
|
+
โ
Test Renewal Success
|
|
211
|
+
Credits Updated!
|
|
212
|
+
|
|
213
|
+
New Balance: 1200
|
|
214
|
+
|
|
215
|
+
(ACCUMULATE mode - credits added to existing)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### onCheckCredits
|
|
219
|
+
|
|
220
|
+
Displays current credit information:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const onCheckCredits = () => {
|
|
224
|
+
// Shows alert with:
|
|
225
|
+
// - Current credit balance
|
|
226
|
+
// - Purchase date (or "N/A")
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Example output:**
|
|
231
|
+
```
|
|
232
|
+
๐ Current Credits
|
|
233
|
+
Credits: 100
|
|
234
|
+
|
|
235
|
+
Purchased: January 15, 2024
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### onTestDuplicate
|
|
239
|
+
|
|
240
|
+
Tests duplicate renewal protection:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
const onTestDuplicate = () => {
|
|
244
|
+
// Performs two initialization calls with same renewalId:
|
|
245
|
+
// - First call: Adds credits
|
|
246
|
+
// - Second call: Should be skipped
|
|
247
|
+
|
|
248
|
+
// Shows alert with:
|
|
249
|
+
// - First call result
|
|
250
|
+
// - Second call result (should indicate protection worked)
|
|
251
|
+
};
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Example output:**
|
|
255
|
+
```
|
|
256
|
+
Duplicate Test
|
|
257
|
+
First call: โ
Added credits
|
|
258
|
+
|
|
259
|
+
Second call: โ
Skipped (protection works!)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Development Logging
|
|
263
|
+
|
|
264
|
+
The hook logs detailed information in development:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// Test renewal
|
|
268
|
+
๐งช [Dev Test] Simulating auto-renewal...
|
|
269
|
+
{
|
|
270
|
+
userId: 'user-123',
|
|
271
|
+
renewalId: 'dev_renewal_1705311234567'
|
|
272
|
+
}
|
|
273
|
+
โ
[Dev Test] Renewal completed:
|
|
274
|
+
{
|
|
275
|
+
success: true,
|
|
276
|
+
credits: 1100
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Check credits
|
|
280
|
+
๐ Current Credits
|
|
281
|
+
Credits: 100
|
|
282
|
+
Purchased: January 15, 2024
|
|
283
|
+
|
|
284
|
+
// Test duplicate
|
|
285
|
+
๐งช [Dev Test] Testing duplicate protection...
|
|
286
|
+
First call: { credits: 100, ... }
|
|
287
|
+
Second call: { credits: 100, ... }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Examples
|
|
291
|
+
|
|
292
|
+
### Comprehensive Dev Dashboard
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
function DevDashboard() {
|
|
296
|
+
const devCallbacks = useDevTestCallbacks();
|
|
297
|
+
|
|
298
|
+
if (!__DEV__ || !devCallbacks) return null;
|
|
299
|
+
|
|
300
|
+
const { user } = useAuth();
|
|
301
|
+
const { credits } = useCredits();
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<View style={styles.dashboard}>
|
|
305
|
+
<View style={styles.header}>
|
|
306
|
+
<Text style={styles.title}>Development Dashboard</Text>
|
|
307
|
+
<Text style={styles.subtitle}>
|
|
308
|
+
User: {user?.uid || 'Not logged in'}
|
|
309
|
+
</Text>
|
|
310
|
+
<Text style={styles.subtitle}>
|
|
311
|
+
Credits: {credits?.credits || 0}
|
|
312
|
+
</Text>
|
|
313
|
+
</View>
|
|
314
|
+
|
|
315
|
+
<View style={styles.actions}>
|
|
316
|
+
<TouchableOpacity
|
|
317
|
+
style={styles.actionButton}
|
|
318
|
+
onPress={devCallbacks.onTestRenewal}
|
|
319
|
+
>
|
|
320
|
+
<Icon name="refresh" size={20} />
|
|
321
|
+
<Text>Simulate Renewal</Text>
|
|
322
|
+
</TouchableOpacity>
|
|
323
|
+
|
|
324
|
+
<TouchableOpacity
|
|
325
|
+
style={styles.actionButton}
|
|
326
|
+
onPress={devCallbacks.onCheckCredits}
|
|
327
|
+
>
|
|
328
|
+
<Icon name="search" size={20} />
|
|
329
|
+
<Text>Inspect Credits</Text>
|
|
330
|
+
</TouchableOpacity>
|
|
331
|
+
|
|
332
|
+
<TouchableOpacity
|
|
333
|
+
style={styles.actionButton}
|
|
334
|
+
onPress={devCallbacks.onTestDuplicate}
|
|
335
|
+
>
|
|
336
|
+
<Icon name="shield" size={20} />
|
|
337
|
+
<Text>Test Duplicate Protection</Text>
|
|
338
|
+
</TouchableOpacity>
|
|
339
|
+
</View>
|
|
340
|
+
|
|
341
|
+
<View style={styles.info}>
|
|
342
|
+
<Text style={styles.infoTitle}>Test Mode Active</Text>
|
|
343
|
+
<Text style={styles.infoText}>
|
|
344
|
+
All operations use test data and won't affect production
|
|
345
|
+
</Text>
|
|
346
|
+
</View>
|
|
347
|
+
</View>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### With Flow Testing
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
function RenewalFlowTest() {
|
|
356
|
+
const devCallbacks = useDevTestCallbacks();
|
|
357
|
+
|
|
358
|
+
if (!__DEV__ || !devCallbacks) return null;
|
|
359
|
+
|
|
360
|
+
const testRenewalFlow = async () => {
|
|
361
|
+
// 1. Check initial credits
|
|
362
|
+
devCallbacks.onCheckCredits();
|
|
363
|
+
|
|
364
|
+
// 2. Simulate renewal
|
|
365
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
366
|
+
devCallbacks.onTestRenewal();
|
|
367
|
+
|
|
368
|
+
// 3. Verify credits increased
|
|
369
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
370
|
+
devCallbacks.onCheckCredits();
|
|
371
|
+
|
|
372
|
+
// 4. Test duplicate protection
|
|
373
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
374
|
+
devCallbacks.onTestDuplicate();
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<Button
|
|
379
|
+
onPress={testRenewalFlow}
|
|
380
|
+
title="Run Complete Renewal Flow Test"
|
|
381
|
+
/>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Best Practices
|
|
387
|
+
|
|
388
|
+
1. **Development only** - Never use in production code
|
|
389
|
+
2. **Guard with `__DEV__`** - Always check if in development mode
|
|
390
|
+
3. **Clear UI** - Make dev tools visually distinct
|
|
391
|
+
4. **Test thoroughly** - Use for testing renewal flows
|
|
392
|
+
5. **Document behavior** - Leave clear comments for other developers
|
|
393
|
+
6. **Remove before release** - Ensure dev UI doesn't ship
|
|
394
|
+
7. **Test edge cases** - Duplicate handling, error cases
|
|
395
|
+
|
|
396
|
+
## Production Safety
|
|
397
|
+
|
|
398
|
+
The hook is production-safe:
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
export const useDevTestCallbacks = (): DevTestActions | undefined => {
|
|
402
|
+
// ... implementation
|
|
403
|
+
|
|
404
|
+
if (!__DEV__) {
|
|
405
|
+
return undefined; // โ
Returns undefined in production
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return { onTestRenewal, onCheckCredits, onTestDuplicate };
|
|
409
|
+
};
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Related Hooks
|
|
413
|
+
|
|
414
|
+
- **useCredits** - For accessing credits balance
|
|
415
|
+
- **useInitializeCredits** - For credit initialization
|
|
416
|
+
- **usePremiumWithCredits** - For premium + credits integration
|
|
417
|
+
|
|
418
|
+
## See Also
|
|
419
|
+
|
|
420
|
+
- [Credits README](../../../domains/wallet/README.md)
|
|
421
|
+
- [Renewal Testing Guide](../../../docs/RENEWAL_TESTING.md)
|
|
422
|
+
- [Development Tools](../../../docs/DEV_TOOLS.md)
|