@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,374 @@
1
+ # Infrastructure Services
2
+
3
+ Service implementations in the infrastructure layer.
4
+
5
+ ## Services
6
+
7
+ ### SubscriptionService
8
+
9
+ Main service for subscription management.
10
+
11
+ ```typescript
12
+ import { SubscriptionService } from '@umituz/react-native-subscription/infrastructure';
13
+
14
+ const service = new SubscriptionService({
15
+ repository: myRepository,
16
+ onStatusChanged: (userId, status) => {
17
+ console.log(`Status changed for ${userId}`);
18
+ },
19
+ onError: (error) => {
20
+ console.error('Error:', error);
21
+ },
22
+ });
23
+ ```
24
+
25
+ ### SubscriptionInitializer
26
+
27
+ Initializes the subscription system on app startup.
28
+
29
+ ```typescript
30
+ import { initializeSubscription } from '@umituz/react-native-subscription/infrastructure';
31
+
32
+ await initializeSubscription({
33
+ revenueCatApiKey: 'your_key',
34
+ revenueCatEntitlementId: 'premium',
35
+ creditPackages: [...],
36
+ });
37
+ ```
38
+
39
+ ## Features
40
+
41
+ ### 1. Automatic Status Sync
42
+
43
+ ```typescript
44
+ const service = new SubscriptionService({
45
+ repository: myRepository,
46
+ onStatusChanged: (userId, newStatus) => {
47
+ // React to status changes
48
+ analytics.track('subscription_changed', {
49
+ userId,
50
+ status: newStatus.type,
51
+ });
52
+ },
53
+ });
54
+ ```
55
+
56
+ ### 2. Error Handling
57
+
58
+ ```typescript
59
+ const service = new SubscriptionService({
60
+ repository: myRepository,
61
+ onError: (error) => {
62
+ if (error instanceof SubscriptionValidationError) {
63
+ // Log validation errors
64
+ logger.warn('Validation error:', error.message);
65
+ } else {
66
+ // Log critical errors
67
+ logger.error('Service error:', error);
68
+ crashlytics().recordError(error);
69
+ }
70
+ },
71
+ });
72
+ ```
73
+
74
+ ### 3. Caching
75
+
76
+ ```typescript
77
+ const service = new SubscriptionService({
78
+ repository: myRepository,
79
+ cache: {
80
+ enabled: true,
81
+ ttl: 5 * 60 * 1000, // 5 minutes
82
+ },
83
+ });
84
+ ```
85
+
86
+ ## Custom Services
87
+
88
+ ### Creating a Custom Service
89
+
90
+ ```typescript
91
+ import type { ISubscriptionService } from '@umituz/react-native-subscription/application';
92
+
93
+ export class CustomSubscriptionService implements ISubscriptionService {
94
+ constructor(
95
+ private repository: ISubscriptionRepository,
96
+ private apiClient: ApiClient
97
+ ) {}
98
+
99
+ async getStatus(userId: string): Promise<SubscriptionStatus | null> {
100
+ // Try cache first
101
+ const cached = await this.cache.get(userId);
102
+ if (cached) return cached;
103
+
104
+ // Fetch from repository
105
+ const status = await this.repository.getSubscriptionStatus(userId);
106
+
107
+ // Update cache
108
+ if (status) {
109
+ await this.cache.set(userId, status, { ttl: 300000 });
110
+ }
111
+
112
+ return status;
113
+ }
114
+
115
+ async activateSubscription(
116
+ userId: string,
117
+ productId: string,
118
+ expiresAt: string | null
119
+ ): Promise<SubscriptionStatus> {
120
+ // Business logic
121
+ const current = await this.getStatus(userId);
122
+ if (current?.isActive) {
123
+ throw new SubscriptionOperationError(
124
+ 'User already has an active subscription'
125
+ );
126
+ }
127
+
128
+ // Activate
129
+ const status = await this.repository.saveSubscriptionStatus(userId, {
130
+ type: 'premium',
131
+ isActive: true,
132
+ isPremium: true,
133
+ expirationDate: expiresAt,
134
+ willRenew: expiresAt !== null,
135
+ productId,
136
+ });
137
+
138
+ return status;
139
+ }
140
+
141
+ async deactivateSubscription(userId: string): Promise<SubscriptionStatus> {
142
+ const status = await this.getStatus(userId);
143
+ if (!status?.isActive) {
144
+ throw new SubscriptionOperationError('Subscription is not active');
145
+ }
146
+
147
+ const deactivated = {
148
+ ...status,
149
+ isActive: false,
150
+ willRenew: false,
151
+ };
152
+
153
+ await this.repository.saveSubscriptionStatus(userId, deactivated);
154
+ return deactivated;
155
+ }
156
+
157
+ async isPremium(userId: string): Promise<boolean> {
158
+ const status = await this.getStatus(userId);
159
+ return status?.isPremium ?? false;
160
+ }
161
+ }
162
+ ```
163
+
164
+ ## Advanced Features
165
+
166
+ ### Retry Logic
167
+
168
+ ```typescript
169
+ class SubscriptionServiceWithRetry {
170
+ async getStatus(userId: string): Promise<SubscriptionStatus | null> {
171
+ return this.retryOperation(
172
+ () => this.repository.getSubscriptionStatus(userId),
173
+ {
174
+ maxRetries: 3,
175
+ delay: 1000,
176
+ backoff: 'exponential',
177
+ }
178
+ );
179
+ }
180
+
181
+ private async retryOperation<T>(
182
+ operation: () => Promise<T>,
183
+ options: RetryOptions
184
+ ): Promise<T> {
185
+ let lastError;
186
+
187
+ for (let attempt = 0; attempt < options.maxRetries; attempt++) {
188
+ try {
189
+ return await operation();
190
+ } catch (error) {
191
+ lastError = error;
192
+
193
+ if (attempt < options.maxRetries - 1) {
194
+ const delay = options.backoff === 'exponential'
195
+ ? Math.pow(2, attempt) * options.delay
196
+ : options.delay;
197
+
198
+ await sleep(delay);
199
+ }
200
+ }
201
+ }
202
+
203
+ throw lastError;
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### Circuit Breaker
209
+
210
+ ```typescript
211
+ class CircuitBreaker {
212
+ private failures = 0;
213
+ private lastFailureTime: number | null = null;
214
+ private state: 'closed' | 'open' | 'half-open' = 'closed';
215
+
216
+ async execute<T>(operation: () => Promise<T>): Promise<T> {
217
+ if (this.state === 'open') {
218
+ if (this.shouldAttemptReset()) {
219
+ this.state = 'half-open';
220
+ } else {
221
+ throw new Error('Circuit breaker is open');
222
+ }
223
+ }
224
+
225
+ try {
226
+ const result = await operation();
227
+ this.onSuccess();
228
+ return result;
229
+ } catch (error) {
230
+ this.onFailure();
231
+ throw error;
232
+ }
233
+ }
234
+
235
+ private onSuccess() {
236
+ this.failures = 0;
237
+ this.state = 'closed';
238
+ }
239
+
240
+ private onFailure() {
241
+ this.failures++;
242
+ this.lastFailureTime = Date.now();
243
+
244
+ if (this.failures >= 5) {
245
+ this.state = 'open';
246
+ }
247
+ }
248
+
249
+ private shouldAttemptReset(): boolean {
250
+ return (
251
+ this.lastFailureTime !== null &&
252
+ Date.now() - this.lastFailureTime > 60000 // 1 minute
253
+ );
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### Observability
259
+
260
+ ```typescript
261
+ class ObservableSubscriptionService {
262
+ private eventEmitter = new EventEmitter();
263
+
264
+ async getStatus(userId: string): Promise<SubscriptionStatus | null> {
265
+ const startTime = Date.now();
266
+
267
+ try {
268
+ const status = await this.repository.getSubscriptionStatus(userId);
269
+
270
+ this.eventEmitter.emit('operation_success', {
271
+ operation: 'getStatus',
272
+ userId,
273
+ duration: Date.now() - startTime,
274
+ });
275
+
276
+ return status;
277
+ } catch (error) {
278
+ this.eventEmitter.emit('operation_error', {
279
+ operation: 'getStatus',
280
+ userId,
281
+ error: error.message,
282
+ duration: Date.now() - startTime,
283
+ });
284
+
285
+ throw error;
286
+ }
287
+ }
288
+
289
+ on(event: string, listener: (data: any) => void) {
290
+ this.eventEmitter.on(event, listener);
291
+ }
292
+
293
+ off(event: string, listener: (data: any) => void) {
294
+ this.eventEmitter.off(event, listener);
295
+ }
296
+ }
297
+ ```
298
+
299
+ ## Testing
300
+
301
+ ### Unit Tests
302
+
303
+ ```typescript
304
+ describe('SubscriptionService', () => {
305
+ let service: SubscriptionService;
306
+ let mockRepository: jest.Mocked<ISubscriptionRepository>;
307
+
308
+ beforeEach(() => {
309
+ mockRepository = {
310
+ getSubscriptionStatus: jest.fn(),
311
+ saveSubscriptionStatus: jest.fn(),
312
+ deleteSubscriptionStatus: jest.fn(),
313
+ isSubscriptionValid: jest.fn(),
314
+ };
315
+
316
+ service = new SubscriptionService({
317
+ repository: mockRepository,
318
+ });
319
+ });
320
+
321
+ it('should get subscription status', async () => {
322
+ const expectedStatus = { type: 'premium', isActive: true };
323
+ mockRepository.getSubscriptionStatus.mockResolvedValue(expectedStatus);
324
+
325
+ const status = await service.getStatus('user-123');
326
+
327
+ expect(status).toEqual(expectedStatus);
328
+ expect(mockRepository.getSubscriptionStatus).toHaveBeenCalledWith('user-123');
329
+ });
330
+ });
331
+ ```
332
+
333
+ ### Integration Tests
334
+
335
+ ```typescript
336
+ describe('SubscriptionService Integration', () => {
337
+ let service: SubscriptionService;
338
+ let repository: FirebaseSubscriptionRepository;
339
+
340
+ beforeAll(async () => {
341
+ // Use test Firebase instance
342
+ const testDb = await initializeTestFirebase();
343
+ repository = new FirebaseSubscriptionRepository(testDb);
344
+ service = new SubscriptionService({ repository });
345
+ });
346
+
347
+ it('should activate subscription', async () => {
348
+ const status = await service.activateSubscription(
349
+ 'user-123',
350
+ 'premium_monthly',
351
+ '2025-12-31'
352
+ );
353
+
354
+ expect(status.type).toBe('premium');
355
+ expect(status.isActive).toBe(true);
356
+ });
357
+ });
358
+ ```
359
+
360
+ ## Best Practices
361
+
362
+ 1. **Dependency Injection** - Accept dependencies via constructor
363
+ 2. **Error Handling** - Handle and transform errors appropriately
364
+ 3. **Logging** - Log important operations
365
+ 4. **Caching** - Cache frequently accessed data
366
+ 5. **Validation** - Validate inputs
367
+ 6. **Type Safety** - Use TypeScript types
368
+ 7. **Testing** - Write comprehensive tests
369
+
370
+ ## Related
371
+
372
+ - [Application Layer](../../application/README.md)
373
+ - [Application Ports](../../application/ports/README.md)
374
+ - [Infrastructure Layer](../../README.md)