@umituz/react-native-subscription 2.14.97 → 2.14.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +462 -0
  3. package/package.json +1 -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/utils/README.md +529 -0
@@ -0,0 +1,307 @@
1
+ # Domain Errors
2
+
3
+ Domain-specific error types for subscription system.
4
+
5
+ ## Purpose
6
+
7
+ Domain errors provide typed, contextual error handling for business logic failures. They make error handling explicit and type-safe.
8
+
9
+ ## Error Hierarchy
10
+
11
+ ```
12
+ Error (JavaScript)
13
+ └── SubscriptionError (Domain base)
14
+ ├── SubscriptionValidationError
15
+ ├── SubscriptionRepositoryError
16
+ ├── SubscriptionOperationError
17
+ └── InsufficientCreditsError
18
+ ```
19
+
20
+ ## Error Types
21
+
22
+ ### SubscriptionError
23
+
24
+ Base error for all subscription-related errors.
25
+
26
+ ```typescript
27
+ class SubscriptionError extends Error {
28
+ constructor(
29
+ message: string,
30
+ public readonly code: string,
31
+ public readonly context?: Record<string, any>
32
+ ) {
33
+ super(message);
34
+ this.name = 'SubscriptionError';
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### SubscriptionValidationError
40
+
41
+ Thrown when validation fails.
42
+
43
+ ```typescript
44
+ throw new SubscriptionValidationError('Invalid user ID');
45
+ ```
46
+
47
+ ### SubscriptionRepositoryError
48
+
49
+ Thrown when repository operations fail.
50
+
51
+ ```typescript
52
+ throw new SubscriptionRepositoryError('Database connection failed');
53
+ ```
54
+
55
+ ### InsufficientCreditsError
56
+
57
+ Thrown when user doesn't have enough credits.
58
+
59
+ ```typescript
60
+ throw new InsufficientCreditsError(
61
+ 'Not enough credits',
62
+ {
63
+ required: 10,
64
+ available: 5,
65
+ featureId: 'export',
66
+ }
67
+ );
68
+ ```
69
+
70
+ ## Usage
71
+
72
+ ### Throwing Errors
73
+
74
+ ```typescript
75
+ function deductCredits(amount: number) {
76
+ if (amount <= 0) {
77
+ throw new SubscriptionValidationError(
78
+ 'Amount must be positive',
79
+ 'INVALID_AMOUNT',
80
+ { amount }
81
+ );
82
+ }
83
+
84
+ if (currentBalance < amount) {
85
+ throw new InsufficientCreditsError(
86
+ 'Insufficient credits',
87
+ {
88
+ required: amount,
89
+ available: currentBalance,
90
+ }
91
+ );
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### Catching Errors
97
+
98
+ ```typescript
99
+ try {
100
+ await purchasePackage(packageToPurchase);
101
+ } catch (error) {
102
+ if (error instanceof InsufficientCreditsError) {
103
+ console.log(`Need ${error.context.required} credits`);
104
+ showPaywall();
105
+ } else if (error instanceof SubscriptionValidationError) {
106
+ console.error('Validation error:', error.message);
107
+ } else if (error instanceof SubscriptionError) {
108
+ console.error('Subscription error:', error.code, error.message);
109
+ } else {
110
+ console.error('Unexpected error:', error);
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Type Guards
116
+
117
+ ```typescript
118
+ function isSubscriptionError(error: unknown): error is SubscriptionError {
119
+ return error instanceof SubscriptionError;
120
+ }
121
+
122
+ function isInsufficientCreditsError(error: unknown): error is InsufficientCreditsError {
123
+ return error instanceof InsufficientCreditsError;
124
+ }
125
+ ```
126
+
127
+ ## Error Codes
128
+
129
+ | Code | Description |
130
+ |------|-------------|
131
+ | `INVALID_USER_ID` | User ID is invalid or missing |
132
+ | `INVALID_PRODUCT_ID` | Product ID is not recognized |
133
+ | `INVALID_AMOUNT` | Amount is invalid (e.g., negative) |
134
+ | `INSUFFICIENT_CREDITS` | User doesn't have enough credits |
135
+ | `SUBSCRIPTION_EXPIRED` | Subscription has expired |
136
+ | `SUBSCRIPTION_NOT_FOUND` | Subscription doesn't exist |
137
+ | `REPOSITORY_ERROR` | Database/repository error |
138
+ | `NETWORK_ERROR` | Network connectivity issue |
139
+ | `PURCHASE_CANCELLED` | User cancelled purchase |
140
+ | `PURCHASE_FAILED` | Purchase operation failed |
141
+
142
+ ## Error Context
143
+
144
+ Errors can include contextual information:
145
+
146
+ ```typescript
147
+ interface InsufficientCreditsContext {
148
+ required: number;
149
+ available: number;
150
+ featureId?: string;
151
+ currency?: string;
152
+ }
153
+
154
+ throw new InsufficientCreditsError(
155
+ 'Insufficient credits',
156
+ {
157
+ required: 10,
158
+ available: 5,
159
+ featureId: 'export',
160
+ currency: 'credits',
161
+ }
162
+ );
163
+ ```
164
+
165
+ ## Custom Errors
166
+
167
+ Create your own domain errors:
168
+
169
+ ```typescript
170
+ class FeatureAccessError extends SubscriptionError {
171
+ constructor(
172
+ featureId: string,
173
+ public readonly requiredTier: UserTier,
174
+ public readonly currentTier: UserTier
175
+ ) {
176
+ super(
177
+ `Feature "${featureId}" requires ${requiredTier} tier`,
178
+ 'FEATURE_ACCESS_DENIED',
179
+ { featureId, requiredTier, currentTier }
180
+ );
181
+ this.name = 'FeatureAccessError';
182
+ }
183
+ }
184
+
185
+ // Usage
186
+ if (!canAccessFeature('advanced_analytics')) {
187
+ throw new FeatureAccessError(
188
+ 'advanced_analytics',
189
+ 'premium',
190
+ userTier
191
+ );
192
+ }
193
+ ```
194
+
195
+ ## Error Handling Patterns
196
+
197
+ ### 1. Try-Catch with Type Guards
198
+
199
+ ```typescript
200
+ try {
201
+ await executeOperation();
202
+ } catch (error) {
203
+ if (error instanceof InsufficientCreditsError) {
204
+ handleInsufficientCredits(error);
205
+ } else if (error instanceof SubscriptionError) {
206
+ handleSubscriptionError(error);
207
+ } else {
208
+ handleUnexpectedError(error);
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### 2. Result Type
214
+
215
+ ```typescript
216
+ type Result<T, E extends Error = Error> =
217
+ | { success: true; data: T }
218
+ | { success: false; error: E };
219
+
220
+ async function deductCredits(
221
+ amount: number
222
+ ): Promise<Result<number, InsufficientCreditsError>> {
223
+ if (currentBalance < amount) {
224
+ return {
225
+ success: false,
226
+ error: new InsufficientCreditsError('...', {...}),
227
+ };
228
+ }
229
+
230
+ return { success: true, data: newBalance };
231
+ }
232
+
233
+ // Usage
234
+ const result = await deductCredits(10);
235
+ if (result.success) {
236
+ console.log('New balance:', result.data);
237
+ } else {
238
+ console.error('Error:', result.error.message);
239
+ }
240
+ ```
241
+
242
+ ### 3. Error Boundary
243
+
244
+ ```typescript
245
+ class SubscriptionErrorBoundary extends React.Component {
246
+ state = { hasError: false, error: null };
247
+
248
+ static getDerivedStateFromError(error: Error) {
249
+ return { hasError: true, error };
250
+ }
251
+
252
+ componentDidCatch(error: Error, errorInfo: any) {
253
+ if (error instanceof SubscriptionError) {
254
+ analytics().logEvent('subscription_error', {
255
+ code: error.code,
256
+ message: error.message,
257
+ });
258
+ }
259
+ }
260
+
261
+ render() {
262
+ if (this.state.hasError) {
263
+ return <ErrorFallback error={this.state.error} />;
264
+ }
265
+
266
+ return this.props.children;
267
+ }
268
+ }
269
+ ```
270
+
271
+ ## Best Practices
272
+
273
+ 1. **Use specific error types** - Don't use generic Error
274
+ 2. **Include context** - Add relevant data to errors
275
+ 3. **Document error codes** - List all possible errors
276
+ 4. **Handle gracefully** - Show user-friendly messages
277
+ 5. **Log errors** - Track for debugging
278
+ 6. **Don't swallow errors** - Always handle or rethrow
279
+ 7. **Use type guards** - Enable type-safe error handling
280
+
281
+ ## Testing
282
+
283
+ ```typescript
284
+ describe('InsufficientCreditsError', () => {
285
+ it('should create error with context', () => {
286
+ const error = new InsufficientCreditsError('Not enough credits', {
287
+ required: 10,
288
+ available: 5,
289
+ });
290
+
291
+ expect(error.context.required).toBe(10);
292
+ expect(error.context.available).toBe(5);
293
+ });
294
+
295
+ it('should be instance of SubscriptionError', () => {
296
+ const error = new InsufficientCreditsError('...', {...});
297
+
298
+ expect(error).toBeInstanceOf(SubscriptionError);
299
+ });
300
+ });
301
+ ```
302
+
303
+ ## Related
304
+
305
+ - [Domain Entities](../entities/README.md)
306
+ - [Value Objects](../value-objects/README.md)
307
+ - [Domain Layer](../../README.md)
@@ -0,0 +1,186 @@
1
+ # Domain Value Objects
2
+
3
+ Value objects for the subscription domain.
4
+
5
+ ## Purpose
6
+
7
+ Value objects are immutable objects that represent concepts by their attributes rather than identity. They are used to ensure validity and prevent primitive obsession.
8
+
9
+ ## Value Objects
10
+
11
+ ### SubscriptionConfig
12
+
13
+ Configuration object for subscription system.
14
+
15
+ ```typescript
16
+ interface SubscriptionConfig {
17
+ revenueCatApiKey: string;
18
+ revenueCatEntitlementId: string;
19
+ plans: Record<string, Plan>;
20
+ defaultPlan: string;
21
+ features: ConfigFeatures;
22
+ ui?: ConfigUI;
23
+ onStatusChanged?: (userId: string, status: SubscriptionStatus) => void;
24
+ onError?: (error: Error) => void;
25
+ }
26
+ ```
27
+
28
+ **Usage:**
29
+ ```typescript
30
+ import { SubscriptionConfig } from '@umituz/react-native-subscription/domain';
31
+
32
+ const config: SubscriptionConfig = {
33
+ revenueCatApiKey: 'your_key',
34
+ revenueCatEntitlementId: 'premium',
35
+ plans: {
36
+ monthly: monthlyPlan,
37
+ annual: annualPlan,
38
+ },
39
+ defaultPlan: 'monthly',
40
+ features: {
41
+ requireAuth: true,
42
+ allowRestore: true,
43
+ },
44
+ };
45
+ ```
46
+
47
+ ## Characteristics
48
+
49
+ ### 1. Immutable
50
+
51
+ ```typescript
52
+ const config = SubscriptionConfig.create({...});
53
+ // config.apiKey = 'new_key'; // Error: Cannot assign
54
+ ```
55
+
56
+ ### 2. Value-Based Equality
57
+
58
+ ```typescript
59
+ const config1 = SubscriptionConfig.create({...});
60
+ const config2 = SubscriptionConfig.create({...});
61
+
62
+ config1.equals(config2); // true (if same values)
63
+ ```
64
+
65
+ ### 3. Self-Validating
66
+
67
+ ```typescript
68
+ const config = SubscriptionConfig.create({
69
+ apiKey: '', // Invalid!
70
+ entitlementId: 'premium',
71
+ });
72
+ // Throws ValidationError
73
+ ```
74
+
75
+ ## Creating Value Objects
76
+
77
+ ### Factory Method
78
+
79
+ ```typescript
80
+ const config = SubscriptionConfig.create({
81
+ apiKey: 'your_key',
82
+ entitlementId: 'premium',
83
+ });
84
+ ```
85
+
86
+ ### Builder Pattern
87
+
88
+ ```typescript
89
+ const config = SubscriptionConfig.builder()
90
+ .apiKey('your_key')
91
+ .entitlementId('premium')
92
+ .addPlan('monthly', monthlyPlan)
93
+ .addPlan('annual', annualPlan)
94
+ .build();
95
+ ```
96
+
97
+ ## Common Value Objects
98
+
99
+ ### Money
100
+
101
+ ```typescript
102
+ class Money {
103
+ private constructor(
104
+ private amount: number,
105
+ private currency: string
106
+ ) {}
107
+
108
+ static create(amount: number, currency: string): Money {
109
+ if (amount < 0) throw new Error('Amount cannot be negative');
110
+ return new Money(amount, currency);
111
+ }
112
+
113
+ format(): string {
114
+ return new Intl.NumberFormat('en-US', {
115
+ style: 'currency',
116
+ currency: this.currency,
117
+ }).format(this.amount);
118
+ }
119
+
120
+ add(other: Money): Money {
121
+ if (this.currency !== other.currency) {
122
+ throw new Error('Cannot add different currencies');
123
+ }
124
+ return new Money(this.amount + other.amount, this.currency);
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### DateRange
130
+
131
+ ```typescript
132
+ class DateRange {
133
+ constructor(
134
+ private start: Date,
135
+ private end: Date
136
+ ) {}
137
+
138
+ includes(date: Date): boolean {
139
+ return date >= this.start && date <= this.end;
140
+ }
141
+
142
+ durationInDays(): number {
143
+ return Math.ceil(
144
+ (this.end.getTime() - this.start.getTime()) / (1000 * 60 * 60 * 24)
145
+ );
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Best Practices
151
+
152
+ 1. **Make immutable** - All properties readonly
153
+ 2. **Validate on creation** - Fail fast
154
+ 3. **Override equality** - Compare by value, not reference
155
+ 4. **Use for complex attributes** - Don't use for simple primitives
156
+ 5. **Keep small** - Value objects should be focused
157
+
158
+ ## Testing
159
+
160
+ ```typescript
161
+ describe('SubscriptionConfig', () => {
162
+ it('should create valid config', () => {
163
+ const config = SubscriptionConfig.create({
164
+ apiKey: 'test_key',
165
+ entitlementId: 'premium',
166
+ });
167
+
168
+ expect(config.apiKey).toBe('test_key');
169
+ });
170
+
171
+ it('should reject invalid config', () => {
172
+ expect(() => {
173
+ SubscriptionConfig.create({
174
+ apiKey: '',
175
+ entitlementId: 'premium',
176
+ });
177
+ }).toThrow();
178
+ });
179
+ });
180
+ ```
181
+
182
+ ## Related
183
+
184
+ - [Domain Entities](../entities/README.md)
185
+ - [Domain Errors](../errors/README.md)
186
+ - [Domain Layer](../../README.md)