@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,438 @@
1
+ # useSubscriptionDetails Hook
2
+
3
+ Hook for accessing detailed subscription information including package details.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useSubscriptionDetails } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
14
+ function useSubscriptionDetails(): {
15
+ subscription: SubscriptionStatus | null;
16
+ package: Package | null;
17
+ period: PackagePeriod | null;
18
+ price: number | null;
19
+ pricePerMonth: number | null;
20
+ features: string[] | null;
21
+ isLoading: boolean;
22
+ error: Error | null;
23
+ refetch: () => Promise<void>;
24
+ }
25
+ ```
26
+
27
+ ## Returns
28
+
29
+ | Property | Type | Description |
30
+ |----------|------|-------------|
31
+ | `subscription` | `SubscriptionStatus \| null` | Subscription status object |
32
+ | `package` | `Package \| null` | RevenueCat package object |
33
+ | `period` | `'monthly' \| 'annual' \| 'lifetime' \| null` | Subscription period |
34
+ | `price` | `number \| null` | Current price |
35
+ | `pricePerMonth` | `number \| null` | Monthly equivalent price |
36
+ | `features` | `string[] \| null` | List of features |
37
+ | `isLoading` | `boolean` | Loading state |
38
+ | `error` | `Error \| null` | Error if any |
39
+
40
+ ## Basic Usage
41
+
42
+ ```typescript
43
+ function SubscriptionDetails() {
44
+ const {
45
+ subscription,
46
+ package,
47
+ period,
48
+ price,
49
+ features,
50
+ isLoading,
51
+ } = useSubscriptionDetails();
52
+
53
+ if (isLoading) return <ActivityIndicator />;
54
+
55
+ return (
56
+ <View>
57
+ <Text>Plan: {package?.product.title}</Text>
58
+ <Text>Price: {price} / {period}</Text>
59
+ <Text>Features:</Text>
60
+ {features?.map((feature, index) => (
61
+ <Text key={index}>• {feature}</Text>
62
+ ))}
63
+ </View>
64
+ );
65
+ }
66
+ ```
67
+
68
+ ## Advanced Usage
69
+
70
+ ### Detailed Subscription Card
71
+
72
+ ```typescript
73
+ function DetailedSubscriptionCard() {
74
+ const {
75
+ subscription,
76
+ package,
77
+ period,
78
+ price,
79
+ pricePerMonth,
80
+ features,
81
+ } = useSubscriptionDetails();
82
+
83
+ if (!subscription || !package) return null;
84
+
85
+ return (
86
+ <Card>
87
+ <Card.Title>
88
+ {package.product.title}
89
+ </Card.Title>
90
+
91
+ <Card.Body>
92
+ <DetailRow
93
+ label="Status"
94
+ value={subscription.isActive ? 'Active' : 'Inactive'}
95
+ />
96
+
97
+ <DetailRow
98
+ label="Price"
99
+ value={`${price} ${package?.product.currencyCode}`}
100
+ />
101
+
102
+ {period !== 'lifetime' && pricePerMonth && (
103
+ <DetailRow
104
+ label="Per Month"
105
+ value={`${pricePerMonth.toFixed(2)} ${package?.product.currencyCode}`}
106
+ />
107
+ )}
108
+
109
+ <Divider />
110
+
111
+ <Text>Features:</Text>
112
+ {features?.map((feature, index) => (
113
+ <FeatureItem key={index} feature={feature} />
114
+ ))}
115
+ </Card.Body>
116
+ </Card>
117
+ );
118
+ }
119
+ ```
120
+
121
+ ### Price Comparison
122
+
123
+ ```typescript
124
+ function PriceComparison() {
125
+ const { price, period, pricePerMonth } = useSubscriptionDetails();
126
+
127
+ if (!price || !period) return null;
128
+
129
+ const monthlyPrices = {
130
+ monthly: price,
131
+ annual: pricePerMonth ? pricePerMonth * 12 : price,
132
+ };
133
+
134
+ return (
135
+ <View>
136
+ <Text>Total: ${price}</Text>
137
+ <Text>Per Month: ${pricePerMonth?.toFixed(2) || 'N/A'}</Text>
138
+
139
+ {period === 'annual' && (
140
+ <SavingsBadge
141
+ original={monthlyPrices.monthly * 12}
142
+ current={price}
143
+ />
144
+ )}
145
+ </View>
146
+ );
147
+ }
148
+ ```
149
+
150
+ ### With Upgrade Suggestion
151
+
152
+ ```typescript
153
+ function UpgradeSuggestion() {
154
+ const {
155
+ subscription,
156
+ package: currentPackage,
157
+ price,
158
+ } = useSubscriptionDetails();
159
+
160
+ const { packages: allPackages } = useSubscriptionPackages();
161
+
162
+ // Find upgrade options
163
+ const upgradeOptions = allPackages.filter(
164
+ (pkg) =>
165
+ pkg.product.price > (currentPackage?.product.price || 0)
166
+ );
167
+
168
+ if (!subscription?.isPremium) {
169
+ return (
170
+ <View>
171
+ <Text>Upgrade to Premium for full access</Text>
172
+ <Button
173
+ onPress={() => navigation.navigate('Paywall')}
174
+ title="View Plans"
175
+ />
176
+ </View>
177
+ );
178
+ }
179
+
180
+ return (
181
+ <View>
182
+ <Text>Current Plan: {currentPackage?.product.title}</Text>
183
+ <Button
184
+ onPress={() => navigation.navigate('ManageSubscription')}
185
+ title="Manage Subscription"
186
+ />
187
+ </View>
188
+ );
189
+ }
190
+ ```
191
+
192
+ ### With Feature List
193
+
194
+ ```typescript
195
+ function FeatureList() {
196
+ const { features } = useSubscriptionDetails();
197
+
198
+ if (!features) return null;
199
+
200
+ const featureIcons = {
201
+ 'unlimited_access': '∞',
202
+ 'ai_tools': '🤖',
203
+ 'ad_free': '🛡️',
204
+ 'priority_support': '💬',
205
+ 'advanced_filters': '🔍',
206
+ };
207
+
208
+ return (
209
+ <View>
210
+ <Text style={styles.title}>Your Premium Features:</Text>
211
+
212
+ {features.map((feature) => (
213
+ <FeatureItem
214
+ key={feature}
215
+ icon={featureIcons[feature] || '✓'}
216
+ text={formatFeatureName(feature)}
217
+ />
218
+ ))}
219
+ </View>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ## Examples
225
+
226
+ ### Subscription Info Screen
227
+
228
+ ```typescript
229
+ function SubscriptionInfoScreen() {
230
+ const {
231
+ subscription,
232
+ package,
233
+ period,
234
+ price,
235
+ features,
236
+ isLoading,
237
+ refetch,
238
+ } = useSubscriptionDetails();
239
+
240
+ useFocusEffect(
241
+ useCallback(() => {
242
+ refetch();
243
+ }, [refetch])
244
+ );
245
+
246
+ if (isLoading) {
247
+ return <LoadingScreen />;
248
+ }
249
+
250
+ if (!subscription || !package) {
251
+ return (
252
+ <View>
253
+ <Text>No subscription found</Text>
254
+ <Button
255
+ onPress={() => navigation.navigate('Paywall')}
256
+ title="Subscribe"
257
+ />
258
+ </View>
259
+ );
260
+ }
261
+
262
+ return (
263
+ <ScrollView>
264
+ {/* Status Banner */}
265
+ <StatusBanner
266
+ isActive={subscription.isActive}
267
+ type={subscription.type}
268
+ />
269
+
270
+ {/* Package Details */}
271
+ <Card style={styles.section}>
272
+ <Card.Title>Current Plan</Card.Title>
273
+ <Card.Body>
274
+ <Text style={styles.planName}>{package.product.title}</Text>
275
+ <Text style={styles.price}>
276
+ {price} {package.product.currencyCode}
277
+ </Text>
278
+
279
+ {period !== 'lifetime' && (
280
+ <Text style={styles.period}>
281
+ per {period.slice(0, -2)} // "month" -> "month"
282
+ </Text>
283
+ )}
284
+
285
+ {subscription.expirationDate && (
286
+ <Text style={styles.expires}>
287
+ Renews on {new Date(subscription.expirationDate).toLocaleDateString()}
288
+ </Text>
289
+ )}
290
+ </Card.Body>
291
+ </Card>
292
+
293
+ {/* Features */}
294
+ {features && features.length > 0 && (
295
+ <Card style={styles.section}>
296
+ <Card.Title>Features</Card.Title>
297
+ <Card.Body>
298
+ {features.map((feature, index) => (
299
+ <FeatureItem
300
+ key={index}
301
+ icon="✓"
302
+ text={formatFeatureName(feature)}
303
+ />
304
+ ))}
305
+ </Card.Body>
306
+ </Card>
307
+ )}
308
+
309
+ {/* Actions */}
310
+ <Card style={styles.section}>
311
+ <Card.Body>
312
+ <Button
313
+ onPress={() => navigation.navigate('ManageSubscription')}
314
+ title="Manage Subscription"
315
+ />
316
+
317
+ <Button
318
+ onPress={refetch}
319
+ title="Refresh"
320
+ variant="outline"
321
+ />
322
+ </Card.Body>
323
+ </Card>
324
+ </ScrollView>
325
+ );
326
+ }
327
+ ```
328
+
329
+ ### Billing History
330
+
331
+ ```typescript
332
+ function BillingHistory() {
333
+ const { subscription, package, price } = useSubscriptionDetails();
334
+
335
+ if (!subscription || !package) return null;
336
+
337
+ const transactions = [
338
+ {
339
+ date: subscription.expirationDate,
340
+ description: `Renewal - ${package.product.title}`,
341
+ amount: price,
342
+ status: 'completed',
343
+ },
344
+ ];
345
+
346
+ return (
347
+ <View>
348
+ <Text style={styles.title}>Billing History</Text>
349
+
350
+ {transactions.map((tx, index) => (
351
+ <TransactionItem
352
+ key={index}
353
+ date={tx.date}
354
+ description={tx.description}
355
+ amount={tx.amount}
356
+ status={tx.status}
357
+ />
358
+ ))}
359
+ </View>
360
+ );
361
+ }
362
+ ```
363
+
364
+ ### Cancellation Flow
365
+
366
+ ```typescript
367
+ function CancellationFlow() {
368
+ const { subscription, package, willRenew } = useSubscriptionDetails();
369
+
370
+ const handleCancel = async () => {
371
+ if (!willRenew) {
372
+ Alert.alert('Info', 'Your subscription will not renew');
373
+ return;
374
+ }
375
+
376
+ Alert.alert(
377
+ 'Cancel Subscription',
378
+ 'Your subscription will expire at the end of the current period',
379
+ [
380
+ { text: 'Keep', style: 'cancel' },
381
+ {
382
+ text: 'Cancel',
383
+ style: 'destructive',
384
+ onPress: async () => {
385
+ // Cancel with RevenueCat
386
+ try {
387
+ // Link to platform-specific subscription management
388
+ if (Platform.OS === 'ios') {
389
+ Linking.openURL('https://apps.apple.com/account/subscriptions');
390
+ } else {
391
+ Linking.openURL('https://play.google.com/store/account/subscriptions');
392
+ }
393
+ } catch (error) {
394
+ Alert.alert('Error', 'Could not open subscription settings');
395
+ }
396
+ },
397
+ },
398
+ ]
399
+ );
400
+ };
401
+
402
+ return (
403
+ <View>
404
+ <Text>Plan: {package?.product.title}</Text>
405
+ <Text>Auto-renew: {willRenew ? 'Yes' : 'No'}</Text>
406
+
407
+ <Button
408
+ onPress={handleCancel}
409
+ title="Cancel Subscription"
410
+ color="#F44336"
411
+ />
412
+ </View>
413
+ );
414
+ }
415
+ ```
416
+
417
+ ## Best Practices
418
+
419
+ 1. **Handle null values** - Check if subscription and package exist
420
+ 2. **Format prices** - Use currency formatters
421
+ 3. **Display all info** - Show status, price, features
422
+ 4. **Provide actions** - Add manage, cancel, upgrade buttons
423
+ 5. **Refresh on focus** - Update when screen gains focus
424
+ 6. **Handle errors** - Show user-friendly error messages
425
+ 7. **Test different plans** - Monthly, annual, lifetime
426
+
427
+ ## Related Hooks
428
+
429
+ - **useSubscription** - Basic subscription status
430
+ - **useSubscriptionStatus** - Detailed status
431
+ - **usePremium** - Simple premium check
432
+ - **useSubscriptionPackages** - Available packages
433
+
434
+ ## See Also
435
+
436
+ - [useSubscriptionStatus](./useSubscriptionStatus.md)
437
+ - [Subscription Details Screen](../screens/README.md)
438
+ - [Package Utilities](../../../utils/README.md#package-utilities)
@@ -0,0 +1,168 @@
1
+ # useSubscriptionGate Hook
2
+
3
+ Subscription-only feature gating with simple, focused API.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useSubscriptionGate } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
14
+ function useSubscriptionGate(params: {
15
+ hasSubscription: boolean;
16
+ onSubscriptionRequired: () => void;
17
+ }): {
18
+ hasSubscription: boolean;
19
+ requireSubscription: (action: () => void | Promise<void>) => boolean;
20
+ }
21
+ ```
22
+
23
+ ## Parameters
24
+
25
+ | Parameter | Type | Default | Description |
26
+ |-----------|------|---------|-------------|
27
+ | `hasSubscription` | `boolean` | **Required** | Whether user has active subscription |
28
+ | `onSubscriptionRequired` | `() => void` | **Required** | Callback when subscription is required |
29
+
30
+ ## Returns
31
+
32
+ | Property | Type | Description |
33
+ |----------|------|-------------|
34
+ | `hasSubscription` | `boolean` | User has active subscription |
35
+ | `requireSubscription` | `(action) => boolean` | Gate action behind subscription |
36
+
37
+ ## Basic Usage
38
+
39
+ ```typescript
40
+ function PremiumFeature() {
41
+ const { isPremium } = usePremium();
42
+
43
+ const { requireSubscription, hasSubscription } = useSubscriptionGate({
44
+ hasSubscription: isPremium,
45
+ onSubscriptionRequired: () => {
46
+ showPaywall();
47
+ },
48
+ });
49
+
50
+ const handlePremiumAction = () => {
51
+ const allowed = requireSubscription(() => {
52
+ executePremiumFeature();
53
+ });
54
+
55
+ if (!allowed) {
56
+ console.log('User was shown paywall');
57
+ }
58
+ };
59
+
60
+ return (
61
+ <Button
62
+ onPress={handlePremiumAction}
63
+ title={hasSubscription ? 'Access' : 'Upgrade to Premium'}
64
+ />
65
+ );
66
+ }
67
+ ```
68
+
69
+ ## Advanced Usage
70
+
71
+ ### With Conditional Rendering
72
+
73
+ ```typescript
74
+ function ConditionalPremiumFeature() {
75
+ const { isPremium } = usePremium();
76
+
77
+ const { requireSubscription, hasSubscription } = useSubscriptionGate({
78
+ hasSubscription: isPremium,
79
+ onSubscriptionRequired: () => {
80
+ navigation.navigate('Paywall');
81
+ },
82
+ });
83
+
84
+ if (!hasSubscription) {
85
+ return (
86
+ <View>
87
+ <PremiumLockedIcon />
88
+ <Text>This feature requires Premium</Text>
89
+ <Button
90
+ onPress={() => requireSubscription(() => {})}
91
+ title="Upgrade Now"
92
+ />
93
+ </View>
94
+ );
95
+ }
96
+
97
+ return (
98
+ <View>
99
+ <Text>Premium Content</Text>
100
+ <PremiumFeatureComponent />
101
+ </View>
102
+ );
103
+ }
104
+ ```
105
+
106
+ ### With Async Actions
107
+
108
+ ```typescript
109
+ function AsyncPremiumFeature() {
110
+ const { isPremium } = usePremium();
111
+
112
+ const { requireSubscription } = useSubscriptionGate({
113
+ hasSubscription: isPremium,
114
+ onSubscriptionRequired: () => {
115
+ showPaywall({
116
+ feature: 'Advanced Analytics',
117
+ onSubscribe: () => {
118
+ // Action will execute after subscription
119
+ },
120
+ });
121
+ },
122
+ });
123
+
124
+ const handleExportData = () => {
125
+ requireSubscription(async () => {
126
+ await exportLargeDataset();
127
+ Alert.alert('Success', 'Data exported successfully');
128
+ });
129
+ };
130
+
131
+ return <Button onPress={handleExportData} title="Export Data" />;
132
+ }
133
+ ```
134
+
135
+ ## Best Practices
136
+
137
+ 1. **Simple checks** - Keep gating logic straightforward
138
+ 2. **Clear messaging** - Tell users what they're missing
139
+ 3. **Smooth upgrades** - Make subscription path obvious
140
+ 4. **Track access** - Monitor gate triggers for optimization
141
+ 5. **Value first** - Show benefits before locking
142
+ 6. **Graceful fallbacks** - Show preview or limited version
143
+ 7. **Context-aware** - Customize paywall based on feature
144
+
145
+ ## Development Logging
146
+
147
+ In development mode, the hook logs useful information:
148
+
149
+ ```typescript
150
+ // User not premium
151
+ [useSubscriptionGate] No subscription, showing paywall
152
+
153
+ // User has subscription
154
+ [useSubscriptionGate] Has subscription, proceeding
155
+ ```
156
+
157
+ ## Related Hooks
158
+
159
+ - **useAuthGate** - Add authentication requirements
160
+ - **useCreditsGate** - Credits-based gating
161
+ - **useFeatureGate** - Combined auth + subscription + credits
162
+ - **usePremiumGate** - Premium feature gating with auth option
163
+
164
+ ## See Also
165
+
166
+ - [Subscription Gating](../../../docs/SUBSCRIPTION_GATING.md)
167
+ - [Paywall Triggers](../screens/README.md#triggers)
168
+ - [usePremium](./usePremium.md)