@umituz/react-native-subscription 3.1.33 → 3.1.35

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 (40) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditsInitializer.ts +34 -39
  3. package/src/domains/credits/application/DeductCreditsCommand.ts +13 -12
  4. package/src/domains/credits/presentation/deduct-credit/useDeductCredit.ts +9 -16
  5. package/src/domains/credits/presentation/useCredits.ts +2 -2
  6. package/src/domains/credits/presentation/useCredits.types.ts +1 -1
  7. package/src/domains/paywall/components/PaywallScreen.tsx +12 -11
  8. package/src/domains/paywall/hooks/usePaywallActions.ts +4 -3
  9. package/src/domains/paywall/hooks/usePaywallActions.utils.ts +14 -19
  10. package/src/domains/paywall/hooks/usePaywallPurchase.ts +10 -17
  11. package/src/domains/paywall/hooks/usePaywallRestore.ts +8 -15
  12. package/src/domains/revenuecat/infrastructure/services/RevenueCatInitializer.ts +6 -5
  13. package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +8 -10
  14. package/src/domains/revenuecat/infrastructure/services/userSwitchCore.ts +16 -33
  15. package/src/domains/revenuecat/infrastructure/services/userSwitchHelpers.ts +3 -4
  16. package/src/domains/revenuecat/infrastructure/services/userSwitchInitializer.ts +18 -28
  17. package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +17 -29
  18. package/src/domains/subscription/application/sync/CreditDocumentOperations.ts +16 -17
  19. package/src/domains/subscription/application/sync/PurchaseSyncHandler.ts +20 -23
  20. package/src/domains/subscription/application/sync/RenewalSyncHandler.ts +8 -7
  21. package/src/domains/subscription/application/sync/StatusChangeSyncHandler.ts +4 -3
  22. package/src/domains/subscription/application/sync/SyncProcessorLogger.ts +39 -64
  23. package/src/domains/subscription/application/sync/UserIdResolver.ts +5 -1
  24. package/src/domains/subscription/infrastructure/handlers/package-operations/PackageFetcher.ts +7 -6
  25. package/src/domains/subscription/infrastructure/handlers/package-operations/PackagePurchaser.ts +8 -7
  26. package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +4 -3
  27. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +20 -27
  28. package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +10 -9
  29. package/src/domains/subscription/infrastructure/services/OfferingsFetcher.ts +14 -21
  30. package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +8 -7
  31. package/src/domains/subscription/infrastructure/services/RevenueCatService.types.ts +4 -3
  32. package/src/domains/subscription/infrastructure/services/listeners/CustomerInfoHandler.ts +15 -29
  33. package/src/domains/subscription/infrastructure/services/purchase/PurchaseErrorHandler.ts +4 -2
  34. package/src/domains/subscription/infrastructure/services/purchase/PurchaseExecutor.ts +27 -33
  35. package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +5 -1
  36. package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +11 -17
  37. package/src/domains/subscription/presentation/providers/SubscriptionFlowProvider.tsx +11 -12
  38. package/src/domains/subscription/presentation/useSyncStatusListener.ts +10 -9
  39. package/src/init/createSubscriptionInitModule.ts +4 -1
  40. package/src/shared/infrastructure/SubscriptionEventBus.ts +4 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "3.1.33",
3
+ "version": "3.1.35",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -9,6 +9,9 @@ import { calculateNewCredits, buildCreditsData } from "./creditOperationUtils";
9
9
  import { CreditLimitService } from "../domain/services/CreditLimitService";
10
10
  import { generatePurchaseMetadata } from "./PurchaseMetadataGenerator";
11
11
  import { PURCHASE_ID_PREFIXES } from "../core/CreditsConstants";
12
+ import { createLogger } from "../../../shared/utils/logger";
13
+
14
+ const logger = createLogger("CreditsInitializer");
12
15
 
13
16
  export async function initializeCreditsTransaction(
14
17
  _db: Firestore,
@@ -19,19 +22,17 @@ export async function initializeCreditsTransaction(
19
22
  userId: string
20
23
  ): Promise<InitializationResult> {
21
24
 
22
- if (__DEV__) {
23
- console.log('[CreditsInitializer] 🔵 initializeCreditsTransaction: START', {
24
- userId,
25
- purchaseId,
26
- productId: metadata.productId,
27
- source: metadata.source,
28
- type: metadata.type,
29
- isPremium: metadata.isPremium,
30
- willRenew: metadata.willRenew,
31
- storeTransactionId: metadata.storeTransactionId,
32
- timestamp: new Date().toISOString(),
33
- });
34
- }
25
+ logger.debug("initializeCreditsTransaction: START", {
26
+ userId,
27
+ purchaseId,
28
+ productId: metadata.productId,
29
+ source: metadata.source,
30
+ type: metadata.type,
31
+ isPremium: metadata.isPremium,
32
+ willRenew: metadata.willRenew,
33
+ storeTransactionId: metadata.storeTransactionId,
34
+ timestamp: new Date().toISOString(),
35
+ });
35
36
 
36
37
  if (!purchaseId.startsWith(PURCHASE_ID_PREFIXES.PURCHASE) && !purchaseId.startsWith(PURCHASE_ID_PREFIXES.RENEWAL)) {
37
38
  throw new Error(`[CreditsInitializer] Only purchase and renewal operations can allocate credits. Received: ${purchaseId}`);
@@ -46,14 +47,12 @@ export async function initializeCreditsTransaction(
46
47
  const existingData = getCreditDocumentOrDefault(creditsDoc, platform);
47
48
 
48
49
  if (existingData.processedPurchases.includes(purchaseId)) {
49
- if (__DEV__) {
50
- console.log('[CreditsInitializer] 🟡 Transaction already processed, skipping', {
51
- userId,
52
- purchaseId,
53
- existingCredits: existingData.credits,
54
- processedPurchasesCount: existingData.processedPurchases.length,
55
- });
56
- }
50
+ logger.debug("Transaction already processed, skipping", {
51
+ userId,
52
+ purchaseId,
53
+ existingCredits: existingData.credits,
54
+ processedPurchasesCount: existingData.processedPurchases.length,
55
+ });
57
56
  return {
58
57
  credits: existingData.credits,
59
58
  alreadyProcessed: true,
@@ -61,14 +60,12 @@ export async function initializeCreditsTransaction(
61
60
  };
62
61
  }
63
62
 
64
- if (__DEV__) {
65
- console.log('[CreditsInitializer] 🔵 Processing credit allocation', {
66
- userId,
67
- purchaseId,
68
- existingCredits: existingData.credits,
69
- productId: metadata.productId,
70
- });
71
- }
63
+ logger.debug("Processing credit allocation", {
64
+ userId,
65
+ purchaseId,
66
+ existingCredits: existingData.credits,
67
+ productId: metadata.productId,
68
+ });
72
69
 
73
70
  const creditLimitService = new CreditLimitService(config);
74
71
  const creditLimit = creditLimitService.calculate(metadata.productId);
@@ -100,16 +97,14 @@ export async function initializeCreditsTransaction(
100
97
 
101
98
  transaction.set(creditsRef, creditsData, { merge: true });
102
99
 
103
- if (__DEV__) {
104
- console.log('[CreditsInitializer] 🟢 Credit allocation successful', {
105
- userId,
106
- purchaseId,
107
- previousCredits: existingData.credits,
108
- newCredits,
109
- creditLimit,
110
- productId: metadata.productId,
111
- });
112
- }
100
+ logger.debug("Credit allocation successful", {
101
+ userId,
102
+ purchaseId,
103
+ previousCredits: existingData.credits,
104
+ newCredits,
105
+ creditLimit,
106
+ productId: metadata.productId,
107
+ });
113
108
 
114
109
  return {
115
110
  credits: newCredits,
@@ -2,6 +2,9 @@ import { runTransaction, serverTimestamp, type DocumentReference } from "firebas
2
2
  import type { Firestore } from "@umituz/react-native-firebase";
3
3
  import type { DeductCreditsResult } from "../core/Credits";
4
4
  import { CREDIT_ERROR_CODES, MAX_SINGLE_DEDUCTION } from "../core/CreditsConstants";
5
+ import { createLogger } from "../../../shared/utils/logger";
6
+
7
+ const logger = createLogger("DeductCreditsCommand");
5
8
 
6
9
  export async function deductCreditsOperation(
7
10
  _db: Firestore,
@@ -32,12 +35,12 @@ export async function deductCreditsOperation(
32
35
  }
33
36
 
34
37
  try {
35
- if (__DEV__) console.log('[DeductCreditsCommand] >>> starting transaction', { userId, cost, creditsRefPath: creditsRef.path });
38
+ logger.debug(">>> starting transaction", { userId, cost, creditsRefPath: creditsRef.path });
36
39
 
37
40
  const remaining = await runTransaction(_db, async (tx) => {
38
41
  const docSnap = await tx.get(creditsRef);
39
42
 
40
- if (__DEV__) console.log('[DeductCreditsCommand] doc exists:', docSnap.exists());
43
+ logger.debug("doc exists", { exists: docSnap.exists() });
41
44
 
42
45
  if (!docSnap.exists()) {
43
46
  throw new Error(CREDIT_ERROR_CODES.NO_CREDITS);
@@ -46,7 +49,7 @@ export async function deductCreditsOperation(
46
49
  const rawCredits = docSnap.data().credits;
47
50
  const current = typeof rawCredits === "number" && Number.isFinite(rawCredits) ? rawCredits : 0;
48
51
 
49
- if (__DEV__) console.log('[DeductCreditsCommand] current credits:', current, 'cost:', cost);
52
+ logger.debug("current credits", { current, cost });
50
53
 
51
54
  if (current < cost) {
52
55
  throw new Error(CREDIT_ERROR_CODES.CREDITS_EXHAUSTED);
@@ -58,12 +61,12 @@ export async function deductCreditsOperation(
58
61
  lastUpdatedAt: serverTimestamp()
59
62
  });
60
63
 
61
- if (__DEV__) console.log('[DeductCreditsCommand] updated credits to:', updated);
64
+ logger.debug("updated credits to", { updated });
62
65
 
63
66
  return updated;
64
67
  });
65
68
 
66
- if (__DEV__) console.log('[DeductCreditsCommand] transaction SUCCESS, remaining:', remaining);
69
+ logger.debug("transaction SUCCESS", { remaining });
67
70
 
68
71
  return {
69
72
  success: true,
@@ -76,13 +79,11 @@ export async function deductCreditsOperation(
76
79
  ? message
77
80
  : CREDIT_ERROR_CODES.DEDUCT_ERR;
78
81
 
79
- // Use console.warn instead of console.error for "no credits" - this is expected behavior, not a system error
80
- if (__DEV__) {
81
- if (code === CREDIT_ERROR_CODES.NO_CREDITS || code === CREDIT_ERROR_CODES.CREDITS_EXHAUSTED) {
82
- console.warn('[DeductCreditsCommand] ⚠️ User has no credits - paywall should open', { code, message });
83
- } else {
84
- console.error('[DeductCreditsCommand] ❌ Unexpected transaction error:', { code, message });
85
- }
82
+ // Use logger.warn instead of logger.error for "no credits" - this is expected behavior, not a system error
83
+ if (code === CREDIT_ERROR_CODES.NO_CREDITS || code === CREDIT_ERROR_CODES.CREDITS_EXHAUSTED) {
84
+ logger.warn("User has no credits - paywall should open", { code, message });
85
+ } else {
86
+ logger.error("Unexpected transaction error", e, { code, message });
86
87
  }
87
88
 
88
89
  return {
@@ -1,7 +1,10 @@
1
1
  import { useCallback, useState } from "react";
2
2
  import { getCreditsRepository } from "../../infrastructure/CreditsRepositoryManager";
3
+ import { createLogger } from "../../../../shared/utils/logger";
3
4
  import type { UseDeductCreditParams, UseDeductCreditResult } from "./types";
4
5
 
6
+ const logger = createLogger("useDeductCredit");
7
+
5
8
  export const useDeductCredit = ({
6
9
  userId,
7
10
  onCreditsExhausted,
@@ -15,26 +18,22 @@ export const useDeductCredit = ({
15
18
  setIsDeducting(true);
16
19
  try {
17
20
  const res = await repository.deductCredit(userId, cost);
18
- if (__DEV__) console.log('[useDeductCredit] deduction result:', JSON.stringify(res));
21
+ logger.debug("Deduction result", { result: res });
19
22
 
20
23
  if (!res.success) {
21
- if (__DEV__) console.log('[useDeductCredit] deduction FAILED:', res.error?.code, res.error?.message);
24
+ logger.warn("Deduction failed", { code: res.error?.code, message: res.error?.message });
22
25
 
23
26
  if (res.error?.code === "CREDITS_EXHAUSTED" || res.error?.code === "DEDUCT_ERR" || res.error?.code === "NO_CREDITS") {
24
- if (__DEV__) console.log('[useDeductCredit] Credits exhausted, calling onCreditsExhausted callback');
27
+ logger.info("Credits exhausted, calling onCreditsExhausted callback");
25
28
  onCreditsExhausted?.();
26
29
  }
27
30
  return false;
28
31
  }
29
32
 
30
- if (__DEV__) console.log('[useDeductCredit] deduction SUCCESS, remaining:', res.remainingCredits);
33
+ logger.debug("Deduction success", { remainingCredits: res.remainingCredits });
31
34
  return true;
32
35
  } catch (error) {
33
- if (__DEV__) console.error('[useDeductCredit] UNEXPECTED ERROR during credit deduction', {
34
- cost,
35
- userId,
36
- error: error instanceof Error ? error.message : String(error)
37
- });
36
+ logger.error("Unexpected error during credit deduction", error, { cost, userId });
38
37
  return false;
39
38
  } finally {
40
39
  setIsDeducting(false);
@@ -52,13 +51,7 @@ export const useDeductCredit = ({
52
51
  const result = await repository.refundCredit(userId, amount);
53
52
  return result.success;
54
53
  } catch (error) {
55
- if (__DEV__) {
56
- console.error('[useDeductCredit] Unexpected error during credit refund', {
57
- amount,
58
- userId,
59
- error: error instanceof Error ? error.message : String(error),
60
- });
61
- }
54
+ logger.error("Unexpected error during credit refund", error, { amount, userId });
62
55
  return false;
63
56
  }
64
57
  }, [userId, repository]);
@@ -28,7 +28,7 @@ export const useCredits = (): UseCreditsResult => {
28
28
  const hasUser = isAuthenticated(userId);
29
29
  const queryEnabled = hasUser && isConfigured;
30
30
 
31
- const { credits, isLoading, error } = useCreditsRealTime(userId);
31
+ const { credits, isLoading, error, refetch } = useCreditsRealTime(userId);
32
32
 
33
33
  const derivedValues = useMemo(() => {
34
34
  const has = (credits?.credits ?? 0) > 0;
@@ -53,7 +53,7 @@ export const useCredits = (): UseCreditsResult => {
53
53
  error,
54
54
  hasCredits: derivedValues.hasCredits,
55
55
  creditsPercent: derivedValues.creditsPercent,
56
- refetch: async () => {},
56
+ refetch,
57
57
  canAfford,
58
58
  };
59
59
  };
@@ -10,6 +10,6 @@ export interface UseCreditsResult {
10
10
  error: Error | null;
11
11
  hasCredits: boolean;
12
12
  creditsPercent: number;
13
- refetch: () => Promise<any>;
13
+ refetch: () => void;
14
14
  canAfford: (cost: number) => boolean;
15
15
  }
@@ -29,6 +29,9 @@ import {
29
29
  } from "../utils/paywallLayoutUtils";
30
30
  import { hasItems } from "../../../shared/utils/arrayUtils";
31
31
  import { PaywallRenderItem } from "./PaywallScreen.renderItem";
32
+ import { createLogger } from "../../../../shared/utils/logger";
33
+
34
+ const logger = createLogger("PaywallScreen");
32
35
 
33
36
  // Paywall layout constants
34
37
  const PAYWALL_HEADER_HEIGHT = 60; // Close button + spacing
@@ -37,13 +40,11 @@ const PAYWALL_FOOTER_HEIGHT_BASE = 280; // Base footer height
37
40
  export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) => {
38
41
  const navigation = useNavigation();
39
42
 
40
- if (__DEV__) {
41
- console.log('[PaywallScreen] 📱 Rendering PaywallScreen', {
42
- hasPackages: !!props.packages?.length,
43
- packagesCount: props.packages?.length || 0,
44
- isPremium: props.isPremium,
45
- });
46
- }
43
+ logger.debug("Rendering PaywallScreen", {
44
+ hasPackages: !!props.packages?.length,
45
+ packagesCount: props.packages?.length || 0,
46
+ isPremium: props.isPremium,
47
+ });
47
48
 
48
49
  const {
49
50
  translations,
@@ -69,7 +70,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
69
70
  const responsive = useResponsive();
70
71
 
71
72
  const handleClose = useCallback(() => {
72
- if (__DEV__) console.log('[PaywallScreen] 🔙 Closing paywall');
73
+ logger.debug("Closing paywall");
73
74
  if (onClose) {
74
75
  onClose();
75
76
  } else if (navigation.canGoBack()) {
@@ -98,7 +99,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
98
99
  // Cleanup on unmount only - resetState is stable from useCallback
99
100
  useEffect(() => {
100
101
  return () => {
101
- if (__DEV__) console.log('[PaywallScreen] 🧹 Cleanup: resetting state');
102
+ logger.debug("Cleanup: resetting state");
102
103
  resetState();
103
104
  };
104
105
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -118,7 +119,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
118
119
  try {
119
120
  if (await Linking.canOpenURL(url)) await Linking.openURL(url);
120
121
  } catch (error) {
121
- console.error('[PaywallScreen] Failed to open URL:', error);
122
+ logger.error("Failed to open URL", error);
122
123
  }
123
124
  }, []);
124
125
 
@@ -176,7 +177,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
176
177
 
177
178
  // Defensive check for translations
178
179
  if (!translations) {
179
- if (__DEV__) console.warn("[PaywallScreen] Translations prop is missing");
180
+ logger.warn("Translations prop is missing");
180
181
  return null;
181
182
  }
182
183
 
@@ -9,6 +9,9 @@ import { useState, useRef, useMemo, useCallback } from "react";
9
9
  import type { UsePaywallActionsParams } from "./usePaywallActions.types";
10
10
  import { usePurchaseHandler } from "./usePaywallPurchase";
11
11
  import { useRestoreHandler } from "./usePaywallRestore";
12
+ import { createLogger } from "../../../../shared/utils/logger";
13
+
14
+ const logger = createLogger("usePaywallActions");
12
15
 
13
16
  export function usePaywallActions({
14
17
  packages = [],
@@ -61,9 +64,7 @@ export function usePaywallActions({
61
64
 
62
65
  // Reset state - memoized with useCallback to prevent infinite loops
63
66
  const resetState = useCallback(() => {
64
- if (__DEV__) {
65
- console.log('[usePaywallActions] 🧹 Resetting state');
66
- }
67
+ logger.debug("Resetting state");
67
68
  setSelectedPlanId(null);
68
69
  setIsProcessing(false);
69
70
  }, []);
@@ -5,32 +5,27 @@
5
5
 
6
6
  import { useSubscriptionStatus } from "../../subscription/presentation/useSubscriptionStatus";
7
7
  import { useCredits } from "../../credits/presentation/useCredits";
8
+ import { createLogger } from "../../../../shared/utils/logger";
9
+
10
+ const logger = createLogger("PremiumVerification");
8
11
 
9
12
  export function usePremiumVerification() {
10
- const { refetch: refetchStatus } = useSubscriptionStatus();
11
- const { refetch: refetchCredits } = useCredits();
13
+ const { isPremium: isSubscriptionPremium } = useSubscriptionStatus();
14
+ const { credits } = useCredits();
12
15
 
13
16
  const verifyPremiumStatus = async (): Promise<boolean> => {
14
- if (__DEV__) {
15
- console.log('[PremiumVerification] 🔍 Checking premium status as fallback...');
16
- }
17
-
18
- const [statusResult, creditsResult] = await Promise.all([
19
- refetchStatus(),
20
- refetchCredits()
21
- ]);
17
+ logger.debug("Checking premium status as fallback...");
22
18
 
23
- const isSubscriptionPremium = statusResult.data?.isPremium ?? false;
24
- const isCreditsPremium = creditsResult.data?.isPremium ?? false;
19
+ // With real-time sync, data is already up-to-date via onSnapshot
20
+ // No need to manually refetch - just check current state
21
+ const isCreditsPremium = credits?.isPremium ?? false;
25
22
  const isActuallySuccessful = isSubscriptionPremium || isCreditsPremium;
26
23
 
27
- if (__DEV__) {
28
- console.log('[PremiumVerification] 📊 Fallback check result:', {
29
- isSubscriptionPremium,
30
- isCreditsPremium,
31
- isActuallySuccessful
32
- });
33
- }
24
+ logger.debug("Fallback check result", {
25
+ isSubscriptionPremium,
26
+ isCreditsPremium,
27
+ isActuallySuccessful
28
+ });
34
29
 
35
30
  return isActuallySuccessful;
36
31
  };
@@ -7,6 +7,9 @@
7
7
  import { useCallback } from "react";
8
8
  import type { UsePaywallActionsParams } from "./usePaywallActions.types";
9
9
  import { usePremiumVerification } from "./usePaywallActions.utils";
10
+ import { createLogger } from "../../../../shared/utils/logger";
11
+
12
+ const logger = createLogger("usePurchaseHandler");
10
13
 
11
14
  interface UsePurchaseHandlerParams {
12
15
  selectedPlanId: string | null;
@@ -36,18 +39,14 @@ export function usePurchaseHandler({
36
39
  return;
37
40
  }
38
41
 
39
- if (__DEV__) {
40
- console.log('[usePurchaseHandler] 🛒 Starting purchase', {
41
- productId: pkg.product.identifier,
42
- });
43
- }
42
+ logger.debug("Starting purchase", {
43
+ productId: pkg.product.identifier,
44
+ });
44
45
 
45
46
  try {
46
47
  const success = await callbacksRef.current.purchasePackage(pkg);
47
48
 
48
- if (__DEV__) {
49
- console.log('[usePurchaseHandler] ✅ Purchase completed', { success });
50
- }
49
+ logger.debug("Purchase completed", { success });
51
50
 
52
51
  let isActuallySuccessful = success === true;
53
52
 
@@ -56,20 +55,14 @@ export function usePurchaseHandler({
56
55
  }
57
56
 
58
57
  if (isActuallySuccessful) {
59
- if (__DEV__) {
60
- console.log('[usePurchaseHandler] 🎉 Purchase successful, closing paywall');
61
- }
58
+ logger.debug("Purchase successful, closing paywall");
62
59
  callbacksRef.current.onPurchaseSuccess?.();
63
60
  callbacksRef.current.onClose?.();
64
61
  } else {
65
- if (__DEV__) {
66
- console.warn('[usePurchaseHandler] ⚠️ Purchase did not indicate success');
67
- }
62
+ logger.warn("Purchase did not indicate success");
68
63
  }
69
64
  } catch (error) {
70
- if (__DEV__) {
71
- console.error('[usePurchaseHandler] ❌ Purchase error:', error);
72
- }
65
+ logger.error("Purchase error", error);
73
66
  callbacksRef.current.onPurchaseError?.(error instanceof Error ? error : new Error(String(error)));
74
67
  }
75
68
  }, [selectedPlanId, verifyPremiumStatus, callbacksRef, isProcessingRef]);
@@ -7,6 +7,9 @@
7
7
  import { useCallback } from "react";
8
8
  import type { UsePaywallActionsParams } from "./usePaywallActions.types";
9
9
  import { usePremiumVerification } from "./usePaywallActions.utils";
10
+ import { createLogger } from "../../../../shared/utils/logger";
11
+
12
+ const logger = createLogger("useRestoreHandler");
10
13
 
11
14
  interface UseRestoreHandlerParams {
12
15
  isProcessingRef: React.MutableRefObject<boolean>;
@@ -26,16 +29,12 @@ export function useRestoreHandler({
26
29
  const handleRestore = useCallback(async () => {
27
30
  if (isProcessingRef.current) return;
28
31
 
29
- if (__DEV__) {
30
- console.log('[useRestoreHandler] 🔄 Starting restore');
31
- }
32
+ logger.debug("Starting restore");
32
33
 
33
34
  try {
34
35
  const success = await callbacksRef.current.restorePurchase();
35
36
 
36
- if (__DEV__) {
37
- console.log('[useRestoreHandler] ✅ Restore completed', { success });
38
- }
37
+ logger.debug("Restore completed", { success });
39
38
 
40
39
  let isActuallySuccessful = success === true;
41
40
 
@@ -44,20 +43,14 @@ export function useRestoreHandler({
44
43
  }
45
44
 
46
45
  if (isActuallySuccessful) {
47
- if (__DEV__) {
48
- console.log('[useRestoreHandler] 🎉 Restore successful, closing paywall');
49
- }
46
+ logger.debug("Restore successful, closing paywall");
50
47
  callbacksRef.current.onPurchaseSuccess?.();
51
48
  callbacksRef.current.onClose?.();
52
49
  } else {
53
- if (__DEV__) {
54
- console.warn('[useRestoreHandler] ⚠️ Restore did not indicate success');
55
- }
50
+ logger.warn("Restore did not indicate success");
56
51
  }
57
52
  } catch (error) {
58
- if (__DEV__) {
59
- console.error('[useRestoreHandler] ❌ Restore error:', error);
60
- }
53
+ logger.error("Restore error", error);
61
54
  callbacksRef.current.onPurchaseError?.(error instanceof Error ? error : new Error(String(error)));
62
55
  }
63
56
  }, [verifyPremiumStatus, callbacksRef, isProcessingRef]);
@@ -3,6 +3,9 @@ import type { InitializerDeps } from "./RevenueCatInitializer.types";
3
3
  import { FAILED_INITIALIZATION_RESULT, CONFIGURATION_RETRY_DELAY_MS, MAX_INIT_RETRIES } from "./initializerConstants";
4
4
  import { configState } from "./ConfigurationStateManager";
5
5
  import { handleUserSwitch, handleInitialConfiguration, fetchCurrentUserData } from "./userSwitchHandler";
6
+ import { createLogger } from "../../../../../shared/utils/logger";
7
+
8
+ const logger = createLogger("RevenueCatInitializer");
6
9
 
7
10
  const MAX_CONFIG_START_RETRIES = 3;
8
11
 
@@ -32,7 +35,7 @@ export async function initializeSDK(
32
35
  }
33
36
 
34
37
  if (configState.isConfiguring) {
35
- console.error('[RevenueCatInitializer] Max retry attempts reached', { userId });
38
+ logger.error("Max retry attempts reached", undefined, { userId });
36
39
  return FAILED_INITIALIZATION_RESULT;
37
40
  }
38
41
 
@@ -49,18 +52,16 @@ export async function initializeSDK(
49
52
  resolveConfig = configState.startConfiguration();
50
53
  } catch (error) {
51
54
  if (configStartRetryCount >= MAX_CONFIG_START_RETRIES) {
52
- console.error('[RevenueCatInitializer] Max configuration start retries reached', {
55
+ logger.error("Max configuration start retries reached", error, {
53
56
  userId,
54
57
  retryCount: configStartRetryCount,
55
- error
56
58
  });
57
59
  return FAILED_INITIALIZATION_RESULT;
58
60
  }
59
61
 
60
- console.error('[RevenueCatInitializer] Failed to start configuration, retrying', {
62
+ logger.error("Failed to start configuration, retrying", error, {
61
63
  userId,
62
64
  retryCount: configStartRetryCount,
63
- error
64
65
  });
65
66
  await new Promise<void>(resolve => setTimeout(() => resolve(), CONFIGURATION_RETRY_DELAY_MS));
66
67
  return initializeSDK(deps, userId, apiKey, configStartRetryCount + 1);
@@ -1,3 +1,7 @@
1
+ import { createLogger } from "../../../../../shared/utils/logger";
2
+
3
+ const logger = createLogger("UserSwitchMutex");
4
+
1
5
  class UserSwitchMutexImpl {
2
6
  private activeSwitchPromise: Promise<unknown> | null = null;
3
7
  private activeUserId: string | null = null;
@@ -6,30 +10,24 @@ class UserSwitchMutexImpl {
6
10
 
7
11
  async acquire(userId: string): Promise<{ shouldProceed: boolean; existingPromise?: Promise<unknown> }> {
8
12
  if (this.activeSwitchPromise && this.activeUserId === userId) {
9
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
10
- console.log('[UserSwitchMutex] Switch already in progress for this user, returning existing promise');
11
- }
13
+ logger.debug("Switch already in progress for this user, returning existing promise");
12
14
  return { shouldProceed: false, existingPromise: this.activeSwitchPromise };
13
15
  }
14
16
 
15
17
  if (this.activeSwitchPromise) {
16
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
17
- console.log('[UserSwitchMutex] Waiting for active switch to complete...');
18
- }
18
+ logger.debug("Waiting for active switch to complete...");
19
19
  try {
20
20
  await this.activeSwitchPromise;
21
21
  } catch (error) {
22
22
  // Previous switch failed — this is non-fatal for the current switch,
23
23
  // but worth logging so the failure is visible in diagnostics.
24
- console.warn('[UserSwitchMutex] Previous user switch failed:', error instanceof Error ? error.message : String(error));
24
+ logger.warn("Previous user switch failed", error);
25
25
  }
26
26
 
27
27
  const timeSinceLastSwitch = Date.now() - this.lastSwitchTime;
28
28
  if (timeSinceLastSwitch < this.MIN_SWITCH_INTERVAL_MS) {
29
29
  const delayNeeded = this.MIN_SWITCH_INTERVAL_MS - timeSinceLastSwitch;
30
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
31
- console.log(`[UserSwitchMutex] Rate limiting: waiting ${delayNeeded}ms`);
32
- }
30
+ logger.debug(`Rate limiting: waiting ${delayNeeded}ms`);
33
31
  await new Promise<void>(resolve => setTimeout(() => resolve(), delayNeeded));
34
32
  }
35
33