@umituz/react-native-subscription 2.14.98 → 2.14.100
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/README.md +211 -395
- package/package.json +1 -1
- package/src/application/README.md +46 -225
- package/src/application/ports/README.md +42 -97
- package/src/domain/README.md +36 -384
- package/src/domain/constants/README.md +0 -56
- package/src/domain/entities/README.md +43 -169
- package/src/domain/errors/README.md +33 -287
- package/src/domain/value-objects/README.md +43 -179
- package/src/domains/README.md +52 -0
- package/src/domains/README.md.bak +274 -0
- package/src/domains/config/README.md +93 -383
- package/src/domains/config/domain/README.md +37 -0
- package/src/domains/config/domain/entities/README.md +41 -0
- package/src/domains/paywall/README.md +99 -369
- package/src/domains/paywall/components/README.md +34 -178
- package/src/domains/paywall/entities/README.md +34 -193
- package/src/domains/paywall/hooks/README.md +34 -122
- package/src/domains/wallet/README.md +34 -275
- package/src/domains/wallet/README.md.bak +209 -0
- package/src/domains/wallet/domain/README.md +34 -101
- package/src/domains/wallet/domain/entities/README.md +34 -115
- package/src/domains/wallet/domain/errors/README.md +34 -151
- package/src/domains/wallet/infrastructure/README.md +34 -89
- package/src/domains/wallet/presentation/components/README.md +34 -224
- package/src/domains/wallet/presentation/hooks/README.md +34 -248
- package/src/infrastructure/README.md +37 -496
- package/src/infrastructure/mappers/README.md +0 -13
- package/src/infrastructure/repositories/README.md +74 -360
- package/src/infrastructure/services/README.md +95 -370
- package/src/presentation/README.md +123 -408
- package/src/presentation/README.md.bak +172 -0
- package/src/presentation/components/README.md +151 -179
- package/src/presentation/components/README.md.bak +217 -0
- package/src/presentation/components/details/CreditRow.md +65 -310
- package/src/presentation/components/details/DetailRow.md +63 -255
- package/src/presentation/components/details/PremiumDetailsCard.md +65 -238
- package/src/presentation/components/details/PremiumStatusBadge.md +64 -239
- package/src/presentation/components/details/README.md +97 -447
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +63 -287
- package/src/presentation/components/feedback/README.md +97 -445
- package/src/presentation/components/paywall/PaywallModal.md +66 -416
- package/src/presentation/components/paywall/README.md +50 -186
- package/src/presentation/components/sections/README.md +97 -466
- package/src/presentation/components/sections/SubscriptionSection.md +92 -244
- package/src/presentation/hooks/README.md +154 -741
- package/src/presentation/hooks/useAuthAwarePurchase.md +58 -325
- package/src/presentation/hooks/useAuthGate.md +61 -375
- package/src/presentation/hooks/useAuthSubscriptionSync.md +66 -370
- package/src/presentation/hooks/useCreditChecker.md +73 -378
- package/src/presentation/hooks/useCredits.md +74 -313
- package/src/presentation/hooks/useCredits.md.bak +231 -0
- package/src/presentation/hooks/useCreditsGate.md +66 -318
- package/src/presentation/hooks/useDeductCredit.md +96 -156
- package/src/presentation/hooks/useDevTestCallbacks.md +63 -394
- package/src/presentation/hooks/useFeatureGate.md +105 -150
- package/src/presentation/hooks/useFeatureGate.md.bak +284 -0
- package/src/presentation/hooks/useInitializeCredits.md +64 -430
- package/src/presentation/hooks/usePaywall.md +61 -306
- package/src/presentation/hooks/usePaywallOperations.md +64 -458
- package/src/presentation/hooks/usePaywallVisibility.md +67 -316
- package/src/presentation/hooks/usePremium.md +84 -226
- package/src/presentation/hooks/usePremiumGate.md +60 -395
- package/src/presentation/hooks/usePremiumWithCredits.md +64 -401
- package/src/presentation/hooks/useSubscription.md +66 -422
- package/src/presentation/hooks/useSubscriptionDetails.md +65 -410
- package/src/presentation/hooks/useSubscriptionGate.md +80 -164
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +66 -346
- package/src/presentation/hooks/useSubscriptionStatus.md +66 -396
- package/src/presentation/hooks/useUserTier.md +63 -328
- package/src/presentation/hooks/useUserTierWithRepository.md +64 -424
- package/src/presentation/screens/README.md +48 -190
- package/src/presentation/types/README.md +0 -16
- package/src/presentation/utils/README.md +0 -21
- package/src/revenuecat/README.md +99 -518
- package/src/revenuecat/application/README.md +43 -0
- package/src/revenuecat/application/ports/README.md +41 -0
- package/src/revenuecat/domain/README.md +42 -141
- package/src/revenuecat/domain/constants/README.md +41 -0
- package/src/revenuecat/domain/entities/README.md +42 -0
- package/src/revenuecat/domain/errors/README.md +47 -191
- package/src/revenuecat/domain/types/README.md +41 -0
- package/src/revenuecat/domain/value-objects/README.md +41 -0
- package/src/revenuecat/infrastructure/README.md +41 -0
- package/src/revenuecat/infrastructure/config/README.md +32 -23
- package/src/revenuecat/infrastructure/handlers/README.md +41 -0
- package/src/revenuecat/infrastructure/managers/README.md +34 -42
- package/src/revenuecat/infrastructure/services/README.md +42 -0
- package/src/revenuecat/infrastructure/utils/README.md +41 -0
- package/src/revenuecat/presentation/README.md +42 -0
- package/src/revenuecat/presentation/hooks/README.md +29 -35
- package/src/utils/README.md +38 -525
|
@@ -2,345 +2,93 @@
|
|
|
2
2
|
|
|
3
3
|
Hook for gating features behind credit requirements.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { useCreditsGate } from '@umituz/react-native-subscription';
|
|
9
|
-
```
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
**File**: `src/presentation/hooks/useCreditsGate.ts`
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
function useCreditsGate(config: {
|
|
15
|
-
creditCost: number;
|
|
16
|
-
featureId: string;
|
|
17
|
-
}): {
|
|
18
|
-
hasCredits: boolean;
|
|
19
|
-
credits: number;
|
|
20
|
-
consumeCredit: () => Promise<CreditsResult>;
|
|
21
|
-
isLoading: boolean;
|
|
22
|
-
showPurchasePrompt: () => void;
|
|
23
|
-
}
|
|
24
|
-
```
|
|
11
|
+
**Type**: Hook
|
|
25
12
|
|
|
26
|
-
##
|
|
13
|
+
## Strategy
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|-----------|------|----------|-------------|
|
|
30
|
-
| `config.creditCost` | `number` | Yes | Number of credits required |
|
|
31
|
-
| `config.featureId` | `string` | Yes | Unique identifier for the feature |
|
|
15
|
+
### Credit Gating Flow
|
|
32
16
|
|
|
33
|
-
|
|
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
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|----------|------|-------------|
|
|
37
|
-
| `hasCredits` | `boolean` | Whether user has enough credits |
|
|
38
|
-
| `credits` | `number` | Current credit balance |
|
|
39
|
-
| `consumeCredit` | `() => Promise<CreditsResult>` | Function to consume credits |
|
|
40
|
-
| `isLoading` | `boolean` | Whether hook is loading |
|
|
41
|
-
| `showPurchasePrompt` | `() => void` | Show purchase prompt UI |
|
|
24
|
+
### Integration Points
|
|
42
25
|
|
|
43
|
-
|
|
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
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
function CreditFeature() {
|
|
47
|
-
const { hasCredits, credits, consumeCredit } = useCreditsGate({
|
|
48
|
-
creditCost: 5,
|
|
49
|
-
featureId: 'export_data',
|
|
50
|
-
});
|
|
31
|
+
## Restrictions
|
|
51
32
|
|
|
52
|
-
|
|
53
|
-
if (!hasCredits) {
|
|
54
|
-
Alert.alert('Insufficient Credits', `You need 5 credits, have ${credits}`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
33
|
+
### REQUIRED
|
|
57
34
|
|
|
58
|
-
|
|
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
|
|
59
39
|
|
|
60
|
-
|
|
61
|
-
await exportData();
|
|
62
|
-
Alert.alert('Success', `Data exported! ${result.newBalance} credits remaining`);
|
|
63
|
-
} else {
|
|
64
|
-
Alert.alert('Error', result.error?.message || 'Failed to consume credits');
|
|
65
|
-
}
|
|
66
|
-
};
|
|
40
|
+
### PROHIBITED
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
disabled={!hasCredits}
|
|
73
|
-
/>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
```
|
|
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
|
|
77
46
|
|
|
78
|
-
|
|
47
|
+
### CRITICAL SAFETY
|
|
79
48
|
|
|
80
|
-
|
|
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
|
|
81
53
|
|
|
82
|
-
|
|
83
|
-
function AdvancedCreditFeature() {
|
|
84
|
-
const { hasCredits, credits, consumeCredit, showPurchasePrompt } =
|
|
85
|
-
useCreditsGate({
|
|
86
|
-
creditCost: 10,
|
|
87
|
-
featureId: 'ai_analysis',
|
|
88
|
-
});
|
|
54
|
+
## AI Agent Guidelines
|
|
89
55
|
|
|
90
|
-
|
|
91
|
-
if (!hasCredits) {
|
|
92
|
-
showPurchasePrompt();
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
56
|
+
### When Implementing Credit Gates
|
|
95
57
|
|
|
96
|
-
|
|
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
|
|
97
63
|
|
|
98
|
-
|
|
99
|
-
await performAIAnalysis();
|
|
100
|
-
}
|
|
101
|
-
};
|
|
64
|
+
### Integration Checklist
|
|
102
65
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
107
77
|
|
|
108
|
-
|
|
109
|
-
onPress={handleAnalyze}
|
|
110
|
-
title={hasCredits ? 'Analyze (10 credits)' : 'Get More Credits'}
|
|
111
|
-
disabled={!hasCredits && credits < 10}
|
|
112
|
-
/>
|
|
113
|
-
</View>
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
```
|
|
78
|
+
### Common Patterns
|
|
117
79
|
|
|
118
|
-
|
|
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
|
|
119
85
|
|
|
120
|
-
|
|
121
|
-
function TrackedCreditFeature() {
|
|
122
|
-
const { consumeCredit, hasCredits } = useCreditsGate({
|
|
123
|
-
creditCost: 3,
|
|
124
|
-
featureId: 'filter_apply',
|
|
125
|
-
});
|
|
86
|
+
## Related Documentation
|
|
126
87
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
amount: 3,
|
|
135
|
-
transaction_id: result.transaction?.id,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Apply filter
|
|
139
|
-
await applyFilter();
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
return <Button onPress={handleApplyFilter} title="Apply Filter (3 credits)" />;
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### With Loading State
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
function CreditFeatureWithLoading() {
|
|
151
|
-
const { consumeCredit, isLoading, hasCredits } = useCreditsGate({
|
|
152
|
-
creditCost: 5,
|
|
153
|
-
featureId: 'premium_template',
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
const handleUse = async () => {
|
|
157
|
-
if (!hasCredits) return;
|
|
158
|
-
|
|
159
|
-
const result = await consumeCredit();
|
|
160
|
-
|
|
161
|
-
if (result.success) {
|
|
162
|
-
// Show loading while processing
|
|
163
|
-
await processFeature();
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
return (
|
|
168
|
-
<View>
|
|
169
|
-
<Button
|
|
170
|
-
onPress={handleUse}
|
|
171
|
-
disabled={isLoading || !hasCredits}
|
|
172
|
-
title={isLoading ? 'Processing...' : 'Use Feature (5 credits)'}
|
|
173
|
-
/>
|
|
174
|
-
</View>
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Examples
|
|
180
|
-
|
|
181
|
-
### Credit-Based Action Button
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
function CreditActionButton({ featureId, cost, action, label }) {
|
|
185
|
-
const { hasCredits, credits, consumeCredit } = useCreditsGate({
|
|
186
|
-
creditCost: cost,
|
|
187
|
-
featureId,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const handlePress = async () => {
|
|
191
|
-
if (!hasCredits) {
|
|
192
|
-
showInsufficientCreditsDialog(cost, credits);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const result = await consumeCredit();
|
|
197
|
-
if (result.success) {
|
|
198
|
-
await action();
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
return (
|
|
203
|
-
<TouchableOpacity
|
|
204
|
-
onPress={handlePress}
|
|
205
|
-
disabled={!hasCredits}
|
|
206
|
-
style={hasCredits ? styles.buttonEnabled : styles.buttonDisabled}
|
|
207
|
-
>
|
|
208
|
-
<Text style={styles.buttonText}>{label}</Text>
|
|
209
|
-
<Text style={styles.costText}>{cost} credits</Text>
|
|
210
|
-
</TouchableOpacity>
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Credit Balance Display
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
function CreditBalanceDisplay() {
|
|
219
|
-
const { credits, isLoading } = useCreditsGate({
|
|
220
|
-
creditCost: 1,
|
|
221
|
-
featureId: 'any',
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
return (
|
|
225
|
-
<View style={styles.balanceContainer}>
|
|
226
|
-
<Text style={styles.balanceLabel}>Your Credits:</Text>
|
|
227
|
-
|
|
228
|
-
{isLoading ? (
|
|
229
|
-
<ActivityIndicator size="small" />
|
|
230
|
-
) : (
|
|
231
|
-
<Text style={styles.balanceValue}>{credits}</Text>
|
|
232
|
-
)}
|
|
233
|
-
</View>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Multiple Credit Tiers
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
function TieredFeatureAccess() {
|
|
242
|
-
const { credits: basicCredits, hasCredits: hasBasic } = useCreditsGate({
|
|
243
|
-
creditCost: 5,
|
|
244
|
-
featureId: 'basic_feature',
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
const { credits: proCredits, hasCredits: hasPro } = useCreditsGate({
|
|
248
|
-
creditCost: 20,
|
|
249
|
-
featureId: 'pro_feature',
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
return (
|
|
253
|
-
<View>
|
|
254
|
-
<Button
|
|
255
|
-
onPress={handleBasicFeature}
|
|
256
|
-
disabled={!hasBasic}
|
|
257
|
-
title={`Basic Feature (5 credits)`}
|
|
258
|
-
/>
|
|
259
|
-
|
|
260
|
-
<Button
|
|
261
|
-
onPress={handleProFeature}
|
|
262
|
-
disabled={!hasPro}
|
|
263
|
-
title={`Pro Feature (20 credits)`}
|
|
264
|
-
/>
|
|
265
|
-
|
|
266
|
-
<Text>Balance: {basicCredits} credits</Text>
|
|
267
|
-
</View>
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Result Type
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
interface CreditsResult {
|
|
276
|
-
success: boolean;
|
|
277
|
-
newBalance?: number;
|
|
278
|
-
transaction?: {
|
|
279
|
-
id: string;
|
|
280
|
-
amount: number;
|
|
281
|
-
reason: string;
|
|
282
|
-
timestamp: string;
|
|
283
|
-
};
|
|
284
|
-
error?: Error;
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
## Error Handling
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
function RobustCreditFeature() {
|
|
292
|
-
const { consumeCredit } = useCreditsGate({
|
|
293
|
-
creditCost: 10,
|
|
294
|
-
featureId: 'robust_feature',
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
const handleAction = async () => {
|
|
298
|
-
const result = await consumeCredit();
|
|
299
|
-
|
|
300
|
-
if (!result.success) {
|
|
301
|
-
if (result.error?.message.includes('Insufficient')) {
|
|
302
|
-
Alert.alert('Insufficient Credits', 'Please purchase more credits');
|
|
303
|
-
} else if (result.error?.message.includes('Network')) {
|
|
304
|
-
Alert.alert('Network Error', 'Please check your connection');
|
|
305
|
-
} else {
|
|
306
|
-
Alert.alert('Error', 'Failed to process. Please try again');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Log error
|
|
310
|
-
analytics.track('credit_consumption_failed', {
|
|
311
|
-
feature_id: 'robust_feature',
|
|
312
|
-
error: result.error?.message,
|
|
313
|
-
});
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Success
|
|
318
|
-
await executeFeature();
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
return <Button onPress={handleAction} title="Execute (10 credits)" />;
|
|
322
|
-
}
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
## Best Practices
|
|
326
|
-
|
|
327
|
-
1. **Always check `hasCredits`** before enabling actions
|
|
328
|
-
2. **Show credit cost** in UI
|
|
329
|
-
3. **Handle errors gracefully**
|
|
330
|
-
4. **Provide purchase prompt** when credits are insufficient
|
|
331
|
-
5. **Display current balance**
|
|
332
|
-
6. **Track credit consumption** in analytics
|
|
333
|
-
7. **Refetch balance** after consumption
|
|
334
|
-
|
|
335
|
-
## Related Hooks
|
|
336
|
-
|
|
337
|
-
- **useCredits** - For credit balance only
|
|
338
|
-
- **useDeductCredit** - For manual credit deduction
|
|
339
|
-
- **usePremiumWithCredits** - For premium OR credits access
|
|
340
|
-
- **useCreditChecker** - For simple credit checks
|
|
341
|
-
|
|
342
|
-
## See Also
|
|
343
|
-
|
|
344
|
-
- [useCredits](./useCredits.md)
|
|
345
|
-
- [useDeductCredit](./useDeductCredit.md)
|
|
346
|
-
- [usePremiumWithCredits](./usePremiumWithCredits.md)
|
|
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`
|
|
@@ -2,159 +2,99 @@
|
|
|
2
2
|
|
|
3
3
|
Hook for deducting credits from user balance with optimistic updates.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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)
|
|
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
|
+
## AI Agent Guidelines
|
|
64
|
+
|
|
65
|
+
### When Implementing Features
|
|
66
|
+
|
|
67
|
+
1. **Always** check if user has sufficient credits BEFORE allowing action
|
|
68
|
+
2. **Always** show the credit cost to user before deducting
|
|
69
|
+
3. **Always** disable buttons while `isDeducting` is true
|
|
70
|
+
4. **Always** handle the case where deduction returns false
|
|
71
|
+
5. **Never** allow zero or negative credit costs
|
|
72
|
+
|
|
73
|
+
### Integration Checklist
|
|
74
|
+
|
|
75
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
76
|
+
- [ ] Provide valid `userId` from authentication context
|
|
77
|
+
- [ ] Implement `onCreditsExhausted` callback
|
|
78
|
+
- [ ] Check `isDeducting` before calling deduct functions
|
|
79
|
+
- [ ] Validate return value before proceeding
|
|
80
|
+
- [ ] Show credit cost to user before action
|
|
81
|
+
- [ ] Handle error cases gracefully
|
|
82
|
+
- [ ] Test with insufficient credits
|
|
83
|
+
- [ ] Test with zero balance
|
|
84
|
+
|
|
85
|
+
### Common Patterns to Implement
|
|
86
|
+
|
|
87
|
+
1. **Pre-check**: Use `useCreditChecker` before showing feature button
|
|
88
|
+
2. **Confirmation Dialog**: Ask user before expensive operations (>5 credits)
|
|
89
|
+
3. **Success Feedback**: Show success message after deduction
|
|
90
|
+
4. **Failure Handling**: Show appropriate error message on failure
|
|
91
|
+
5. **Purchase Flow**: Navigate to purchase screen on exhaustion
|
|
92
|
+
|
|
93
|
+
## Related Documentation
|
|
94
|
+
|
|
95
|
+
- **useCredits**: Access current credit balance
|
|
96
|
+
- **useCreditChecker**: Check credit availability before operations
|
|
97
|
+
- **useInitializeCredits**: Initialize credits after purchase
|
|
98
|
+
- **useFeatureGate**: Unified feature gating with credits
|
|
99
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
100
|
+
- **Wallet Domain**: `src/domains/wallet/README.md`
|