@umituz/react-native-subscription 2.39.8 → 2.39.10

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 (67) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditLimitCalculator.ts +6 -17
  3. package/src/domains/credits/core/UserCreditsDocument.ts +1 -1
  4. package/src/domains/credits/infrastructure/CreditsRepository.ts +3 -3
  5. package/src/domains/credits/infrastructure/operations/CreditsInitializer.ts +1 -1
  6. package/src/domains/credits/infrastructure/operations/CreditsWriter.ts +1 -1
  7. package/src/domains/paywall/components/PaywallFeatures.tsx +1 -1
  8. package/src/domains/paywall/components/PaywallFooter.tsx +1 -1
  9. package/src/domains/paywall/components/PaywallScreen.styles.ts +116 -44
  10. package/src/domains/paywall/components/PaywallScreen.tsx +184 -138
  11. package/src/domains/paywall/entities/types.ts +2 -0
  12. package/src/domains/paywall/hooks/usePaywallActions.ts +32 -40
  13. package/src/domains/paywall/utils/paywallLayoutUtils.ts +55 -0
  14. package/src/domains/revenuecat/core/types/RevenueCatData.ts +1 -1
  15. package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +2 -2
  16. package/src/domains/revenuecat/infrastructure/services/RevenueCatInitializer.types.ts +1 -1
  17. package/src/domains/revenuecat/infrastructure/services/userSwitchHandler.ts +1 -1
  18. package/src/domains/subscription/application/SubscriptionInitializerTypes.ts +1 -1
  19. package/src/domains/subscription/application/SubscriptionSyncProcessor.ts +5 -22
  20. package/src/domains/subscription/application/SubscriptionSyncUtils.ts +1 -1
  21. package/src/domains/subscription/application/featureGate/featureGateBusinessRules.ts +27 -10
  22. package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +42 -41
  23. package/src/domains/subscription/core/SubscriptionEvents.ts +1 -1
  24. package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +1 -5
  25. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +4 -6
  26. package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +2 -2
  27. package/src/domains/subscription/infrastructure/handlers/package-operations/PackageRestorer.ts +1 -1
  28. package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +1 -1
  29. package/src/domains/subscription/infrastructure/hooks/useRestorePurchase.ts +1 -1
  30. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.types.ts +2 -2
  31. package/src/domains/subscription/infrastructure/managers/initializationHandler.ts +1 -1
  32. package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +1 -1
  33. package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +2 -2
  34. package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +4 -4
  35. package/src/domains/subscription/infrastructure/services/RevenueCatService.types.ts +1 -1
  36. package/src/domains/subscription/infrastructure/services/ServiceStateManager.ts +1 -1
  37. package/src/domains/subscription/infrastructure/services/listeners/CustomerInfoHandler.ts +4 -2
  38. package/src/domains/subscription/infrastructure/services/listeners/ListenerState.ts +1 -1
  39. package/src/domains/subscription/infrastructure/services/purchase/PurchaseErrorHandler.ts +3 -3
  40. package/src/domains/subscription/infrastructure/services/purchase/PurchaseExecutor.ts +2 -1
  41. package/src/domains/subscription/infrastructure/services/purchase/PurchaseValidator.ts +1 -1
  42. package/src/domains/subscription/infrastructure/services/revenueCatServiceInstance.ts +1 -1
  43. package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +35 -42
  44. package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -3
  45. package/src/domains/subscription/presentation/components/details/PremiumDetailsCardTypes.ts +1 -1
  46. package/src/domains/subscription/presentation/components/sections/SubscriptionSection.types.ts +1 -1
  47. package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.tsx +1 -1
  48. package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.types.ts +1 -1
  49. package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +11 -8
  50. package/src/domains/subscription/presentation/useSubscriptionStatus.types.ts +1 -1
  51. package/src/domains/subscription/utils/featureGateUtils.ts +37 -0
  52. package/src/domains/subscription/utils/packageTypeFormatter.ts +1 -1
  53. package/src/domains/wallet/infrastructure/repositories/transaction/CollectionBuilder.ts +1 -1
  54. package/src/domains/wallet/infrastructure/repositories/transaction/TransactionFetcher.ts +2 -1
  55. package/src/domains/wallet/infrastructure/repositories/transaction/TransactionWriter.ts +2 -1
  56. package/src/domains/wallet/presentation/screens/WalletScreen.tsx +1 -1
  57. package/src/index.ts +5 -2
  58. package/src/init/createSubscriptionInitModule.ts +2 -1
  59. package/src/shared/infrastructure/SubscriptionEventBus.ts +24 -18
  60. package/src/domains/revenuecat/core/errors/index.ts +0 -3
  61. package/src/domains/revenuecat/core/types/index.ts +0 -3
  62. package/src/domains/subscription/application/initializer/index.ts +0 -2
  63. package/src/domains/subscription/core/types/index.ts +0 -3
  64. package/src/domains/subscription/infrastructure/handlers/package-operations/index.ts +0 -4
  65. package/src/domains/subscription/infrastructure/utils/renewal/index.ts +0 -3
  66. package/src/shared/infrastructure/firestore/index.ts +0 -2
  67. package/src/shared/presentation/index.ts +0 -1
@@ -1,4 +1,3 @@
1
- import Purchases from "react-native-purchases";
2
1
  import { PURCHASE_SOURCE, PURCHASE_TYPE } from "../core/SubscriptionConstants";
3
2
  import type { PremiumStatusChangedEvent, PurchaseCompletedEvent, RenewalDetectedEvent } from "../core/SubscriptionEvents";
4
3
  import { getCreditsRepository } from "../../credits/infrastructure/CreditsRepositoryManager";
@@ -80,14 +79,6 @@ export class SubscriptionSyncProcessor {
80
79
 
81
80
  // ─── Internal Processing ──────────────────────────────────────────
82
81
 
83
- private async getRevenueCatAppUserId(): Promise<string | null> {
84
- try {
85
- return await Purchases.getAppUserID();
86
- } catch {
87
- return null;
88
- }
89
- }
90
-
91
82
  private async getCreditsUserId(revenueCatUserId: string | null | undefined): Promise<string> {
92
83
  const trimmed = revenueCatUserId?.trim();
93
84
  if (trimmed && trimmed.length > 0 && trimmed !== 'undefined' && trimmed !== 'null') {
@@ -108,8 +99,8 @@ export class SubscriptionSyncProcessor {
108
99
  try {
109
100
  const revenueCatData = extractRevenueCatData(event.customerInfo, this.entitlementId);
110
101
  revenueCatData.packageType = event.packageType ?? null;
111
- const revenueCatAppUserId = await this.getRevenueCatAppUserId();
112
- revenueCatData.revenueCatUserId = revenueCatAppUserId ?? event.userId;
102
+ // Use the event.userId instead of polling the SDK to avoid race conditions during rapid user switching
103
+ revenueCatData.revenueCatUserId = event.userId;
113
104
  const purchaseId = generatePurchaseId(revenueCatData.storeTransactionId, event.productId);
114
105
 
115
106
  const creditsUserId = await this.getCreditsUserId(event.userId);
@@ -138,8 +129,8 @@ export class SubscriptionSyncProcessor {
138
129
  try {
139
130
  const revenueCatData = extractRevenueCatData(event.customerInfo, this.entitlementId);
140
131
  revenueCatData.expirationDate = event.newExpirationDate ?? revenueCatData.expirationDate;
141
- const revenueCatAppUserId = await this.getRevenueCatAppUserId();
142
- revenueCatData.revenueCatUserId = revenueCatAppUserId ?? event.userId;
132
+ // Use the event.userId instead of polling the SDK to avoid race conditions during rapid user switching
133
+ revenueCatData.revenueCatUserId = event.userId;
143
134
  const purchaseId = generateRenewalId(revenueCatData.storeTransactionId, event.productId, event.newExpirationDate);
144
135
 
145
136
  const creditsUserId = await this.getCreditsUserId(event.userId);
@@ -164,9 +155,6 @@ export class SubscriptionSyncProcessor {
164
155
  }
165
156
 
166
157
  private async processStatusChange(event: PremiumStatusChangedEvent): Promise<void> {
167
- // If a purchase is in progress, skip metadata sync (purchase handler does it)
168
- // but still allow recovery to run — the purchase handler's credit initialization
169
- // might have failed, and this is the safety net.
170
158
  if (this.purchaseInProgress) {
171
159
  if (typeof __DEV__ !== "undefined" && __DEV__) {
172
160
  console.log("[SubscriptionSyncProcessor] Purchase in progress - running recovery only");
@@ -186,9 +174,6 @@ export class SubscriptionSyncProcessor {
186
174
  }
187
175
 
188
176
  if (!event.isPremium && !event.productId) {
189
- // No entitlement and no productId — could be:
190
- // 1. Free user who never purchased (no credits doc) → skip
191
- // 2. Previously premium user whose entitlement was removed → expire
192
177
  const hasDoc = await getCreditsRepository().creditsDocumentExists(creditsUserId);
193
178
  if (hasDoc) {
194
179
  await this.expireSubscription(creditsUserId);
@@ -203,7 +188,7 @@ export class SubscriptionSyncProcessor {
203
188
  await this.syncPremiumStatus(creditsUserId, event);
204
189
  }
205
190
 
206
- // ─── Credit Document Operations (replaces statusChangeHandlers) ───
191
+ // ─── Credit Document Operations ───
207
192
 
208
193
  private async expireSubscription(userId: string): Promise<void> {
209
194
  await getCreditsRepository().syncExpiredStatus(userId);
@@ -213,8 +198,6 @@ export class SubscriptionSyncProcessor {
213
198
  private async syncPremiumStatus(userId: string, event: PremiumStatusChangedEvent): Promise<void> {
214
199
  const repo = getCreditsRepository();
215
200
 
216
- // Recovery: if premium user has no credits document, create one.
217
- // Handles edge cases like test store, reinstalls, or failed purchase initialization.
218
201
  if (event.isPremium) {
219
202
  const created = await repo.ensurePremiumCreditsExist(
220
203
  userId,
@@ -1,5 +1,5 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
- import type { RevenueCatData } from "../../revenuecat/core/types";
2
+ import type { RevenueCatData } from "../../revenuecat/core/types/RevenueCatData";
3
3
  import { PERIOD_TYPE, type PeriodType } from "../core/SubscriptionConstants";
4
4
 
5
5
  function validatePeriodType(periodType: string | undefined): PeriodType | null {
@@ -1,5 +1,13 @@
1
+ import {
2
+ canExecuteAuthAction as canAuth,
3
+ canExecutePurchaseAction as canPurchase
4
+ } from "../../utils/featureGateUtils";
5
+
1
6
  export const DEFAULT_REQUIRED_CREDITS = 1;
2
7
 
8
+ /**
9
+ * Business rule for executing auth-related actions.
10
+ */
3
11
  export function canExecuteAuthAction(
4
12
  isWaitingForAuthCredits: boolean,
5
13
  isCreditsLoaded: boolean,
@@ -8,12 +16,19 @@ export function canExecuteAuthAction(
8
16
  creditBalance: number,
9
17
  requiredCredits: number
10
18
  ): boolean {
11
- if (!isWaitingForAuthCredits || !isCreditsLoaded || !hasPendingAction) {
12
- return false;
13
- }
14
- return hasSubscription || creditBalance >= requiredCredits;
19
+ return canAuth(
20
+ isWaitingForAuthCredits,
21
+ isCreditsLoaded,
22
+ hasPendingAction,
23
+ hasSubscription,
24
+ creditBalance,
25
+ requiredCredits
26
+ );
15
27
  }
16
28
 
29
+ /**
30
+ * Business rule for executing purchase-related actions.
31
+ */
17
32
  export function canExecutePurchaseAction(
18
33
  isWaitingForPurchase: boolean,
19
34
  creditBalance: number,
@@ -22,10 +37,12 @@ export function canExecutePurchaseAction(
22
37
  prevHasSubscription: boolean,
23
38
  hasPendingAction: boolean
24
39
  ): boolean {
25
- if (!isWaitingForPurchase || !hasPendingAction) {
26
- return false;
27
- }
28
- const creditsIncreased = creditBalance > prevBalance;
29
- const subscriptionAcquired = hasSubscription && !prevHasSubscription;
30
- return creditsIncreased || subscriptionAcquired;
40
+ return canPurchase(
41
+ isWaitingForPurchase,
42
+ creditBalance,
43
+ prevBalance,
44
+ hasSubscription,
45
+ prevHasSubscription,
46
+ hasPendingAction
47
+ );
31
48
  }
@@ -2,16 +2,18 @@ import { SubscriptionManager } from "../../infrastructure/managers/SubscriptionM
2
2
  import { getCurrentUserId, setupAuthStateListener } from "../SubscriptionAuthListener";
3
3
  import type { SubscriptionInitConfig } from "../SubscriptionInitializerTypes";
4
4
 
5
- const AUTH_STATE_DEBOUNCE_MS = 500; // Wait 500ms before processing auth state changes
5
+ const AUTH_STATE_DEBOUNCE_MS = 500;
6
6
  const MAX_RETRY_ATTEMPTS = 3;
7
7
  const RETRY_DELAY_MS = 2000;
8
8
 
9
9
  export async function startBackgroundInitialization(config: SubscriptionInitConfig): Promise<() => void> {
10
10
  let debounceTimer: ReturnType<typeof setTimeout> | null = null;
11
11
  let retryTimer: ReturnType<typeof setTimeout> | null = null;
12
- let lastUserId: string | undefined = undefined;
12
+
13
+ // Track the ID of the current initialization sequence to abort stale retries/state updates
14
+ let currentSequenceId = 0;
13
15
  let lastInitSucceeded = false;
14
- let isInitializing = false; // true while attemptInitWithRetry is awaited
16
+ let lastUserId: string | undefined = undefined;
15
17
 
16
18
  const initializeInBackground = async (revenueCatUserId?: string): Promise<void> => {
17
19
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
@@ -20,20 +22,23 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
20
22
  await SubscriptionManager.initialize(revenueCatUserId);
21
23
  };
22
24
 
23
- const attemptInitWithRetry = async (revenueCatUserId?: string, attempt = 0): Promise<void> => {
24
- // Abort if user changed since retry was scheduled
25
- if (attempt > 0 && lastUserId !== revenueCatUserId) {
25
+ const attemptInitWithRetry = async (revenueCatUserId: string | undefined, attempt: number, sequenceId: number): Promise<void> => {
26
+ // Abort if this is no longer the active sequence (e.g., user changed)
27
+ if (sequenceId !== currentSequenceId) {
26
28
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
27
- console.log('[BackgroundInitializer] Aborting retry - user changed');
29
+ console.log('[BackgroundInitializer] Aborting retry - sequence changed');
28
30
  }
29
31
  return;
30
32
  }
31
33
 
32
34
  try {
33
35
  await initializeInBackground(revenueCatUserId);
34
- lastUserId = revenueCatUserId;
35
- lastInitSucceeded = true;
36
+ if (sequenceId === currentSequenceId) {
37
+ lastInitSucceeded = true;
38
+ }
36
39
  } catch (error) {
40
+ if (sequenceId !== currentSequenceId) return;
41
+
37
42
  lastInitSucceeded = false;
38
43
  console.error('[BackgroundInitializer] Initialization failed:', {
39
44
  userId: revenueCatUserId,
@@ -47,12 +52,11 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
47
52
  console.log('[BackgroundInitializer] Scheduling retry', { attempt: attempt + 2 });
48
53
  }
49
54
  retryTimer = setTimeout(() => {
50
- void attemptInitWithRetry(revenueCatUserId, attempt + 1);
55
+ // Fire and forget promise, but safe because of sequenceId check
56
+ attemptInitWithRetry(revenueCatUserId, attempt + 1, sequenceId).catch(err => {
57
+ console.error('[BackgroundInitializer] Retry failed unhandled:', err);
58
+ });
51
59
  }, RETRY_DELAY_MS * (attempt + 1));
52
- } else {
53
- // After all retries failed, set lastUserId so we don't block
54
- // but mark as failed so next auth change can retry
55
- lastUserId = revenueCatUserId;
56
60
  }
57
61
  }
58
62
  };
@@ -66,7 +70,7 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
66
70
  retryTimer = null;
67
71
  }
68
72
 
69
- if (lastUserId === revenueCatUserId && (lastInitSucceeded || isInitializing)) {
73
+ if (lastUserId === revenueCatUserId && lastInitSucceeded) {
70
74
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
71
75
  console.log('[BackgroundInitializer] UserId unchanged and init succeeded, skipping');
72
76
  }
@@ -74,8 +78,10 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
74
78
  }
75
79
 
76
80
  debounceTimer = setTimeout(async () => {
77
- // Don't initialize when there's no user and no previous user.
78
- // This is the initial signed-out state before auth resolves, not a logout.
81
+ // Start a new sequence
82
+ currentSequenceId++;
83
+ const sequenceId = currentSequenceId;
84
+
79
85
  if (!revenueCatUserId && !lastUserId) {
80
86
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
81
87
  console.log('[BackgroundInitializer] No user and no previous user, waiting for auth');
@@ -87,18 +93,19 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
87
93
  console.log('[BackgroundInitializer] Auth state listener triggered, reinitializing with userId:', revenueCatUserId || '(undefined - anonymous)');
88
94
  }
89
95
 
90
- // Reset subscription state on logout to prevent stale cache
91
- if (!revenueCatUserId && lastUserId) {
92
- await SubscriptionManager.reset();
93
- lastInitSucceeded = false;
96
+ // Important: Always reset on user change, not just on logout.
97
+ // This ensures previous user's cached state is cleared before init.
98
+ if (lastUserId !== revenueCatUserId) {
99
+ await SubscriptionManager.reset();
100
+ lastInitSucceeded = false;
94
101
  }
95
102
 
96
- isInitializing = true;
97
- try {
98
- await attemptInitWithRetry(revenueCatUserId);
99
- } finally {
100
- isInitializing = false;
101
- }
103
+ lastUserId = revenueCatUserId;
104
+
105
+ // Start the retry chain
106
+ attemptInitWithRetry(revenueCatUserId, 0, sequenceId).catch(err => {
107
+ console.error('[BackgroundInitializer] Init sequence failed unhandled:', err);
108
+ });
102
109
  }, AUTH_STATE_DEBOUNCE_MS);
103
110
  };
104
111
 
@@ -114,12 +121,11 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
114
121
  console.log('[BackgroundInitializer] Initial RevenueCat userId:', initialRevenueCatUserId || '(undefined - anonymous)');
115
122
  }
116
123
 
117
- // Initialize RevenueCat for all users (including anonymous).
118
- // Anonymous users get their Firebase UID passed to RevenueCat so they can make purchases.
119
- // Credits are stored at users/{uid}/credits/balance regardless of auth status.
120
124
  if (initialRevenueCatUserId) {
121
- await initializeInBackground(initialRevenueCatUserId);
122
- lastInitSucceeded = true;
125
+ currentSequenceId++;
126
+ attemptInitWithRetry(initialRevenueCatUserId, 0, currentSequenceId).catch(err => {
127
+ console.error('[BackgroundInitializer] Initial sequence failed unhandled:', err);
128
+ });
123
129
  } else if (typeof __DEV__ !== 'undefined' && __DEV__) {
124
130
  console.log('[BackgroundInitializer] No user available yet, waiting for auth state');
125
131
  }
@@ -127,14 +133,9 @@ export async function startBackgroundInitialization(config: SubscriptionInitConf
127
133
  const unsubscribe = setupAuthStateListener(() => auth, debouncedInitialize);
128
134
 
129
135
  return () => {
130
- if (debounceTimer) {
131
- clearTimeout(debounceTimer);
132
- }
133
- if (retryTimer) {
134
- clearTimeout(retryTimer);
135
- }
136
- if (unsubscribe) {
137
- unsubscribe();
138
- }
136
+ currentSequenceId++; // Invalidate any running sequences
137
+ if (debounceTimer) clearTimeout(debounceTimer);
138
+ if (retryTimer) clearTimeout(retryTimer);
139
+ if (unsubscribe) unsubscribe();
139
140
  };
140
141
  }
@@ -1,6 +1,6 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
2
  import type { PurchaseSource } from "./SubscriptionConstants";
3
- import type { SubscriptionMetadata } from "./types";
3
+ import type { SubscriptionMetadata } from "./types/SubscriptionMetadata";
4
4
  import type { PackageType } from "../../revenuecat/core/types/RevenueCatTypes";
5
5
 
6
6
  export interface PurchaseCompletedEvent {
@@ -13,10 +13,6 @@ abstract class BaseStatusHandler {
13
13
  }
14
14
 
15
15
  abstract handle(input: StatusResolverInput): SubscriptionStatusType;
16
-
17
- protected nextOrFallback(input: StatusResolverInput, fallback: SubscriptionStatusType): SubscriptionStatusType {
18
- return this.next ? this.next.handle(input) : fallback;
19
- }
20
16
  }
21
17
 
22
18
  export class InactiveStatusHandler extends BaseStatusHandler {
@@ -26,7 +22,7 @@ export class InactiveStatusHandler extends BaseStatusHandler {
26
22
  if (!input.isPremium || isExpired) {
27
23
  return isExpired ? SUBSCRIPTION_STATUS.EXPIRED : SUBSCRIPTION_STATUS.NONE;
28
24
  }
29
- return this.nextOrFallback(input, SUBSCRIPTION_STATUS.NONE);
25
+ return this.next ? this.next.handle(input) : SUBSCRIPTION_STATUS.NONE;
30
26
  }
31
27
  }
32
28
 
@@ -1,12 +1,10 @@
1
1
  import type { PurchasesPackage, CustomerInfo } from "react-native-purchases";
2
2
  import type { IRevenueCatService } from "../../../../shared/application/ports/IRevenueCatService";
3
3
  import { PurchaseStatusResolver, type PremiumStatus } from "./PurchaseStatusResolver";
4
- import {
5
- fetchPackages,
6
- executePurchase,
7
- restorePurchases,
8
- type RestoreResultInfo,
9
- } from "./package-operations";
4
+ import { fetchPackages } from "./package-operations/PackageFetcher";
5
+ import { executePurchase } from "./package-operations/PackagePurchaser";
6
+ import { restorePurchases } from "./package-operations/PackageRestorer";
7
+ import type { RestoreResultInfo } from "./package-operations/types";
10
8
 
11
9
  export class PackageHandler {
12
10
  constructor(
@@ -1,8 +1,8 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
- import { getPremiumEntitlement } from "../../../revenuecat/core/types";
2
+ import { getPremiumEntitlement } from "../../../revenuecat/core/types/RevenueCatTypes";
3
3
  import { toDate } from "../../../../shared/utils/dateConverter";
4
4
  import { detectPackageType } from "../../../../utils/packageTypeDetector";
5
- import type { PremiumStatus } from "../../core/types";
5
+ import type { PremiumStatus } from "../../core/types/PremiumStatus";
6
6
 
7
7
  export type { PremiumStatus };
8
8
 
@@ -1,5 +1,5 @@
1
1
  import type { IRevenueCatService } from "../../../../../shared/application/ports/IRevenueCatService";
2
- import { getPremiumEntitlement } from "../../../../revenuecat/core/types";
2
+ import { getPremiumEntitlement } from "../../../../revenuecat/core/types/RevenueCatTypes";
3
3
  import type { RestoreResultInfo } from "./types";
4
4
 
5
5
  export async function restorePurchases(
@@ -9,7 +9,7 @@ import { SubscriptionManager } from "../../infrastructure/managers/SubscriptionM
9
9
  import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
10
10
  import { subscriptionStatusQueryKeys } from "../../presentation/useSubscriptionStatus";
11
11
  import { creditsQueryKeys } from "../../../credits/presentation/creditsQueryKeys";
12
- import { getErrorMessage } from "../../../revenuecat/core/errors";
12
+ import { getErrorMessage } from "../../../revenuecat/core/errors/RevenueCatErrorHandler";
13
13
 
14
14
  interface PurchaseMutationResult {
15
15
  success: boolean;
@@ -8,7 +8,7 @@ import { SubscriptionManager } from "../../infrastructure/managers/SubscriptionM
8
8
  import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
9
9
  import { subscriptionStatusQueryKeys } from "../../presentation/useSubscriptionStatus";
10
10
  import { creditsQueryKeys } from "../../../credits/presentation/creditsQueryKeys";
11
- import { getErrorMessage } from "../../../revenuecat/core/errors";
11
+ import { getErrorMessage } from "../../../revenuecat/core/errors/RevenueCatErrorHandler";
12
12
 
13
13
  interface RestoreResult {
14
14
  success: boolean;
@@ -1,5 +1,5 @@
1
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
2
- import type { PremiumStatus } from "../../core/types";
1
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
2
+ import type { PremiumStatus } from "../../core/types/PremiumStatus";
3
3
  import type { RestoreResultInfo } from "../handlers/package-operations/types";
4
4
 
5
5
  export interface SubscriptionManagerConfig {
@@ -1,7 +1,7 @@
1
1
  import type { IRevenueCatService } from "../../../../shared/application/ports/IRevenueCatService";
2
2
  import { initializeRevenueCatService, getRevenueCatService } from "../services/revenueCatServiceInstance";
3
3
  import { ensureServiceAvailable } from "./subscriptionManagerUtils";
4
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
4
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
5
5
 
6
6
  export const performServiceInitialization = async (config: RevenueCatConfig, userId: string): Promise<{ service: IRevenueCatService; success: boolean }> => {
7
7
  await initializeRevenueCatService(config);
@@ -1,5 +1,5 @@
1
1
  import Purchases, { type CustomerInfo } from "react-native-purchases";
2
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
2
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
3
3
  import { ListenerState } from "./listeners/ListenerState";
4
4
  import { processCustomerInfo } from "./listeners/CustomerInfoHandler";
5
5
 
@@ -1,7 +1,7 @@
1
1
  import type { PurchasesPackage } from "react-native-purchases";
2
2
  import type { PurchaseResult } from "../../../../shared/application/ports/IRevenueCatService";
3
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
4
- import { isUserCancelledError, isAlreadyPurchasedError } from "../../../revenuecat/core/types";
3
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
4
+ import { isUserCancelledError, isAlreadyPurchasedError } from "../../../revenuecat/core/types/RevenueCatTypes";
5
5
  import { validatePurchaseReady, isConsumableProduct } from "./purchase/PurchaseValidator";
6
6
  import { executePurchase } from "./purchase/PurchaseExecutor";
7
7
  import { handleAlreadyPurchasedError, handlePurchaseError } from "./purchase/PurchaseErrorHandler";
@@ -4,14 +4,14 @@ import {
4
4
  RevenueCatRestoreError,
5
5
  RevenueCatInitializationError,
6
6
  RevenueCatNetworkError,
7
- } from "../../../revenuecat/core/errors";
8
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
7
+ } from "../../../revenuecat/core/errors/RevenueCatError";
8
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
9
9
  import {
10
10
  getRawErrorMessage,
11
11
  getErrorCode,
12
12
  isNetworkError,
13
13
  isInvalidCredentialsError,
14
- } from "../../../revenuecat/core/types";
14
+ } from "../../../revenuecat/core/types/RevenueCatTypes";
15
15
  import { notifyRestoreCompleted } from "../utils/PremiumStatusSyncer";
16
16
 
17
17
  interface RestoreHandlerDeps {
@@ -50,7 +50,7 @@ export async function handleRestore(deps: RestoreHandlerDeps, userId: string): P
50
50
 
51
51
  // Generic error with code
52
52
  const errorCode = getErrorCode(error);
53
- const errorMessage = getRawErrorMessage(error, "Restore failed");
53
+ const errorMessage = getRawErrorMessage(error);
54
54
  const enhancedMessage = errorCode
55
55
  ? `${errorMessage} (Code: ${errorCode})`
56
56
  : errorMessage;
@@ -1,7 +1,7 @@
1
1
  import Purchases from "react-native-purchases";
2
2
  import type { PurchasesOffering, PurchasesPackage, CustomerInfo } from "react-native-purchases";
3
3
  import type { IRevenueCatService, InitializeResult, PurchaseResult, RestoreResult } from "../../../../shared/application/ports/IRevenueCatService";
4
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
4
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
5
5
  import { initializeSDK } from "../../../revenuecat/infrastructure/services/RevenueCatInitializer";
6
6
  import { fetchOfferings } from "./OfferingsFetcher";
7
7
  import { handlePurchase } from "./PurchaseHandler";
@@ -1,4 +1,4 @@
1
- import type { RevenueCatConfig } from '../../../revenuecat/core/types';
1
+ import type { RevenueCatConfig } from '../../../revenuecat/core/types/RevenueCatConfig';
2
2
 
3
3
  export class ServiceStateManager {
4
4
  private isInitializedFlag = false;
@@ -1,7 +1,9 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
- import type { RevenueCatConfig } from "../../../../revenuecat/core/types";
2
+ import type { RevenueCatConfig } from "../../../../revenuecat/core/types/RevenueCatConfig";
3
3
  import { syncPremiumStatus } from "../../utils/PremiumStatusSyncer";
4
- import { detectRenewal, updateRenewalState, type RenewalState } from "../../utils/renewal";
4
+ import { detectRenewal } from "../../utils/renewal/RenewalDetector";
5
+ import { updateRenewalState } from "../../utils/renewal/RenewalStateUpdater";
6
+ import type { RenewalState } from "../../utils/renewal/types";
5
7
 
6
8
  async function handleRenewal(
7
9
  userId: string,
@@ -1,5 +1,5 @@
1
1
  import type { CustomerInfoUpdateListener } from "react-native-purchases";
2
- import type { RenewalState } from "../../utils/renewal";
2
+ import type { RenewalState } from "../../utils/renewal/types";
3
3
 
4
4
  export class ListenerState {
5
5
  listener: CustomerInfoUpdateListener | null = null;
@@ -3,14 +3,14 @@ import type { PurchaseResult } from "../../../../../shared/application/ports/IRe
3
3
  import {
4
4
  RevenueCatPurchaseError,
5
5
  RevenueCatNetworkError,
6
- } from "../../../../revenuecat/core/errors";
6
+ } from "../../../../revenuecat/core/errors/RevenueCatError";
7
7
  import {
8
8
  isUserCancelledError,
9
9
  isNetworkError,
10
10
  isInvalidCredentialsError,
11
11
  getRawErrorMessage,
12
12
  getErrorCode,
13
- } from "../../../../revenuecat/core/types";
13
+ } from "../../../../revenuecat/core/types/RevenueCatTypes";
14
14
  import { getSavedPurchase, clearSavedPurchase } from "../../../presentation/useAuthAwarePurchase";
15
15
  import { notifyPurchaseCompleted } from "../../utils/PremiumStatusSyncer";
16
16
  import { handleRestore } from "../RestoreHandler";
@@ -80,7 +80,7 @@ export function handlePurchaseError(
80
80
  }
81
81
 
82
82
  const errorCode = getErrorCode(error);
83
- const errorMessage = getRawErrorMessage(error, "Purchase failed");
83
+ const errorMessage = getRawErrorMessage(error);
84
84
  const enhancedMessage = errorCode
85
85
  ? `${errorMessage} (Code: ${errorCode})`
86
86
  : errorMessage;
@@ -1,6 +1,7 @@
1
1
  import Purchases, { type PurchasesPackage, type CustomerInfo } from "react-native-purchases";
2
2
  import type { PurchaseResult } from "../../../../../shared/application/ports/IRevenueCatService";
3
- import type { RevenueCatConfig, PackageType } from "../../../../revenuecat/core/types";
3
+ import type { RevenueCatConfig } from "../../../../revenuecat/core/types/RevenueCatConfig";
4
+ import type { PackageType } from "../../../../revenuecat/core/types/RevenueCatTypes";
4
5
  import { notifyPurchaseCompleted, syncPremiumStatus } from "../../utils/PremiumStatusSyncer";
5
6
  import { getSavedPurchase, clearSavedPurchase } from "../../../presentation/useAuthAwarePurchase";
6
7
 
@@ -1,5 +1,5 @@
1
1
  import type { PurchasesPackage } from "react-native-purchases";
2
- import { RevenueCatInitializationError } from "../../../../revenuecat/core/errors";
2
+ import { RevenueCatInitializationError } from "../../../../revenuecat/core/errors/RevenueCatError";
3
3
 
4
4
  export function validatePurchaseReady(isInitialized: boolean): void {
5
5
  if (!isInitialized) {
@@ -1,5 +1,5 @@
1
1
  import { RevenueCatService } from "./RevenueCatService.types";
2
- import type { RevenueCatConfig } from "../../../revenuecat/core/types";
2
+ import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
3
3
 
4
4
  let revenueCatServiceInstance: RevenueCatService | null = null;
5
5