@umituz/react-native-subscription 2.27.113 → 2.27.114

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 (42) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditsInitializer.ts +27 -116
  3. package/src/domains/credits/application/credit-strategies/CreditAllocationOrchestrator.ts +1 -6
  4. package/src/domains/credits/application/creditDocumentHelpers.ts +58 -0
  5. package/src/domains/credits/application/creditOperationUtils.ts +154 -0
  6. package/src/domains/credits/presentation/useCredits.ts +1 -2
  7. package/src/domains/paywall/hooks/usePaywallActions.ts +0 -3
  8. package/src/domains/subscription/application/SubscriptionSyncService.ts +16 -20
  9. package/src/domains/subscription/core/RevenueCatError.ts +40 -31
  10. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +0 -1
  11. package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +19 -85
  12. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +33 -75
  13. package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +57 -0
  14. package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +3 -12
  15. package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +0 -2
  16. package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +2 -4
  17. package/src/domains/subscription/infrastructure/services/RevenueCatService.ts +1 -5
  18. package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -12
  19. package/src/domains/subscription/infrastructure/utils/authPurchaseState.ts +69 -0
  20. package/src/domains/subscription/infrastructure/utils/trialEligibilityUtils.ts +77 -0
  21. package/src/domains/subscription/presentation/components/feedback/FeedbackOption.tsx +139 -0
  22. package/src/domains/subscription/presentation/components/feedback/PaywallFeedbackModal.tsx +15 -70
  23. package/src/domains/subscription/presentation/components/feedback/paywallFeedbackStyles.ts +0 -92
  24. package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +1 -18
  25. package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +19 -69
  26. package/src/domains/subscription/presentation/usePremium.ts +2 -11
  27. package/src/domains/subscription/presentation/useSubscriptionStatus.ts +1 -6
  28. package/src/domains/trial/application/TrialService.ts +4 -8
  29. package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +1 -1
  30. package/src/domains/wallet/infrastructure/services/ProductMetadataService.ts +0 -13
  31. package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +0 -10
  32. package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +0 -8
  33. package/src/init/createSubscriptionInitModule.ts +1 -4
  34. package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +0 -14
  35. package/src/shared/application/FeedbackService.ts +0 -21
  36. package/src/shared/infrastructure/SubscriptionEventBus.ts +1 -2
  37. package/src/shared/types/CommonTypes.ts +65 -0
  38. package/src/shared/utils/BaseError.ts +26 -0
  39. package/src/shared/utils/Logger.ts +14 -45
  40. package/src/shared/utils/Result.ts +16 -0
  41. package/src/shared/utils/SubscriptionError.ts +20 -30
  42. package/src/utils/packageTypeDetector.ts +0 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.27.113",
3
+ "version": "2.27.114",
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,23 +1,16 @@
1
- import {
2
- getFirestore,
3
- } from "@umituz/react-native-firebase";
4
- import {
5
- runTransaction,
6
- serverTimestamp,
7
- type Transaction,
8
- type DocumentReference,
9
- } from "firebase/firestore";
10
1
  import type { CreditsConfig } from "../core/Credits";
11
2
  import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
12
- import { resolveSubscriptionStatus } from "../../subscription/core/SubscriptionStatus";
3
+ import { getAppVersion, validatePlatform } from "../../../utils";
4
+ import type { InitializeCreditsMetadata, InitializationResult } from "../../subscription/application/SubscriptionInitializerTypes";
5
+ import { runTransaction, type Transaction, type DocumentReference } from "firebase/firestore";
6
+ import type { Firestore } from "firebase/firestore";
7
+ import { getCreditDocumentOrDefault } from "./creditDocumentHelpers";
8
+ import { calculateNewCredits, buildCreditsData, shouldSkipStatusSyncWrite } from "./creditOperationUtils";
13
9
  import { CreditLimitCalculator } from "./CreditLimitCalculator";
14
10
  import { PurchaseMetadataGenerator } from "./PurchaseMetadataGenerator";
15
- import { creditAllocationOrchestrator } from "./credit-strategies/CreditAllocationOrchestrator";
16
- import { getAppVersion, validatePlatform, isPast } from "../../../utils";
17
- import type { InitializeCreditsMetadata, InitializationResult } from "../../subscription/application/SubscriptionInitializerTypes";
18
11
 
19
12
  export async function initializeCreditsTransaction(
20
- db: ReturnType<typeof getFirestore>,
13
+ db: Firestore,
21
14
  creditsRef: DocumentReference,
22
15
  config: CreditsConfig,
23
16
  purchaseId: string,
@@ -29,33 +22,9 @@ export async function initializeCreditsTransaction(
29
22
 
30
23
  return runTransaction(db, async (transaction: Transaction) => {
31
24
  const creditsDoc = await transaction.get(creditsRef);
32
- const now = serverTimestamp();
33
- const existingData = creditsDoc.exists()
34
- ? creditsDoc.data() as UserCreditsDocumentRead
35
- : {
36
- credits: 0,
37
- creditLimit: 0,
38
- isPremium: false,
39
- status: "none",
40
- processedPurchases: [],
41
- purchaseHistory: [],
42
- platform: validatePlatform() as any,
43
- lastUpdatedAt: now,
44
- purchasedAt: now,
45
- expirationDate: null,
46
- lastPurchaseAt: null,
47
- willRenew: false,
48
- productId: null,
49
- packageType: null,
50
- originalTransactionId: null,
51
- appVersion: null,
52
- periodType: null,
53
- isTrialing: false,
54
- trialStartDate: null,
55
- trialEndDate: null,
56
- trialCredits: 0,
57
- convertedFromTrial: false,
58
- } as any;
25
+ const platform = validatePlatform();
26
+
27
+ const existingData = getCreditDocumentOrDefault(creditsDoc, platform);
59
28
 
60
29
  if (existingData.processedPurchases.includes(purchaseId)) {
61
30
  return {
@@ -66,8 +35,6 @@ export async function initializeCreditsTransaction(
66
35
  }
67
36
 
68
37
  const creditLimit = CreditLimitCalculator.calculate(metadata.productId, config);
69
-
70
- const platform = validatePlatform();
71
38
  const appVersion = getAppVersion();
72
39
 
73
40
  const { purchaseHistory } = PurchaseMetadataGenerator.generate({
@@ -79,85 +46,29 @@ export async function initializeCreditsTransaction(
79
46
  appVersion,
80
47
  }, existingData);
81
48
 
82
- const isPremium = metadata.isPremium;
83
-
84
- let isExpired = false;
85
- if (metadata.expirationDate) {
86
- isExpired = isPast(metadata.expirationDate);
87
- }
88
-
89
- const status = resolveSubscriptionStatus({
90
- isPremium,
91
- willRenew: metadata.willRenew ?? false,
92
- isExpired,
93
- periodType: metadata.periodType ?? undefined,
94
- });
95
-
96
- const isStatusSync = purchaseId.startsWith("status_sync_");
97
- const isSubscriptionActive = isPremium && !isExpired;
98
-
99
- const newCredits = creditAllocationOrchestrator.allocate({
100
- status,
101
- isStatusSync,
49
+ const newCredits = calculateNewCredits({
50
+ metadata,
102
51
  existingData,
103
52
  creditLimit,
104
- isSubscriptionActive,
105
- productId: metadata.productId,
53
+ purchaseId,
106
54
  });
107
55
 
108
- const newProcessedPurchases = [...existingData.processedPurchases, purchaseId].slice(-50);
109
-
110
- const creditsData: Record<string, any> = {
111
- isPremium,
112
- status,
113
- credits: newCredits,
56
+ const creditsData = buildCreditsData({
57
+ existingData,
58
+ newCredits,
114
59
  creditLimit,
115
- lastUpdatedAt: now,
116
- processedPurchases: newProcessedPurchases,
117
- };
118
-
119
- if (purchaseHistory.length > 0) {
120
- creditsData.purchaseHistory = purchaseHistory;
121
- }
122
-
123
- const isNewPurchaseOrRenewal = purchaseId.startsWith("purchase_")
124
- || purchaseId.startsWith("renewal_");
125
-
126
- if (isNewPurchaseOrRenewal) {
127
- creditsData.lastPurchaseAt = now;
128
- }
129
-
130
- if (metadata.expirationDate) {
131
- creditsData.expirationDate = serverTimestamp();
132
- }
133
-
134
- if (metadata.willRenew !== undefined) {
135
- creditsData.willRenew = metadata.willRenew;
136
- }
137
-
138
- if (metadata.originalTransactionId) {
139
- creditsData.originalTransactionId = metadata.originalTransactionId;
140
- }
141
-
142
- creditsData.productId = metadata.productId;
143
- creditsData.platform = platform;
144
-
145
- // Skip write if it's a status sync and data hasn't changed to save costs
146
- if (isStatusSync && existingData) {
147
- const hasChanged =
148
- existingData.isPremium !== creditsData.isPremium ||
149
- existingData.status !== creditsData.status ||
150
- existingData.credits !== creditsData.credits ||
151
- existingData.creditLimit !== creditsData.creditLimit ||
152
- existingData.productId !== creditsData.productId;
60
+ purchaseId,
61
+ metadata,
62
+ purchaseHistory,
63
+ platform,
64
+ });
153
65
 
154
- if (!hasChanged) {
155
- return {
156
- credits: existingData.credits,
157
- alreadyProcessed: true,
158
- finalData: existingData
159
- };
160
- }
66
+ if (shouldSkipStatusSyncWrite(purchaseId, existingData, creditsData)) {
67
+ return {
68
+ credits: existingData.credits,
69
+ alreadyProcessed: true,
70
+ finalData: existingData
71
+ };
161
72
  }
162
73
 
163
74
  transaction.set(creditsRef, creditsData, { merge: true });
@@ -18,16 +18,11 @@ export class CreditAllocationOrchestrator {
18
18
  */
19
19
  allocate(params: CreditAllocationParams): number {
20
20
  const strategy = this.strategies.find(s => s.canHandle(params));
21
-
21
+
22
22
  if (!strategy) {
23
- // Should theoretically never happen due to StandardPurchaseCreditStrategy fallback
24
23
  return params.creditLimit;
25
24
  }
26
25
 
27
- if (__DEV__) {
28
- console.log(`[CreditAllocationOrchestrator] Using strategy: ${strategy.constructor.name}`);
29
- }
30
-
31
26
  return strategy.execute(params);
32
27
  }
33
28
  }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Credit Document Helpers
3
+ * Utilities for getting and creating credit documents
4
+ */
5
+
6
+ import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
7
+ import { serverTimestamp, type DocumentSnapshot } from "firebase/firestore";
8
+
9
+ /**
10
+ * Get existing credit document or create default
11
+ */
12
+ export function getCreditDocumentOrDefault(
13
+ creditsDoc: DocumentSnapshot,
14
+ platform: "ios" | "android"
15
+ ): UserCreditsDocumentRead {
16
+ if (creditsDoc.exists()) {
17
+ return creditsDoc.data() as UserCreditsDocumentRead;
18
+ }
19
+
20
+ const now = serverTimestamp();
21
+
22
+ return {
23
+ credits: 0,
24
+ creditLimit: 0,
25
+ isPremium: false,
26
+ status: "none",
27
+ processedPurchases: [],
28
+ purchaseHistory: [],
29
+ platform,
30
+ lastUpdatedAt: now,
31
+ purchasedAt: now,
32
+ expirationDate: null,
33
+ lastPurchaseAt: null,
34
+ willRenew: false,
35
+ productId: null,
36
+ packageType: null,
37
+ originalTransactionId: null,
38
+ appVersion: null,
39
+ periodType: null,
40
+ isTrialing: false,
41
+ trialStartDate: null,
42
+ trialEndDate: null,
43
+ trialCredits: 0,
44
+ convertedFromTrial: false,
45
+ } as any;
46
+ }
47
+
48
+ /**
49
+ * Add purchase ID to processed purchases list
50
+ * Maintains last 50 purchases
51
+ */
52
+ export function addProcessedPurchase(
53
+ existing: string[],
54
+ purchaseId: string,
55
+ limit: number = 50
56
+ ): string[] {
57
+ return [...existing, purchaseId].slice(-limit);
58
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Credit Operation Utilities
3
+ * Business logic for credit calculations and data building
4
+ */
5
+
6
+ import { resolveSubscriptionStatus } from "../../subscription/core/SubscriptionStatus";
7
+ import { creditAllocationOrchestrator } from "./credit-strategies/CreditAllocationOrchestrator";
8
+ import { isPast } from "../../../utils";
9
+ import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
10
+ import type { InitializeCreditsMetadata } from "../../subscription/application/SubscriptionInitializerTypes";
11
+ import { serverTimestamp } from "firebase/firestore";
12
+
13
+ interface CalculateCreditsParams {
14
+ metadata: InitializeCreditsMetadata;
15
+ existingData: UserCreditsDocumentRead;
16
+ creditLimit: number;
17
+ purchaseId: string;
18
+ }
19
+
20
+ interface BuildCreditsDataParams {
21
+ existingData: UserCreditsDocumentRead;
22
+ newCredits: number;
23
+ creditLimit: number;
24
+ purchaseId: string;
25
+ metadata: InitializeCreditsMetadata;
26
+ purchaseHistory: any[];
27
+ platform: "ios" | "android";
28
+ }
29
+
30
+ /**
31
+ * Calculate new credits based on subscription status
32
+ */
33
+ export function calculateNewCredits(params: CalculateCreditsParams): number {
34
+ const { metadata, existingData, creditLimit, purchaseId } = params;
35
+
36
+ const isPremium = metadata.isPremium;
37
+ const isExpired = metadata.expirationDate ? isPast(metadata.expirationDate) : false;
38
+
39
+ const status = resolveSubscriptionStatus({
40
+ isPremium,
41
+ willRenew: metadata.willRenew ?? false,
42
+ isExpired,
43
+ periodType: metadata.periodType ?? undefined,
44
+ });
45
+
46
+ const isStatusSync = purchaseId.startsWith("status_sync_");
47
+ const isSubscriptionActive = isPremium && !isExpired;
48
+
49
+ return creditAllocationOrchestrator.allocate({
50
+ status,
51
+ isStatusSync,
52
+ existingData,
53
+ creditLimit,
54
+ isSubscriptionActive,
55
+ productId: metadata.productId,
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Build credits data object for Firestore update
61
+ */
62
+ export function buildCreditsData(params: BuildCreditsDataParams): Record<string, any> {
63
+ const {
64
+ existingData,
65
+ newCredits,
66
+ creditLimit,
67
+ purchaseId,
68
+ metadata,
69
+ purchaseHistory,
70
+ platform,
71
+ } = params;
72
+
73
+ const isPremium = metadata.isPremium;
74
+ const isExpired = metadata.expirationDate ? isPast(metadata.expirationDate) : false;
75
+
76
+ const status = resolveSubscriptionStatus({
77
+ isPremium,
78
+ willRenew: metadata.willRenew ?? false,
79
+ isExpired,
80
+ periodType: metadata.periodType ?? undefined,
81
+ });
82
+
83
+ const newProcessedPurchases = addProcessedPurchase(existingData.processedPurchases, purchaseId);
84
+
85
+ const creditsData: Record<string, any> = {
86
+ isPremium,
87
+ status,
88
+ credits: newCredits,
89
+ creditLimit,
90
+ lastUpdatedAt: serverTimestamp(),
91
+ processedPurchases: newProcessedPurchases,
92
+ };
93
+
94
+ if (purchaseHistory.length > 0) {
95
+ creditsData.purchaseHistory = purchaseHistory;
96
+ }
97
+
98
+ const isNewPurchaseOrRenewal = purchaseId.startsWith("purchase_") || purchaseId.startsWith("renewal_");
99
+ if (isNewPurchaseOrRenewal) {
100
+ creditsData.lastPurchaseAt = serverTimestamp();
101
+ }
102
+
103
+ if (metadata.expirationDate) {
104
+ creditsData.expirationDate = serverTimestamp();
105
+ }
106
+
107
+ if (metadata.willRenew !== undefined) {
108
+ creditsData.willRenew = metadata.willRenew;
109
+ }
110
+
111
+ if (metadata.originalTransactionId) {
112
+ creditsData.originalTransactionId = metadata.originalTransactionId;
113
+ }
114
+
115
+ creditsData.productId = metadata.productId;
116
+ creditsData.platform = platform;
117
+
118
+ return creditsData;
119
+ }
120
+
121
+ /**
122
+ * Check if status sync write should be skipped (no changes)
123
+ */
124
+ export function shouldSkipStatusSyncWrite(
125
+ purchaseId: string,
126
+ existingData: UserCreditsDocumentRead,
127
+ newCreditsData: Record<string, any>
128
+ ): boolean {
129
+ const isStatusSync = purchaseId.startsWith("status_sync_");
130
+
131
+ if (!isStatusSync) {
132
+ return false;
133
+ }
134
+
135
+ const hasChanged =
136
+ existingData.isPremium !== newCreditsData.isPremium ||
137
+ existingData.status !== newCreditsData.status ||
138
+ existingData.credits !== newCreditsData.credits ||
139
+ existingData.creditLimit !== newCreditsData.creditLimit ||
140
+ existingData.productId !== newCreditsData.productId;
141
+
142
+ return !hasChanged;
143
+ }
144
+
145
+ /**
146
+ * Add purchase ID to processed purchases list
147
+ */
148
+ function addProcessedPurchase(
149
+ existing: string[],
150
+ purchaseId: string,
151
+ limit: number = 50
152
+ ): string[] {
153
+ return [...existing, purchaseId].slice(-limit);
154
+ }
@@ -81,10 +81,9 @@ export const useCredits = (): UseCreditsResult => {
81
81
  // Observer Pattern: Listen for credit updates
82
82
  useEffect(() => {
83
83
  if (!userId) return;
84
-
84
+
85
85
  const unsubscribe = subscriptionEventBus.on(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, (updatedUserId) => {
86
86
  if (updatedUserId === userId) {
87
- if (__DEV__) console.log("[useCredits] Event received: CREDITS_UPDATED, refetching...");
88
87
  queryClient.invalidateQueries({ queryKey: creditsQueryKeys.user(userId) });
89
88
  }
90
89
  });
@@ -60,9 +60,6 @@ export function usePaywallActions({
60
60
  } catch (error) {
61
61
  const err = error instanceof Error ? error : new Error(String(error));
62
62
  onPurchaseError?.(err);
63
- if (__DEV__) {
64
- console.error("[usePaywallActions] Purchase failed:", err);
65
- }
66
63
  } finally {
67
64
  setIsLocalProcessing(false);
68
65
  endPurchase();
@@ -15,24 +15,22 @@ export class SubscriptionSyncService {
15
15
  async handlePurchase(userId: string, productId: string, customerInfo: CustomerInfo, source?: PurchaseSource) {
16
16
  try {
17
17
  const revenueCatData = extractRevenueCatData(customerInfo, this.entitlementId);
18
- const purchaseId = revenueCatData.originalTransactionId
18
+ const purchaseId = revenueCatData.originalTransactionId
19
19
  ? `purchase_${revenueCatData.originalTransactionId}`
20
20
  : `purchase_${productId}_${Date.now()}`;
21
21
 
22
22
  await getCreditsRepository().initializeCredits(
23
- userId,
24
- purchaseId,
25
- productId,
26
- source ?? PURCHASE_SOURCE.SETTINGS, // Default to settings if source unknown
23
+ userId,
24
+ purchaseId,
25
+ productId,
26
+ source ?? PURCHASE_SOURCE.SETTINGS,
27
27
  revenueCatData,
28
- PURCHASE_TYPE.INITIAL // Default to INITIAL
28
+ PURCHASE_TYPE.INITIAL
29
29
  );
30
-
31
- // Notify listeners via Event Bus
30
+
32
31
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
33
32
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.PURCHASE_COMPLETED, { userId, productId });
34
- } catch (error) {
35
- if (__DEV__) console.error('[SubscriptionSyncService] Credits init failed:', error);
33
+ } catch {
36
34
  }
37
35
  }
38
36
 
@@ -40,23 +38,22 @@ export class SubscriptionSyncService {
40
38
  try {
41
39
  const revenueCatData = extractRevenueCatData(customerInfo, this.entitlementId);
42
40
  revenueCatData.expirationDate = newExpirationDate || revenueCatData.expirationDate;
43
- const purchaseId = revenueCatData.originalTransactionId
41
+ const purchaseId = revenueCatData.originalTransactionId
44
42
  ? `renewal_${revenueCatData.originalTransactionId}_${newExpirationDate}`
45
43
  : `renewal_${productId}_${Date.now()}`;
46
44
 
47
45
  await getCreditsRepository().initializeCredits(
48
- userId,
49
- purchaseId,
50
- productId,
51
- PURCHASE_SOURCE.RENEWAL,
46
+ userId,
47
+ purchaseId,
48
+ productId,
49
+ PURCHASE_SOURCE.RENEWAL,
52
50
  revenueCatData,
53
51
  PURCHASE_TYPE.RENEWAL
54
52
  );
55
-
53
+
56
54
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
57
55
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.RENEWAL_DETECTED, { userId, productId });
58
- } catch (error) {
59
- if (__DEV__) console.error('[SubscriptionSyncService] Renewal credits init failed:', error);
56
+ } catch {
60
57
  }
61
58
  }
62
59
 
@@ -123,8 +120,7 @@ export class SubscriptionSyncService {
123
120
 
124
121
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
125
122
  subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.PREMIUM_STATUS_CHANGED, { userId, isPremium });
126
- } catch (error) {
127
- if (__DEV__) console.error('[SubscriptionSyncService] Premium status sync failed:', error);
123
+ } catch {
128
124
  }
129
125
  }
130
126
  }
@@ -3,54 +3,63 @@
3
3
  * Domain-specific error types for RevenueCat operations
4
4
  */
5
5
 
6
- export class RevenueCatError extends Error {
7
- constructor(message: string) {
8
- super(message);
9
- this.name = "RevenueCatError";
10
- }
6
+ import { BaseError } from "../../../shared/utils/BaseError";
7
+
8
+ export class RevenueCatError extends BaseError {
9
+ constructor(message: string, code: string = 'REVENUE_CAT_ERROR', cause?: Error) {
10
+ super(message, code, cause);
11
+ this.name = "RevenueCatError";
12
+ }
11
13
  }
12
14
 
13
15
  export class RevenueCatInitializationError extends RevenueCatError {
14
- constructor(message = "RevenueCat service is not initialized") {
15
- super(message);
16
- this.name = "RevenueCatInitializationError";
17
- }
16
+ constructor(message = "RevenueCat service is not initialized", cause?: Error) {
17
+ super(message, 'REVENUE_CAT_NOT_INITIALIZED', cause);
18
+ this.name = "RevenueCatInitializationError";
19
+ }
18
20
  }
19
21
 
20
22
  export class RevenueCatConfigurationError extends RevenueCatError {
21
- constructor(message = "RevenueCat configuration is invalid") {
22
- super(message);
23
- this.name = "RevenueCatConfigurationError";
24
- }
23
+ constructor(message = "RevenueCat configuration is invalid", cause?: Error) {
24
+ super(message, 'REVENUE_CAT_CONFIGURATION_ERROR', cause);
25
+ this.name = "RevenueCatConfigurationError";
26
+ }
25
27
  }
26
28
 
27
29
  export class RevenueCatPurchaseError extends RevenueCatError {
28
- public productId: string | undefined;
30
+ public readonly productId: string | undefined;
31
+
32
+ constructor(message: string, productId?: string, cause?: Error) {
33
+ super(message, 'REVENUE_CAT_PURCHASE_ERROR', cause);
34
+ this.name = "RevenueCatPurchaseError";
35
+ this.productId = productId;
36
+ }
29
37
 
30
- constructor(message: string, productId?: string) {
31
- super(message);
32
- this.name = "RevenueCatPurchaseError";
33
- this.productId = productId;
34
- }
38
+ override toJSON() {
39
+ return {
40
+ ...super.toJSON(),
41
+ productId: this.productId,
42
+ };
43
+ }
35
44
  }
36
45
 
37
46
  export class RevenueCatRestoreError extends RevenueCatError {
38
- constructor(message = "Failed to restore purchases") {
39
- super(message);
40
- this.name = "RevenueCatRestoreError";
41
- }
47
+ constructor(message = "Failed to restore purchases", cause?: Error) {
48
+ super(message, 'REVENUE_CAT_RESTORE_ERROR', cause);
49
+ this.name = "RevenueCatRestoreError";
50
+ }
42
51
  }
43
52
 
44
53
  export class RevenueCatNetworkError extends RevenueCatError {
45
- constructor(message = "Network error during RevenueCat operation") {
46
- super(message);
47
- this.name = "RevenueCatNetworkError";
48
- }
54
+ constructor(message = "Network error during RevenueCat operation", cause?: Error) {
55
+ super(message, 'REVENUE_CAT_NETWORK_ERROR', cause);
56
+ this.name = "RevenueCatNetworkError";
57
+ }
49
58
  }
50
59
 
51
60
  export class RevenueCatExpoGoError extends RevenueCatError {
52
- constructor(message = "RevenueCat is not available in Expo Go. Use a development build or test store.") {
53
- super(message);
54
- this.name = "RevenueCatExpoGoError";
55
- }
61
+ constructor(message = "RevenueCat is not available in Expo Go. Use a development build or test store.", cause?: Error) {
62
+ super(message, 'REVENUE_CAT_EXPO_GO_ERROR', cause);
63
+ this.name = "RevenueCatExpoGoError";
64
+ }
56
65
  }
@@ -44,7 +44,6 @@ export class PackageHandler {
44
44
 
45
45
  return packages;
46
46
  } catch (error) {
47
- // Re-throw with more detail
48
47
  throw new Error(
49
48
  `Failed to fetch subscription packages. ${
50
49
  error instanceof Error ? error.message : "Unknown error"