@umituz/react-native-subscription 2.14.97 โ 2.14.98
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 +462 -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/config/README.md +390 -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 +160 -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/domain/README.md +147 -0
- package/src/revenuecat/domain/errors/README.md +197 -0
- package/src/revenuecat/infrastructure/config/README.md +40 -0
- package/src/revenuecat/infrastructure/managers/README.md +49 -0
- package/src/revenuecat/presentation/hooks/README.md +56 -0
- package/src/utils/README.md +529 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# useDeductCredit Hook
|
|
2
|
+
|
|
3
|
+
Hook for deducting credits from user balance with optimistic updates.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { useDeductCredit } from '@umituz/react-native-subscription';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Signature
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
function useDeductCredit(params: {
|
|
15
|
+
userId: string | undefined;
|
|
16
|
+
onCreditsExhausted?: () => void;
|
|
17
|
+
}): {
|
|
18
|
+
deductCredit: (cost?: number) => Promise<boolean>;
|
|
19
|
+
deductCredits: (cost: number) => Promise<boolean>;
|
|
20
|
+
isDeducting: boolean;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Parameters
|
|
25
|
+
|
|
26
|
+
| Parameter | Type | Default | Description |
|
|
27
|
+
|-----------|------|---------|-------------|
|
|
28
|
+
| `userId` | `string \| undefined` | **Required** | User ID for credit deduction |
|
|
29
|
+
| `onCreditsExhausted` | `() => void` | `undefined` | Callback when credits are exhausted |
|
|
30
|
+
|
|
31
|
+
## Returns
|
|
32
|
+
|
|
33
|
+
| Property | Type | Description |
|
|
34
|
+
|----------|------|-------------|
|
|
35
|
+
| `deductCredit` | `(cost?: number) => Promise<boolean>` | Deduct credits (defaults to 1) |
|
|
36
|
+
| `deductCredits` | `(cost: number) => Promise<boolean>` | Deduct specific amount |
|
|
37
|
+
| `isDeducting` | `boolean` | Mutation is in progress |
|
|
38
|
+
|
|
39
|
+
## Basic Usage
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
function CreditButton() {
|
|
43
|
+
const { user } = useAuth();
|
|
44
|
+
const { deductCredit, isDeducting } = useDeductCredit({
|
|
45
|
+
userId: user?.uid,
|
|
46
|
+
onCreditsExhausted: () => {
|
|
47
|
+
Alert.alert('Low Credits', 'Please purchase more credits');
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const handleUseFeature = async () => {
|
|
52
|
+
const success = await deductCredit(1);
|
|
53
|
+
if (success) {
|
|
54
|
+
executePremiumFeature();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Button onPress={handleUseFeature} disabled={isDeducting}>
|
|
60
|
+
Use Feature (1 Credit)
|
|
61
|
+
</Button>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Advanced Usage
|
|
67
|
+
|
|
68
|
+
### With Custom Cost
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
function FeatureWithCost() {
|
|
72
|
+
const { deductCredit } = useDeductCredit({
|
|
73
|
+
userId: user?.uid,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const features = {
|
|
77
|
+
basic: { cost: 1, name: 'Basic Generation' },
|
|
78
|
+
advanced: { cost: 5, name: 'Advanced Generation' },
|
|
79
|
+
premium: { cost: 10, name: 'Premium Generation' },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleFeature = async (cost: number) => {
|
|
83
|
+
const success = await deductCredit(cost);
|
|
84
|
+
if (success) {
|
|
85
|
+
console.log('Feature used');
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<View>
|
|
91
|
+
{Object.entries(features).map(([key, { cost, name }]) => (
|
|
92
|
+
<Button
|
|
93
|
+
key={key}
|
|
94
|
+
onPress={() => handleFeature(cost)}
|
|
95
|
+
title={`${name} (${cost} credits)`}
|
|
96
|
+
/>
|
|
97
|
+
))}
|
|
98
|
+
</View>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### With Confirmation
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
function CreditDeductionWithConfirmation() {
|
|
107
|
+
const { deductCredit } = useDeductCredit({
|
|
108
|
+
userId: user?.uid,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const handleActionWithConfirmation = async (cost: number, action: string) => {
|
|
112
|
+
Alert.alert(
|
|
113
|
+
'Confirm Action',
|
|
114
|
+
`This will cost ${cost} credit${cost > 1 ? 's' : ''}. Continue?`,
|
|
115
|
+
[
|
|
116
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
117
|
+
{
|
|
118
|
+
text: 'Confirm',
|
|
119
|
+
onPress: async () => {
|
|
120
|
+
const success = await deductCredit(cost);
|
|
121
|
+
if (success) {
|
|
122
|
+
await performAction(action);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
]
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<Button
|
|
132
|
+
onPress={() => handleActionWithConfirmation(5, 'export')}
|
|
133
|
+
title="Export Data (5 Credits)"
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Best Practices
|
|
140
|
+
|
|
141
|
+
1. **Check balance first** - Show user if they have enough credits
|
|
142
|
+
2. **Handle exhausted credits** - Provide purchase option
|
|
143
|
+
3. **Show loading state** - Disable button during deduction
|
|
144
|
+
4. **Optimistic updates** - UI updates automatically
|
|
145
|
+
5. **Error handling** - Handle deduction failures gracefully
|
|
146
|
+
6. **Confirm expensive actions** - Ask before large deductions
|
|
147
|
+
7. **Clear messaging** - Tell users cost before deducting
|
|
148
|
+
|
|
149
|
+
## Related Hooks
|
|
150
|
+
|
|
151
|
+
- **useCredits** - For accessing credits balance
|
|
152
|
+
- **useCreditsGate** - For gating features with credits
|
|
153
|
+
- **useInitializeCredits** - For initializing credits after purchase
|
|
154
|
+
- **useCreditChecker** - For checking credit availability
|
|
155
|
+
|
|
156
|
+
## See Also
|
|
157
|
+
|
|
158
|
+
- [Credits README](../../../domains/wallet/README.md)
|
|
159
|
+
- [Credits Entity](../../../domains/wallet/domain/entities/Credits.md)
|
|
160
|
+
- [Credits Repository](../../../infrastructure/repositories/CreditsRepository.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)
|