@umituz/react-native-subscription 2.37.48 → 2.37.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.37.48",
3
+ "version": "2.37.50",
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",
@@ -1,10 +1,10 @@
1
1
  class UserSwitchMutexImpl {
2
- private activeSwitchPromise: Promise<any> | null = null;
2
+ private activeSwitchPromise: Promise<unknown> | null = null;
3
3
  private activeUserId: string | null = null;
4
4
  private lastSwitchTime = 0;
5
5
  private readonly MIN_SWITCH_INTERVAL_MS = 1000;
6
6
 
7
- async acquire(userId: string): Promise<{ shouldProceed: boolean; existingPromise?: Promise<any> }> {
7
+ async acquire(userId: string): Promise<{ shouldProceed: boolean; existingPromise?: Promise<unknown> }> {
8
8
  if (this.activeSwitchPromise && this.activeUserId === userId) {
9
9
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
10
10
  console.log('[UserSwitchMutex] Switch already in progress for this user, returning existing promise');
@@ -43,7 +43,7 @@ class UserSwitchMutexImpl {
43
43
  return { shouldProceed: true };
44
44
  }
45
45
 
46
- setPromise(promise: Promise<any>): void {
46
+ setPromise(promise: Promise<unknown>): void {
47
47
  this.activeSwitchPromise = promise;
48
48
  this.lastSwitchTime = Date.now();
49
49
 
@@ -4,6 +4,9 @@ import type { InitializerDeps } from "./RevenueCatInitializer.types";
4
4
  import { FAILED_INITIALIZATION_RESULT } from "./initializerConstants";
5
5
  import { UserSwitchMutex } from "./UserSwitchMutex";
6
6
  import { getPremiumEntitlement } from "../../core/types";
7
+ import type { PeriodType } from "../../../subscription/core/SubscriptionConstants";
8
+
9
+ const ANONYMOUS_CACHE_KEY = '__anonymous__';
7
10
 
8
11
  declare const __DEV__: boolean;
9
12
 
@@ -29,7 +32,7 @@ async function fetchOfferingsSafe(): Promise<PurchasesOfferings | null> {
29
32
  }
30
33
 
31
34
  function normalizeUserId(userId: string): string | null {
32
- return (userId && userId.length > 0 && userId !== "__anonymous__") ? userId : null;
35
+ return (userId && userId.length > 0 && userId !== ANONYMOUS_CACHE_KEY) ? userId : null;
33
36
  }
34
37
 
35
38
  function isAnonymousId(userId: string): boolean {
@@ -40,7 +43,7 @@ export async function handleUserSwitch(
40
43
  deps: InitializerDeps,
41
44
  userId: string
42
45
  ): Promise<InitializeResult> {
43
- const mutexKey = userId || '__anonymous__';
46
+ const mutexKey = userId || ANONYMOUS_CACHE_KEY;
44
47
 
45
48
  // Acquire mutex to prevent concurrent Purchases.logIn() calls
46
49
  const { shouldProceed, existingPromise } = await UserSwitchMutex.acquire(mutexKey);
@@ -49,7 +52,7 @@ export async function handleUserSwitch(
49
52
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
50
53
  console.log('[UserSwitchHandler] Using result from active switch operation');
51
54
  }
52
- return existingPromise;
55
+ return existingPromise as Promise<InitializeResult>;
53
56
  }
54
57
 
55
58
  const switchOperation = performUserSwitch(deps, userId);
@@ -190,7 +193,7 @@ export async function handleInitialConfiguration(
190
193
  premiumEntitlement.productIdentifier,
191
194
  premiumEntitlement.expirationDate ?? undefined,
192
195
  premiumEntitlement.willRenew,
193
- premiumEntitlement.periodType as "NORMAL" | "INTRO" | undefined
196
+ premiumEntitlement.periodType as PeriodType | undefined
194
197
  );
195
198
  } else {
196
199
  await deps.config.onPremiumStatusChanged(
@@ -39,6 +39,7 @@ export class SubscriptionSyncService {
39
39
  newExpirationDate,
40
40
  error: error instanceof Error ? error.message : String(error)
41
41
  });
42
+ throw error;
42
43
  }
43
44
  }
44
45
 
@@ -60,6 +61,7 @@ export class SubscriptionSyncService {
60
61
  productId,
61
62
  error: error instanceof Error ? error.message : String(error)
62
63
  });
64
+ throw error;
63
65
  }
64
66
  }
65
67
  }
@@ -19,8 +19,6 @@ export const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId:
19
19
 
20
20
  const entitlement = customerInfo.entitlements.active[entitlementId];
21
21
 
22
- const isPremium = !!customerInfo.entitlements.active[entitlementId];
23
-
24
22
  if (!entitlement) {
25
23
  return {
26
24
  expirationDate: null,
@@ -36,13 +34,15 @@ export const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId:
36
34
  };
37
35
  }
38
36
 
37
+ const subscription = customerInfo.subscriptionsByProductIdentifier?.[entitlement.productIdentifier];
38
+
39
39
  return {
40
40
  expirationDate: entitlement.expirationDate ?? null,
41
41
  willRenew: entitlement.willRenew ?? null,
42
- originalTransactionId: entitlement.originalPurchaseDate ?? null,
42
+ originalTransactionId: subscription?.storeTransactionId ?? null,
43
43
  periodType: validatePeriodType(entitlement.periodType),
44
44
  packageType: null,
45
- isPremium,
45
+ isPremium: true,
46
46
  unsubscribeDetectedAt: entitlement.unsubscribeDetectedAt ?? null,
47
47
  billingIssueDetectedAt: entitlement.billingIssueDetectedAt ?? null,
48
48
  store: entitlement.store ?? null,
@@ -1,11 +1,10 @@
1
1
  import type { PurchasesPackage, CustomerInfo } from "react-native-purchases";
2
2
  import type { IRevenueCatService } from "../../../../shared/application/ports/IRevenueCatService";
3
- import type { PremiumStatus } from "./PurchaseStatusResolver";
3
+ import { PurchaseStatusResolver, type PremiumStatus } from "./PurchaseStatusResolver";
4
4
  import {
5
5
  fetchPackages,
6
6
  executePurchase,
7
7
  restorePurchases,
8
- checkPremiumStatus,
9
8
  type RestoreResultInfo,
10
9
  } from "./package-operations";
11
10
 
@@ -33,6 +32,6 @@ export class PackageHandler {
33
32
  }
34
33
 
35
34
  checkPremiumStatusFromInfo(customerInfo: CustomerInfo): PremiumStatus {
36
- return checkPremiumStatus(customerInfo, this.entitlementId);
35
+ return PurchaseStatusResolver.resolve(customerInfo, this.entitlementId);
37
36
  }
38
37
  }
@@ -2,4 +2,3 @@ export type { RestoreResultInfo } from "./types";
2
2
  export { fetchPackages } from "./PackageFetcher";
3
3
  export { executePurchase } from "./PackagePurchaser";
4
4
  export { restorePurchases } from "./PackageRestorer";
5
- export { checkPremiumStatus } from "./PremiumStatusChecker";
@@ -10,6 +10,8 @@ import { getPackagesOperation, purchasePackageOperation, restoreOperation } from
10
10
  import { performServiceInitialization } from "./initializationHandler";
11
11
  import { initializationState } from "../state/initializationState";
12
12
 
13
+ const ANONYMOUS_CACHE_KEY = '__anonymous__';
14
+
13
15
  class SubscriptionManagerImpl {
14
16
  private managerConfig: SubscriptionManagerConfig | null = null;
15
17
  private serviceInstance: IRevenueCatService | null = null;
@@ -43,7 +45,7 @@ class SubscriptionManagerImpl {
43
45
  });
44
46
  }
45
47
 
46
- const cacheKey = actualUserId || '__anonymous__';
48
+ const cacheKey = actualUserId || ANONYMOUS_CACHE_KEY;
47
49
  const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(cacheKey);
48
50
 
49
51
  if (!shouldInit && existingPromise) {
@@ -62,8 +64,6 @@ class SubscriptionManagerImpl {
62
64
  }
63
65
 
64
66
  private async performInitialization(userId: string): Promise<boolean> {
65
- this.ensureConfigured();
66
-
67
67
  if (typeof __DEV__ !== 'undefined' && __DEV__) {
68
68
  console.log('[SubscriptionManager] performInitialization:', {
69
69
  userId: userId || '(empty - anonymous)',
@@ -74,13 +74,8 @@ class SubscriptionManagerImpl {
74
74
  this.serviceInstance = service ?? null;
75
75
  this.ensurePackageHandlerInitialized();
76
76
 
77
- // Always notify reactive state when service is available, even if offerings fetch failed.
78
- // This ensures React components (useSubscriptionPackages, useSubscriptionStatus) are unblocked.
79
- // The service IS configured and usable - empty offerings is not a fatal error.
80
77
  const notifyUserId = (userId && userId.length > 0) ? userId : null;
81
- if (service) {
82
- initializationState.markInitialized(notifyUserId);
83
- } else if (success) {
78
+ if (service || success) {
84
79
  initializationState.markInitialized(notifyUserId);
85
80
  }
86
81
 
@@ -142,7 +137,7 @@ class SubscriptionManagerImpl {
142
137
 
143
138
  getEntitlementId(): string {
144
139
  this.ensureConfigured();
145
- return this.managerConfig!.config.entitlementIdentifier ?? '';
140
+ return this.managerConfig!.config.entitlementIdentifier;
146
141
  }
147
142
  }
148
143
 
@@ -11,7 +11,7 @@ export function ensureConfigured(config: SubscriptionManagerConfig | null): void
11
11
 
12
12
  export function getCurrentUserIdOrThrow(state: SubscriptionInternalState): string {
13
13
  const userId = state.initCache.getCurrentUserId();
14
- if (userId === null || userId === undefined) {
14
+ if (userId == null) {
15
15
  throw new Error("SubscriptionManager not initialized - no current user ID available");
16
16
  }
17
17
  return userId;
@@ -113,5 +113,5 @@ export async function processCustomerInfo(
113
113
  await handlePremiumStatusSync(config, userId, customerInfo);
114
114
  }
115
115
 
116
- return updateRenewalState(renewalState, renewalResult);
116
+ return updateRenewalState(renewalResult);
117
117
  }
@@ -27,28 +27,26 @@ export class InitializationCache {
27
27
  this.promiseUserId = userId;
28
28
  this.promiseCompleted = false;
29
29
 
30
- const targetUserId = userId;
31
-
32
30
  const chain: Promise<boolean> = promise
33
31
  .then((result) => {
34
- if (result && this.promiseUserId === targetUserId) {
35
- this.currentUserId = targetUserId;
32
+ if (result && this.promiseUserId === userId) {
33
+ this.currentUserId = userId;
36
34
  }
37
35
  this.promiseCompleted = true;
38
36
  return result;
39
37
  })
40
38
  .catch((error) => {
41
- if (this.promiseUserId === targetUserId) {
39
+ if (this.promiseUserId === userId) {
42
40
  this.initPromise = null;
43
41
  this.promiseUserId = null;
44
42
  this.currentUserId = null;
45
43
  }
46
44
  this.promiseCompleted = true;
47
- console.error('[InitializationCache] Initialization failed', { userId: targetUserId, error });
48
- return false as boolean;
45
+ console.error('[InitializationCache] Initialization failed', { userId, error });
46
+ return false;
49
47
  })
50
48
  .finally(() => {
51
- this.pendingQueue.delete(targetUserId);
49
+ this.pendingQueue.delete(userId);
52
50
  });
53
51
 
54
52
  this.initPromise = chain;
@@ -1,7 +1,6 @@
1
- import type { RenewalState, RenewalDetectionResult } from "./types";
1
+ import type { RenewalDetectionResult, RenewalState } from "./types";
2
2
 
3
3
  export function updateRenewalState(
4
- _state: RenewalState,
5
4
  result: RenewalDetectionResult
6
5
  ): RenewalState {
7
6
  return {
@@ -41,12 +41,7 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
41
41
  return null;
42
42
  }
43
43
 
44
- try {
45
- const result = await SubscriptionManager.checkPremiumStatus();
46
- return result;
47
- } catch {
48
- return null;
49
- }
44
+ return SubscriptionManager.checkPremiumStatus();
50
45
  },
51
46
  enabled: queryEnabled,
52
47
  ...NO_CACHE_QUERY_CONFIG,
@@ -1,4 +1,5 @@
1
1
  import { buildCollectionRef, type CollectionConfig } from "../../../../../shared/infrastructure/firestore";
2
+ import type { Firestore } from "@umituz/react-native-firebase";
2
3
  import type { TransactionRepositoryConfig } from "../../../domain/types/transaction.types";
3
4
 
4
5
  function getCollectionConfig(config: TransactionRepositoryConfig): CollectionConfig {
@@ -8,7 +9,7 @@ function getCollectionConfig(config: TransactionRepositoryConfig): CollectionCon
8
9
  };
9
10
  }
10
11
 
11
- export function getCollectionRef(db: any, userId: string, config: TransactionRepositoryConfig) {
12
+ export function getCollectionRef(db: Firestore, userId: string, config: TransactionRepositoryConfig) {
12
13
  const collectionConfig = getCollectionConfig(config);
13
14
  return buildCollectionRef(db, userId, collectionConfig);
14
15
  }
@@ -34,7 +34,7 @@ export function createSubscriptionInitModule(config: SubscriptionInitModuleConfi
34
34
  return true;
35
35
  } catch (error) {
36
36
  console.error('[SubscriptionInitModule] Initialization failed:', error instanceof Error ? error.message : String(error));
37
- return false;
37
+ throw error;
38
38
  }
39
39
  },
40
40
  };
@@ -2,7 +2,7 @@ type EventCallback<T = unknown> = (data: T) => void;
2
2
 
3
3
  class SubscriptionEventBus {
4
4
  private static instance: SubscriptionEventBus;
5
- private listeners: Record<string, EventCallback<any>[]> = {};
5
+ private listeners: Record<string, EventCallback[]> = {};
6
6
 
7
7
  private constructor() {}
8
8
 
@@ -17,7 +17,7 @@ class SubscriptionEventBus {
17
17
  if (!this.listeners[event]) {
18
18
  this.listeners[event] = [];
19
19
  }
20
- this.listeners[event].push(callback);
20
+ this.listeners[event].push(callback as EventCallback);
21
21
 
22
22
  return () => {
23
23
  const listeners = this.listeners[event];
@@ -1,9 +0,0 @@
1
- import type { CustomerInfo } from "react-native-purchases";
2
- import { PurchaseStatusResolver, type PremiumStatus } from "../PurchaseStatusResolver";
3
-
4
- export function checkPremiumStatus(
5
- customerInfo: CustomerInfo,
6
- entitlementId: string
7
- ): PremiumStatus {
8
- return PurchaseStatusResolver.resolve(customerInfo, entitlementId);
9
- }