@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,157 @@
1
+ # useFeatureGate Hook
2
+
3
+ Unified feature gate combining authentication, subscription, and credits checks.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useFeatureGate } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
14
+ function useFeatureGate(params: {
15
+ isAuthenticated: boolean;
16
+ onShowAuthModal: (pendingCallback: () => void | Promise<void>) => void;
17
+ hasSubscription?: boolean;
18
+ hasCredits: boolean;
19
+ creditBalance: number;
20
+ requiredCredits?: number;
21
+ onShowPaywall: (requiredCredits?: number) => void;
22
+ }): {
23
+ requireFeature: (action: () => void | Promise<void>) => void;
24
+ isAuthenticated: boolean;
25
+ hasSubscription: boolean;
26
+ hasCredits: boolean;
27
+ creditBalance: number;
28
+ canAccess: boolean;
29
+ }
30
+ ```
31
+
32
+ ## Parameters
33
+
34
+ | Parameter | Type | Default | Description |
35
+ |-----------|------|---------|-------------|
36
+ | `isAuthenticated` | `boolean` | **Required** | Whether user is authenticated |
37
+ | `onShowAuthModal` | `(callback) => void` | **Required** | Show auth modal with pending action |
38
+ | `hasSubscription` | `boolean` | `false` | Whether user has subscription |
39
+ | `hasCredits` | `boolean` | **Required** | Whether user has enough credits |
40
+ | `creditBalance` | `number` | **Required** | Current credit balance |
41
+ | `requiredCredits` | `number` | `undefined` | Credits required for action |
42
+ | `onShowPaywall` | `(credits?) => void` | **Required** | Show paywall/upgrade prompt |
43
+
44
+ ## Returns
45
+
46
+ | Property | Type | Description |
47
+ |----------|------|-------------|
48
+ | `requireFeature` | `(action) => void` | Gate action behind auth, subscription, and credits |
49
+ | `isAuthenticated` | `boolean` | User is authenticated |
50
+ | `hasSubscription` | `boolean` | User has subscription |
51
+ | `hasCredits` | `boolean` | User has enough credits |
52
+ | `creditBalance` | `number` | Current credit balance |
53
+ | `canAccess` | `boolean` | User can access the feature |
54
+
55
+ ## Basic Usage
56
+
57
+ ```typescript
58
+ function PremiumFeature() {
59
+ const { user } = useAuth();
60
+ const { isPremium } = usePremium();
61
+ const { credits } = useCredits();
62
+
63
+ const { requireFeature, canAccess } = useFeatureGate({
64
+ isAuthenticated: !!user,
65
+ onShowAuthModal: (pendingAction) => {
66
+ navigation.navigate('Auth', { pendingAction });
67
+ },
68
+ hasSubscription: isPremium,
69
+ hasCredits: credits >= 5,
70
+ creditBalance: credits,
71
+ requiredCredits: 5,
72
+ onShowPaywall: (required) => {
73
+ if (isPremium) {
74
+ showInsufficientCreditsModal(required);
75
+ } else {
76
+ showPaywall();
77
+ }
78
+ },
79
+ });
80
+
81
+ const handleGenerate = () => {
82
+ requireFeature(async () => {
83
+ await generateContent();
84
+ });
85
+ };
86
+
87
+ return (
88
+ <Button
89
+ onPress={handleGenerate}
90
+ title={!canAccess ? 'Unlock Feature' : 'Generate'}
91
+ />
92
+ );
93
+ }
94
+ ```
95
+
96
+ ## Advanced Usage
97
+
98
+ ### Progressive Access Control
99
+
100
+ ```typescript
101
+ function ProgressiveFeatureGate() {
102
+ const { user } = useAuth();
103
+ const { isPremium } = usePremium();
104
+ const { credits } = useCredits();
105
+
106
+ const { requireFeature, canAccess, isAuthenticated, hasSubscription, hasCredits } =
107
+ useFeatureGate({
108
+ isAuthenticated: !!user,
109
+ onShowAuthModal: (pending) => showAuthModal({ pendingAction: pending }),
110
+ hasSubscription: isPremium,
111
+ hasCredits: credits >= 3,
112
+ creditBalance: credits,
113
+ requiredCredits: 3,
114
+ onShowPaywall: (required) => showUpgradeModal({ requiredCredits: required }),
115
+ });
116
+
117
+ const getAccessMessage = () => {
118
+ if (!isAuthenticated) return 'Sign in to access';
119
+ if (!hasSubscription && !hasCredits) return 'Upgrade or purchase credits';
120
+ if (!hasCredits) return 'Insufficient credits';
121
+ return 'Full access';
122
+ };
123
+
124
+ return (
125
+ <View>
126
+ <Text>{getAccessMessage()}</Text>
127
+ <Button
128
+ onPress={() => requireFeature(() => executeAction())}
129
+ disabled={!canAccess}
130
+ title={canAccess ? 'Execute Action' : 'Unlock'}
131
+ />
132
+ </View>
133
+ );
134
+ }
135
+ ```
136
+
137
+ ## Best Practices
138
+
139
+ 1. **Check in order** - Auth → Subscription → Credits
140
+ 2. **Clear messaging** - Tell users exactly what's required
141
+ 3. **Preserve intent** - Queue pending actions through auth/purchase
142
+ 4. **Smart prompts** - Show relevant upgrade option
143
+ 5. **Track events** - Monitor gate triggers for optimization
144
+ 6. **Respect premium** - Bypass credits for subscribers
145
+ 7. **Show alternatives** - Offer both subscription and credits options
146
+
147
+ ## Related Hooks
148
+
149
+ - **useAuthGate** - Auth gating only
150
+ - **useSubscriptionGate** - Subscription gating only
151
+ - **useCreditsGate** - Credits gating only
152
+ - **usePremiumGate** - Premium feature gating
153
+
154
+ ## See Also
155
+
156
+ - [Feature Gating](../../../docs/FEATURE_GATING.md)
157
+ - [Access Control Patterns](../../../docs/ACCESS_PATTERNS.md)
@@ -0,0 +1,458 @@
1
+ # useInitializeCredits Hook
2
+
3
+ TanStack Query mutation hook for initializing credits after purchase.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useInitializeCredits } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
14
+ function useInitializeCredits(params: {
15
+ userId: string | undefined;
16
+ }): {
17
+ initializeCredits: (options?: {
18
+ purchaseId?: string;
19
+ productId?: string;
20
+ }) => Promise<boolean>;
21
+ isInitializing: boolean;
22
+ }
23
+ ```
24
+
25
+ ## Parameters
26
+
27
+ | Parameter | Type | Default | Description |
28
+ |-----------|------|---------|-------------|
29
+ | `userId` | `string \| undefined` | **Required** | User ID for credit initialization |
30
+
31
+ ## Returns
32
+
33
+ | Property | Type | Description |
34
+ |----------|------|-------------|
35
+ | `initializeCredits` | `(options?) => Promise<boolean>` | Initialize credits function |
36
+ | `isInitializing` | `boolean` | Mutation is in progress |
37
+
38
+ ## Options
39
+
40
+ | Property | Type | Default | Description |
41
+ |-----------|------|---------|-------------|
42
+ | `purchaseId` | `string` | `undefined` | Optional purchase/renewal ID |
43
+ | `productId` | `string` | `undefined` | Optional product ID |
44
+
45
+ ## Basic Usage
46
+
47
+ ```typescript
48
+ function CreditsInitializer() {
49
+ const { user } = useAuth();
50
+
51
+ const { initializeCredits, isInitializing } = useInitializeCredits({
52
+ userId: user?.uid,
53
+ });
54
+
55
+ const handleInitialize = async () => {
56
+ const success = await initializeCredits();
57
+
58
+ if (success) {
59
+ Alert.alert('Success', 'Credits initialized successfully');
60
+ } else {
61
+ Alert.alert('Error', 'Failed to initialize credits');
62
+ }
63
+ };
64
+
65
+ return (
66
+ <Button
67
+ onPress={handleInitialize}
68
+ disabled={isInitializing}
69
+ title={isInitializing ? 'Initializing...' : 'Initialize Credits'}
70
+ />
71
+ );
72
+ }
73
+ ```
74
+
75
+ ## Advanced Usage
76
+
77
+ ### With Purchase ID
78
+
79
+ ```typescript
80
+ function PurchaseCompletion() {
81
+ const { user } = useAuth();
82
+
83
+ const { initializeCredits, isInitializing } = useInitializeCredits({
84
+ userId: user?.uid,
85
+ });
86
+
87
+ const handlePurchaseComplete = async (transaction: PurchaseTransaction) => {
88
+ const success = await initializeCredits({
89
+ purchaseId: transaction.transactionId,
90
+ productId: transaction.productId,
91
+ });
92
+
93
+ if (success) {
94
+ analytics.track('credits_initialized', {
95
+ userId: user?.uid,
96
+ purchaseId: transaction.transactionId,
97
+ });
98
+ }
99
+
100
+ return success;
101
+ };
102
+
103
+ return <PurchaseFlow onComplete={handlePurchaseComplete} />;
104
+ }
105
+ ```
106
+
107
+ ### With Auto-Initialize for Premium
108
+
109
+ ```typescript
110
+ function PremiumUserSetup() {
111
+ const { user } = useAuth();
112
+ const { isPremium } = usePremium();
113
+ const { credits } = useCredits();
114
+
115
+ const { initializeCredits, isInitializing } = useInitializeCredits({
116
+ userId: user?.uid,
117
+ });
118
+
119
+ useEffect(() => {
120
+ // Auto-initialize credits for premium users who don't have them yet
121
+ if (isPremium && !credits && !isInitializing) {
122
+ const init = async () => {
123
+ const success = await initializeCredits();
124
+ if (success) {
125
+ console.log('Credits initialized for premium user');
126
+ }
127
+ };
128
+ init();
129
+ }
130
+ }, [isPremium, credits]);
131
+
132
+ return <YourAppContent />;
133
+ }
134
+ ```
135
+
136
+ ### With Product-Specific Allocations
137
+
138
+ ```typescript
139
+ function ProductBasedAllocation() {
140
+ const { user } = useAuth();
141
+
142
+ const { initializeCredits } = useInitializeCredits({
143
+ userId: user?.uid,
144
+ });
145
+
146
+ const handlePurchase = async (productId: string) => {
147
+ // Different products provide different credit amounts
148
+ const productConfig = {
149
+ 'premium_monthly': { credits: 100 },
150
+ 'premium_yearly': { credits: 1200 },
151
+ 'credits_small': { credits: 50 },
152
+ 'credits_large': { credits: 500 },
153
+ };
154
+
155
+ const config = productConfig[productId];
156
+ if (!config) {
157
+ Alert.alert('Error', 'Unknown product');
158
+ return;
159
+ }
160
+
161
+ const success = await initializeCredits({
162
+ purchaseId: `purchase_${Date.now()}`,
163
+ productId,
164
+ });
165
+
166
+ if (success) {
167
+ Alert.alert('Success', `You received ${config.credits} credits!`);
168
+ }
169
+ };
170
+
171
+ return <PackageList onSelectPackage={handlePurchase} />;
172
+ }
173
+ ```
174
+
175
+ ### With Error Handling
176
+
177
+ ```typescript
178
+ function RobustInitialization() {
179
+ const { user } = useAuth();
180
+
181
+ const {
182
+ initializeCredits,
183
+ isInitializing,
184
+ } = useInitializeCredits({
185
+ userId: user?.uid,
186
+ });
187
+
188
+ const handleInitialize = async () => {
189
+ if (!user?.uid) {
190
+ Alert.alert('Error', 'User not authenticated');
191
+ return;
192
+ }
193
+
194
+ try {
195
+ const success = await initializeCredits({
196
+ purchaseId: `init_${Date.now()}`,
197
+ productId: 'premium_subscription',
198
+ });
199
+
200
+ if (success) {
201
+ Alert.alert('Success', 'Credits initialized');
202
+ } else {
203
+ Alert.alert('Failed', 'Could not initialize credits');
204
+ }
205
+ } catch (error) {
206
+ console.error('Initialization error:', error);
207
+ Alert.alert(
208
+ 'Error',
209
+ 'Failed to initialize credits. Please try again.'
210
+ );
211
+ }
212
+ };
213
+
214
+ return (
215
+ <Button
216
+ onPress={handleInitialize}
217
+ disabled={isInitializing}
218
+ title="Initialize Credits"
219
+ />
220
+ );
221
+ }
222
+ ```
223
+
224
+ ### With Retry Logic
225
+
226
+ ```typescript
227
+ function InitializationWithRetry() {
228
+ const { user } = useAuth();
229
+
230
+ const { initializeCredits, isInitializing } = useInitializeCredits({
231
+ userId: user?.uid,
232
+ });
233
+
234
+ const handleInitializeWithRetry = async (retries = 3) => {
235
+ for (let i = 0; i < retries; i++) {
236
+ const success = await initializeCredits();
237
+
238
+ if (success) {
239
+ Alert.alert('Success', 'Credits initialized');
240
+ return true;
241
+ }
242
+
243
+ // Wait before retry
244
+ if (i < retries - 1) {
245
+ await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
246
+ }
247
+ }
248
+
249
+ Alert.alert('Failed', 'Could not initialize credits after retries');
250
+ return false;
251
+ };
252
+
253
+ return (
254
+ <Button
255
+ onPress={() => handleInitializeWithRetry()}
256
+ disabled={isInitializing}
257
+ title="Initialize (with retry)"
258
+ />
259
+ );
260
+ }
261
+ ```
262
+
263
+ ## Examples
264
+
265
+ ### Post-Purchase Flow
266
+
267
+ ```typescript
268
+ function PostPurchaseFlow() {
269
+ const { user } = useAuth();
270
+ const navigation = useNavigation();
271
+
272
+ const { initializeCredits, isInitializing } = useInitializeCredits({
273
+ userId: user?.uid,
274
+ });
275
+
276
+ useEffect(() => {
277
+ const handlePurchase = async (purchase: any) => {
278
+ console.log('Purchase completed:', purchase);
279
+
280
+ const success = await initializeCredits({
281
+ purchaseId: purchase.transactionId,
282
+ productId: purchase.productId,
283
+ });
284
+
285
+ if (success) {
286
+ // Navigate to success screen
287
+ navigation.replace('PurchaseSuccess', {
288
+ credits: purchase.credits,
289
+ });
290
+ } else {
291
+ // Show error
292
+ Alert.alert(
293
+ 'Setup Required',
294
+ 'Could not initialize credits. Please contact support.'
295
+ );
296
+ }
297
+ };
298
+
299
+ const subscription = purchasesEmitter.on('purchase_complete', handlePurchase);
300
+
301
+ return () => subscription.remove();
302
+ }, []);
303
+
304
+ if (isInitializing) {
305
+ return (
306
+ <View>
307
+ <ActivityIndicator size="large" />
308
+ <Text>Setting up your credits...</Text>
309
+ </View>
310
+ );
311
+ }
312
+
313
+ return <YourAppContent />;
314
+ }
315
+ ```
316
+
317
+ ### Subscription Renewal Handler
318
+
319
+ ```typescript
320
+ function RenewalHandler() {
321
+ const { user } = useAuth();
322
+
323
+ const { initializeCredits } = useInitializeCredits({
324
+ userId: user?.uid,
325
+ });
326
+
327
+ useEffect(() => {
328
+ const handleRenewal = async (renewalInfo: RenewalEvent) => {
329
+ console.log('Subscription renewed:', renewalInfo);
330
+
331
+ const success = await initializeCredits({
332
+ purchaseId: renewalInfo.renewalId,
333
+ productId: renewalInfo.productId,
334
+ });
335
+
336
+ if (success) {
337
+ analytics.track('subscription_renewed', {
338
+ userId: user?.uid,
339
+ renewalId: renewalInfo.renewalId,
340
+ });
341
+
342
+ // Notify user
343
+ showNotification('Credits Added!', 'Your subscription has renewed');
344
+ }
345
+ };
346
+
347
+ const subscription = subscriptionEmitter.on('renewal', handleRenewal);
348
+
349
+ return () => subscription.remove();
350
+ }, []);
351
+
352
+ return null;
353
+ }
354
+ ```
355
+
356
+ ### Manual Admin Initialization
357
+
358
+ ```typescript
359
+ function AdminCreditInitializer() {
360
+ const { user } = useAuth();
361
+ const { isAdmin } = useAdmin();
362
+
363
+ const { initializeCredits, isInitializing } = useInitializeCredits({
364
+ userId: targetUserId, // Admin can initialize for other users
365
+ });
366
+
367
+ const handleAdminInit = async () => {
368
+ if (!isAdmin) {
369
+ Alert.alert('Access Denied', 'Admin access required');
370
+ return;
371
+ }
372
+
373
+ Alert.alert(
374
+ 'Confirm Initialization',
375
+ 'Initialize credits for this user?',
376
+ [
377
+ { text: 'Cancel', style: 'cancel' },
378
+ {
379
+ text: 'Initialize',
380
+ onPress: async () => {
381
+ const success = await initializeCredits({
382
+ purchaseId: `admin_init_${Date.now()}`,
383
+ productId: 'admin_grant',
384
+ });
385
+
386
+ Alert.alert(
387
+ success ? 'Success' : 'Failed',
388
+ success ? 'Credits initialized' : 'Could not initialize credits'
389
+ );
390
+ },
391
+ },
392
+ ]
393
+ );
394
+ };
395
+
396
+ if (!isAdmin) return null;
397
+
398
+ return (
399
+ <Button
400
+ onPress={handleAdminInit}
401
+ disabled={isInitializing}
402
+ title="Initialize Credits (Admin)"
403
+ />
404
+ );
405
+ }
406
+ ```
407
+
408
+ ## Development Logging
409
+
410
+ In development mode, the hook logs useful information:
411
+
412
+ ```typescript
413
+ // Initialization starts
414
+ [useInitializeCredits] Initializing: { userId: 'user-123', purchaseId: 'purchase-456', productId: 'premium_monthly' }
415
+
416
+ // Success
417
+ [useInitializeCredits] Success: { credits: 100, purchasedAt: '2024-01-15T10:30:00Z' }
418
+
419
+ // Error
420
+ [useInitializeCredits] Error: Some error message
421
+ ```
422
+
423
+ ## Best Practices
424
+
425
+ 1. **Validate userId** - Always check user is authenticated
426
+ 2. **Provide purchase info** - Include purchaseId and productId when possible
427
+ 3. **Handle loading** - Show loading state during initialization
428
+ 4. **Track success** - Log successful initializations
429
+ 5. **Retry on failure** - Implement retry logic for reliability
430
+ 6. **Use duplicate protection** - Repository handles duplicate purchase IDs
431
+ 7. **Test scenarios** - Test new purchase, renewal, admin init
432
+
433
+ ## Duplicate Protection
434
+
435
+ The repository prevents duplicate initialization with the same purchase ID:
436
+
437
+ ```typescript
438
+ // First call
439
+ await initializeCredits({ purchaseId: 'renewal_123', productId: 'premium' });
440
+ // Returns: success, credits: 100 ✅
441
+
442
+ // Second call with same purchaseId
443
+ await initializeCredits({ purchaseId: 'renewal_123', productId: 'premium' });
444
+ // Returns: success, credits: 100 (same as before, not added again) ✅
445
+ ```
446
+
447
+ ## Related Hooks
448
+
449
+ - **useCredits** - For accessing credits balance
450
+ - **useDeductCredit** - For deducting credits
451
+ - **usePremiumWithCredits** - For premium + credits integration
452
+ - **useDevTestCallbacks** - For testing credit initialization
453
+
454
+ ## See Also
455
+
456
+ - [Credits Repository](../../infrastructure/repositories/CreditsRepository.md)
457
+ - [Credits Entity](../../../domains/wallet/domain/entities/Credits.md)
458
+ - [Credits README](../../../domains/wallet/README.md)