@umituz/react-native-subscription 2.27.65 → 2.27.67

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 (121) hide show
  1. package/package.json +3 -1
  2. package/src/domains/credits/application/CreditLimitCalculator.ts +17 -0
  3. package/src/domains/credits/application/CreditsInitializer.ts +85 -0
  4. package/src/domains/credits/application/DeductCreditsCommand.ts +52 -0
  5. package/src/domains/credits/application/PurchaseMetadataGenerator.ts +59 -0
  6. package/src/domains/credits/application/credit-strategies/CreditAllocationContext.ts +35 -0
  7. package/src/domains/credits/application/credit-strategies/ICreditStrategy.ts +18 -0
  8. package/src/domains/credits/application/credit-strategies/StandardPurchaseCreditStrategy.ts +16 -0
  9. package/src/domains/credits/application/credit-strategies/SyncCreditStrategy.ts +15 -0
  10. package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +18 -0
  11. package/src/{infrastructure/mappers → domains/credits/core}/CreditsMapper.ts +4 -4
  12. package/src/domains/credits/infrastructure/CreditsRepository.ts +102 -0
  13. package/src/{presentation/hooks → domains/credits/presentation}/useCredits.ts +21 -4
  14. package/src/domains/subscription/application/SubscriptionAuthListener.ts +26 -0
  15. package/src/domains/subscription/application/SubscriptionInitializer.ts +77 -0
  16. package/src/{infrastructure/services → domains/subscription/application}/SubscriptionInitializerTypes.ts +21 -1
  17. package/src/domains/subscription/application/SubscriptionSyncService.ts +71 -0
  18. package/src/domains/subscription/application/SubscriptionSyncUtils.ts +16 -0
  19. package/src/{revenuecat/domain/value-objects → domains/subscription/core}/RevenueCatConfig.ts +1 -1
  20. package/src/{domain/types → domains/subscription/core}/RevenueCatData.ts +1 -1
  21. package/src/{domain/entities → domains/subscription/core}/SubscriptionStatus.ts +13 -21
  22. package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +51 -0
  23. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +67 -0
  24. package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +27 -0
  25. package/src/domains/subscription/infrastructure/managers/SubscriptionInternalState.ts +12 -0
  26. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +110 -0
  27. package/src/{presentation/hooks → domains/subscription/presentation}/usePremium.ts +7 -4
  28. package/src/domains/trial/application/TrialEligibilityService.ts +25 -0
  29. package/src/domains/trial/application/TrialService.ts +68 -0
  30. package/src/{infrastructure/services → domains/trial/core}/TrialTypes.ts +1 -1
  31. package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +30 -0
  32. package/src/presentation/components/details/PremiumStatusBadge.tsx +2 -2
  33. package/src/presentation/hooks/index.ts +11 -11
  34. package/src/shared/infrastructure/SubscriptionEventBus.ts +51 -0
  35. package/src/utils/packageTypeDetector.ts +13 -18
  36. package/src/application/README.md +0 -50
  37. package/src/domain/entities/README.md +0 -50
  38. package/src/domain/entities/SubscriptionStatus.test.ts +0 -105
  39. package/src/domain/errors/README.md +0 -53
  40. package/src/domain/value-objects/README.md +0 -50
  41. package/src/infrastructure/README.md +0 -55
  42. package/src/infrastructure/mappers/README.md +0 -21
  43. package/src/infrastructure/models/README.md +0 -26
  44. package/src/infrastructure/repositories/CreditsRepository.ts +0 -132
  45. package/src/infrastructure/repositories/README.md +0 -99
  46. package/src/infrastructure/services/CreditsInitializer.ts +0 -170
  47. package/src/infrastructure/services/README.md +0 -99
  48. package/src/infrastructure/services/SubscriptionInitializer.ts +0 -176
  49. package/src/infrastructure/services/SubscriptionService.ts +0 -133
  50. package/src/infrastructure/services/TrialService.ts +0 -197
  51. package/src/infrastructure/services/app-service-helpers.ts +0 -111
  52. package/src/revenuecat/README.md +0 -104
  53. package/src/revenuecat/application/README.md +0 -43
  54. package/src/revenuecat/application/ports/IRevenueCatService.ts +0 -76
  55. package/src/revenuecat/application/ports/README.md +0 -41
  56. package/src/revenuecat/domain/README.md +0 -48
  57. package/src/revenuecat/domain/constants/README.md +0 -41
  58. package/src/revenuecat/domain/entities/README.md +0 -42
  59. package/src/revenuecat/domain/errors/README.md +0 -53
  60. package/src/revenuecat/domain/types/README.md +0 -41
  61. package/src/revenuecat/domain/value-objects/README.md +0 -41
  62. package/src/revenuecat/index.ts +0 -13
  63. package/src/revenuecat/infrastructure/handlers/PackageHandler.ts +0 -161
  64. package/src/revenuecat/infrastructure/managers/SubscriptionManager.ts +0 -165
  65. package/src/revenuecat/presentation/README.md +0 -42
  66. /package/src/{domain/entities → domains/credits/core}/Credits.ts +0 -0
  67. /package/src/{infrastructure/models → domains/credits/core}/UserCreditsDocument.ts +0 -0
  68. /package/src/{infrastructure/repositories → domains/credits/infrastructure}/CreditsRepositoryProvider.ts +0 -0
  69. /package/src/{presentation/hooks → domains/credits/presentation}/useDeductCredit.ts +0 -0
  70. /package/src/{revenuecat/domain/constants → domains/subscription/core}/RevenueCatConstants.ts +0 -0
  71. /package/src/{revenuecat/domain/errors → domains/subscription/core}/RevenueCatError.ts +0 -0
  72. /package/src/{revenuecat/domain/types → domains/subscription/core}/RevenueCatTypes.ts +0 -0
  73. /package/src/{domain/entities → domains/subscription/core}/SubscriptionConstants.ts +0 -0
  74. /package/src/{revenuecat → domains/subscription}/infrastructure/README.md +0 -0
  75. /package/src/{revenuecat → domains/subscription}/infrastructure/config/README.md +0 -0
  76. /package/src/{revenuecat → domains/subscription}/infrastructure/handlers/README.md +0 -0
  77. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/README.md +0 -0
  78. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/subscriptionQueryKeys.ts +0 -0
  79. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useCustomerInfo.ts +0 -0
  80. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useInitializeSubscription.ts +0 -0
  81. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePaywallFlow.ts +0 -0
  82. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePurchasePackage.ts +0 -0
  83. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRestorePurchase.ts +0 -0
  84. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCat.ts +0 -0
  85. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCatTrialEligibility.ts +0 -0
  86. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionPackages.ts +0 -0
  87. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionQueries.ts +0 -0
  88. /package/src/{revenuecat → domains/subscription}/infrastructure/managers/README.md +0 -0
  89. /package/src/{revenuecat → domains/subscription}/infrastructure/services/CustomerInfoListenerManager.ts +0 -0
  90. /package/src/{revenuecat → domains/subscription}/infrastructure/services/OfferingsFetcher.ts +0 -0
  91. /package/src/{revenuecat → domains/subscription}/infrastructure/services/PurchaseHandler.ts +0 -0
  92. /package/src/{revenuecat → domains/subscription}/infrastructure/services/README.md +0 -0
  93. /package/src/{revenuecat → domains/subscription}/infrastructure/services/RestoreHandler.ts +0 -0
  94. /package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatInitializer.ts +0 -0
  95. /package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatService.ts +0 -0
  96. /package/src/{revenuecat → domains/subscription}/infrastructure/services/ServiceStateManager.ts +0 -0
  97. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/ApiKeyResolver.ts +0 -0
  98. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/InitializationCache.ts +0 -0
  99. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/PremiumStatusSyncer.ts +0 -0
  100. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/README.md +0 -0
  101. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/RenewalDetector.ts +0 -0
  102. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/UserIdProvider.ts +0 -0
  103. /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthAwarePurchase.ts +0 -0
  104. /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthSubscriptionSync.ts +0 -0
  105. /package/src/{presentation/hooks → domains/subscription/presentation}/useFeatureGate.ts +0 -0
  106. /package/src/{presentation/hooks → domains/subscription/presentation}/usePaywallVisibility.ts +0 -0
  107. /package/src/{presentation/hooks → domains/subscription/presentation}/usePremiumGate.ts +0 -0
  108. /package/src/{presentation/hooks → domains/subscription/presentation}/useSavedPurchaseAutoExecution.ts +0 -0
  109. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.ts +0 -0
  110. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.utils.ts +0 -0
  111. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionStatus.ts +0 -0
  112. /package/src/{infrastructure/services → shared/application}/ActivationHandler.ts +0 -0
  113. /package/src/{infrastructure/services → shared/application}/FeedbackService.ts +0 -0
  114. /package/src/{application → shared/application}/ports/ISubscriptionRepository.ts +0 -0
  115. /package/src/{application → shared/application}/ports/ISubscriptionService.ts +0 -0
  116. /package/src/{application → shared/application}/ports/README.md +0 -0
  117. /package/src/{domain/errors → shared/utils}/InsufficientCreditsError.ts +0 -0
  118. /package/src/{infrastructure → shared}/utils/Logger.ts +0 -0
  119. /package/src/{domain/value-objects → shared/utils}/Result.ts +0 -0
  120. /package/src/{domain/value-objects → shared/utils}/SubscriptionConfig.ts +0 -0
  121. /package/src/{domain/errors → shared/utils}/SubscriptionError.ts +0 -0
@@ -1,42 +0,0 @@
1
- # RevenueCat Domain Entities
2
-
3
- ## Location
4
- Core domain entities for RevenueCat integration.
5
-
6
- ## Strategy
7
- This directory contains TypeScript type definitions and entities representing RevenueCat concepts like customer info, entitlements, offerings, and packages with proper type safety.
8
-
9
- ## Restrictions
10
-
11
- ### REQUIRED
12
- - Must use TypeScript types for all entities
13
- - Must maintain type safety
14
- - Must validate entity data
15
- - Must provide clear type definitions
16
-
17
- ### PROHIBITED
18
- - DO NOT use `any` type for entities
19
- - DO NOT bypass type checking
20
- - DO NOT create invalid entity states
21
- - DO NOT use loose type definitions
22
-
23
- ### CRITICAL SAFETY
24
- - All entities MUST be type-safe
25
- - Entity data MUST be validated
26
- - Type definitions MUST be clear and specific
27
- - Entity states MUST be valid
28
-
29
- ## AI Agent Guidelines
30
- 1. Define clear TypeScript types for all RevenueCat concepts
31
- 2. Maintain strict type safety for all entities
32
- 3. Validate entity data before use
33
- 4. Provide comprehensive type definitions
34
- 5. Document entity properties and relationships
35
- 6. Use type guards for runtime validation
36
- 7. Test entity creation and validation thoroughly
37
-
38
- ## Related Documentation
39
- - [RevenueCat Domain](../README.md)
40
- - [RevenueCat Value Objects](../value-objects/README.md)
41
- - [RevenueCat Errors](../errors/README.md)
42
- - [RevenueCat Types](../types/README.md)
@@ -1,53 +0,0 @@
1
- # RevenueCat Domain Errors
2
-
3
- Domain-specific errors for RevenueCat operations.
4
-
5
- ## Location
6
-
7
- `src/revenuecat/domain/errors/`
8
-
9
- ## Strategy
10
-
11
- Custom error classes for RevenueCat-specific error conditions, providing typed error handling for purchase, entitlement, configuration, and offering failures.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST use specific error types (not generic errors)
18
- - MUST include machine-readable error codes
19
- - MUST include relevant contextual data
20
- - MUST implement recovery strategies
21
- - MUST convert errors to user-friendly messages
22
- - MUST log errors for debugging
23
- - MUST track errors for analytics
24
-
25
- ### PROHIBITED
26
-
27
- - MUST NOT expose sensitive RevenueCat API keys or tokens
28
- - MUST NOT leak raw RevenueCat errors to users
29
- - MUST NOT ignore error conditions
30
-
31
- ### CRITICAL
32
-
33
- - Always implement specific error types
34
- - Include machine-readable codes for programmatic handling
35
- - Provide recovery strategies where applicable
36
- - Convert technical errors to user-friendly messages
37
- - Log all errors for debugging and monitoring
38
-
39
- ## AI Agent Guidelines
40
-
41
- When working with RevenueCat errors:
42
- 1. Specific Errors - use specific error types
43
- 2. Error Codes - include machine-readable codes
44
- 3. Context - include relevant data in error
45
- 4. Recovery - implement recovery strategies
46
- 5. User Feedback - convert errors to user-friendly messages
47
- 6. Logging - log errors for debugging
48
- 7. Analytics - track errors for monitoring
49
-
50
- ## Related Documentation
51
-
52
- - [RevenueCat Domain](../README.md)
53
- - [RevenueCat Services](../../infrastructure/services/README.md)
@@ -1,41 +0,0 @@
1
- # RevenueCat Domain Types
2
-
3
- ## Location
4
- Type definitions and type utilities for RevenueCat integration.
5
-
6
- ## Strategy
7
- This directory contains TypeScript type definitions, type guards, and utility types used throughout the RevenueCat integration for type safety and compile-time checks.
8
-
9
- ## Restrictions
10
-
11
- ### REQUIRED
12
- - Must use type guards for runtime checks
13
- - Must provide utility types for common operations
14
- - Must maintain type safety throughout
15
- - Must use strict TypeScript types
16
-
17
- ### PROHIBITED
18
- - DO NOT use `any` type
19
- - DO NOT bypass type guards
20
- - DO NOT ignore type safety
21
- - DO NOT use loose type definitions
22
-
23
- ### CRITICAL SAFETY
24
- - All type checks MUST use type guards
25
- - All types MUST be strictly defined
26
- - Type assertions MUST be safe
27
- - Utility types MUST be used appropriately
28
-
29
- ## AI Agent Guidelines
30
- 1. Use type guards for runtime type checking
31
- 2. Define utility types for common operations
32
- 3. Maintain strict type safety throughout codebase
33
- 4. Provide clear type definitions for all RevenueCat concepts
34
- 5. Use type assertions only when safe
35
- 6. Test type guards thoroughly
36
- 7. Document complex types with comments
37
-
38
- ## Related Documentation
39
- - [RevenueCat Domain](../README.md)
40
- - [RevenueCat Entities](../entities/README.md)
41
- - [RevenueCat Value Objects](../value-objects/README.md)
@@ -1,41 +0,0 @@
1
- # RevenueCat Domain Value Objects
2
-
3
- ## Location
4
- Value objects for RevenueCat integration.
5
-
6
- ## Strategy
7
- This directory contains value objects - immutable types that represent concepts in the RevenueCat domain with no identity, defined by their attributes rather than identity.
8
-
9
- ## Restrictions
10
-
11
- ### REQUIRED
12
- - Must validate in constructor
13
- - Must keep value objects immutable
14
- - Must implement proper equality based on values
15
- - Must provide locale-aware formatting
16
-
17
- ### PROHIBITED
18
- - DO NOT modify value objects after creation
19
- - DO NOT bypass validation in constructor
20
- - DO NOT use identity-based equality
21
- - DO NOT ignore locale in formatting
22
-
23
- ### CRITICAL SAFETY
24
- - All value objects MUST be immutable
25
- - Validation MUST happen in constructor
26
- - Equality MUST be based on values, not identity
27
- - Formatting MUST be locale-aware
28
-
29
- ## AI Agent Guidelines
30
- 1. Never modify value objects after creation
31
- 2. Validate all inputs in constructor with fail-fast
32
- 3. Implement proper equality based on values
33
- 4. Provide locale-aware formatting methods
34
- 5. Return new instances for all operations
35
- 6. Use TypeScript for compile-time type safety
36
- 7. Provide toString/JSON methods if needed for serialization
37
-
38
- ## Related Documentation
39
- - [RevenueCat Domain](../README.md)
40
- - [RevenueCat Entities](../entities/README.md)
41
- - [RevenueCat Types](../types/README.md)
@@ -1,13 +0,0 @@
1
- export * from "./domain/errors/RevenueCatError";
2
- export * from "./domain/value-objects/RevenueCatConfig";
3
- export * from "./domain/types/RevenueCatTypes";
4
- export * from "./domain/constants/RevenueCatConstants";
5
- export * from "./application/ports/IRevenueCatService";
6
- export * from "./infrastructure/services/RevenueCatService";
7
- export * from "./infrastructure/managers/SubscriptionManager";
8
- export * from "./infrastructure/handlers/PackageHandler";
9
- export * from "./presentation/hooks/useRevenueCat";
10
- export * from "./presentation/hooks/useCustomerInfo";
11
- export * from "./presentation/hooks/usePaywallFlow";
12
- export * from "./presentation/hooks/useSubscriptionQueries";
13
- export * from "./presentation/hooks/useRevenueCatTrialEligibility";
@@ -1,161 +0,0 @@
1
- /**
2
- * Package Handler
3
- * Handles package operations (fetch, purchase, restore, premium status)
4
- */
5
-
6
- import type { PurchasesPackage, CustomerInfo } from "react-native-purchases";
7
- import type { IRevenueCatService } from "../../application/ports/IRevenueCatService";
8
- import { getPremiumEntitlement } from "../../domain/types/RevenueCatTypes";
9
-
10
- export interface PremiumStatus {
11
- isPremium: boolean;
12
- expirationDate: Date | null;
13
- }
14
-
15
- export interface RestoreResultInfo {
16
- success: boolean;
17
- productId: string | null;
18
- }
19
-
20
- export class PackageHandler {
21
- constructor(
22
- private service: IRevenueCatService | null,
23
- private entitlementId: string
24
- ) { }
25
-
26
- setService(service: IRevenueCatService | null): void {
27
- this.service = service;
28
- }
29
-
30
- async fetchPackages(): Promise<PurchasesPackage[]> {
31
- if (__DEV__) {
32
- console.log('[DEBUG PackageHandler] fetchPackages called', {
33
- hasService: !!this.service,
34
- isInitialized: this.service?.isInitialized(),
35
- });
36
- }
37
- if (!this.service?.isInitialized()) {
38
- if (__DEV__) {
39
- console.log('[DEBUG PackageHandler] Service not initialized, returning empty');
40
- }
41
- return [];
42
- }
43
-
44
- try {
45
- const offering = await this.service.fetchOfferings();
46
-
47
- if (__DEV__) {
48
- console.log('[DEBUG PackageHandler] fetchOfferings result:', {
49
- hasOffering: !!offering,
50
- identifier: offering?.identifier,
51
- packagesCount: offering?.availablePackages?.length ?? 0,
52
- packages: offering?.availablePackages?.map(p => ({
53
- identifier: p.identifier,
54
- productIdentifier: p.product.identifier,
55
- offeringIdentifier: p.offeringIdentifier
56
- }))
57
- });
58
- }
59
-
60
- return offering?.availablePackages ?? [];
61
- } catch (error) {
62
- if (__DEV__) {
63
- console.error('[DEBUG PackageHandler] fetchOfferings failed:', error);
64
- }
65
- return [];
66
- }
67
- }
68
-
69
- async purchase(pkg: PurchasesPackage, userId: string): Promise<boolean> {
70
- if (__DEV__) {
71
- console.log('[DEBUG PackageHandler] purchase() called', {
72
- productId: pkg.product.identifier,
73
- userId,
74
- serviceExists: !!this.service,
75
- isInitialized: this.service?.isInitialized(),
76
- });
77
- }
78
-
79
- if (!this.service?.isInitialized()) {
80
- if (__DEV__) {
81
- console.log('[DEBUG PackageHandler] Service not initialized', {
82
- productId: pkg.product.identifier,
83
- });
84
- }
85
- return false;
86
- }
87
-
88
- try {
89
- if (__DEV__) {
90
- console.log('[DEBUG PackageHandler] Calling service.purchasePackage...');
91
- }
92
- const result = await this.service.purchasePackage(pkg, userId);
93
- if (__DEV__) {
94
- console.log('[DEBUG PackageHandler] Purchase result:', {
95
- success: result.success,
96
- productId: pkg.product.identifier,
97
- isPremium: result.isPremium,
98
- });
99
- }
100
- return result.success;
101
- } catch (error) {
102
- if (__DEV__) {
103
- console.error('[DEBUG PackageHandler] Purchase failed:', {
104
- error,
105
- productId: pkg.product.identifier,
106
- });
107
- }
108
- return false;
109
- }
110
- }
111
-
112
- async restore(userId: string): Promise<RestoreResultInfo> {
113
- if (!this.service?.isInitialized()) {
114
- return { success: false, productId: null };
115
- }
116
-
117
- try {
118
- const result = await this.service.restorePurchases(userId);
119
-
120
- // Extract product ID from active entitlement
121
- let productId: string | null = null;
122
- if (result.success && result.customerInfo) {
123
- const entitlement = getPremiumEntitlement(
124
- result.customerInfo,
125
- this.entitlementId
126
- );
127
- if (entitlement) {
128
- productId = entitlement.productIdentifier;
129
- }
130
- }
131
-
132
- return { success: result.success, productId };
133
- } catch (error) {
134
- if (__DEV__) {
135
- console.error('[DEBUG PackageHandler] Restore failed:', {
136
- error,
137
- userId,
138
- });
139
- }
140
- return { success: false, productId: null };
141
- }
142
- }
143
-
144
- checkPremiumStatusFromInfo(customerInfo: CustomerInfo): PremiumStatus {
145
- const entitlement = getPremiumEntitlement(customerInfo, this.entitlementId);
146
-
147
- if (entitlement) {
148
- return {
149
- isPremium: true,
150
- expirationDate: entitlement.expirationDate
151
- ? new Date(entitlement.expirationDate)
152
- : null,
153
- };
154
- }
155
-
156
- return {
157
- isPremium: false,
158
- expirationDate: null,
159
- };
160
- }
161
- }
@@ -1,165 +0,0 @@
1
- /**
2
- * Subscription Manager
3
- * Facade for subscription operations
4
- * Coordinates UserIdProvider, InitializationCache, and PackageHandler
5
- */
6
-
7
- import type { PurchasesPackage } from "react-native-purchases";
8
- import type { RevenueCatConfig } from "../../domain/value-objects/RevenueCatConfig";
9
- import type { IRevenueCatService } from "../../application/ports/IRevenueCatService";
10
- import { initializeRevenueCatService, getRevenueCatService } from "../services/RevenueCatService";
11
- import { UserIdProvider } from "../utils/UserIdProvider";
12
- import { InitializationCache } from "../utils/InitializationCache";
13
- import { PackageHandler } from "../handlers/PackageHandler";
14
- import type { PremiumStatus, RestoreResultInfo } from "../handlers/PackageHandler";
15
-
16
- export interface SubscriptionManagerConfig {
17
- config: RevenueCatConfig;
18
- apiKey: string;
19
- getAnonymousUserId?: () => Promise<string>;
20
- }
21
-
22
- class SubscriptionManagerImpl {
23
- private managerConfig: SubscriptionManagerConfig | null = null;
24
- private serviceInstance: IRevenueCatService | null = null;
25
- private userIdProvider = new UserIdProvider();
26
- private initCache = new InitializationCache();
27
- private packageHandler: PackageHandler | null = null;
28
-
29
- configure(config: SubscriptionManagerConfig): void {
30
- this.managerConfig = config;
31
- this.packageHandler = new PackageHandler(null, config.config.entitlementIdentifier);
32
-
33
- if (config.getAnonymousUserId) {
34
- this.userIdProvider.configure(config.getAnonymousUserId);
35
- }
36
- }
37
-
38
- private ensureConfigured(): void {
39
- if (!this.managerConfig || !this.packageHandler) {
40
- throw new Error("SubscriptionManager not configured");
41
- }
42
- }
43
-
44
- private async performInitialization(userId: string): Promise<boolean> {
45
- this.ensureConfigured();
46
-
47
- await initializeRevenueCatService(this.managerConfig!.config);
48
- this.serviceInstance = getRevenueCatService();
49
-
50
- if (!this.serviceInstance) {
51
- return false;
52
- }
53
-
54
- this.packageHandler!.setService(this.serviceInstance);
55
-
56
- const result = await this.serviceInstance.initialize(userId);
57
- return result.success;
58
- }
59
-
60
- async initialize(userId?: string): Promise<boolean> {
61
- this.ensureConfigured();
62
-
63
- const effectiveUserId = userId || (await this.userIdProvider.getOrCreateAnonymousUserId());
64
-
65
- // Atomic check-and-acquire to prevent race conditions
66
- const { shouldInit, existingPromise } = this.initCache.tryAcquireInitialization(effectiveUserId);
67
-
68
- if (!shouldInit && existingPromise) {
69
- return existingPromise;
70
- }
71
-
72
- const promise = this.performInitialization(effectiveUserId);
73
- this.initCache.setPromise(promise, effectiveUserId);
74
-
75
- return promise;
76
- }
77
-
78
- isConfigured(): boolean {
79
- return this.managerConfig !== null && this.packageHandler !== null;
80
- }
81
-
82
- isInitialized(): boolean {
83
- return this.serviceInstance?.isInitialized() ?? false;
84
- }
85
-
86
- isInitializedForUser(userId: string): boolean {
87
- return this.serviceInstance?.isInitialized() === true &&
88
- this.initCache.getCurrentUserId() === userId;
89
- }
90
-
91
- getEntitlementId(): string | null {
92
- return this.managerConfig?.config.entitlementIdentifier || null;
93
- }
94
-
95
- async getPackages(): Promise<PurchasesPackage[]> {
96
- if (__DEV__) {
97
- console.log('[DEBUG SubscriptionManager] getPackages called', {
98
- isConfigured: this.isConfigured(),
99
- isInitialized: this.isInitialized(),
100
- hasServiceInstance: !!this.serviceInstance,
101
- });
102
- }
103
- this.ensureConfigured();
104
- if (!this.serviceInstance) {
105
- this.serviceInstance = getRevenueCatService();
106
- this.packageHandler!.setService(this.serviceInstance);
107
- }
108
- return this.packageHandler!.fetchPackages();
109
- }
110
-
111
- async purchasePackage(pkg: PurchasesPackage): Promise<boolean> {
112
- if (__DEV__) {
113
- console.log('[DEBUG SubscriptionManager] purchasePackage called', {
114
- productId: pkg.product.identifier,
115
- isConfigured: this.isConfigured(),
116
- isInitialized: this.isInitialized(),
117
- });
118
- }
119
- this.ensureConfigured();
120
- const userId = this.initCache.getCurrentUserId();
121
- if (__DEV__) {
122
- console.log('[DEBUG SubscriptionManager] userId from cache:', userId);
123
- }
124
- if (!userId) {
125
- if (__DEV__) {
126
- console.log('[DEBUG SubscriptionManager] No userId, returning false');
127
- }
128
- return false;
129
- }
130
- return this.packageHandler!.purchase(pkg, userId);
131
- }
132
-
133
- async restore(): Promise<RestoreResultInfo> {
134
- this.ensureConfigured();
135
- const userId = this.initCache.getCurrentUserId();
136
- if (!userId) return { success: false, productId: null };
137
- return this.packageHandler!.restore(userId);
138
- }
139
-
140
- async checkPremiumStatus(): Promise<PremiumStatus> {
141
- this.ensureConfigured();
142
- const userId = this.initCache.getCurrentUserId();
143
- if (!userId) return { isPremium: false, expirationDate: null };
144
-
145
- const customerInfo = await this.serviceInstance?.getCustomerInfo();
146
- if (customerInfo) {
147
- return this.packageHandler!.checkPremiumStatusFromInfo(customerInfo);
148
- }
149
-
150
- return { isPremium: false, expirationDate: null };
151
- }
152
-
153
- async reset(): Promise<void> {
154
- if (this.serviceInstance) {
155
- await this.serviceInstance.reset();
156
- }
157
- this.initCache.reset();
158
- this.userIdProvider.reset();
159
- this.serviceInstance = null;
160
- }
161
- }
162
-
163
- export const SubscriptionManager = new SubscriptionManagerImpl();
164
-
165
- export type { PremiumStatus };
@@ -1,42 +0,0 @@
1
- # RevenueCat Presentation
2
-
3
- ## Location
4
- Presentation layer for RevenueCat integration.
5
-
6
- ## Strategy
7
- This directory contains React hooks, components, and utilities for integrating RevenueCat functionality into the UI layer with proper loading states, error handling, and caching.
8
-
9
- ## Restrictions
10
-
11
- ### REQUIRED
12
- - Must show loading states during async operations
13
- - Must handle and display errors appropriately
14
- - Must cache customer info and offerings
15
- - Must re-render on entitlement changes
16
-
17
- ### PROHIBITED
18
- - DO NOT skip loading states
19
- - DO NOT show technical errors to users
20
- - DO NOT ignore cache invalidation
21
- - DO NOT prevent re-renders on state changes
22
-
23
- ### CRITICAL SAFETY
24
- - All async operations MUST show loading states
25
- - All errors MUST be handled and displayed appropriately
26
- - Data MUST be cached appropriately
27
- - UI MUST re-render on entitlement changes
28
-
29
- ## AI Agent Guidelines
30
- 1. Always show loading states during async operations
31
- 2. Handle and display errors appropriately to users
32
- 3. Update UI optimistically where possible
33
- 4. Cache customer info and offerings appropriately
34
- 5. Re-render on entitlement changes
35
- 6. Provide clear feedback during purchases
36
- 7. Validate data before displaying to users
37
-
38
- ## Related Documentation
39
- - [RevenueCat Integration](../README.md)
40
- - [RevenueCat Application](../application/README.md)
41
- - [RevenueCat Infrastructure](../infrastructure/README.md)
42
- - [RevenueCat Domain](../domain/README.md)