@umituz/react-native-subscription 2.14.99 → 2.14.100
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.
- package/README.md +211 -394
- package/package.json +1 -1
- package/src/application/README.md +46 -225
- package/src/application/ports/README.md +42 -97
- package/src/domain/README.md +36 -384
- package/src/domain/constants/README.md +0 -56
- package/src/domain/entities/README.md +43 -169
- package/src/domain/errors/README.md +33 -287
- package/src/domain/value-objects/README.md +43 -179
- package/src/domains/README.md +50 -238
- package/src/domains/README.md.bak +274 -0
- package/src/domains/config/README.md +93 -383
- package/src/domains/config/domain/README.md +23 -376
- package/src/domains/config/domain/entities/README.md +34 -343
- package/src/domains/paywall/README.md +99 -369
- package/src/domains/paywall/components/README.md +34 -178
- package/src/domains/paywall/entities/README.md +34 -193
- package/src/domains/paywall/hooks/README.md +34 -122
- package/src/domains/wallet/README.md +34 -275
- package/src/domains/wallet/README.md.bak +209 -0
- package/src/domains/wallet/domain/README.md +34 -101
- package/src/domains/wallet/domain/entities/README.md +34 -115
- package/src/domains/wallet/domain/errors/README.md +34 -151
- package/src/domains/wallet/infrastructure/README.md +34 -89
- package/src/domains/wallet/presentation/components/README.md +34 -224
- package/src/domains/wallet/presentation/hooks/README.md +34 -248
- package/src/infrastructure/README.md +37 -496
- package/src/infrastructure/mappers/README.md +0 -13
- package/src/infrastructure/repositories/README.md +74 -360
- package/src/infrastructure/services/README.md +95 -370
- package/src/presentation/README.md +123 -408
- package/src/presentation/README.md.bak +172 -0
- package/src/presentation/components/README.md +151 -179
- package/src/presentation/components/README.md.bak +217 -0
- package/src/presentation/components/details/CreditRow.md +65 -310
- package/src/presentation/components/details/DetailRow.md +63 -255
- package/src/presentation/components/details/PremiumDetailsCard.md +65 -238
- package/src/presentation/components/details/PremiumStatusBadge.md +64 -239
- package/src/presentation/components/details/README.md +97 -447
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +63 -287
- package/src/presentation/components/feedback/README.md +97 -445
- package/src/presentation/components/paywall/PaywallModal.md +66 -416
- package/src/presentation/components/paywall/README.md +50 -186
- package/src/presentation/components/sections/README.md +97 -466
- package/src/presentation/components/sections/SubscriptionSection.md +92 -244
- package/src/presentation/hooks/README.md +154 -741
- package/src/presentation/hooks/useAuthAwarePurchase.md +58 -325
- package/src/presentation/hooks/useAuthGate.md +61 -375
- package/src/presentation/hooks/useAuthSubscriptionSync.md +66 -370
- package/src/presentation/hooks/useCreditChecker.md +73 -378
- package/src/presentation/hooks/useCredits.md +74 -313
- package/src/presentation/hooks/useCredits.md.bak +231 -0
- package/src/presentation/hooks/useCreditsGate.md +66 -318
- package/src/presentation/hooks/useDeductCredit.md +0 -76
- package/src/presentation/hooks/useDevTestCallbacks.md +63 -394
- package/src/presentation/hooks/useFeatureGate.md +105 -150
- package/src/presentation/hooks/useFeatureGate.md.bak +284 -0
- package/src/presentation/hooks/useInitializeCredits.md +64 -430
- package/src/presentation/hooks/usePaywall.md +61 -306
- package/src/presentation/hooks/usePaywallOperations.md +64 -458
- package/src/presentation/hooks/usePaywallVisibility.md +67 -316
- package/src/presentation/hooks/usePremium.md +84 -226
- package/src/presentation/hooks/usePremiumGate.md +60 -395
- package/src/presentation/hooks/usePremiumWithCredits.md +64 -401
- package/src/presentation/hooks/useSubscription.md +66 -422
- package/src/presentation/hooks/useSubscriptionDetails.md +65 -410
- package/src/presentation/hooks/useSubscriptionGate.md +80 -164
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +66 -346
- package/src/presentation/hooks/useSubscriptionStatus.md +66 -396
- package/src/presentation/hooks/useUserTier.md +63 -328
- package/src/presentation/hooks/useUserTierWithRepository.md +64 -424
- package/src/presentation/screens/README.md +48 -190
- package/src/presentation/types/README.md +0 -16
- package/src/presentation/utils/README.md +0 -21
- package/src/revenuecat/README.md +99 -518
- package/src/revenuecat/application/README.md +35 -150
- package/src/revenuecat/application/ports/README.md +34 -162
- package/src/revenuecat/domain/README.md +42 -141
- package/src/revenuecat/domain/constants/README.md +34 -176
- package/src/revenuecat/domain/entities/README.md +34 -374
- package/src/revenuecat/domain/errors/README.md +47 -191
- package/src/revenuecat/domain/types/README.md +34 -366
- package/src/revenuecat/domain/value-objects/README.md +34 -434
- package/src/revenuecat/infrastructure/README.md +34 -43
- package/src/revenuecat/infrastructure/config/README.md +32 -23
- package/src/revenuecat/infrastructure/handlers/README.md +34 -211
- package/src/revenuecat/infrastructure/managers/README.md +34 -42
- package/src/revenuecat/infrastructure/services/README.md +35 -318
- package/src/revenuecat/infrastructure/utils/README.md +34 -375
- package/src/revenuecat/presentation/README.md +34 -176
- package/src/revenuecat/presentation/hooks/README.md +29 -35
- package/src/utils/README.md +38 -525
|
@@ -1,218 +1,41 @@
|
|
|
1
1
|
# RevenueCat Infrastructure Handlers
|
|
2
2
|
|
|
3
|
+
## Location
|
|
3
4
|
Event handlers for RevenueCat lifecycle events.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
### PurchaseErrorHandler
|
|
39
|
-
|
|
40
|
-
Handles purchase errors.
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
class PurchaseErrorHandler {
|
|
44
|
-
async handle(error: PurchasesError): Promise<void> {
|
|
45
|
-
// 1. Categorize error
|
|
46
|
-
const category = categorizeError(error);
|
|
47
|
-
|
|
48
|
-
// 2. Show appropriate message
|
|
49
|
-
showErrorMessage(category);
|
|
50
|
-
|
|
51
|
-
// 3. Log error
|
|
52
|
-
logPurchaseError(error);
|
|
53
|
-
|
|
54
|
-
// 4. Trigger error callbacks
|
|
55
|
-
triggerErrorCallbacks(error);
|
|
56
|
-
|
|
57
|
-
// 5. Offer recovery options
|
|
58
|
-
if (category === 'network') {
|
|
59
|
-
offerRetry();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### CustomerInfoChangedHandler
|
|
66
|
-
|
|
67
|
-
Handles customer info changes.
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
class CustomerInfoChangedHandler {
|
|
71
|
-
async handle(customerInfo: CustomerInfo): Promise<void> {
|
|
72
|
-
// 1. Check for subscription changes
|
|
73
|
-
const changes = detectSubscriptionChanges(customerInfo);
|
|
74
|
-
|
|
75
|
-
// 2. Update local state
|
|
76
|
-
await updateLocalState(customerInfo);
|
|
77
|
-
|
|
78
|
-
// 3. Handle status changes
|
|
79
|
-
if (changes.subscriptionChanged) {
|
|
80
|
-
await handleSubscriptionChange(changes);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 4. Trigger UI refresh
|
|
84
|
-
triggerUIRefresh();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### EntitlementsChangedHandler
|
|
90
|
-
|
|
91
|
-
Handles entitlement changes.
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
class EntitlementsChangedHandler {
|
|
95
|
-
async handle(entitlements: EntitlementInfos): Promise<void> {
|
|
96
|
-
// 1. Check premium status
|
|
97
|
-
const premium = entitlements.premium;
|
|
98
|
-
|
|
99
|
-
// 2. Update access controls
|
|
100
|
-
if (premium?.isActive) {
|
|
101
|
-
grantPremiumAccess();
|
|
102
|
-
} else {
|
|
103
|
-
revokePremiumAccess();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// 3. Handle new entitlements
|
|
107
|
-
for (const [key, entitlement] of Object.entries(entitlements)) {
|
|
108
|
-
if (entitlement.isActive) {
|
|
109
|
-
await handleNewEntitlement(key, entitlement);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Usage
|
|
117
|
-
|
|
118
|
-
### Setting Up Handlers
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
import { Purchases } from '@revenuecat/purchases-capacitor';
|
|
122
|
-
import {
|
|
123
|
-
PurchaseCompletedHandler,
|
|
124
|
-
PurchaseErrorHandler,
|
|
125
|
-
CustomerInfoChangedHandler,
|
|
126
|
-
} from './handlers';
|
|
127
|
-
|
|
128
|
-
// Set up customer info listener
|
|
129
|
-
Purchases.addCustomerInfoUpdateListener((customerInfo) => {
|
|
130
|
-
const handler = new CustomerInfoChangedHandler();
|
|
131
|
-
handler.handle(customerInfo);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// Use in purchase flow
|
|
135
|
-
async function purchasePackage(pkg: Package) {
|
|
136
|
-
const completedHandler = new PurchaseCompletedHandler();
|
|
137
|
-
const errorHandler = new PurchaseErrorHandler();
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const result = await Purchases.purchasePackage({ aPackage: pkg });
|
|
141
|
-
await completedHandler.handle(result);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
await errorHandler.handle(error);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Custom Handlers
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
class CustomPurchaseHandler {
|
|
152
|
-
constructor(
|
|
153
|
-
private onSuccess: (result: PurchaseResult) => void,
|
|
154
|
-
private onError: (error: PurchasesError) => void
|
|
155
|
-
) {}
|
|
156
|
-
|
|
157
|
-
async handle(result: PurchaseResult | PurchasesError): Promise<void> {
|
|
158
|
-
if (isPurchasesError(result)) {
|
|
159
|
-
await this.onError(result);
|
|
160
|
-
} else {
|
|
161
|
-
await this.onSuccess(result);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Usage
|
|
167
|
-
const handler = new CustomPurchaseHandler(
|
|
168
|
-
(result) => {
|
|
169
|
-
Alert.alert('Success', 'Purchase completed!');
|
|
170
|
-
navigation.navigate('Home');
|
|
171
|
-
},
|
|
172
|
-
(error) => {
|
|
173
|
-
Alert.alert('Error', error.message);
|
|
174
|
-
}
|
|
175
|
-
);
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Error Categorization
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
function categorizeError(error: PurchasesError): ErrorCategory {
|
|
182
|
-
switch (error.code) {
|
|
183
|
-
case 'PURCHASE_CANCELLED':
|
|
184
|
-
return 'cancelled';
|
|
185
|
-
case 'NETWORK_ERROR':
|
|
186
|
-
return 'network';
|
|
187
|
-
case 'INVALID_CREDENTIALS_ERROR':
|
|
188
|
-
return 'config';
|
|
189
|
-
case 'PRODUCT_NOT_AVAILABLE_FOR_PURCHASE':
|
|
190
|
-
return 'availability';
|
|
191
|
-
default:
|
|
192
|
-
return 'unknown';
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
type ErrorCategory =
|
|
197
|
-
| 'cancelled'
|
|
198
|
-
| 'network'
|
|
199
|
-
| 'config'
|
|
200
|
-
| 'availability'
|
|
201
|
-
| 'unknown';
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Best Practices
|
|
205
|
-
|
|
206
|
-
1. **Immutability**: Don't mutate incoming data
|
|
207
|
-
2. **Error Boundaries**: Handle errors gracefully
|
|
208
|
-
3. **Logging**: Log all events for debugging
|
|
209
|
-
4. **Callbacks**: Invoke registered callbacks appropriately
|
|
210
|
-
5. **Async Handling**: Use async/await for asynchronous operations
|
|
211
|
-
6. **Validation**: Validate incoming data before processing
|
|
212
|
-
7. **Recovery**: Provide recovery options when possible
|
|
213
|
-
|
|
214
|
-
## Related
|
|
215
|
-
|
|
6
|
+
## Strategy
|
|
7
|
+
This directory contains handler implementations for managing RevenueCat events including purchase callbacks, customer info changes, and error handling with proper data validation and recovery.
|
|
8
|
+
|
|
9
|
+
## Restrictions
|
|
10
|
+
|
|
11
|
+
### REQUIRED
|
|
12
|
+
- Must handle all events gracefully
|
|
13
|
+
- Must validate incoming data before processing
|
|
14
|
+
- Must log all events for debugging
|
|
15
|
+
- Must invoke callbacks appropriately
|
|
16
|
+
|
|
17
|
+
### PROHIBITED
|
|
18
|
+
- DO NOT mutate incoming event data
|
|
19
|
+
- DO NOT ignore event handling errors
|
|
20
|
+
- DO NOT skip callback invocation
|
|
21
|
+
- DO NOT process unvalidated data
|
|
22
|
+
|
|
23
|
+
### CRITICAL SAFETY
|
|
24
|
+
- All incoming data MUST be validated
|
|
25
|
+
- All events MUST be logged for debugging
|
|
26
|
+
- All callbacks MUST be invoked appropriately
|
|
27
|
+
- Recovery options MUST be provided when possible
|
|
28
|
+
|
|
29
|
+
## AI Agent Guidelines
|
|
30
|
+
1. Never mutate incoming event data
|
|
31
|
+
2. Handle all errors gracefully with proper boundaries
|
|
32
|
+
3. Log all events for debugging and monitoring
|
|
33
|
+
4. Invoke registered callbacks appropriately
|
|
34
|
+
5. Use async/await for asynchronous operations
|
|
35
|
+
6. Validate all incoming data before processing
|
|
36
|
+
7. Provide recovery options when possible
|
|
37
|
+
|
|
38
|
+
## Related Documentation
|
|
216
39
|
- [RevenueCat Infrastructure](../README.md)
|
|
217
40
|
- [RevenueCat Services](../services/README.md)
|
|
218
41
|
- [RevenueCat Errors](../../domain/errors/README.md)
|
|
@@ -1,49 +1,41 @@
|
|
|
1
1
|
# RevenueCat Infrastructure Managers
|
|
2
2
|
|
|
3
|
+
## Location
|
|
3
4
|
Manager classes for coordinating RevenueCat operations.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// Get entitlement ID
|
|
39
|
-
const entitlementId = SubscriptionManager.getEntitlementId();
|
|
40
|
-
|
|
41
|
-
// Get offerings
|
|
42
|
-
const offerings = await SubscriptionManager.getOfferings();
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Related
|
|
46
|
-
|
|
6
|
+
## Strategy
|
|
7
|
+
This directory contains high-level manager classes that coordinate between different RevenueCat services and handle complex operations like SDK configuration and entitlement access.
|
|
8
|
+
|
|
9
|
+
## Restrictions
|
|
10
|
+
|
|
11
|
+
### REQUIRED
|
|
12
|
+
- Must coordinate between services properly
|
|
13
|
+
- Must manage SDK configuration correctly
|
|
14
|
+
- Must provide access to entitlement IDs
|
|
15
|
+
- Must handle complex operations safely
|
|
16
|
+
|
|
17
|
+
### PROHIBITED
|
|
18
|
+
- DO NOT bypass service coordination
|
|
19
|
+
- DO NOT misconfigure SDK
|
|
20
|
+
- DO NOT expose invalid entitlement IDs
|
|
21
|
+
- DO NOT oversimplify complex operations
|
|
22
|
+
|
|
23
|
+
### CRITICAL SAFETY
|
|
24
|
+
- Service coordination MUST be correct
|
|
25
|
+
- SDK configuration MUST be valid
|
|
26
|
+
- Entitlement access MUST be safe
|
|
27
|
+
- Complex operations MUST be handled properly
|
|
28
|
+
|
|
29
|
+
## AI Agent Guidelines
|
|
30
|
+
1. Coordinate properly between RevenueCat services
|
|
31
|
+
2. Manage SDK configuration with correct parameters
|
|
32
|
+
3. Provide safe access to entitlement IDs
|
|
33
|
+
4. Handle complex operations with proper error handling
|
|
34
|
+
5. Test coordination logic thoroughly
|
|
35
|
+
6. Document manager responsibilities clearly
|
|
36
|
+
7. Maintain separation of concerns
|
|
37
|
+
|
|
38
|
+
## Related Documentation
|
|
47
39
|
- [Services](../services/README.md)
|
|
48
40
|
- [Config](../config/README.md)
|
|
49
41
|
- [Domain](../../domain/README.md)
|
|
@@ -1,325 +1,42 @@
|
|
|
1
1
|
# RevenueCat Infrastructure Services
|
|
2
2
|
|
|
3
|
+
## Location
|
|
3
4
|
Service implementations for RevenueCat operations.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return await Purchases.purchaseProduct({ productIdentifier: productId });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async restorePurchases(): Promise<RestoreResult> {
|
|
43
|
-
return await Purchases.restorePurchases();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Customer Information
|
|
47
|
-
async getCustomerInfo(): Promise<CustomerInfo> {
|
|
48
|
-
return await Purchases.getCustomerInfo();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async getCustomerInfoUserId(): Promise<string | null> {
|
|
52
|
-
const info = await this.getCustomerInfo();
|
|
53
|
-
return info.originalAppUserId;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Offerings
|
|
57
|
-
async getOfferings(): Promise<Offerings> {
|
|
58
|
-
return await Purchases.getOfferings();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async getCurrentOffering(): Promise<Offering | null> {
|
|
62
|
-
const offerings = await this.getOfferings();
|
|
63
|
-
return offerings.current;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Entitlements
|
|
67
|
-
async checkEntitlement(entitlementId: string): Promise<boolean> {
|
|
68
|
-
const info = await this.getCustomerInfo();
|
|
69
|
-
return info.entitlements[entitlementId]?.isActive ?? false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async checkEntitlementInfo(
|
|
73
|
-
entitlementId: string
|
|
74
|
-
): Promise<EntitlementInfo | null> {
|
|
75
|
-
const info = await this.getCustomerInfo();
|
|
76
|
-
return info.entitlements[entitlementId] ?? null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Subscriber Attributes
|
|
80
|
-
async setAttributes(attributes: SubscriberAttributes): Promise<void> {
|
|
81
|
-
await Purchases.setAttributes(attributes);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async setEmail(email: string): Promise<void> {
|
|
85
|
-
await Purchases.setEmail(email);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async setPhoneNumber(phoneNumber: string): Promise<void> {
|
|
89
|
-
await Purchases.setPhoneNumber(phoneNumber);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Log Out
|
|
93
|
-
async logOut(): Promise<void> {
|
|
94
|
-
await Purchases.logOut();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### RevenueCatServiceProvider
|
|
100
|
-
|
|
101
|
-
Provides the RevenueCat service instance.
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
class RevenueCatServiceProvider {
|
|
105
|
-
private static instance: IRevenueCatService | null = null;
|
|
106
|
-
|
|
107
|
-
static getInstance(): IRevenueCatService {
|
|
108
|
-
if (!this.instance) {
|
|
109
|
-
this.instance = new RevenueCatServiceImpl();
|
|
110
|
-
}
|
|
111
|
-
return this.instance;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
static configure(config: {
|
|
115
|
-
apiKey: string;
|
|
116
|
-
userId?: string;
|
|
117
|
-
observerMode?: boolean;
|
|
118
|
-
}): IRevenueCatService {
|
|
119
|
-
const service = this.getInstance();
|
|
120
|
-
service.configure(config);
|
|
121
|
-
return service;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Usage
|
|
127
|
-
|
|
128
|
-
### Initializing the Service
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
import { RevenueCatServiceProvider } from './services/RevenueCatServiceProvider';
|
|
132
|
-
|
|
133
|
-
// Configure RevenueCat
|
|
134
|
-
const service = RevenueCatServiceProvider.configure({
|
|
135
|
-
apiKey: 'your_api_key',
|
|
136
|
-
userId: user?.uid,
|
|
137
|
-
observerMode: false,
|
|
138
|
-
});
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Making Purchases
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
import { RevenueCatServiceProvider } from './services/RevenueCatServiceProvider';
|
|
145
|
-
|
|
146
|
-
async function purchasePremium(userId: string) {
|
|
147
|
-
const service = RevenueCatServiceProvider.getInstance();
|
|
148
|
-
|
|
149
|
-
// Get offerings
|
|
150
|
-
const offerings = await service.getOfferings();
|
|
151
|
-
const monthlyPackage = offerings.current?.monthly;
|
|
152
|
-
|
|
153
|
-
if (!monthlyPackage) {
|
|
154
|
-
throw new Error('No package available');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Purchase
|
|
158
|
-
const result = await service.purchasePackage(monthlyPackage);
|
|
159
|
-
|
|
160
|
-
if (result.error) {
|
|
161
|
-
throw new Error(result.error.message);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Check entitlement
|
|
165
|
-
const hasPremium = await service.checkEntitlement('premium');
|
|
166
|
-
if (hasPremium) {
|
|
167
|
-
console.log('Premium activated!');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return result;
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Restoring Purchases
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
async function restorePurchases() {
|
|
178
|
-
const service = RevenueCatServiceProvider.getInstance();
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
const result = await service.restorePurchases();
|
|
182
|
-
|
|
183
|
-
if (result.error) {
|
|
184
|
-
throw new Error(result.error.message);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Update local state
|
|
188
|
-
await updateSubscriptionStatus(result.customerInfo);
|
|
189
|
-
|
|
190
|
-
return result.customerInfo;
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.error('Restore failed:', error);
|
|
193
|
-
throw error;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### Checking Entitlements
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
async function checkPremiumAccess(): Promise<boolean> {
|
|
202
|
-
const service = RevenueCatServiceProvider.getInstance();
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const isActive = await service.checkEntitlement('premium');
|
|
206
|
-
|
|
207
|
-
if (isActive) {
|
|
208
|
-
const entitlement = await service.checkEntitlementInfo('premium');
|
|
209
|
-
console.log('Premium details:', entitlement);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return isActive;
|
|
213
|
-
} catch (error) {
|
|
214
|
-
console.error('Failed to check entitlement:', error);
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Setting Subscriber Attributes
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
async function updateUserAttributes(user: User) {
|
|
224
|
-
const service = RevenueCatServiceProvider.getInstance();
|
|
225
|
-
|
|
226
|
-
// Set email
|
|
227
|
-
if (user.email) {
|
|
228
|
-
await service.setEmail(user.email);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Set phone number
|
|
232
|
-
if (user.phoneNumber) {
|
|
233
|
-
await service.setPhoneNumber(user.phoneNumber);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Set custom attributes
|
|
237
|
-
await service.setAttributes({
|
|
238
|
-
$displayName: user.displayName,
|
|
239
|
-
account_created_at: user.createdAt.toISOString(),
|
|
240
|
-
subscription_tier: user.tier,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
## Error Handling
|
|
246
|
-
|
|
247
|
-
### Wrapping Service Calls
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
class RevenueCatServiceWrapper {
|
|
251
|
-
private service: IRevenueCatService;
|
|
252
|
-
|
|
253
|
-
constructor() {
|
|
254
|
-
this.service = RevenueCatServiceProvider.getInstance();
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
async safePurchase(
|
|
258
|
-
pkg: Package
|
|
259
|
-
): Promise<PurchaseResult> {
|
|
260
|
-
try {
|
|
261
|
-
return await this.service.purchasePackage(pkg);
|
|
262
|
-
} catch (error) {
|
|
263
|
-
// Convert to domain error
|
|
264
|
-
if (isPurchasesError(error)) {
|
|
265
|
-
return {
|
|
266
|
-
customerInfo: {} as CustomerInfo,
|
|
267
|
-
error,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
throw error;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async safeGetOfferings(): Promise<Offerings | null> {
|
|
275
|
-
try {
|
|
276
|
-
return await this.getOfferings();
|
|
277
|
-
} catch (error) {
|
|
278
|
-
console.error('Failed to get offerings:', error);
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## Testing
|
|
286
|
-
|
|
287
|
-
### Mock Implementation
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
class MockRevenueCatService implements IRevenueCatService {
|
|
291
|
-
async configure(): Promise<void> {
|
|
292
|
-
console.log('Mock: Configure called');
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async purchasePackage(pkg: Package): Promise<PurchaseResult> {
|
|
296
|
-
console.log('Mock: Purchase package', pkg.identifier);
|
|
297
|
-
return {
|
|
298
|
-
customerInfo: mockCustomerInfo,
|
|
299
|
-
transaction: mockTransaction,
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// ... other mock implementations
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Use in tests
|
|
307
|
-
const mockService = new MockRevenueCatService();
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
## Best Practices
|
|
311
|
-
|
|
312
|
-
1. **Singleton**: Use singleton pattern for service instance
|
|
313
|
-
2. **Error Handling**: Wrap all service calls in try-catch
|
|
314
|
-
3. **Type Safety**: Use TypeScript types for all operations
|
|
315
|
-
4. **Logging**: Log all RevenueCat operations
|
|
316
|
-
5. **Caching**: Cache customer info and offerings appropriately
|
|
317
|
-
6. **Validation**: Validate parameters before calling SDK
|
|
318
|
-
7. **Configuration**: Configure service only once
|
|
319
|
-
8. **Testing**: Use mock implementations for testing
|
|
320
|
-
|
|
321
|
-
## Related
|
|
322
|
-
|
|
6
|
+
## Strategy
|
|
7
|
+
This directory contains concrete implementations of RevenueCat interfaces, providing the actual integration with the RevenueCat SDK using singleton pattern with proper error handling and caching.
|
|
8
|
+
|
|
9
|
+
## Restrictions
|
|
10
|
+
|
|
11
|
+
### REQUIRED
|
|
12
|
+
- Must use singleton pattern for service instance
|
|
13
|
+
- Must wrap all service calls in try-catch
|
|
14
|
+
- Must use TypeScript types for all operations
|
|
15
|
+
- Must log all RevenueCat operations
|
|
16
|
+
|
|
17
|
+
### PROHIBITED
|
|
18
|
+
- DO NOT create multiple service instances
|
|
19
|
+
- DO NOT skip error handling
|
|
20
|
+
- DO NOT bypass type safety
|
|
21
|
+
- DO NOT skip logging operations
|
|
22
|
+
|
|
23
|
+
### CRITICAL SAFETY
|
|
24
|
+
- Service MUST be configured only once
|
|
25
|
+
- All calls MUST be wrapped in error handling
|
|
26
|
+
- All operations MUST be type-safe
|
|
27
|
+
- All operations MUST be logged
|
|
28
|
+
|
|
29
|
+
## AI Agent Guidelines
|
|
30
|
+
1. Use singleton pattern for service instance
|
|
31
|
+
2. Wrap all service calls in try-catch blocks
|
|
32
|
+
3. Use TypeScript types for all operations
|
|
33
|
+
4. Log all RevenueCat operations for debugging
|
|
34
|
+
5. Cache customer info and offerings appropriately
|
|
35
|
+
6. Validate parameters before calling SDK
|
|
36
|
+
7. Configure service only once at startup
|
|
37
|
+
8. Use mock implementations for testing
|
|
38
|
+
|
|
39
|
+
## Related Documentation
|
|
323
40
|
- [RevenueCat Infrastructure](../README.md)
|
|
324
41
|
- [RevenueCat Handlers](../handlers/README.md)
|
|
325
42
|
- [RevenueCat Application Ports](../../application/ports/README.md)
|