@umituz/react-native-subscription 2.14.98 → 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.
@@ -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)
@@ -0,0 +1,169 @@
1
+ # RevenueCat Application Ports
2
+
3
+ Interface definitions for RevenueCat service layer.
4
+
5
+ ## Overview
6
+
7
+ This directory defines the ports (interfaces) that the RevenueCat infrastructure must implement. Following Dependency Inversion Principle, the application layer depends on these abstractions rather than concrete implementations.
8
+
9
+ ## Ports
10
+
11
+ ### IRevenueCatService
12
+
13
+ Core service interface for RevenueCat operations.
14
+
15
+ ```typescript
16
+ interface IRevenueCatService {
17
+ // Configuration
18
+ configure(params: {
19
+ apiKey: string;
20
+ userId?: string;
21
+ observerMode?: boolean;
22
+ }): Promise<void>;
23
+
24
+ // Purchasing Operations
25
+ purchasePackage(pkg: PurchasesPackage): Promise<PurchaseResult>;
26
+ purchaseProduct(productId: string): Promise<PurchaseResult>;
27
+ restorePurchases(): Promise<RestoreResult>;
28
+
29
+ // Customer Information
30
+ getCustomerInfo(): Promise<CustomerInfo>;
31
+ getCustomerInfoUserId(): string | null;
32
+
33
+ // Offerings
34
+ getOfferings(): Promise<Offerings>;
35
+ getCurrentOffering(): Promise<Offering | null>;
36
+
37
+ // Entitlements
38
+ checkEntitlement(entitlementId: string): Promise<boolean>;
39
+ checkEntitlementInfo(entitlementId: string): Promise<EntitlementInfo | null>;
40
+
41
+ // Subscriber Attributes
42
+ setAttributes(attributes: SubscriberAttributes): Promise<void>;
43
+ setEmail(email: string): Promise<void>;
44
+ setPhoneNumber(phoneNumber: string): Promise<void>;
45
+
46
+ // Log Out
47
+ logOut(): Promise<void>;
48
+ }
49
+ ```
50
+
51
+ ### IRevenueCatPurchaseHandler
52
+
53
+ Handles purchase callbacks and results.
54
+
55
+ ```typescript
56
+ interface IRevenueCatPurchaseHandler {
57
+ onPurchaseStarted?(package: PurchasesPackage): void;
58
+ onPurchaseSuccess?(result: PurchaseResult): void;
59
+ onPurchaseError?(error: PurchaseError): void;
60
+ onPurchaseCancelled?(result: PurchaseResult): void;
61
+ onRestoreStarted?(): void;
62
+ onRestoreSuccess?(result: RestoreResult): void;
63
+ onRestoreError?(error: Error): void;
64
+ }
65
+ ```
66
+
67
+ ### IRevenueCatCustomerInfoListener
68
+
69
+ Listens to customer info changes.
70
+
71
+ ```typescript
72
+ interface IRevenueCatCustomerInfoListener {
73
+ onCustomerInfoChanged(customerInfo: CustomerInfo): void;
74
+ onEntitlementsChanged(entitlements: EntitlementInfos): void;
75
+ }
76
+ ```
77
+
78
+ ## Usage
79
+
80
+ ### Injecting Dependencies
81
+
82
+ ```typescript
83
+ import type { IRevenueCatService } from './ports/IRevenueCatService';
84
+
85
+ class PurchaseManager {
86
+ constructor(
87
+ private revenueCatService: IRevenueCatService
88
+ ) {}
89
+
90
+ async purchasePremium() {
91
+ const offerings = await this.revenueCatService.getOfferings();
92
+ const package = offerings.current?.monthly;
93
+
94
+ if (!package) {
95
+ throw new Error('No package available');
96
+ }
97
+
98
+ return await this.revenueCatService.purchasePackage(package);
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### Implementing Ports
104
+
105
+ ```typescript
106
+ import type { IRevenueCatService } from './ports/IRevenueCatService';
107
+ import { Purchases } from '@revenuecat/purchases-capacitor';
108
+
109
+ class RevenueCatServiceImpl implements IRevenueCatService {
110
+ async configure(params: {
111
+ apiKey: string;
112
+ userId?: string;
113
+ observerMode?: boolean;
114
+ }): Promise<void> {
115
+ await Purchases.configure({
116
+ apiKey: params.apiKey,
117
+ appUserID: params.userId,
118
+ observerMode: params.observerMode ?? false,
119
+ });
120
+ }
121
+
122
+ async purchasePackage(pkg: PurchasesPackage): Promise<PurchaseResult> {
123
+ return await Purchases.purchasePackage({
124
+ aPackage: pkg,
125
+ });
126
+ }
127
+
128
+ // ... other implementations
129
+ }
130
+
131
+ // Provide implementation
132
+ const revenueCatService: IRevenueCatService = new RevenueCatServiceImpl();
133
+ ```
134
+
135
+ ## Benefits of Ports
136
+
137
+ 1. **Testability**: Easy to mock for testing
138
+ 2. **Flexibility**: Can swap implementations
139
+ 3. **Decoupling**: Application layer doesn't depend on concrete implementations
140
+ 4. **Maintainability**: Clear contracts between layers
141
+
142
+ ## Testing with Mocks
143
+
144
+ ```typescript
145
+ import type { IRevenueCatService } from './ports/IRevenueCatService';
146
+
147
+ const mockRevenueCatService: IRevenueCatService = {
148
+ configure: vi.fn().mockResolvedValue(undefined),
149
+ purchasePackage: vi.fn().mockResolvedValue({
150
+ customerInfo: mockCustomerInfo,
151
+ }),
152
+ restorePurchases: vi.fn().mockResolvedValue({
153
+ customerInfo: mockCustomerInfo,
154
+ }),
155
+ getCustomerInfo: vi.fn().mockResolvedValue(mockCustomerInfo),
156
+ getOfferings: vi.fn().mockResolvedValue(mockOfferings),
157
+ checkEntitlement: vi.fn().mockResolvedValue(true),
158
+ // ... other methods
159
+ };
160
+
161
+ // Use in tests
162
+ const manager = new PurchaseManager(mockRevenueCatService);
163
+ ```
164
+
165
+ ## Related
166
+
167
+ - [RevenueCat Application Layer](../README.md)
168
+ - [RevenueCat Infrastructure](../infrastructure/README.md)
169
+ - [RevenueCat Domain](../domain/README.md)
@@ -0,0 +1,183 @@
1
+ # RevenueCat Domain Constants
2
+
3
+ Constants used throughout the RevenueCat integration.
4
+
5
+ ## Overview
6
+
7
+ This directory contains constant definitions for RevenueCat-specific values including entitlement IDs, error codes, and configuration defaults.
8
+
9
+ ## Constants
10
+
11
+ ### Entitlement IDs
12
+
13
+ ```typescript
14
+ export const ENTITLEMENT_IDS = {
15
+ PREMIUM: 'premium',
16
+ PRO: 'pro',
17
+ LIFETIME: 'lifetime',
18
+ } as const;
19
+
20
+ export type EntitlementId = typeof ENTITLEMENT_IDS[keyof typeof ENTITLEMENT_IDS];
21
+ ```
22
+
23
+ ### Offering Identifiers
24
+
25
+ ```typescript
26
+ export const OFFERING_IDS = {
27
+ DEFAULT: 'default',
28
+ ANNUAL_OFFER: 'annual_offer',
29
+ LIFETIME_OFFER: 'lifetime_offer',
30
+ } as const;
31
+
32
+ export type OfferingId = typeof OFFERING_IDS[keyof typeof OFFERING_IDS];
33
+ ```
34
+
35
+ ### Package Periods
36
+
37
+ ```typescript
38
+ export const PACKAGE_PERIODS = {
39
+ MONTHLY: 'monthly',
40
+ ANNUAL: 'annual',
41
+ LIFETIME: 'lifetime',
42
+ WEEKLY: 'weekly',
43
+ } as const;
44
+
45
+ export type PackagePeriod = typeof PACKAGE_PERIODS[keyof typeof PACKAGE_PERIODS];
46
+ ```
47
+
48
+ ### Product Categories
49
+
50
+ ```typescript
51
+ export const PRODUCT_CATEGORIES = {
52
+ SUBSCRIPTION: 'subscription',
53
+ ONE_TIME_PURCHASE: 'non_subscription',
54
+ CONSUMABLE: 'consumable',
55
+ } as const;
56
+
57
+ export type ProductCategory = typeof PRODUCT_CATEGORIES[keyof typeof PRODUCT_CATEGORIES];
58
+ ```
59
+
60
+ ### Error Codes
61
+
62
+ ```typescript
63
+ export const ERROR_CODES = {
64
+ // Configuration Errors
65
+ CONFIGURATION_ERROR: 'CONFIGURATION_ERROR',
66
+ API_KEY_NOT_SET: 'API_KEY_NOT_SET',
67
+ INVALID_API_KEY: 'INVALID_API_KEY',
68
+
69
+ // Purchase Errors
70
+ PURCHASE_ERROR: 'PURCHASE_ERROR',
71
+ PURCHASE_CANCELLED: 'PURCHASE_CANCELLED',
72
+ PURCHASE_INVALID: 'PURCHASE_INVALID',
73
+ PRODUCT_NOT_AVAILABLE: 'PRODUCT_NOT_AVAILABLE',
74
+
75
+ // Network Errors
76
+ NETWORK_ERROR: 'NETWORK_ERROR',
77
+ CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT',
78
+ SERVER_ERROR: 'SERVER_ERROR',
79
+
80
+ // Customer Info Errors
81
+ CUSTOMER_INFO_ERROR: 'CUSTOMER_INFO_ERROR',
82
+ ENTITLEMENT_NOT_FOUND: 'ENTITLEMENT_NOT_FOUND',
83
+ OFFERING_NOT_FOUND: 'OFFERING_NOT_FOUND',
84
+
85
+ // Receipt Errors
86
+ RECEIPT_ERROR: 'RECEIPT_ERROR',
87
+ RECEIPT_INVALID: 'RECEIPT_INVALID',
88
+ RECEIPT_ALREADY_USED: 'RECEIPT_ALREADY_USES',
89
+
90
+ // User Errors
91
+ USER_NOT_AUTHENTICATED: 'USER_NOT_AUTHENTICATED',
92
+ USER_ID_NOT_SET: 'USER_ID_NOT_SET',
93
+ } as const;
94
+ ```
95
+
96
+ ### Configuration Defaults
97
+
98
+ ```typescript
99
+ export const CONFIG_DEFAULTS = {
100
+ // Timeout Settings
101
+ NETWORK_TIMEOUT_MS: 10000,
102
+ PURCHASE_TIMEOUT_MS: 30000,
103
+
104
+ // Retry Settings
105
+ MAX_RETRIES: 3,
106
+ RETRY_DELAY_MS: 1000,
107
+
108
+ // Cache Settings
109
+ CUSTOMER_INFO_CACHE_TTL_MS: 60000, // 1 minute
110
+ OFFERINGS_CACHE_TTL_MS: 300000, // 5 minutes
111
+
112
+ // Debug Settings
113
+ ENABLE_DEBUG_LOGS: __DEV__,
114
+ LOG_LEVEL: 'info' as const,
115
+ } as const;
116
+ ```
117
+
118
+ ### Entitlement Verification
119
+
120
+ ```typescript
121
+ export const ENTITLEMENT_VERIFICATION = {
122
+ // Verification Modes
123
+ MODE_STRICT: 'strict',
124
+ MODE_LOOSE: 'loose',
125
+
126
+ // Grace Periods (milliseconds)
127
+ GRACE_PERIOD_MS: 3 * 24 * 60 * 60 * 1000, // 3 days
128
+ REFUND_GRACE_PERIOD_MS: 7 * 24 * 60 * 60 * 1000, // 7 days
129
+ } as const;
130
+ ```
131
+
132
+ ## Usage Examples
133
+
134
+ ### Checking Entitlements
135
+
136
+ ```typescript
137
+ import { ENTITLEMENT_IDS } from './constants';
138
+
139
+ const hasPremium = await checkEntitlement(ENTITLEMENT_IDS.PREMIUM);
140
+ ```
141
+
142
+ ### Filtering Offerings
143
+
144
+ ```typescript
145
+ import { OFFERING_IDS } from './constants';
146
+
147
+ const defaultOffering = offerings[OFFERING_IDS.DEFAULT];
148
+ const annualOffering = offerings[OFFERING_IDS.ANNUAL_OFFER];
149
+ ```
150
+
151
+ ### Handling Errors
152
+
153
+ ```typescript
154
+ import { ERROR_CODES } from './constants';
155
+
156
+ try {
157
+ await purchasePackage(pkg);
158
+ } catch (error) {
159
+ if (error.code === ERROR_CODES.PURCHASE_CANCELLED) {
160
+ // Handle cancellation
161
+ } else if (error.code === ERROR_CODES.NETWORK_ERROR) {
162
+ // Handle network error
163
+ }
164
+ }
165
+ ```
166
+
167
+ ### Configuration
168
+
169
+ ```typescript
170
+ import { CONFIG_DEFAULTS } from './constants';
171
+
172
+ const config = {
173
+ apiKey: 'your_api_key',
174
+ timeout: CONFIG_DEFAULTS.NETWORK_TIMEOUT_MS,
175
+ enableDebugLogs: CONFIG_DEFAULTS.ENABLE_DEBUG_LOGS,
176
+ };
177
+ ```
178
+
179
+ ## Related
180
+
181
+ - [RevenueCat Domain](../README.md)
182
+ - [RevenueCat Errors](../errors/README.md)
183
+ - [RevenueCat Entities](../entities/README.md)