@umituz/react-native-subscription 2.27.94 → 2.27.96

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.27.94",
3
+ "version": "2.27.96",
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",
@@ -12,6 +12,8 @@ import { CreditsMapper } from "../core/CreditsMapper";
12
12
  import type { RevenueCatData } from "../../subscription/core/RevenueCatData";
13
13
  import { DeductCreditsCommand } from "../application/DeductCreditsCommand";
14
14
  import { CreditLimitCalculator } from "../application/CreditLimitCalculator";
15
+ import { PURCHASE_TYPE, type PurchaseType } from "../../subscription/core/SubscriptionConstants";
16
+ import { updateDoc } from "firebase/firestore";
15
17
 
16
18
  export class CreditsRepository extends BaseRepository {
17
19
  private deductCommand: DeductCreditsCommand;
@@ -47,7 +49,8 @@ export class CreditsRepository extends BaseRepository {
47
49
  purchaseId: string,
48
50
  productId: string,
49
51
  source: PurchaseSource,
50
- revenueCatData: RevenueCatData
52
+ revenueCatData: RevenueCatData,
53
+ type: PurchaseType = PURCHASE_TYPE.INITIAL
51
54
  ): Promise<CreditsResult> {
52
55
  const db = getFirestore();
53
56
  if (!db) {
@@ -70,6 +73,7 @@ export class CreditsRepository extends BaseRepository {
70
73
  originalTransactionId: revenueCatData.originalTransactionId,
71
74
  isPremium: revenueCatData.isPremium,
72
75
  periodType: revenueCatData.periodType,
76
+ type,
73
77
  }
74
78
  );
75
79
 
@@ -86,4 +90,27 @@ export class CreditsRepository extends BaseRepository {
86
90
  async deductCredit(userId: string, cost: number): Promise<DeductCreditsResult> {
87
91
  return this.deductCommand.execute(userId, cost);
88
92
  }
93
+
94
+ async hasCredits(userId: string, cost: number): Promise<boolean> {
95
+ const result = await this.getCredits(userId);
96
+ if (!result.success || !result.data) return false;
97
+ return result.data.credits >= cost;
98
+ }
99
+
100
+ async syncExpiredStatus(userId: string): Promise<void> {
101
+ const db = getFirestore();
102
+ if (!db) throw new Error("Firestore instance is not available");
103
+
104
+ const ref = this.getRef(db, userId);
105
+ await updateDoc(ref, {
106
+ isPremium: false,
107
+ status: "expired",
108
+ willRenew: false,
109
+ expirationDate: new Date().toISOString()
110
+ });
111
+ }
112
+ }
113
+
114
+ export function createCreditsRepository(config: CreditsConfig): CreditsRepository {
115
+ return new CreditsRepository(config);
89
116
  }
@@ -92,7 +92,7 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
92
92
  showAuthModal,
93
93
  });
94
94
 
95
- const initializeInBackground = async (userId: string): Promise<void> => {
95
+ const initializeInBackground = async (userId?: string): Promise<void> => {
96
96
  await SubscriptionManager.initialize(userId);
97
97
  if (__DEV__) {
98
98
  console.log('[SubscriptionInitializer] Background init complete');
@@ -1,6 +1,6 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
2
  import type { RevenueCatData } from "../core/RevenueCatData";
3
- import { type PeriodType, type PurchaseSource } from "../core/SubscriptionConstants";
3
+ import { type PeriodType, type PurchaseSource, PURCHASE_SOURCE, PURCHASE_TYPE } from "../core/SubscriptionConstants";
4
4
  import { getCreditsRepository } from "../../credits/infrastructure/CreditsRepositoryProvider";
5
5
  import { extractRevenueCatData } from "./SubscriptionSyncUtils";
6
6
  import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
@@ -19,7 +19,14 @@ export class SubscriptionSyncService {
19
19
  ? `purchase_${revenueCatData.originalTransactionId}`
20
20
  : `purchase_${productId}_${Date.now()}`;
21
21
 
22
- await getCreditsRepository().initializeCredits(userId, purchaseId, productId, source, revenueCatData);
22
+ await getCreditsRepository().initializeCredits(
23
+ userId,
24
+ purchaseId,
25
+ productId,
26
+ source ?? PURCHASE_SOURCE.SETTINGS, // Default to settings if source unknown
27
+ revenueCatData,
28
+ PURCHASE_TYPE.INITIAL // Default to INITIAL
29
+ );
23
30
 
24
31
  // Notify listeners via Event Bus
25
32
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
@@ -37,7 +44,14 @@ export class SubscriptionSyncService {
37
44
  ? `renewal_${revenueCatData.originalTransactionId}_${newExpirationDate}`
38
45
  : `renewal_${productId}_${Date.now()}`;
39
46
 
40
- await getCreditsRepository().initializeCredits(userId, purchaseId, productId, "renewal", revenueCatData);
47
+ await getCreditsRepository().initializeCredits(
48
+ userId,
49
+ purchaseId,
50
+ productId,
51
+ PURCHASE_SOURCE.RENEWAL,
52
+ revenueCatData,
53
+ PURCHASE_TYPE.RENEWAL
54
+ );
41
55
 
42
56
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
43
57
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.RENEWAL_DETECTED, { userId, productId });
@@ -61,14 +75,27 @@ export class SubscriptionSyncService {
61
75
  return;
62
76
  }
63
77
 
78
+ // If productId is missing, we can't initialize credits fully,
79
+ // but if isPremium is true, we should have it.
80
+ // Fallback to 'unknown' if missing, but this might throw in CreditLimitCalculator.
81
+ const validProductId = productId ?? 'unknown_product';
82
+
64
83
  const revenueCatData: RevenueCatData = {
65
84
  expirationDate: expiresAt ?? null,
66
85
  willRenew: willRenew ?? false,
67
86
  isPremium,
68
- periodType
87
+ periodType: periodType ?? null, // Fix undefined vs null
88
+ originalTransactionId: null // Initialize with null as we might not have it here
69
89
  };
70
90
 
71
- await getCreditsRepository().initializeCredits(userId, `status_sync_${Date.now()}`, productId, "settings", revenueCatData);
91
+ await getCreditsRepository().initializeCredits(
92
+ userId,
93
+ `status_sync_${Date.now()}`,
94
+ validProductId,
95
+ PURCHASE_SOURCE.SETTINGS,
96
+ revenueCatData,
97
+ PURCHASE_TYPE.INITIAL // Status sync treated as Initial or Update
98
+ );
72
99
 
73
100
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
74
101
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.PREMIUM_STATUS_CHANGED, { userId, isPremium });
@@ -12,7 +12,7 @@ export const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId:
12
12
  willRenew: entitlement?.willRenew ?? false,
13
13
  // Use latestPurchaseDate if originalPurchaseDate is missing, or a combine id
14
14
  originalTransactionId: entitlement?.originalPurchaseDate || customerInfo.firstSeen,
15
- periodType: entitlement?.periodType as PeriodType | undefined,
15
+ periodType: (entitlement?.periodType as PeriodType) ?? null,
16
16
  isPremium: !!customerInfo.entitlements.active[entitlementId],
17
17
  };
18
18
  };
@@ -5,10 +5,10 @@ import type { PeriodType } from "./SubscriptionStatus";
5
5
  * Used across the subscription package for storing RevenueCat data in Firestore
6
6
  */
7
7
  export interface RevenueCatData {
8
- expirationDate?: string | null;
9
- willRenew?: boolean;
10
- originalTransactionId?: string;
11
- isPremium?: boolean;
8
+ expirationDate: string | null;
9
+ willRenew: boolean | null;
10
+ originalTransactionId: string | null;
11
+ isPremium: boolean;
12
12
  /** RevenueCat period type: NORMAL, INTRO, or TRIAL */
13
- periodType?: PeriodType;
13
+ periodType: PeriodType | null;
14
14
  }
@@ -53,10 +53,11 @@ class SubscriptionManagerImpl {
53
53
  }
54
54
  }
55
55
 
56
- async initialize(userId: string): Promise<boolean> {
56
+ async initialize(userId?: string): Promise<boolean> {
57
57
  this.ensureConfigured();
58
58
 
59
- const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(userId);
59
+ const actualUserId = userId ?? (await this.managerConfig!.getAnonymousUserId());
60
+ const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(actualUserId);
60
61
 
61
62
  if (!shouldInit && existingPromise) {
62
63
  return existingPromise;
@@ -71,11 +72,11 @@ class SubscriptionManagerImpl {
71
72
  }
72
73
 
73
74
  this.ensurePackageHandlerInitialized();
74
- const result = await this.serviceInstance.initialize(userId);
75
+ const result = await this.serviceInstance.initialize(actualUserId);
75
76
  return result.success;
76
77
  })();
77
78
 
78
- this.state.initCache.setPromise(promise, userId);
79
+ this.state.initCache.setPromise(promise, actualUserId);
79
80
  return promise;
80
81
  }
81
82
 
@@ -44,7 +44,7 @@ export function getSubscriptionStatusType(
44
44
  isPremium: boolean,
45
45
  willRenew?: boolean,
46
46
  expiresAt?: string | null,
47
- periodType?: PeriodType
47
+ periodType?: PeriodType | null
48
48
  ): SubscriptionStatusType {
49
49
  const isExpired = expiresAt ? new Date(expiresAt) < new Date() : false;
50
50
 
@@ -52,6 +52,6 @@ export function getSubscriptionStatusType(
52
52
  isPremium,
53
53
  willRenew,
54
54
  isExpired,
55
- periodType,
55
+ periodType: periodType ?? undefined,
56
56
  });
57
57
  }
@@ -0,0 +1,2 @@
1
+ declare module 'i18next';
2
+ declare module 'react-i18next';