@umituz/react-native-subscription 2.14.96 → 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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +462 -0
  3. package/package.json +3 -3
  4. package/src/application/README.md +229 -0
  5. package/src/application/ports/README.md +103 -0
  6. package/src/domain/README.md +402 -0
  7. package/src/domain/constants/README.md +80 -0
  8. package/src/domain/entities/README.md +176 -0
  9. package/src/domain/errors/README.md +307 -0
  10. package/src/domain/value-objects/README.md +186 -0
  11. package/src/domains/config/README.md +390 -0
  12. package/src/domains/paywall/README.md +371 -0
  13. package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
  14. package/src/domains/paywall/components/README.md +185 -0
  15. package/src/domains/paywall/entities/README.md +199 -0
  16. package/src/domains/paywall/hooks/README.md +129 -0
  17. package/src/domains/wallet/README.md +292 -0
  18. package/src/domains/wallet/domain/README.md +108 -0
  19. package/src/domains/wallet/domain/entities/README.md +122 -0
  20. package/src/domains/wallet/domain/errors/README.md +157 -0
  21. package/src/domains/wallet/infrastructure/README.md +96 -0
  22. package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
  23. package/src/domains/wallet/presentation/components/README.md +231 -0
  24. package/src/domains/wallet/presentation/hooks/README.md +255 -0
  25. package/src/infrastructure/README.md +514 -0
  26. package/src/infrastructure/mappers/README.md +34 -0
  27. package/src/infrastructure/models/README.md +26 -0
  28. package/src/infrastructure/repositories/README.md +385 -0
  29. package/src/infrastructure/services/README.md +374 -0
  30. package/src/presentation/README.md +410 -0
  31. package/src/presentation/components/README.md +183 -0
  32. package/src/presentation/components/details/CreditRow.md +337 -0
  33. package/src/presentation/components/details/DetailRow.md +283 -0
  34. package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
  35. package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
  36. package/src/presentation/components/details/README.md +449 -0
  37. package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
  38. package/src/presentation/components/feedback/README.md +447 -0
  39. package/src/presentation/components/paywall/PaywallModal.md +444 -0
  40. package/src/presentation/components/paywall/README.md +190 -0
  41. package/src/presentation/components/sections/README.md +468 -0
  42. package/src/presentation/components/sections/SubscriptionSection.md +246 -0
  43. package/src/presentation/hooks/README.md +743 -0
  44. package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
  45. package/src/presentation/hooks/useAuthGate.md +403 -0
  46. package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
  47. package/src/presentation/hooks/useCreditChecker.md +407 -0
  48. package/src/presentation/hooks/useCredits.md +342 -0
  49. package/src/presentation/hooks/useCreditsGate.md +346 -0
  50. package/src/presentation/hooks/useDeductCredit.md +160 -0
  51. package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
  52. package/src/presentation/hooks/useFeatureGate.md +157 -0
  53. package/src/presentation/hooks/useInitializeCredits.md +458 -0
  54. package/src/presentation/hooks/usePaywall.md +334 -0
  55. package/src/presentation/hooks/usePaywallOperations.md +486 -0
  56. package/src/presentation/hooks/usePaywallVisibility.md +344 -0
  57. package/src/presentation/hooks/usePremium.md +230 -0
  58. package/src/presentation/hooks/usePremiumGate.md +423 -0
  59. package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
  60. package/src/presentation/hooks/useSubscription.md +450 -0
  61. package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
  62. package/src/presentation/hooks/useSubscriptionGate.md +168 -0
  63. package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
  64. package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
  65. package/src/presentation/hooks/useUserTier.md +356 -0
  66. package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
  67. package/src/presentation/screens/README.md +194 -0
  68. package/src/presentation/types/README.md +38 -0
  69. package/src/presentation/utils/README.md +52 -0
  70. package/src/revenuecat/README.md +523 -0
  71. package/src/revenuecat/domain/README.md +147 -0
  72. package/src/revenuecat/domain/errors/README.md +197 -0
  73. package/src/revenuecat/infrastructure/config/README.md +40 -0
  74. package/src/revenuecat/infrastructure/managers/README.md +49 -0
  75. package/src/revenuecat/presentation/hooks/README.md +56 -0
  76. package/src/revenuecat/presentation/hooks/usePurchasePackage.ts +1 -1
  77. package/src/utils/README.md +529 -0
@@ -0,0 +1,342 @@
1
+ # useCredits Hook
2
+
3
+ Hook for accessing and managing credits balance.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useCredits } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
14
+ function useCredits(): {
15
+ credits: number;
16
+ balance: number;
17
+ transactions: Transaction[];
18
+ isLoading: boolean;
19
+ error: Error | null;
20
+ refetch: () => Promise<void>;
21
+ }
22
+ ```
23
+
24
+ ## Returns
25
+
26
+ | Property | Type | Description |
27
+ |----------|------|-------------|
28
+ | `credits` | `number` | Current credit balance |
29
+ | `balance` | `number` | Balance in currency (USD, etc.) |
30
+ | `transactions` | `Transaction[]` | Transaction history |
31
+ | `isLoading` | `boolean` | Loading state |
32
+ | `error` | `Error \| null` | Error if any |
33
+ | `refetch` | `() => Promise<void>` | Manually refetch data |
34
+
35
+ ## Basic Usage
36
+
37
+ ```typescript
38
+ function CreditsDisplay() {
39
+ const { credits, isLoading } = useCredits();
40
+
41
+ if (isLoading) return <ActivityIndicator />;
42
+
43
+ return (
44
+ <View>
45
+ <Text>Your Credits: {credits}</Text>
46
+ <Text>Balance: ${balance.toFixed(2)}</Text>
47
+ </View>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## Advanced Usage
53
+
54
+ ### With Refresh Control
55
+
56
+ ```typescript
57
+ function CreditsWithRefresh() {
58
+ const { credits, refetch, isLoading } = useCredits();
59
+
60
+ const [refreshing, setRefreshing] = useState(false);
61
+
62
+ const handleRefresh = async () => {
63
+ setRefreshing(true);
64
+ await refetch();
65
+ setRefreshing(false);
66
+ };
67
+
68
+ return (
69
+ <View>
70
+ <Text>Credits: {credits}</Text>
71
+
72
+ <Button
73
+ onPress={handleRefresh}
74
+ disabled={refreshing || isLoading}
75
+ title={refreshing ? 'Refreshing...' : 'Refresh'}
76
+ />
77
+ </View>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ### With Auto-Refresh
83
+
84
+ ```typescript
85
+ function AutoRefreshCredits() {
86
+ const { credits, refetch } = useCredits();
87
+
88
+ // Refresh every 30 seconds
89
+ useEffect(() => {
90
+ const interval = setInterval(() => {
91
+ refetch();
92
+ }, 30000);
93
+
94
+ return () => clearInterval(interval);
95
+ }, [refetch]);
96
+
97
+ // Refresh when app comes to foreground
98
+ useFocusEffect(
99
+ useCallback(() => {
100
+ refetch();
101
+ }, [refetch])
102
+ );
103
+
104
+ return <Text>Credits: {credits}</Text>;
105
+ }
106
+ ```
107
+
108
+ ### With Credit History
109
+
110
+ ```typescript
111
+ function CreditsWithHistory() {
112
+ const { credits, transactions, isLoading } = useCredits();
113
+
114
+ return (
115
+ <View>
116
+ <BalanceDisplay credits={credits} />
117
+
118
+ <Text style={styles.historyTitle}>Recent Transactions</Text>
119
+
120
+ {isLoading ? (
121
+ <ActivityIndicator />
122
+ ) : (
123
+ <FlatList
124
+ data={transactions}
125
+ keyExtractor={(item) => item.id}
126
+ renderItem={({ item }) => (
127
+ <TransactionItem
128
+ amount={item.amount}
129
+ reason={item.reason}
130
+ timestamp={item.timestamp}
131
+ />
132
+ )}
133
+ />
134
+ )}
135
+ </View>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ### With Balance Conversion
141
+
142
+ ```typescript
143
+ function CreditsWithConversion() {
144
+ const { credits, balance } = useCredits();
145
+
146
+ const creditValue = 0.01; // 1 credit = $0.01
147
+
148
+ return (
149
+ <View>
150
+ <Text style={styles.credits}>{credits} Credits</Text>
151
+ <Text style={styles.balance}>
152
+ Worth approximately ${(balance || 0).toFixed(2)}
153
+ </Text>
154
+ <Text style={styles.info}>
155
+ (1 credit = ${(creditValue).toFixed(2)})
156
+ </Text>
157
+ </View>
158
+ );
159
+ }
160
+ ```
161
+
162
+ ## Examples
163
+
164
+ ### Credits Balance Card
165
+
166
+ ```typescript
167
+ function CreditsBalanceCard() {
168
+ const { credits, balance, isLoading } = useCredits();
169
+
170
+ if (isLoading) {
171
+ return <SkeletonLoader />;
172
+ }
173
+
174
+ return (
175
+ <Card style={styles.card}>
176
+ <Card.Header>
177
+ <Card.Title>Your Balance</Card.Title>
178
+ </Card.Header>
179
+
180
+ <Card.Body>
181
+ <View style={styles.balanceContainer}>
182
+ <Text style={styles.credits}>{credits}</Text>
183
+ <Text style={styles.label}>Credits</Text>
184
+ </View>
185
+
186
+ <View style={styles.balanceContainer}>
187
+ <Text style={styles.balance}>
188
+ ${(balance || 0).toFixed(2)}
189
+ </Text>
190
+ <Text style={styles.label}>Value</Text>
191
+ </View>
192
+ </Card.Body>
193
+
194
+ <Card.Footer>
195
+ <Button
196
+ onPress={() => navigation.navigate('CreditPackages')}
197
+ title="Get More Credits"
198
+ size="sm"
199
+ />
200
+ </Card.Footer>
201
+ </Card>
202
+ );
203
+ }
204
+ ```
205
+
206
+ ### Credits Indicator
207
+
208
+ ```typescript
209
+ function CreditsIndicator() {
210
+ const { credits } = useCredits();
211
+
212
+ return (
213
+ <TouchableOpacity
214
+ onPress={() => navigation.navigate('Wallet')}
215
+ style={styles.indicator}
216
+ >
217
+ <Icon name="coins" size={20} color="#FFD700" />
218
+ <Text style={styles.credits}>{credits}</Text>
219
+ </TouchableOpacity>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ### Low Credits Warning
225
+
226
+ ```typescript
227
+ function LowCreditsWarning() {
228
+ const { credits } = useCredits();
229
+ const lowCreditThreshold = 10;
230
+
231
+ useEffect(() => {
232
+ if (credits <= lowCreditThreshold && credits > 0) {
233
+ notifications.show({
234
+ title: 'Low Credits',
235
+ body: `You have ${credits} credits remaining. Purchase more to continue using features.`,
236
+ });
237
+ }
238
+ }, [credits]);
239
+
240
+ if (credits <= lowCreditThreshold) {
241
+ return (
242
+ <Alert
243
+ severity={credits === 0 ? 'error' : 'warning'}
244
+ message={
245
+ credits === 0
246
+ ? 'No credits remaining. Purchase more to continue.'
247
+ : `Low credits: ${credits} remaining`
248
+ }
249
+ action={
250
+ <Button onPress={() => navigation.navigate('CreditPackages')}>
251
+ Get More
252
+ </Button>
253
+ }
254
+ />
255
+ );
256
+ }
257
+
258
+ return null;
259
+ }
260
+ ```
261
+
262
+ ### Credits Usage Chart
263
+
264
+ ```typescript
265
+ function CreditsUsageChart() {
266
+ const { transactions } = useCredits();
267
+
268
+ // Group transactions by date
269
+ const usageByDate = useMemo(() => {
270
+ const grouped = transactions.reduce((acc, tx) => {
271
+ const date = new Date(tx.timestamp).toDateString();
272
+ acc[date] = (acc[date] || 0) + Math.abs(tx.amount);
273
+ return acc;
274
+ }, {});
275
+
276
+ return Object.entries(grouped)
277
+ .map(([date, amount]) => ({ date, amount }))
278
+ .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
279
+ .slice(-7); // Last 7 days
280
+ }, [transactions]);
281
+
282
+ return (
283
+ <View>
284
+ <Text>Credits Usage (Last 7 Days)</Text>
285
+
286
+ <LineChart
287
+ data={usageByDate}
288
+ xKey="date"
289
+ yKey="amount"
290
+ height={200}
291
+ />
292
+ </View>
293
+ );
294
+ }
295
+ ```
296
+
297
+ ### Real-Time Balance Updates
298
+
299
+ ```typescript
300
+ function RealtimeCreditsBalance() {
301
+ const { credits, refetch } = useCredits();
302
+
303
+ // Listen to credit updates
304
+ useEffect(() => {
305
+ const unsubscribe = creditsListener.on((newBalance) => {
306
+ console.log('Credits updated:', newBalance);
307
+ refetch();
308
+ });
309
+
310
+ return () => unsubscribe?.();
311
+ }, []);
312
+
313
+ return (
314
+ <View>
315
+ <Text>Credits: {credits}</Text>
316
+ </View>
317
+ );
318
+ }
319
+ ```
320
+
321
+ ## Best Practices
322
+
323
+ 1. **Handle loading** - Show skeleton or spinner
324
+ 2. **Display balance** - Show both credits and value
325
+ 3. **Provide purchase option** - Link to credit packages
326
+ 4. **Show warnings** - Alert when credits are low
327
+ 5. **Track usage** - Display transaction history
328
+ 6. **Auto-refresh** - Keep balance up to date
329
+ 7. **Handle errors** - Show user-friendly messages
330
+
331
+ ## Related Hooks
332
+
333
+ - **useCreditsGate** - For gating features with credits
334
+ - **useDeductCredit** - For deducting credits
335
+ - **useTransactionHistory** - For transaction list
336
+ - **useWallet** - Wallet domain hook
337
+
338
+ ## See Also
339
+
340
+ - [CreditsGate](./useCreditsGate.md)
341
+ - [DeductCredit](./useDeductCredit.md)
342
+ - [Wallet Domain](../../../domains/wallet/README.md)
@@ -0,0 +1,346 @@
1
+ # useCreditsGate Hook
2
+
3
+ Hook for gating features behind credit requirements.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useCreditsGate } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
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
+ ```
25
+
26
+ ## Parameters
27
+
28
+ | Parameter | Type | Required | Description |
29
+ |-----------|------|----------|-------------|
30
+ | `config.creditCost` | `number` | Yes | Number of credits required |
31
+ | `config.featureId` | `string` | Yes | Unique identifier for the feature |
32
+
33
+ ## Returns
34
+
35
+ | Property | Type | Description |
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 |
42
+
43
+ ## Basic Usage
44
+
45
+ ```typescript
46
+ function CreditFeature() {
47
+ const { hasCredits, credits, consumeCredit } = useCreditsGate({
48
+ creditCost: 5,
49
+ featureId: 'export_data',
50
+ });
51
+
52
+ const handleExport = async () => {
53
+ if (!hasCredits) {
54
+ Alert.alert('Insufficient Credits', `You need 5 credits, have ${credits}`);
55
+ return;
56
+ }
57
+
58
+ const result = await consumeCredit();
59
+
60
+ if (result.success) {
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
+ };
67
+
68
+ return (
69
+ <Button
70
+ onPress={handleExport}
71
+ title={`Export Data (5 credits)`}
72
+ disabled={!hasCredits}
73
+ />
74
+ );
75
+ }
76
+ ```
77
+
78
+ ## Advanced Usage
79
+
80
+ ### With Purchase Prompt
81
+
82
+ ```typescript
83
+ function AdvancedCreditFeature() {
84
+ const { hasCredits, credits, consumeCredit, showPurchasePrompt } =
85
+ useCreditsGate({
86
+ creditCost: 10,
87
+ featureId: 'ai_analysis',
88
+ });
89
+
90
+ const handleAnalyze = async () => {
91
+ if (!hasCredits) {
92
+ showPurchasePrompt();
93
+ return;
94
+ }
95
+
96
+ const result = await consumeCredit();
97
+
98
+ if (result.success) {
99
+ await performAIAnalysis();
100
+ }
101
+ };
102
+
103
+ return (
104
+ <View>
105
+ <Text>Credits: {credits}</Text>
106
+ <Text>Cost: 10 credits</Text>
107
+
108
+ <Button
109
+ onPress={handleAnalyze}
110
+ title={hasCredits ? 'Analyze (10 credits)' : 'Get More Credits'}
111
+ disabled={!hasCredits && credits < 10}
112
+ />
113
+ </View>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### With Transaction Tracking
119
+
120
+ ```typescript
121
+ function TrackedCreditFeature() {
122
+ const { consumeCredit, hasCredits } = useCreditsGate({
123
+ creditCost: 3,
124
+ featureId: 'filter_apply',
125
+ });
126
+
127
+ const handleApplyFilter = async () => {
128
+ const result = await consumeCredit();
129
+
130
+ if (result.success) {
131
+ // Track usage
132
+ analytics.track('credit_consumed', {
133
+ feature_id: 'filter_apply',
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)