@umituz/react-native-subscription 2.14.97 → 2.14.99

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 (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +461 -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/README.md +240 -0
  12. package/src/domains/config/README.md +390 -0
  13. package/src/domains/config/domain/README.md +390 -0
  14. package/src/domains/config/domain/entities/README.md +350 -0
  15. package/src/domains/paywall/README.md +371 -0
  16. package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
  17. package/src/domains/paywall/components/README.md +185 -0
  18. package/src/domains/paywall/entities/README.md +199 -0
  19. package/src/domains/paywall/hooks/README.md +129 -0
  20. package/src/domains/wallet/README.md +292 -0
  21. package/src/domains/wallet/domain/README.md +108 -0
  22. package/src/domains/wallet/domain/entities/README.md +122 -0
  23. package/src/domains/wallet/domain/errors/README.md +157 -0
  24. package/src/domains/wallet/infrastructure/README.md +96 -0
  25. package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
  26. package/src/domains/wallet/presentation/components/README.md +231 -0
  27. package/src/domains/wallet/presentation/hooks/README.md +255 -0
  28. package/src/infrastructure/README.md +514 -0
  29. package/src/infrastructure/mappers/README.md +34 -0
  30. package/src/infrastructure/models/README.md +26 -0
  31. package/src/infrastructure/repositories/README.md +385 -0
  32. package/src/infrastructure/services/README.md +374 -0
  33. package/src/presentation/README.md +410 -0
  34. package/src/presentation/components/README.md +183 -0
  35. package/src/presentation/components/details/CreditRow.md +337 -0
  36. package/src/presentation/components/details/DetailRow.md +283 -0
  37. package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
  38. package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
  39. package/src/presentation/components/details/README.md +449 -0
  40. package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
  41. package/src/presentation/components/feedback/README.md +447 -0
  42. package/src/presentation/components/paywall/PaywallModal.md +444 -0
  43. package/src/presentation/components/paywall/README.md +190 -0
  44. package/src/presentation/components/sections/README.md +468 -0
  45. package/src/presentation/components/sections/SubscriptionSection.md +246 -0
  46. package/src/presentation/hooks/README.md +743 -0
  47. package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
  48. package/src/presentation/hooks/useAuthGate.md +403 -0
  49. package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
  50. package/src/presentation/hooks/useCreditChecker.md +407 -0
  51. package/src/presentation/hooks/useCredits.md +342 -0
  52. package/src/presentation/hooks/useCreditsGate.md +346 -0
  53. package/src/presentation/hooks/useDeductCredit.md +176 -0
  54. package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
  55. package/src/presentation/hooks/useFeatureGate.md +157 -0
  56. package/src/presentation/hooks/useInitializeCredits.md +458 -0
  57. package/src/presentation/hooks/usePaywall.md +334 -0
  58. package/src/presentation/hooks/usePaywallOperations.md +486 -0
  59. package/src/presentation/hooks/usePaywallVisibility.md +344 -0
  60. package/src/presentation/hooks/usePremium.md +230 -0
  61. package/src/presentation/hooks/usePremiumGate.md +423 -0
  62. package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
  63. package/src/presentation/hooks/useSubscription.md +450 -0
  64. package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
  65. package/src/presentation/hooks/useSubscriptionGate.md +168 -0
  66. package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
  67. package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
  68. package/src/presentation/hooks/useUserTier.md +356 -0
  69. package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
  70. package/src/presentation/screens/README.md +194 -0
  71. package/src/presentation/types/README.md +38 -0
  72. package/src/presentation/utils/README.md +52 -0
  73. package/src/revenuecat/README.md +523 -0
  74. package/src/revenuecat/application/README.md +158 -0
  75. package/src/revenuecat/application/ports/README.md +169 -0
  76. package/src/revenuecat/domain/README.md +147 -0
  77. package/src/revenuecat/domain/constants/README.md +183 -0
  78. package/src/revenuecat/domain/entities/README.md +382 -0
  79. package/src/revenuecat/domain/errors/README.md +197 -0
  80. package/src/revenuecat/domain/types/README.md +373 -0
  81. package/src/revenuecat/domain/value-objects/README.md +441 -0
  82. package/src/revenuecat/infrastructure/README.md +50 -0
  83. package/src/revenuecat/infrastructure/config/README.md +40 -0
  84. package/src/revenuecat/infrastructure/handlers/README.md +218 -0
  85. package/src/revenuecat/infrastructure/managers/README.md +49 -0
  86. package/src/revenuecat/infrastructure/services/README.md +325 -0
  87. package/src/revenuecat/infrastructure/utils/README.md +382 -0
  88. package/src/revenuecat/presentation/README.md +184 -0
  89. package/src/revenuecat/presentation/hooks/README.md +56 -0
  90. package/src/utils/README.md +529 -0
@@ -0,0 +1,52 @@
1
+ # Presentation Utils
2
+
3
+ Utility functions and helpers for the presentation layer.
4
+
5
+ ## Overview
6
+
7
+ This directory contains utility functions used by presentation components and hooks.
8
+
9
+ ## Contents
10
+
11
+ - **subscriptionDateUtils.ts** - Date formatting and manipulation for subscription display
12
+ - **paywallUtils.ts** - Paywall-related utility functions
13
+
14
+ ## Date Utilities
15
+
16
+ ### formatDate
17
+
18
+ Format ISO date string to localized date string.
19
+
20
+ ```typescript
21
+ formatDate('2024-01-15T10:30:00Z'); // "January 15, 2024"
22
+ ```
23
+
24
+ ### calculateDaysRemaining
25
+
26
+ Calculate days between now and expiration date.
27
+
28
+ ```typescript
29
+ calculateDaysRemaining('2024-02-15T10:30:00Z'); // 31
30
+ ```
31
+
32
+ ### convertPurchasedAt
33
+
34
+ Convert purchasedAt date to ISO string.
35
+
36
+ ```typescript
37
+ convertPurchasedAt(new Date()); // "2024-01-15T10:30:00Z"
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```typescript
43
+ import { formatDate, calculateDaysRemaining } from '../utils/subscriptionDateUtils';
44
+
45
+ const expirationDisplay = formatDate(subscription.expirationDate);
46
+ const daysLeft = calculateDaysRemaining(subscription.expirationDate);
47
+ ```
48
+
49
+ ## Related
50
+
51
+ - [Hooks](../hooks/README.md)
52
+ - [Types](../types/README.md)
@@ -0,0 +1,523 @@
1
+ # RevenueCat Integration
2
+
3
+ RevenueCat ile abonelik yönetimi için kapsamlı entegrasyon ve API wrapper.
4
+
5
+ ## Özellikler
6
+
7
+ - **Otomatik Başlatma**: RevenueCat SDK otomatik başlatma ve konfigürasyon
8
+ - **User ID Yönetimi**: Auth sistemleriyle entegre user ID yönetimi
9
+ - **Purchase Flow**: Satın alma işlemleri için yönetilen flow
10
+ - **Restore İşlemi**: Satın alma geri yükleme desteği
11
+ - **Customer Info**: Kullanıcı abonelik bilgilerini takip
12
+ - **Error Handling**: RevenueCat hatalarını yönetme
13
+
14
+ ## Kurulum
15
+
16
+ ### 1. RevenueCat SDK Kurulumu
17
+
18
+ ```bash
19
+ npm install react-native-purchases
20
+ # veya
21
+ yarn add react-native-purchases
22
+ ```
23
+
24
+ ### 2. Başlatma
25
+
26
+ ```typescript
27
+ import {
28
+ initializeSubscription,
29
+ SubscriptionInitConfig,
30
+ } from '@umituz/react-native-subscription';
31
+
32
+ const config: SubscriptionInitConfig = {
33
+ revenueCatApiKey: 'your_api_key',
34
+ revenueCatEntitlementId: 'premium',
35
+
36
+ // Opsiyonel
37
+ userDefaultsSuiteName: 'app.revenuecat',
38
+ diagnosticsEnabled: __DEV__,
39
+ };
40
+
41
+ await initializeSubscription(config);
42
+ ```
43
+
44
+ ### 3. Provider ile Kullanım
45
+
46
+ ```typescript
47
+ import { SubscriptionProvider } from '@umituz/react-native-subscription';
48
+
49
+ function App() {
50
+ return (
51
+ <SubscriptionProvider config={config}>
52
+ <YourApp />
53
+ </SubscriptionProvider>
54
+ );
55
+ }
56
+ ```
57
+
58
+ ## Hooks
59
+
60
+ ### useRevenueCat
61
+
62
+ RevenueCat'e erişim ve temel işlemler için:
63
+
64
+ ```typescript
65
+ import { useRevenueCat } from '@umituz/react-native-subscription';
66
+
67
+ function RevenueCatExample() {
68
+ const {
69
+ isReady,
70
+ isInitialized,
71
+ error,
72
+ purchaserInfo,
73
+ offerings,
74
+ } = useRevenueCat();
75
+
76
+ if (!isInitialized) {
77
+ return <ActivityIndicator />;
78
+ }
79
+
80
+ return (
81
+ <View>
82
+ <Text>Status: {isReady ? 'Ready' : 'Loading'}</Text>
83
+ </View>
84
+ );
85
+ }
86
+ ```
87
+
88
+ ### useCustomerInfo
89
+
90
+ Kullanıcı abonelik bilgileri için:
91
+
92
+ ```typescript
93
+ import { useCustomerInfo } from '@umituz/react-native-subscription';
94
+
95
+ function SubscriptionStatus() {
96
+ const {
97
+ customerInfo,
98
+ isLoading,
99
+ error,
100
+ refetch,
101
+ } = useCustomerInfo();
102
+
103
+ if (isLoading) return <ActivityIndicator />;
104
+
105
+ const entitlement = customerInfo?.entitlements.active['premium'];
106
+
107
+ return (
108
+ <View>
109
+ <Text>
110
+ Status: {entitlement ? 'Premium' : 'Free'}
111
+ </Text>
112
+ {entitlement && (
113
+ <Text>
114
+ Expires: {new Date(entitlement.expirationDate).toLocaleDateString()}
115
+ </Text>
116
+ )}
117
+ <Button onPress={refetch} title="Refresh" />
118
+ </View>
119
+ );
120
+ }
121
+ ```
122
+
123
+ ### useInitializeSubscription
124
+
125
+ Başlatma durumu kontrolü için:
126
+
127
+ ```typescript
128
+ import { useInitializeSubscription } from '@umituz/react-native-subscription';
129
+
130
+ function InitCheck() {
131
+ const {
132
+ isInitialized,
133
+ isInitializing,
134
+ error,
135
+ initialize,
136
+ } = useInitializeSubscription();
137
+
138
+ useEffect(() => {
139
+ if (!isInitialized && !isInitializing) {
140
+ initialize();
141
+ }
142
+ }, []);
143
+
144
+ if (isInitializing) {
145
+ return <Text>Initializing...</Text>;
146
+ }
147
+
148
+ if (error) {
149
+ return <Text>Error: {error.message}</Text>;
150
+ }
151
+
152
+ return <Text>Ready!</Text>;
153
+ }
154
+ ```
155
+
156
+ ### useSubscriptionPackages
157
+
158
+ Mevcut abonelik paketleri için:
159
+
160
+ ```typescript
161
+ import { useSubscriptionPackages } from '@umituz/react-native-subscription';
162
+
163
+ function PackageList() {
164
+ const {
165
+ packages,
166
+ offerings,
167
+ isLoading,
168
+ error,
169
+ } = useSubscriptionPackages({
170
+ offeringId: 'default',
171
+ });
172
+
173
+ if (isLoading) return <ActivityIndicator />;
174
+
175
+ return (
176
+ <ScrollView>
177
+ {packages.map((pkg) => (
178
+ <PackageCard
179
+ key={pkg.identifier}
180
+ package={pkg}
181
+ onPress={() => handlePurchase(pkg)}
182
+ />
183
+ ))}
184
+ </ScrollView>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ### usePaywallFlow
190
+
191
+ Tam paywall flow'u için:
192
+
193
+ ```typescript
194
+ import { usePaywallFlow } from '@umituz/react-native-subscription';
195
+
196
+ function Paywall() {
197
+ const {
198
+ packages,
199
+ selectedPackage,
200
+ isLoading,
201
+ error,
202
+ selectPackage,
203
+ purchaseSelectedPackage,
204
+ restorePurchases,
205
+ } = usePaywallFlow();
206
+
207
+ const handlePurchase = async () => {
208
+ try {
209
+ const result = await purchaseSelectedPackage();
210
+
211
+ if (result.success) {
212
+ Alert.alert('Success', 'You are now a premium user!');
213
+ } else {
214
+ Alert.alert('Error', result.error?.message);
215
+ }
216
+ } catch (err) {
217
+ Alert.alert('Error', 'Purchase failed');
218
+ }
219
+ };
220
+
221
+ return (
222
+ <View>
223
+ {packages.map((pkg) => (
224
+ <TouchableOpacity
225
+ key={pkg.identifier}
226
+ onPress={() => selectPackage(pkg)}
227
+ style={selectedPackage?.identifier === pkg.identifier &&
228
+ styles.selected
229
+ }
230
+ >
231
+ <Text>{pkg.product.title}</Text>
232
+ <Text>{pkg.product.priceString}</Text>
233
+ </TouchableOpacity>
234
+ ))}
235
+
236
+ <Button
237
+ onPress={handlePurchase}
238
+ disabled={!selectedPackage || isLoading}
239
+ title="Subscribe"
240
+ />
241
+
242
+ <Button
243
+ onPress={restorePurchases}
244
+ disabled={isLoading}
245
+ title="Restore"
246
+ />
247
+ </View>
248
+ );
249
+ }
250
+ ```
251
+
252
+ ### useRestorePurchase
253
+
254
+ Satın alma geri yükleme için:
255
+
256
+ ```typescript
257
+ import { useRestorePurchase } from '@umituz/react-native-subscription';
258
+
259
+ function RestoreButton() {
260
+ const { restorePurchase, isLoading, error, isRestored } = useRestorePurchase();
261
+
262
+ const handleRestore = async () => {
263
+ const result = await restorePurchase();
264
+
265
+ if (result.success) {
266
+ Alert.alert('Success', 'Purchase restored!');
267
+ } else {
268
+ Alert.alert('Error', result.error?.message || 'Restore failed');
269
+ }
270
+ };
271
+
272
+ return (
273
+ <Button
274
+ onPress={handleRestore}
275
+ disabled={isLoading}
276
+ title={isLoading ? 'Restoring...' : 'Restore Purchase'}
277
+ />
278
+ );
279
+ }
280
+ ```
281
+
282
+ ## Hata Yönetimi
283
+
284
+ ### RevenueCatError
285
+
286
+ ```typescript
287
+ import {
288
+ RevenueCatError,
289
+ ErrorCode,
290
+ handleRevenueCatError,
291
+ } from '@umituz/react-native-subscription';
292
+
293
+ try {
294
+ await purchasePackage(packageToPurchase);
295
+ } catch (error) {
296
+ if (error instanceof RevenueCatError) {
297
+ switch (error.code) {
298
+ case ErrorCode.PurchaseCancelledError:
299
+ console.log('User cancelled');
300
+ break;
301
+ case ErrorCode.PurchaseInvalidError:
302
+ console.log('Invalid purchase');
303
+ break;
304
+ case ErrorCode.NetworkError:
305
+ console.log('Network error');
306
+ break;
307
+ default:
308
+ handleRevenueCatError(error);
309
+ }
310
+ }
311
+ }
312
+ ```
313
+
314
+ ## User ID Yönetimi
315
+
316
+ ### Auth ile Entegrasyon
317
+
318
+ ```typescript
319
+ import { useAuthSubscriptionSync } from '@umituz/react-native-subscription';
320
+
321
+ function AuthSync() {
322
+ const { user } = useAuth();
323
+ const { syncUserId, clearUserId } = useAuthSubscriptionSync();
324
+
325
+ useEffect(() => {
326
+ if (user?.uid) {
327
+ // Kullanıcı giriş yaptığında
328
+ syncUserId(user.uid);
329
+ } else {
330
+ // Kullanıcı çıkış yaptığında
331
+ clearUserId();
332
+ }
333
+ }, [user?.uid]);
334
+
335
+ return null;
336
+ }
337
+ ```
338
+
339
+ ### Manuel User ID
340
+
341
+ ```typescript
342
+ import { configureUserId } from '@umituz/react-native-subscription';
343
+
344
+ // Kullanıcı giriş yaptığında
345
+ await configureUserId('user-123');
346
+
347
+ // Kullanıcı çıkış yaptığında
348
+ await configureUserId(null); // veya await resetUserId()
349
+ ```
350
+
351
+ ## API Key Çözümleme
352
+
353
+ ### Environment Bazlı API Keys
354
+
355
+ ```typescript
356
+ import {
357
+ ApiKeyResolver,
358
+ resolveApiKey,
359
+ } from '@umituz/react-native-subscription';
360
+
361
+ const apiKeyResolver = new ApiKeyResolver({
362
+ development: 'dev_api_key',
363
+ production: 'prod_api_key',
364
+ });
365
+
366
+ const apiKey = apiKeyResolver.resolve(__DEV__ ? 'development' : 'production');
367
+ ```
368
+
369
+ ## SubscriptionManager
370
+
371
+ Gelişmiş işlemler için SubscriptionManager kullanımı:
372
+
373
+ ```typescript
374
+ import { SubscriptionManager } from '@umituz/react-native-subscription';
375
+
376
+ const manager = new SubscriptionManager(config);
377
+
378
+ // Başlatma
379
+ await manager.initialize(userId);
380
+
381
+ // Satın alma
382
+ const result = await manager.purchasePackage(packageToPurchase);
383
+
384
+ // Geri yükleme
385
+ const restoreResult = await manager.restorePurchases();
386
+
387
+ // Customer info
388
+ const info = await manager.getCustomerInfo();
389
+
390
+ // Offerings
391
+ const offerings = await manager.getOfferings();
392
+ ```
393
+
394
+ ## Best Practices
395
+
396
+ 1. **User ID Sync**: Auth sistemiyle user ID'leri her zaman senkronize edin
397
+ 2. **Error Handling**: TümRevenueCat işlemlerinde hata yönetimi kullanın
398
+ 3. **Loading States**: Kullanıcıya uygun loading feedback'i verin
399
+ 4. **Restore**: Her zaman "Restore Purchase" seçeneği sunun
400
+ 5. **Test Mode**: Geliştirme sırasında test mode kullanın
401
+ 6. **Debug Mode**: Production'da debug modu kapalı tutun
402
+
403
+ ## Örnek Uygulama
404
+
405
+ ```typescript
406
+ import React, { useEffect } from 'react';
407
+ import { View, Text, Button, ActivityIndicator } from 'react-native';
408
+ import {
409
+ usePaywallFlow,
410
+ useCustomerInfo,
411
+ useRestorePurchase,
412
+ } from '@umituz/react-native-subscription';
413
+
414
+ export default function SubscriptionScreen() {
415
+ const {
416
+ packages,
417
+ selectedPackage,
418
+ isLoading,
419
+ selectPackage,
420
+ purchaseSelectedPackage,
421
+ } = usePaywallFlow();
422
+
423
+ const { customerInfo, isLoading: infoLoading } = useCustomerInfo();
424
+ const { restorePurchase } = useRestorePurchase();
425
+
426
+ const entitlement = customerInfo?.entitlements.active['premium'];
427
+
428
+ const handleSubscribe = async () => {
429
+ if (!selectedPackage) return;
430
+
431
+ try {
432
+ const result = await purchaseSelectedPackage();
433
+
434
+ if (result.success) {
435
+ Alert.alert('Success', 'Welcome to Premium!');
436
+ } else {
437
+ Alert.alert('Error', result.error?.message || 'Purchase failed');
438
+ }
439
+ } catch (error) {
440
+ Alert.alert('Error', 'Something went wrong');
441
+ }
442
+ };
443
+
444
+ if (isLoading || infoLoading) {
445
+ return <ActivityIndicator />;
446
+ }
447
+
448
+ return (
449
+ <View>
450
+ {entitlement ? (
451
+ <>
452
+ <Text>You are Premium!</Text>
453
+ <Text>
454
+ Expires: {new Date(entitlement.expirationDate).toLocaleDateString()}
455
+ </Text>
456
+ </>
457
+ ) : (
458
+ <>
459
+ <Text>Choose your plan:</Text>
460
+ {packages.map((pkg) => (
461
+ <TouchableOpacity
462
+ key={pkg.identifier}
463
+ onPress={() => selectPackage(pkg)}
464
+ style={
465
+ selectedPackage?.identifier === pkg.identifier &&
466
+ styles.selected
467
+ }
468
+ >
469
+ <Text>{pkg.product.title}</Text>
470
+ <Text>{pkg.product.priceString}</Text>
471
+ <Text>{pkg.product.description}</Text>
472
+ </TouchableOpacity>
473
+ ))}
474
+
475
+ <Button
476
+ onPress={handleSubscribe}
477
+ disabled={!selectedPackage || isLoading}
478
+ title="Subscribe"
479
+ />
480
+
481
+ <Button onPress={restorePurchase} title="Restore" />
482
+ </>
483
+ )}
484
+ </View>
485
+ );
486
+ }
487
+ ```
488
+
489
+ ## Tip Tanımlamaları
490
+
491
+ ```typescript
492
+ interface RevenueCatConfig {
493
+ apiKey: string;
494
+ entitlements: {
495
+ premium: string;
496
+ };
497
+ userDefaultsSuiteName?: string;
498
+ diagnosticsEnabled?: boolean;
499
+ }
500
+
501
+ interface PurchaseResult {
502
+ success: boolean;
503
+ error?: Error;
504
+ customerInfo?: CustomerInfo;
505
+ }
506
+
507
+ interface CustomerInfo {
508
+ entitlements: {
509
+ active: Record<string, EntitlementInfo>;
510
+ all: Record<string, EntitlementInfo>;
511
+ };
512
+ activeSubscriptions: string[];
513
+ allPurchasedProductIdentifiers: string[];
514
+ latestExpirationDate: string;
515
+ }
516
+
517
+ interface Package {
518
+ identifier: string;
519
+ packageType: PACKAGE_TYPE;
520
+ product: Product;
521
+ offeringIdentifier: string;
522
+ }
523
+ ```
@@ -0,0 +1,158 @@
1
+ # RevenueCat Application Layer
2
+
3
+ Application layer for RevenueCat integration, containing use cases and orchestration logic.
4
+
5
+ ## Overview
6
+
7
+ This directory contains use cases and application-level operations for managing RevenueCat subscriptions and purchases.
8
+
9
+ ## Structure
10
+
11
+ ```
12
+ application/
13
+ └── ports/ # Interface definitions for RevenueCat services
14
+ ```
15
+
16
+ ## Ports
17
+
18
+ ### IRevenueCatService
19
+
20
+ Main service interface for RevenueCat operations.
21
+
22
+ ```typescript
23
+ interface IRevenueCatService {
24
+ // Purchasing
25
+ purchasePackage(package: PurchasesPackage): Promise<PurchaseResult>;
26
+ restorePurchases(): Promise<RestoreResult>;
27
+
28
+ // Customer Info
29
+ getCustomerInfo(): Promise<CustomerInfo>;
30
+ getOfferings(): Promise<Offerings>;
31
+
32
+ // Configuration
33
+ configure(apiKey: string, userId?: string): Promise<void>;
34
+
35
+ // Entitlements
36
+ checkEntitlement(entitlementId: string): Promise<boolean>;
37
+ }
38
+ ```
39
+
40
+ ## Use Cases
41
+
42
+ ### Purchase Flow
43
+
44
+ ```typescript
45
+ async function purchasePremiumPackage(userId: string) {
46
+ const service = getRevenueCatService();
47
+
48
+ // 1. Get offerings
49
+ const offerings = await service.getOfferings();
50
+ const package = offerings.current?.monthly;
51
+
52
+ if (!package) {
53
+ throw new Error('No package available');
54
+ }
55
+
56
+ // 2. Purchase
57
+ const result = await service.purchasePackage(package);
58
+
59
+ // 3. Handle result
60
+ if (result.error) {
61
+ throw new Error(result.error.message);
62
+ }
63
+
64
+ // 4. Update local state
65
+ await updateSubscriptionStatus(userId, result.customerInfo);
66
+ }
67
+ ```
68
+
69
+ ### Restore Purchases
70
+
71
+ ```typescript
72
+ async function restoreUserPurchases() {
73
+ const service = getRevenueCatService();
74
+
75
+ try {
76
+ const result = await service.restorePurchases();
77
+
78
+ if (result.error) {
79
+ throw new Error(result.error.message);
80
+ }
81
+
82
+ // Update local subscription status
83
+ await updateSubscriptionStatusFromRestored(result.customerInfo);
84
+
85
+ return { success: true, customerInfo: result.customerInfo };
86
+ } catch (error) {
87
+ return { success: false, error };
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### Check Entitlement
93
+
94
+ ```typescript
95
+ async function checkPremiumAccess(userId: string): Promise<boolean> {
96
+ const service = getRevenueCatService();
97
+
98
+ try {
99
+ const hasPremium = await service.checkEntitlement('premium');
100
+
101
+ if (hasPremium) {
102
+ await activatePremiumStatus(userId);
103
+ }
104
+
105
+ return hasPremium;
106
+ } catch (error) {
107
+ console.error('Failed to check entitlement:', error);
108
+ return false;
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## Best Practices
114
+
115
+ 1. **Error Handling**: Always handle RevenueCat errors gracefully
116
+ 2. **User ID**: Provide user ID when available for better tracking
117
+ 3. **Entitlements**: Use entitlements instead of product IDs for flexibility
118
+ 4. **Offerings**: Check if offerings are available before purchasing
119
+ 5. **Validation**: Validate customer info after purchases
120
+ 6. **Sync**: Keep local state synchronized with RevenueCat
121
+ 7. **Timeouts**: Handle network timeouts appropriately
122
+ 8. **Logging**: Log all RevenueCat operations for debugging
123
+
124
+ ## Error Handling
125
+
126
+ ```typescript
127
+ import { PurchaseError } from '../domain/errors';
128
+
129
+ async function safePurchase(pkg: PurchasesPackage) {
130
+ try {
131
+ const result = await service.purchasePackage(pkg);
132
+
133
+ if (result.error) {
134
+ // Map RevenueCat errors to domain errors
135
+ if (result.error.code === 'PURCHASE_CANCELLED') {
136
+ throw new PurchaseError('User cancelled purchase');
137
+ }
138
+
139
+ throw new PurchaseError(result.error.message);
140
+ }
141
+
142
+ return result;
143
+ } catch (error) {
144
+ if (error instanceof PurchaseError) {
145
+ throw error;
146
+ }
147
+
148
+ throw new PurchaseError('Purchase failed', { cause: error });
149
+ }
150
+ }
151
+ ```
152
+
153
+ ## Related
154
+
155
+ - [RevenueCat Integration](../README.md)
156
+ - [RevenueCat Domain](../domain/README.md)
157
+ - [RevenueCat Infrastructure](../infrastructure/README.md)
158
+ - [Application Ports](./ports/README.md)