@umituz/react-native-subscription 2.27.91 → 2.27.93

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 (75) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditsInitializer.ts +91 -38
  3. package/src/domains/credits/core/UserCreditsDocument.ts +33 -33
  4. package/src/domains/credits/infrastructure/CreditsRepository.ts +44 -58
  5. package/src/domains/paywall/components/PaywallModal.tsx +1 -1
  6. package/src/domains/subscription/application/SubscriptionInitializer.ts +59 -18
  7. package/src/domains/subscription/core/RevenueCatTypes.ts +2 -1
  8. package/src/domains/subscription/core/SubscriptionConstants.ts +12 -0
  9. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +46 -27
  10. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +106 -42
  11. package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +4 -2
  12. package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +1 -2
  13. package/src/domains/subscription/infrastructure/utils/RenewalDetector.ts +1 -1
  14. package/src/{presentation → domains/subscription/presentation}/components/details/PremiumStatusBadge.tsx +6 -4
  15. package/src/{presentation → domains/subscription/presentation}/components/feedback/PaywallFeedbackModal.tsx +1 -1
  16. package/src/{presentation → domains/subscription/presentation}/types/SubscriptionDetailTypes.ts +4 -2
  17. package/src/{presentation → domains/subscription/presentation}/types/SubscriptionSettingsTypes.ts +1 -1
  18. package/src/domains/subscription/presentation/usePremiumGate.ts +1 -1
  19. package/src/domains/subscription/presentation/useSavedPurchaseAutoExecution.ts +1 -1
  20. package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.ts +4 -3
  21. package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.utils.ts +1 -1
  22. package/src/domains/trial/application/TrialEligibilityService.ts +1 -1
  23. package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +2 -2
  24. package/src/index.ts +5 -5
  25. package/src/shared/application/ports/IRevenueCatService.ts +2 -0
  26. package/src/shared/infrastructure/SubscriptionEventBus.ts +5 -2
  27. package/src/utils/tierUtils.ts +3 -3
  28. package/src/utils/types.ts +4 -1
  29. package/src/utils/validation.ts +2 -2
  30. package/src/domains/config/README.md +0 -100
  31. package/src/presentation/README.md +0 -125
  32. package/src/presentation/hooks/README.md +0 -156
  33. package/src/presentation/hooks/useAuthSubscriptionSync.md +0 -94
  34. package/src/presentation/hooks/useCredits.md +0 -103
  35. package/src/presentation/hooks/useDeductCredit.md +0 -100
  36. package/src/presentation/hooks/useFeatureGate.md +0 -112
  37. package/src/presentation/hooks/usePaywall.md +0 -89
  38. package/src/presentation/hooks/usePaywallOperations.md +0 -92
  39. package/src/presentation/hooks/usePaywallVisibility.md +0 -95
  40. package/src/presentation/hooks/usePremium.md +0 -88
  41. package/src/presentation/hooks/useSubscriptionSettingsConfig.md +0 -94
  42. package/src/utils/README.md +0 -42
  43. /package/src/{presentation → domains/subscription/presentation}/components/README.md +0 -0
  44. /package/src/{presentation → domains/subscription/presentation}/components/details/CreditRow.md +0 -0
  45. /package/src/{presentation → domains/subscription/presentation}/components/details/CreditRow.tsx +0 -0
  46. /package/src/{presentation → domains/subscription/presentation}/components/details/DetailRow.md +0 -0
  47. /package/src/{presentation → domains/subscription/presentation}/components/details/DetailRow.tsx +0 -0
  48. /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCard.md +0 -0
  49. /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCard.styles.ts +0 -0
  50. /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCard.tsx +0 -0
  51. /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCardTypes.ts +0 -0
  52. /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumStatusBadge.md +0 -0
  53. /package/src/{presentation → domains/subscription/presentation}/components/details/README.md +0 -0
  54. /package/src/{presentation → domains/subscription/presentation}/components/feedback/PaywallFeedbackModal.md +0 -0
  55. /package/src/{presentation → domains/subscription/presentation}/components/feedback/README.md +0 -0
  56. /package/src/{presentation → domains/subscription/presentation}/components/feedback/paywallFeedbackStyles.ts +0 -0
  57. /package/src/{presentation → domains/subscription/presentation}/components/overlay/PurchaseLoadingOverlay.tsx +0 -0
  58. /package/src/{presentation → domains/subscription/presentation}/components/overlay/index.ts +0 -0
  59. /package/src/{presentation → domains/subscription/presentation}/components/paywall/PaywallModal.md +0 -0
  60. /package/src/{presentation → domains/subscription/presentation}/components/paywall/README.md +0 -0
  61. /package/src/{presentation → domains/subscription/presentation}/components/sections/README.md +0 -0
  62. /package/src/{presentation → domains/subscription/presentation}/components/sections/SubscriptionSection.md +0 -0
  63. /package/src/{presentation → domains/subscription/presentation}/components/sections/SubscriptionSection.tsx +0 -0
  64. /package/src/{presentation → domains/subscription/presentation}/screens/README.md +0 -0
  65. /package/src/{presentation → domains/subscription/presentation}/screens/SubscriptionDetailScreen.tsx +0 -0
  66. /package/src/{presentation → domains/subscription/presentation}/screens/components/CreditsList.tsx +0 -0
  67. /package/src/{presentation → domains/subscription/presentation}/screens/components/DevTestSection.tsx +0 -0
  68. /package/src/{presentation → domains/subscription/presentation}/screens/components/SubscriptionActions.tsx +0 -0
  69. /package/src/{presentation → domains/subscription/presentation}/screens/components/SubscriptionHeader.tsx +0 -0
  70. /package/src/{presentation → domains/subscription/presentation}/screens/components/UpgradePrompt.tsx +0 -0
  71. /package/src/{presentation → domains/subscription/presentation}/stores/index.ts +0 -0
  72. /package/src/{presentation → domains/subscription/presentation}/stores/purchaseLoadingStore.ts +0 -0
  73. /package/src/{presentation → domains/subscription/presentation}/types/README.md +0 -0
  74. /package/src/{presentation → domains/subscription/presentation}/utils/README.md +0 -0
  75. /package/src/{presentation → domains/subscription/presentation}/utils/subscriptionDateUtils.ts +0 -0
@@ -14,7 +14,7 @@ import { SubscriptionInternalState } from "./SubscriptionInternalState";
14
14
  export interface SubscriptionManagerConfig {
15
15
  config: RevenueCatConfig;
16
16
  apiKey: string;
17
- getAnonymousUserId?: () => Promise<string>;
17
+ getAnonymousUserId: () => Promise<string>;
18
18
  }
19
19
 
20
20
  class SubscriptionManagerImpl {
@@ -25,97 +25,161 @@ class SubscriptionManagerImpl {
25
25
 
26
26
  configure(config: SubscriptionManagerConfig): void {
27
27
  this.managerConfig = config;
28
- this.packageHandler = new PackageHandler(null, config.config.entitlementIdentifier);
29
- if (config.getAnonymousUserId) this.state.userIdProvider.configure(config.getAnonymousUserId);
28
+ this.state.userIdProvider.configure(config.getAnonymousUserId);
29
+ }
30
+
31
+ private ensurePackageHandlerInitialized(): void {
32
+ if (this.packageHandler) {
33
+ return;
34
+ }
35
+
36
+ if (!this.serviceInstance) {
37
+ throw new Error("Service instance not available");
38
+ }
39
+
40
+ if (!this.managerConfig) {
41
+ throw new Error("Manager not configured");
42
+ }
43
+
44
+ this.packageHandler = new PackageHandler(
45
+ this.serviceInstance,
46
+ this.managerConfig.config.entitlementIdentifier
47
+ );
30
48
  }
31
49
 
32
50
  private ensureConfigured(): void {
33
- if (!this.managerConfig || !this.packageHandler) throw new Error("SubscriptionManager not configured");
51
+ if (!this.managerConfig) {
52
+ throw new Error("SubscriptionManager not configured");
53
+ }
34
54
  }
35
55
 
36
- async initialize(userId?: string): Promise<boolean> {
56
+ async initialize(userId: string): Promise<boolean> {
37
57
  this.ensureConfigured();
38
- const effectiveUserId = userId || (await this.state.userIdProvider.getOrCreateAnonymousUserId());
39
- const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(effectiveUserId);
40
58
 
41
- if (!shouldInit && existingPromise) return existingPromise;
59
+ const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(userId);
60
+
61
+ if (!shouldInit && existingPromise) {
62
+ return existingPromise;
63
+ }
42
64
 
43
65
  const promise = (async () => {
44
- try {
45
- await initializeRevenueCatService(this.managerConfig!.config);
46
- this.serviceInstance = getRevenueCatService();
47
- if (!this.serviceInstance) {
48
- if (__DEV__) {
49
- console.error('[SubscriptionManager] Service instance not available after initialization');
50
- }
51
- return false;
52
- }
53
- this.packageHandler!.setService(this.serviceInstance);
54
- const result = await this.serviceInstance.initialize(effectiveUserId);
55
- return result.success;
56
- } catch (error) {
57
- if (__DEV__) {
58
- console.error('[SubscriptionManager] Initialization failed:', error);
59
- }
60
- return false;
66
+ await initializeRevenueCatService(this.managerConfig!.config);
67
+ this.serviceInstance = getRevenueCatService();
68
+
69
+ if (!this.serviceInstance) {
70
+ throw new Error("Service instance not available after initialization");
61
71
  }
72
+
73
+ this.ensurePackageHandlerInitialized();
74
+ const result = await this.serviceInstance.initialize(userId);
75
+ return result.success;
62
76
  })();
63
77
 
64
- this.state.initCache.setPromise(promise, effectiveUserId);
78
+ this.state.initCache.setPromise(promise, userId);
65
79
  return promise;
66
80
  }
67
81
 
68
82
  isInitializedForUser(userId: string): boolean {
69
- return this.serviceInstance?.isInitialized() === true && this.state.initCache.getCurrentUserId() === userId;
83
+ if (!this.serviceInstance) {
84
+ return false;
85
+ }
86
+
87
+ if (!this.serviceInstance.isInitialized()) {
88
+ return false;
89
+ }
90
+
91
+ return this.state.initCache.getCurrentUserId() === userId;
70
92
  }
71
93
 
72
94
  async getPackages(): Promise<PurchasesPackage[]> {
73
95
  this.ensureConfigured();
96
+
74
97
  if (!this.serviceInstance) {
75
98
  this.serviceInstance = getRevenueCatService();
76
- this.packageHandler!.setService(this.serviceInstance);
77
99
  }
100
+
101
+ if (!this.serviceInstance) {
102
+ throw new Error("Service instance not available");
103
+ }
104
+
105
+ this.ensurePackageHandlerInitialized();
78
106
  return this.packageHandler!.fetchPackages();
79
107
  }
80
108
 
81
109
  async purchasePackage(pkg: PurchasesPackage): Promise<boolean> {
82
110
  this.ensureConfigured();
111
+
83
112
  const userId = this.state.initCache.getCurrentUserId();
84
- if (!userId) return false;
113
+ if (!userId) {
114
+ throw new Error("No current user found");
115
+ }
116
+
117
+ this.ensurePackageHandlerInitialized();
85
118
  return this.packageHandler!.purchase(pkg, userId);
86
119
  }
87
120
 
88
121
  async restore(): Promise<RestoreResultInfo> {
89
122
  this.ensureConfigured();
123
+
90
124
  const userId = this.state.initCache.getCurrentUserId();
91
- if (!userId) return { success: false, productId: null };
125
+ if (!userId) {
126
+ throw new Error("No current user found");
127
+ }
128
+
129
+ this.ensurePackageHandlerInitialized();
92
130
  return this.packageHandler!.restore(userId);
93
131
  }
94
132
 
95
133
  async checkPremiumStatus(): Promise<PremiumStatus> {
96
134
  this.ensureConfigured();
135
+
97
136
  const userId = this.state.initCache.getCurrentUserId();
98
- if (!userId) return { isPremium: false, expirationDate: null };
137
+ if (!userId) {
138
+ throw new Error("No current user found");
139
+ }
140
+
141
+ if (!this.serviceInstance) {
142
+ throw new Error("Service instance not available");
143
+ }
144
+
145
+ const customerInfo = await this.serviceInstance.getCustomerInfo();
99
146
 
100
- try {
101
- const customerInfo = await this.serviceInstance?.getCustomerInfo();
102
- if (customerInfo) return this.packageHandler!.checkPremiumStatusFromInfo(customerInfo);
103
- } catch (error) {
104
- throw error;
147
+ if (!customerInfo) {
148
+ throw new Error("Customer info not available");
105
149
  }
106
- return { isPremium: false, expirationDate: null };
150
+
151
+ this.ensurePackageHandlerInitialized();
152
+ return this.packageHandler!.checkPremiumStatusFromInfo(customerInfo);
107
153
  }
108
154
 
109
155
  async reset(): Promise<void> {
110
- if (this.serviceInstance) await this.serviceInstance.reset();
156
+ if (this.serviceInstance) {
157
+ await this.serviceInstance.reset();
158
+ }
159
+
111
160
  this.state.reset();
112
161
  this.serviceInstance = null;
113
162
  }
114
163
 
115
- // Helper status checks
116
- isConfigured = () => !!this.managerConfig;
117
- isInitialized = () => this.serviceInstance?.isInitialized() ?? false;
118
- getEntitlementId = () => this.managerConfig?.config.entitlementIdentifier || null;
164
+ isConfigured(): boolean {
165
+ return this.managerConfig !== null;
166
+ }
167
+
168
+ isInitialized(): boolean {
169
+ if (!this.serviceInstance) {
170
+ return false;
171
+ }
172
+
173
+ return this.serviceInstance.isInitialized();
174
+ }
175
+
176
+ getEntitlementId(): string {
177
+ if (!this.managerConfig) {
178
+ throw new Error("SubscriptionManager not configured");
179
+ }
180
+
181
+ return this.managerConfig.config.entitlementIdentifier;
182
+ }
119
183
  }
120
184
 
121
185
  export const SubscriptionManager = new SubscriptionManagerImpl();
@@ -15,14 +15,16 @@ export async function handleRestore(deps: RestoreHandlerDeps, userId: string): P
15
15
 
16
16
  try {
17
17
  const customerInfo = await Purchases.restorePurchases();
18
- const isPremium = !!customerInfo.entitlements.active[deps.config.entitlementIdentifier];
18
+ const entitlement = customerInfo.entitlements.active[deps.config.entitlementIdentifier];
19
+ const isPremium = !!entitlement;
20
+ const productId = entitlement?.productIdentifier ?? null;
19
21
 
20
22
  if (isPremium) {
21
23
  await syncPremiumStatus(deps.config, userId, customerInfo);
22
24
  }
23
25
  await notifyRestoreCompleted(deps.config, userId, isPremium, customerInfo);
24
26
 
25
- return { success: true, isPremium, customerInfo };
27
+ return { success: true, isPremium, productId, customerInfo };
26
28
  } catch (error) {
27
29
  throw new RevenueCatRestoreError(getErrorMessage(error, "Restore failed"));
28
30
  }
@@ -19,8 +19,7 @@ const configurationState = {
19
19
  configurationPromise: null as Promise<ReturnType<typeof initializeSDK>> | null,
20
20
  };
21
21
 
22
- // Simple lock mechanism to prevent concurrent configurations
23
- let configurationLocks = new Set<string>();
22
+ // Simple lock mechanism to prevent concurrent configurations (implementation deferred)
24
23
 
25
24
  function configureLogHandler(): void {
26
25
  if (configurationState.isLogHandlerConfigured) return;
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { CustomerInfo } from "react-native-purchases";
8
- import { detectPackageType, type SubscriptionPackageType } from "../../../../utils/packageTypeDetector";
8
+ import { detectPackageType } from "../../../../utils/packageTypeDetector";
9
9
 
10
10
  export interface RenewalState {
11
11
  previousExpirationDate: string | null;
@@ -6,10 +6,12 @@
6
6
  import React, { useMemo } from "react";
7
7
  import { View, StyleSheet } from "react-native";
8
8
  import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
9
- import {
10
- SUBSCRIPTION_STATUS,
11
- type SubscriptionStatusType
12
- } from "../../../domains/subscription/core/SubscriptionConstants";
9
+ import {
10
+ SUBSCRIPTION_STATUS,
11
+ type SubscriptionStatusType
12
+ } from "../../../core/SubscriptionConstants";
13
+
14
+ export type { SubscriptionStatusType };
13
15
 
14
16
  export interface PremiumStatusBadgeProps {
15
17
  status: SubscriptionStatusType;
@@ -1,7 +1,7 @@
1
1
  import React, { useMemo } from "react";
2
2
  import { View, TouchableOpacity, TextInput } from "react-native";
3
3
  import { AtomicText, BaseModal, useAppDesignTokens } from "@umituz/react-native-design-system";
4
- import { usePaywallFeedback } from "../../hooks/feedback/usePaywallFeedback";
4
+ import { usePaywallFeedback } from "../../../../../presentation/hooks/feedback/usePaywallFeedback";
5
5
  import { createPaywallFeedbackStyles } from "./paywallFeedbackStyles";
6
6
 
7
7
  const FEEDBACK_OPTION_IDS = [
@@ -3,7 +3,7 @@
3
3
  * Type definitions for subscription detail screen and components
4
4
  */
5
5
 
6
- import type { SubscriptionStatusType } from "../../domains/subscription/core/SubscriptionStatus";
6
+ import type { SubscriptionStatusType } from "../../core/SubscriptionStatus";
7
7
  import type { CreditInfo } from "../components/details/PremiumDetailsCardTypes";
8
8
 
9
9
  export type { SubscriptionStatusType, CreditInfo };
@@ -16,6 +16,8 @@ export interface SubscriptionDetailTranslations {
16
16
  statusExpired: string;
17
17
  statusInactive: string;
18
18
  statusCanceled: string;
19
+ /** Free status label */
20
+ statusFree: string;
19
21
  /** Trial status label (defaults to statusActive if not provided) */
20
22
  statusTrial?: string;
21
23
  /** Trial canceled status label (defaults to statusCanceled if not provided) */
@@ -109,7 +111,7 @@ export interface SubscriptionHeaderProps {
109
111
  | "expiresLabel"
110
112
  | "purchasedLabel"
111
113
  | "lifetimeLabel"
112
- >;
114
+ > & { statusFree: string };
113
115
  }
114
116
 
115
117
  /** Props for credits list component */
@@ -3,7 +3,7 @@
3
3
  * Type definitions for subscription settings configuration
4
4
  */
5
5
 
6
- import type { SubscriptionStatusType } from "../../domains/subscription/core/SubscriptionConstants";
6
+ import type { SubscriptionStatusType } from "../../core/SubscriptionConstants";
7
7
  import type {
8
8
  SubscriptionDetailConfig,
9
9
  UpgradePromptConfig,
@@ -27,7 +27,7 @@
27
27
  * ```
28
28
  */
29
29
 
30
- import { useCallback, useEffect } from "react";
30
+ import { useCallback } from "react";
31
31
  import { useSubscriptionStatus } from "./useSubscriptionStatus";
32
32
  import { paywallControl } from "./usePaywallVisibility";
33
33
 
@@ -12,7 +12,7 @@ import {
12
12
  import { getSavedPurchase, clearSavedPurchase } from "./useAuthAwarePurchase";
13
13
  import { usePremium } from "./usePremium";
14
14
  import { SubscriptionManager } from "../infrastructure/managers/SubscriptionManager";
15
- import { usePurchaseLoadingStore } from "../../../presentation/stores";
15
+ import { usePurchaseLoadingStore } from "./stores";
16
16
 
17
17
  export interface UseSavedPurchaseAutoExecutionParams {
18
18
  onSuccess?: () => void;
@@ -7,21 +7,21 @@ import { useMemo, useCallback } from "react";
7
7
  import { useCredits } from "../../credits/presentation/useCredits";
8
8
  import { usePaywallVisibility } from "./usePaywallVisibility";
9
9
  import { calculateDaysRemaining } from "../core/SubscriptionStatus";
10
- import { formatDate } from "../../../presentation/utils/subscriptionDateUtils";
10
+ import { formatDate } from "./utils/subscriptionDateUtils";
11
11
  import { useCreditsArray, getSubscriptionStatusType } from "./useSubscriptionSettingsConfig.utils";
12
12
  import { getCreditsConfig } from "../../credits/infrastructure/CreditsRepositoryProvider";
13
13
  import type {
14
14
  SubscriptionSettingsConfig,
15
15
  SubscriptionStatusType,
16
16
  UseSubscriptionSettingsConfigParams,
17
- } from "../../../presentation/types/SubscriptionSettingsTypes";
17
+ } from "./types/SubscriptionSettingsTypes";
18
18
 
19
19
  export type {
20
20
  SubscriptionSettingsConfig,
21
21
  SubscriptionSettingsItemConfig,
22
22
  SubscriptionSettingsTranslations,
23
23
  UseSubscriptionSettingsConfigParams,
24
- } from "../../../presentation/types/SubscriptionSettingsTypes";
24
+ } from "./types/SubscriptionSettingsTypes";
25
25
 
26
26
  export const useSubscriptionSettingsConfig = (
27
27
  params: Omit<UseSubscriptionSettingsConfigParams, 'userId'>
@@ -95,6 +95,7 @@ export const useSubscriptionSettingsConfig = (
95
95
  statusExpired: translations.statusExpired,
96
96
  statusInactive: translations.statusInactive,
97
97
  statusCanceled: translations.statusCanceled,
98
+ statusFree: translations.statusInactive,
98
99
  expiresLabel: translations.expiresLabel,
99
100
  purchasedLabel: translations.purchasedLabel,
100
101
  lifetimeLabel: translations.lifetimeLabel,
@@ -6,7 +6,7 @@
6
6
  import { useMemo } from "react";
7
7
  import type { UserCredits } from "../../credits/core/Credits";
8
8
  import { resolveSubscriptionStatus, type PeriodType, type SubscriptionStatusType } from "../core/SubscriptionStatus";
9
- import type { SubscriptionSettingsTranslations } from "../types/SubscriptionSettingsTypes";
9
+ import type { SubscriptionSettingsTranslations } from "./types/SubscriptionSettingsTypes";
10
10
 
11
11
  export interface CreditsInfo {
12
12
  id: string;
@@ -1,4 +1,4 @@
1
- import type { TrialEligibilityResult, DeviceTrialRecord } from "./TrialTypes";
1
+ import type { TrialEligibilityResult, DeviceTrialRecord } from "../core/TrialTypes";
2
2
 
3
3
  export class TrialEligibilityService {
4
4
  static check(
@@ -1,6 +1,6 @@
1
- import { doc, getDoc, setDoc, serverTimestamp, arrayUnion, type Firestore } from "firebase/firestore";
1
+ import { doc, getDoc, setDoc, serverTimestamp, type Firestore } from "firebase/firestore";
2
2
  import { getFirestore } from "@umituz/react-native-firebase";
3
- import type { DeviceTrialRecord } from "./TrialTypes";
3
+ import type { DeviceTrialRecord } from "../core/TrialTypes";
4
4
 
5
5
  const DEVICE_TRIALS_COLLECTION = "device_trials";
6
6
 
package/src/index.ts CHANGED
@@ -54,11 +54,11 @@ export {
54
54
  export * from "./presentation/hooks";
55
55
 
56
56
  // Presentation Layer - Components
57
- export * from "./presentation/components/details/PremiumDetailsCard";
58
- export * from "./presentation/components/details/PremiumStatusBadge";
59
- export * from "./presentation/components/sections/SubscriptionSection";
60
- export * from "./presentation/components/feedback/PaywallFeedbackModal";
61
- export * from "./presentation/screens/SubscriptionDetailScreen";
57
+ export * from "./domains/subscription/presentation/components/details/PremiumDetailsCard";
58
+ export * from "./domains/subscription/presentation/components/details/PremiumStatusBadge";
59
+ export * from "./domains/subscription/presentation/components/sections/SubscriptionSection";
60
+ export * from "./domains/subscription/presentation/components/feedback/PaywallFeedbackModal";
61
+ export * from "./domains/subscription/presentation/screens/SubscriptionDetailScreen";
62
62
  export * from "./domains/paywall/components/PaywallContainer";
63
63
 
64
64
  export type {
@@ -21,6 +21,8 @@ export interface PurchaseResult {
21
21
  export interface RestoreResult {
22
22
  success: boolean;
23
23
  productId: string | null;
24
+ isPremium?: boolean;
25
+ customerInfo?: CustomerInfo;
24
26
  }
25
27
 
26
28
  export interface IRevenueCatService {
@@ -22,10 +22,13 @@ export class SubscriptionEventBus {
22
22
  this.listeners[event] = [];
23
23
  }
24
24
  this.listeners[event].push(callback);
25
-
25
+
26
26
  // Return unsubscribe function
27
27
  return () => {
28
- this.listeners[event] = this.listeners[event].filter(l => l !== callback);
28
+ const listeners = this.listeners[event];
29
+ if (listeners) {
30
+ this.listeners[event] = listeners.filter(l => l !== callback);
31
+ }
29
32
  };
30
33
  }
31
34
 
@@ -4,7 +4,7 @@
4
4
  * Core logic for determining user tier and premium status
5
5
  */
6
6
 
7
- import type { UserTierInfo } from './types';
7
+ import { USER_TIER, type UserTierInfo } from './types';
8
8
 
9
9
 
10
10
  export function getUserTierInfo(
@@ -14,7 +14,7 @@ export function getUserTierInfo(
14
14
  ): UserTierInfo {
15
15
  if (isAnonymous || userId === null) {
16
16
  return {
17
- tier: 'anonymous',
17
+ tier: USER_TIER.ANONYMOUS,
18
18
  isPremium: false,
19
19
  isAnonymous: true,
20
20
  isAuthenticated: false,
@@ -23,7 +23,7 @@ export function getUserTierInfo(
23
23
  }
24
24
 
25
25
  return {
26
- tier: isPremium ? 'premium' : 'freemium',
26
+ tier: isPremium ? USER_TIER.PREMIUM : USER_TIER.FREEMIUM,
27
27
  isPremium,
28
28
  isAnonymous: false,
29
29
  isAuthenticated: true,
@@ -4,7 +4,10 @@
4
4
  * Type definitions for user tier system
5
5
  */
6
6
 
7
- export type UserTier = 'anonymous' | 'freemium' | 'premium';
7
+ import { USER_TIER, type UserTierType } from '../domains/subscription/core/SubscriptionConstants';
8
+
9
+ export type UserTier = UserTierType;
10
+ export { USER_TIER };
8
11
 
9
12
  export interface UserTierInfo {
10
13
  /** User tier classification */
@@ -4,10 +4,10 @@
4
4
  * Type guards and validation functions for user tier system
5
5
  */
6
6
 
7
- import type { UserTier, UserTierInfo } from './types';
7
+ import { USER_TIER, type UserTier, type UserTierInfo } from './types';
8
8
 
9
9
  export function isValidUserTier(value: unknown): value is UserTier {
10
- return value === 'anonymous' || value === 'freemium' || value === 'premium';
10
+ return value === USER_TIER.ANONYMOUS || value === USER_TIER.FREEMIUM || value === USER_TIER.PREMIUM;
11
11
  }
12
12
 
13
13
  export function isUserTierInfo(value: unknown): value is UserTierInfo {
@@ -1,100 +0,0 @@
1
- # Config Domain
2
-
3
- Central configuration system for subscription plans, product configurations, and package management.
4
-
5
- ## Location
6
-
7
- - **Base Path**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domains/config/`
8
- - **Domain**: `src/domains/config/domain/`
9
- - **Entities**: `src/domains/config/domain/entities/`
10
-
11
- ## Strategy
12
-
13
- ### Plan Management
14
-
15
- Comprehensive subscription plan configuration system.
16
-
17
- - **Plan Types**: Monthly, annual, and lifetime plan configurations
18
- - **Product Metadata**: RevenueCat product metadata management
19
- - **Validation**: Configuration validation and type safety
20
- - **Helper Functions**: Plan comparison and filtering utilities
21
-
22
- ### Configuration Objects
23
-
24
- Structured configuration for different aspects of the system.
25
-
26
- - **SubscriptionConfig**: Main subscription configuration
27
- - **WalletConfig**: Credit system configuration
28
- - **Plan Entities**: Individual plan definitions
29
- - **Validation Rules**: Configuration validation schemas
30
-
31
- ### Helper Utilities
32
-
33
- Plan comparison and manipulation functions.
34
-
35
- - **Plan Comparison**: Value comparison between plans
36
- - **Price Formatting**: Currency-aware price formatting
37
- - **Discount Calculation**: Savings and discount calculations
38
- - **Package Filtering**: Type-based package filtering
39
-
40
- ### Validation System
41
-
42
- Comprehensive configuration validation.
43
-
44
- - **Plan Validation**: Plan entity validation rules
45
- - **Config Validation**: Complete configuration validation
46
- - **Type Safety**: TypeScript type definitions
47
- - **Error Messages**: Detailed validation error reporting
48
-
49
- ## Restrictions
50
-
51
- ### REQUIRED
52
-
53
- - **Type Safety**: Always use TypeScript type definitions
54
- - **Validation**: Validate configurations before runtime use
55
- - **Default Values**: Provide meaningful default values
56
- - **Immutable Updates**: Create new copies instead of modifying
57
-
58
- ### PROHIBITED
59
-
60
- - **Invalid Prices**: Negative or zero prices not allowed
61
- - **Missing IDs**: All plans must have valid IDs
62
- - **Duplicate Plans**: No duplicate plan IDs allowed
63
- - **Hardcoded Values**: Use configuration system
64
-
65
- ### CRITICAL
66
-
67
- - **Configuration Integrity**: All configurations must be valid
68
- - **Plan Consistency**: Related plans must be consistent
69
- - **Currency Handling**: Proper currency code usage
70
- - **Feature Lists**: Accurate feature mapping
71
-
72
- ## AI Agent Guidelines
73
-
74
- ### When Modifying Configuration System
75
-
76
- 1. **Type Definitions**: Update TypeScript types for new config
77
- 2. **Validation Rules**: Add validation for new fields
78
- 3. **Default Values**: Provide sensible defaults
79
- 4. **Documentation**: Document configuration options
80
-
81
- ### When Adding New Plan Types
82
-
83
- 1. **Entity Pattern**: Follow existing entity patterns
84
- 2. **Validation**: Add validation rules
85
- 3. **Helper Functions**: Create helper functions
86
- 4. **Testing**: Test with various configurations
87
-
88
- ### When Fixing Configuration Bugs
89
-
90
- 1. **Validation Logic**: Check validation rules
91
- 2. **Type Definitions**: Verify type correctness
92
- 3. **Default Values**: Ensure proper defaults
93
- 4. **Edge Cases**: Test boundary conditions
94
-
95
- ## Related Documentation
96
-
97
- - [Paywall Domain](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domains/paywall/README.md)
98
- - [Wallet Domain](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domains/wallet/README.md)
99
- - [RevenueCat Integration](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/revenuecat/README.md)
100
- - [Domain Layer](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domain/README.md)