@umituz/react-native-subscription 2.27.66 → 2.27.68

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 (125) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditLimitCalculator.ts +17 -0
  3. package/src/domains/credits/application/CreditsInitializer.ts +85 -0
  4. package/src/domains/credits/application/DeductCreditsCommand.ts +52 -0
  5. package/src/domains/credits/application/PurchaseMetadataGenerator.ts +59 -0
  6. package/src/domains/credits/application/credit-strategies/CreditAllocationContext.ts +35 -0
  7. package/src/domains/credits/application/credit-strategies/ICreditStrategy.ts +18 -0
  8. package/src/domains/credits/application/credit-strategies/StandardPurchaseCreditStrategy.ts +16 -0
  9. package/src/domains/credits/application/credit-strategies/SyncCreditStrategy.ts +15 -0
  10. package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +18 -0
  11. package/src/{infrastructure/mappers → domains/credits/core}/CreditsMapper.ts +4 -4
  12. package/src/domains/credits/infrastructure/CreditsRepository.ts +102 -0
  13. package/src/{presentation/hooks → domains/credits/presentation}/useCredits.ts +21 -4
  14. package/src/domains/subscription/application/SubscriptionAuthListener.ts +26 -0
  15. package/src/domains/subscription/application/SubscriptionInitializer.ts +77 -0
  16. package/src/{infrastructure/services → domains/subscription/application}/SubscriptionInitializerTypes.ts +21 -1
  17. package/src/domains/subscription/application/SubscriptionSyncService.ts +71 -0
  18. package/src/domains/subscription/application/SubscriptionSyncUtils.ts +16 -0
  19. package/src/{revenuecat/domain/value-objects → domains/subscription/core}/RevenueCatConfig.ts +1 -1
  20. package/src/{domain/types → domains/subscription/core}/RevenueCatData.ts +1 -1
  21. package/src/{domain/entities → domains/subscription/core}/SubscriptionStatus.ts +13 -21
  22. package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +51 -0
  23. package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +67 -0
  24. package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +27 -0
  25. package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCat.ts +1 -1
  26. package/src/domains/subscription/infrastructure/managers/SubscriptionInternalState.ts +12 -0
  27. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +110 -0
  28. package/src/{revenuecat → domains/subscription}/infrastructure/services/PurchaseHandler.ts +1 -1
  29. package/src/{revenuecat → domains/subscription}/infrastructure/services/RestoreHandler.ts +1 -1
  30. package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatInitializer.ts +1 -1
  31. package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatService.ts +2 -2
  32. package/src/{presentation/hooks → domains/subscription/presentation}/usePremium.ts +7 -4
  33. package/src/domains/trial/application/TrialEligibilityService.ts +25 -0
  34. package/src/domains/trial/application/TrialService.ts +69 -0
  35. package/src/{infrastructure/services → domains/trial/core}/TrialTypes.ts +1 -1
  36. package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +30 -0
  37. package/src/index.ts +28 -59
  38. package/src/init/createSubscriptionInitModule.ts +1 -1
  39. package/src/presentation/components/details/PremiumStatusBadge.tsx +1 -3
  40. package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +1 -1
  41. package/src/presentation/hooks/index.ts +11 -11
  42. package/src/shared/application/ports/IRevenueCatService.ts +32 -0
  43. package/src/shared/infrastructure/SubscriptionEventBus.ts +51 -0
  44. package/src/application/README.md +0 -50
  45. package/src/domain/README.md +0 -54
  46. package/src/domain/entities/README.md +0 -50
  47. package/src/domain/entities/SubscriptionStatus.test.ts +0 -149
  48. package/src/domain/errors/README.md +0 -53
  49. package/src/domain/value-objects/README.md +0 -50
  50. package/src/infrastructure/README.md +0 -55
  51. package/src/infrastructure/mappers/README.md +0 -21
  52. package/src/infrastructure/models/README.md +0 -26
  53. package/src/infrastructure/repositories/CreditsRepository.ts +0 -132
  54. package/src/infrastructure/repositories/README.md +0 -99
  55. package/src/infrastructure/services/CreditsInitializer.ts +0 -170
  56. package/src/infrastructure/services/README.md +0 -99
  57. package/src/infrastructure/services/SubscriptionInitializer.ts +0 -176
  58. package/src/infrastructure/services/SubscriptionService.ts +0 -133
  59. package/src/infrastructure/services/TrialService.ts +0 -197
  60. package/src/infrastructure/services/app-service-helpers.ts +0 -111
  61. package/src/revenuecat/README.md +0 -104
  62. package/src/revenuecat/application/README.md +0 -43
  63. package/src/revenuecat/application/ports/IRevenueCatService.ts +0 -76
  64. package/src/revenuecat/application/ports/README.md +0 -41
  65. package/src/revenuecat/domain/README.md +0 -48
  66. package/src/revenuecat/domain/constants/README.md +0 -41
  67. package/src/revenuecat/domain/entities/README.md +0 -42
  68. package/src/revenuecat/domain/errors/README.md +0 -53
  69. package/src/revenuecat/domain/types/README.md +0 -41
  70. package/src/revenuecat/domain/value-objects/README.md +0 -41
  71. package/src/revenuecat/index.ts +0 -13
  72. package/src/revenuecat/infrastructure/handlers/PackageHandler.ts +0 -161
  73. package/src/revenuecat/infrastructure/managers/SubscriptionManager.ts +0 -165
  74. package/src/revenuecat/presentation/README.md +0 -42
  75. /package/src/{domain/entities → domains/credits/core}/Credits.ts +0 -0
  76. /package/src/{infrastructure/models → domains/credits/core}/UserCreditsDocument.ts +0 -0
  77. /package/src/{infrastructure/repositories → domains/credits/infrastructure}/CreditsRepositoryProvider.ts +0 -0
  78. /package/src/{presentation/hooks → domains/credits/presentation}/useDeductCredit.ts +0 -0
  79. /package/src/{revenuecat/domain/constants → domains/subscription/core}/RevenueCatConstants.ts +0 -0
  80. /package/src/{revenuecat/domain/errors → domains/subscription/core}/RevenueCatError.ts +0 -0
  81. /package/src/{revenuecat/domain/types → domains/subscription/core}/RevenueCatTypes.ts +0 -0
  82. /package/src/{domain/entities → domains/subscription/core}/SubscriptionConstants.ts +0 -0
  83. /package/src/{revenuecat → domains/subscription}/infrastructure/README.md +0 -0
  84. /package/src/{revenuecat → domains/subscription}/infrastructure/config/README.md +0 -0
  85. /package/src/{revenuecat → domains/subscription}/infrastructure/handlers/README.md +0 -0
  86. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/README.md +0 -0
  87. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/subscriptionQueryKeys.ts +0 -0
  88. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useCustomerInfo.ts +0 -0
  89. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useInitializeSubscription.ts +0 -0
  90. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePaywallFlow.ts +0 -0
  91. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePurchasePackage.ts +0 -0
  92. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRestorePurchase.ts +0 -0
  93. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCatTrialEligibility.ts +0 -0
  94. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionPackages.ts +0 -0
  95. /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionQueries.ts +0 -0
  96. /package/src/{revenuecat → domains/subscription}/infrastructure/managers/README.md +0 -0
  97. /package/src/{revenuecat → domains/subscription}/infrastructure/services/CustomerInfoListenerManager.ts +0 -0
  98. /package/src/{revenuecat → domains/subscription}/infrastructure/services/OfferingsFetcher.ts +0 -0
  99. /package/src/{revenuecat → domains/subscription}/infrastructure/services/README.md +0 -0
  100. /package/src/{revenuecat → domains/subscription}/infrastructure/services/ServiceStateManager.ts +0 -0
  101. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/ApiKeyResolver.ts +0 -0
  102. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/InitializationCache.ts +0 -0
  103. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/PremiumStatusSyncer.ts +0 -0
  104. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/README.md +0 -0
  105. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/RenewalDetector.ts +0 -0
  106. /package/src/{revenuecat → domains/subscription}/infrastructure/utils/UserIdProvider.ts +0 -0
  107. /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthAwarePurchase.ts +0 -0
  108. /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthSubscriptionSync.ts +0 -0
  109. /package/src/{presentation/hooks → domains/subscription/presentation}/useFeatureGate.ts +0 -0
  110. /package/src/{presentation/hooks → domains/subscription/presentation}/usePaywallVisibility.ts +0 -0
  111. /package/src/{presentation/hooks → domains/subscription/presentation}/usePremiumGate.ts +0 -0
  112. /package/src/{presentation/hooks → domains/subscription/presentation}/useSavedPurchaseAutoExecution.ts +0 -0
  113. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.ts +0 -0
  114. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.utils.ts +0 -0
  115. /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionStatus.ts +0 -0
  116. /package/src/{infrastructure/services → shared/application}/ActivationHandler.ts +0 -0
  117. /package/src/{infrastructure/services → shared/application}/FeedbackService.ts +0 -0
  118. /package/src/{application → shared/application}/ports/ISubscriptionRepository.ts +0 -0
  119. /package/src/{application → shared/application}/ports/ISubscriptionService.ts +0 -0
  120. /package/src/{application → shared/application}/ports/README.md +0 -0
  121. /package/src/{domain/errors → shared/utils}/InsufficientCreditsError.ts +0 -0
  122. /package/src/{infrastructure → shared}/utils/Logger.ts +0 -0
  123. /package/src/{domain/value-objects → shared/utils}/Result.ts +0 -0
  124. /package/src/{domain/value-objects → shared/utils}/SubscriptionConfig.ts +0 -0
  125. /package/src/{domain/errors → shared/utils}/SubscriptionError.ts +0 -0
@@ -0,0 +1,30 @@
1
+ import { doc, getDoc, setDoc, serverTimestamp, arrayUnion, type Firestore } from "firebase/firestore";
2
+ import { getFirestore } from "@umituz/react-native-firebase";
3
+ import type { DeviceTrialRecord } from "./TrialTypes";
4
+
5
+ const DEVICE_TRIALS_COLLECTION = "device_trials";
6
+
7
+ export class DeviceTrialRepository {
8
+ private get db(): Firestore | null {
9
+ return getFirestore();
10
+ }
11
+
12
+ async getRecord(deviceId: string): Promise<DeviceTrialRecord | null> {
13
+ if (!this.db) return null;
14
+ const snap = await getDoc(doc(this.db, DEVICE_TRIALS_COLLECTION, deviceId));
15
+ return snap.exists() ? snap.data() as DeviceTrialRecord : null;
16
+ }
17
+
18
+ async saveRecord(deviceId: string, data: Partial<DeviceTrialRecord>): Promise<boolean> {
19
+ if (!this.db) return false;
20
+ const ref = doc(this.db, DEVICE_TRIALS_COLLECTION, deviceId);
21
+ await setDoc(ref, { ...data, updatedAt: serverTimestamp() }, { merge: true });
22
+
23
+ // Ensure createdAt exists
24
+ const snap = await getDoc(ref);
25
+ if (!snap.data()?.createdAt) {
26
+ await setDoc(ref, { createdAt: serverTimestamp() }, { merge: true });
27
+ }
28
+ return true;
29
+ }
30
+ }
package/src/index.ts CHANGED
@@ -2,22 +2,20 @@
2
2
  * React Native Subscription - Public API
3
3
  */
4
4
 
5
- export * from "./domains/wallet";
6
- export * from "./domains/paywall";
7
- export * from "./domains/config";
8
-
9
- // Domain Layer - Constants & Types
10
- export * from "./domain/entities/SubscriptionConstants";
5
+ // Domain Layer - Constants & Types (Now in domains/subscription/core)
6
+ export * from "./domains/subscription/core/SubscriptionConstants";
11
7
  export {
12
8
  createDefaultSubscriptionStatus,
13
9
  isSubscriptionValid,
14
10
  resolveSubscriptionStatus,
15
- } from "./domain/entities/SubscriptionStatus";
16
- export type { SubscriptionStatus, StatusResolverInput } from "./domain/entities/SubscriptionStatus";
17
- export type { SubscriptionConfig } from "./domain/value-objects/SubscriptionConfig";
18
- export type { ISubscriptionRepository } from "./application/ports/ISubscriptionRepository";
11
+ } from "./domains/subscription/core/SubscriptionStatus";
12
+ export type { SubscriptionStatus, StatusResolverInput } from "./domains/subscription/core/SubscriptionStatus";
13
+
14
+ // Application Layer - Ports
15
+ export type { ISubscriptionRepository } from "./shared/application/ports/ISubscriptionRepository";
16
+ export type { IRevenueCatService } from "./shared/application/ports/IRevenueCatService";
19
17
 
20
- // Result Pattern (Functional Error Handling)
18
+ // Result Pattern (Now in shared/utils)
21
19
  export {
22
20
  success,
23
21
  failure,
@@ -30,39 +28,29 @@ export {
30
28
  tryCatch,
31
29
  tryCatchSync,
32
30
  combine,
33
- } from "./domain/value-objects/Result";
34
- export type { Result, Success, Failure } from "./domain/value-objects/Result";
31
+ } from "./shared/utils/Result";
32
+ export type { Result, Success, Failure } from "./shared/utils/Result";
35
33
 
36
- // Infrastructure Layer
37
- export { SubscriptionService, initializeSubscriptionService } from "./infrastructure/services/SubscriptionService";
38
- export {
39
- submitFeedback,
40
- submitPaywallFeedback,
41
- submitSettingsFeedback,
42
- type FeedbackData,
43
- type FeedbackSubmitResult,
44
- } from "./infrastructure/services/FeedbackService";
45
- export { initializeSubscription, type SubscriptionInitConfig, type CreditPackageConfig } from "./infrastructure/services/SubscriptionInitializer";
46
- export {
34
+ // Infrastructure Layer (Services & Repositories)
35
+ export { initializeSubscription, type SubscriptionInitConfig, type CreditPackageConfig } from "./domains/subscription/application/SubscriptionInitializer";
36
+ export {
47
37
  getDeviceId,
48
38
  checkTrialEligibility,
49
39
  recordTrialStart,
50
40
  recordTrialEnd,
51
41
  recordTrialConversion,
52
- TRIAL_CONFIG,
53
- type DeviceTrialRecord,
54
- type TrialEligibilityResult,
55
- } from "./infrastructure/services/TrialService";
56
- export { CreditsRepository, createCreditsRepository } from "./infrastructure/repositories/CreditsRepository";
57
- export { configureCreditsRepository, getCreditsRepository, getCreditsConfig, resetCreditsRepository, isCreditsRepositoryConfigured } from "./infrastructure/repositories/CreditsRepositoryProvider";
58
- export {
59
- getSavedPurchase,
60
- clearSavedPurchase,
61
- configureAuthProvider,
62
- type PurchaseAuthProvider,
63
- } from "./presentation/hooks/useAuthAwarePurchase";
42
+ type TrialEligibilityResult
43
+ } from "./domains/trial/application/TrialService";
44
+
45
+ export { CreditsRepository } from "./domains/credits/infrastructure/CreditsRepository";
46
+ export {
47
+ configureCreditsRepository,
48
+ getCreditsRepository,
49
+ getCreditsConfig,
50
+ isCreditsRepositoryConfigured
51
+ } from "./domains/credits/infrastructure/CreditsRepositoryProvider";
64
52
 
65
- // Presentation Layer - Hooks
53
+ // Presentation Layer - Hooks (Point to the bridge)
66
54
  export * from "./presentation/hooks";
67
55
 
68
56
  // Presentation Layer - Components
@@ -70,12 +58,7 @@ export * from "./presentation/components/details/PremiumDetailsCard";
70
58
  export * from "./presentation/components/details/PremiumStatusBadge";
71
59
  export * from "./presentation/components/sections/SubscriptionSection";
72
60
  export * from "./presentation/components/feedback/PaywallFeedbackModal";
73
- export * from "./presentation/components/overlay";
74
61
  export * from "./presentation/screens/SubscriptionDetailScreen";
75
- export * from "./presentation/types/SubscriptionDetailTypes";
76
-
77
- // Presentation Layer - Stores
78
- export * from "./presentation/stores";
79
62
 
80
63
  export type {
81
64
  CreditType,
@@ -83,27 +66,13 @@ export type {
83
66
  CreditsConfig,
84
67
  CreditsResult,
85
68
  DeductCreditsResult,
86
- CreditAllocation,
87
- PackageAllocationMap,
88
- } from "./domain/entities/Credits";
89
- export { DEFAULT_CREDITS_CONFIG } from "./domain/entities/Credits";
90
- export { InsufficientCreditsError } from "./domain/errors/InsufficientCreditsError";
69
+ } from "./domains/credits/core/Credits";
70
+
71
+ export { DEFAULT_CREDITS_CONFIG } from "./domains/credits/core/Credits";
91
72
 
92
73
  // Utils
93
74
  export * from "./utils";
94
75
 
95
- // RevenueCat
96
- export * from "./revenuecat";
97
-
98
- // App Service Helpers (for configureAppServices)
99
- export {
100
- createCreditService,
101
- createPaywallService,
102
- type CreditServiceConfig,
103
- type ICreditService,
104
- type IPaywallService,
105
- } from "./infrastructure/services/app-service-helpers";
106
-
107
76
  // Init Module Factory
108
77
  export {
109
78
  createSubscriptionInitModule,
@@ -1,5 +1,5 @@
1
1
  import type { InitModule } from '@umituz/react-native-design-system';
2
- import { initializeSubscription, type SubscriptionInitConfig } from '../infrastructure/services/SubscriptionInitializer';
2
+ import { initializeSubscription, type SubscriptionInitConfig } from '../domains/subscription/application/SubscriptionInitializer';
3
3
 
4
4
  export interface SubscriptionInitModuleConfig extends Omit<SubscriptionInitConfig, 'apiKey'> {
5
5
  getApiKey: () => string | undefined;
@@ -9,9 +9,7 @@ import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-syst
9
9
  import {
10
10
  SUBSCRIPTION_STATUS,
11
11
  type SubscriptionStatusType
12
- } from "../../../domain/entities/SubscriptionConstants";
13
-
14
- export type { SubscriptionStatusType };
12
+ } from "../../../domains/subscription/core/SubscriptionConstants";
15
13
 
16
14
  export interface PremiumStatusBadgeProps {
17
15
  status: SubscriptionStatusType;
@@ -8,7 +8,7 @@ import { useAuth } from "@umituz/react-native-auth";
8
8
  import {
9
9
  submitPaywallFeedback,
10
10
  submitSettingsFeedback,
11
- } from "../../../infrastructure/services/FeedbackService";
11
+ } from "../../../shared/application/FeedbackService";
12
12
 
13
13
  export interface UsePaywallFeedbackSubmitOptions {
14
14
  onSuccess?: () => void;
@@ -1,13 +1,13 @@
1
- export * from "./useAuthAwarePurchase";
2
- export * from "./useAuthSubscriptionSync";
3
- export * from "./useSavedPurchaseAutoExecution";
4
- export * from "./useCredits";
5
- export * from "./useDeductCredit";
6
- export * from "./useFeatureGate";
7
- export * from "./usePaywallVisibility";
8
- export * from "./usePremium";
9
- export * from "./usePremiumGate";
10
- export * from "./useSubscriptionSettingsConfig";
11
- export * from "./useSubscriptionStatus";
1
+ export * from "../../domains/subscription/presentation/useAuthAwarePurchase";
2
+ export * from "../../domains/subscription/presentation/useAuthSubscriptionSync";
3
+ export * from "../../domains/subscription/presentation/useSavedPurchaseAutoExecution";
4
+ export * from "../../domains/credits/presentation/useCredits";
5
+ export * from "../../domains/credits/presentation/useDeductCredit";
6
+ export * from "../../domains/subscription/presentation/useFeatureGate";
7
+ export * from "../../domains/subscription/presentation/usePaywallVisibility";
8
+ export * from "../../domains/subscription/presentation/usePremium";
9
+ export * from "../../domains/subscription/presentation/usePremiumGate";
10
+ export * from "../../domains/subscription/presentation/useSubscriptionSettingsConfig";
11
+ export * from "../../domains/subscription/presentation/useSubscriptionStatus";
12
12
  export * from "./feedback/usePaywallFeedback";
13
13
  export * from "./feedback/useFeedbackSubmit";
@@ -0,0 +1,32 @@
1
+ import type {
2
+ PurchasesOffering,
3
+ PurchasesPackage,
4
+ CustomerInfo,
5
+ } from "react-native-purchases";
6
+
7
+ export interface InitializeResult {
8
+ success: boolean;
9
+ offering: PurchasesOffering | null;
10
+ isPremium: boolean;
11
+ }
12
+
13
+ export interface PurchaseResult {
14
+ success: boolean;
15
+ productId: string | null;
16
+ }
17
+
18
+ export interface RestoreResult {
19
+ success: boolean;
20
+ productId: string | null;
21
+ }
22
+
23
+ export interface IRevenueCatService {
24
+ initialize(userId: string, apiKey?: string): Promise<InitializeResult>;
25
+ fetchOfferings(): Promise<PurchasesOffering | null>;
26
+ purchasePackage(pkg: PurchasesPackage, userId: string): Promise<PurchaseResult>;
27
+ restorePurchases(userId: string): Promise<RestoreResult>;
28
+ isInitialized(): boolean;
29
+ getCurrentUserId(): string | null;
30
+ getCustomerInfo(): Promise<CustomerInfo | null>;
31
+ reset(): Promise<void>;
32
+ }
@@ -0,0 +1,51 @@
1
+ type EventCallback<T = any> = (data: T) => void;
2
+
3
+ /**
4
+ * Simple EventBus Implementation
5
+ * Used to decouple services and provide an observer pattern for subscription events.
6
+ */
7
+ export class SubscriptionEventBus {
8
+ private static instance: SubscriptionEventBus;
9
+ private listeners: Record<string, EventCallback[]> = {};
10
+
11
+ private constructor() {}
12
+
13
+ static getInstance(): SubscriptionEventBus {
14
+ if (!SubscriptionEventBus.instance) {
15
+ SubscriptionEventBus.instance = new SubscriptionEventBus();
16
+ }
17
+ return SubscriptionEventBus.instance;
18
+ }
19
+
20
+ on<T>(event: string, callback: EventCallback<T>): () => void {
21
+ if (!this.listeners[event]) {
22
+ this.listeners[event] = [];
23
+ }
24
+ this.listeners[event].push(callback);
25
+
26
+ // Return unsubscribe function
27
+ return () => {
28
+ this.listeners[event] = this.listeners[event].filter(l => l !== callback);
29
+ };
30
+ }
31
+
32
+ emit<T>(event: string, data: T): void {
33
+ if (!this.listeners[event]) return;
34
+ this.listeners[event].forEach(callback => {
35
+ try {
36
+ callback(data);
37
+ } catch (error) {
38
+ if (__DEV__) console.error(`[SubscriptionEventBus] Error in listener for ${event}:`, error);
39
+ }
40
+ });
41
+ }
42
+ }
43
+
44
+ export const subscriptionEventBus = SubscriptionEventBus.getInstance();
45
+
46
+ export const SUBSCRIPTION_EVENTS = {
47
+ CREDITS_UPDATED: "credits_updated",
48
+ PURCHASE_COMPLETED: "purchase_completed",
49
+ RENEWAL_DETECTED: "renewal_detected",
50
+ PREMIUM_STATUS_CHANGED: "premium_status_changed",
51
+ };
@@ -1,50 +0,0 @@
1
- # Application Layer
2
-
3
- Abonelik uygulamasının iş mantığını ve servis kontratlarını içeren katman.
4
-
5
- ## Location
6
-
7
- `src/application/`
8
-
9
- ## Strategy
10
-
11
- Ortak kullanım durumlarını gerçekleştiren, servis kontratlarını tanımlayan ve uygulama kurallarını yöneten katman. Dependency injection ve test edilebilirlik sağlar.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST use dependency injection pattern
18
- - MUST define service contracts through interfaces
19
- - MUST implement error handling and propagation
20
- - MUST ensure type safety for all operations
21
- - MUST support dependency inversion principle
22
-
23
- ### PROHIBITED
24
-
25
- - MUST NOT contain direct infrastructure dependencies
26
- - MUST NOT bypass repository interfaces
27
- - MUST NOT leak implementation details
28
- - MUST NOT mix business logic with presentation
29
-
30
- ### CRITICAL
31
-
32
- - Keep interfaces small and focused (Interface Segregation)
33
- - Maintain single responsibility for each service
34
- - Always handle errors appropriately and propagate upward
35
- - Ensure all operations are type-safe
36
-
37
- ## AI Agent Guidelines
38
-
39
- When working with application layer:
40
- 1. Interface Segregation - küçük, odaklanmış interface'ler tanımlayın
41
- 2. Dependency Inversion - high-level modüller low-level modüllere bağımlı olmamalı
42
- 3. Single Responsibility - her service/repository tek bir sorumluluğa sahip olmalı
43
- 4. Error Handling - hataları uygun şekilde handle edin ve yukarı propagation edin
44
- 5. Testing - interface'ler mock ile kolay test edilebilir
45
-
46
- ## Related Documentation
47
-
48
- - [Domain Layer](../domain/README.md)
49
- - [Infrastructure Layer](../infrastructure/README.md)
50
- - [Ports](./ports/README.md)
@@ -1,54 +0,0 @@
1
- # Domain Layer
2
-
3
- Abonelik sisteminin temel domain logic'ini, entity'lerini ve value object'lerini içeren katman.
4
-
5
- ## Location
6
-
7
- `src/domain/`
8
-
9
- ## Strategy
10
-
11
- Temel iş kavramlarını, iş kurallarını ve domain logic'ini encapsulate eden katman. Framework-agnostic ve tamamen iş mantığına odaklı.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST be framework-agnostic (no React, React Native dependencies)
18
- - MUST encapsulate business rules within entities
19
- - MUST validate all invariants on entity creation
20
- - MUST use immutable objects
21
- - MUST implement type guards for type safety
22
- - MUST define domain-specific error types
23
-
24
- ### PROHIBITED
25
-
26
- - MUST NOT contain any framework or UI code
27
- - MUST NOT have direct dependencies on infrastructure
28
- - MUST NOT expose mutable state
29
- - MUST NOT use generic error types
30
-
31
- ### CRITICAL
32
-
33
- - Keep entities pure and framework-independent
34
- - Validate all business rules on entity creation
35
- - Make all objects immutable
36
- - Encapsulate business logic within entities
37
- - Use strong typing throughout
38
-
39
- ## AI Agent Guidelines
40
-
41
- When working with domain layer:
42
- 1. Immutable Objects - entity'leri immutable olarak tasarlayın
43
- 2. Validation - entity creation'da validation yapın
44
- 3. Encapsulation - business logic'i entity içinde tutun
45
- 4. Type Safety - strong typing kullanın
46
- 5. Domain Events - önemli domain olaylarını event olarak yayınlayın
47
- 6. Error Handling - domain-specific hatalar tanımlayın
48
-
49
- ## Related Documentation
50
-
51
- - [Domain Entities](./entities/README.md)
52
- - [Value Objects](./value-objects/README.md)
53
- - [Domain Errors](./errors/README.md)
54
- - [Application Layer](../application/README.md)
@@ -1,50 +0,0 @@
1
- # Domain Entities
2
-
3
- Core domain entities for subscription management.
4
-
5
- ## Location
6
-
7
- `src/domain/entities/`
8
-
9
- ## Strategy
10
-
11
- Domain entities represent the core business concepts and rules of the subscription system. They are framework-agnostic and contain only business logic, ensuring pure domain modeling.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST validate themselves on creation
18
- - MUST remain immutable after creation
19
- - MUST encapsulate business logic internally
20
- - MUST use value objects for complex attributes
21
- - MUST be framework-agnostic
22
-
23
- ### PROHIBITED
24
-
25
- - MUST NOT have framework dependencies
26
- - MUST NOT expose internal state directly
27
- - MUST NOT allow direct state modification
28
- - MUST NOT contain infrastructure concerns
29
-
30
- ### CRITICAL
31
-
32
- - Always validate invariants to ensure valid state
33
- - Prevent direct state modification through immutability
34
- - Keep business rules encapsulated within entities
35
- - Maintain purity - no external dependencies
36
-
37
- ## AI Agent Guidelines
38
-
39
- When working with domain entities:
40
- 1. Keep entities pure - no framework dependencies
41
- 2. Validate invariants - ensure valid state
42
- 3. Use value objects - for complex attributes
43
- 4. Encapsulate logic - keep business rules inside entities
44
- 5. Make immutable - prevent direct state modification
45
-
46
- ## Related Documentation
47
-
48
- - [Value Objects](../value-objects/README.md)
49
- - [Domain Errors](../errors/README.md)
50
- - [Domain Layer](../README.md)
@@ -1,149 +0,0 @@
1
- import {
2
- SUBSCRIPTION_STATUS,
3
- } from './SubscriptionConstants';
4
- import {
5
- createDefaultSubscriptionStatus,
6
- isSubscriptionValid,
7
- calculateDaysRemaining,
8
- resolveSubscriptionStatus,
9
- } from './SubscriptionStatus';
10
-
11
- describe('SubscriptionStatus', () => {
12
- describe('createDefaultSubscriptionStatus', () => {
13
- it('should create default subscription status', () => {
14
- const status = createDefaultSubscriptionStatus();
15
-
16
- expect(status).toEqual({
17
- isPremium: false,
18
- expiresAt: null,
19
- productId: null,
20
- purchasedAt: null,
21
- customerId: null,
22
- syncedAt: null,
23
- status: SUBSCRIPTION_STATUS.NONE,
24
- });
25
- });
26
- });
27
-
28
- describe('isSubscriptionValid', () => {
29
- it('should return false for null status', () => {
30
- expect(isSubscriptionValid(null)).toBe(false);
31
- });
32
-
33
- it('should return false for non-premium status', () => {
34
- const status = {
35
- isPremium: false,
36
- expiresAt: null,
37
- productId: null,
38
- purchasedAt: null,
39
- customerId: null,
40
- syncedAt: null,
41
- };
42
-
43
- expect(isSubscriptionValid(status)).toBe(false);
44
- });
45
-
46
- it('should return true for lifetime subscription', () => {
47
- const status = {
48
- isPremium: true,
49
- expiresAt: null,
50
- productId: 'lifetime',
51
- purchasedAt: '2024-01-01T00:00:00.000Z',
52
- customerId: 'customer123',
53
- syncedAt: '2024-01-01T00:00:00.000Z',
54
- };
55
-
56
- expect(isSubscriptionValid(status)).toBe(true);
57
- });
58
-
59
- it('should return true for active subscription', () => {
60
- const futureDate = new Date();
61
- futureDate.setDate(futureDate.getDate() + 30);
62
-
63
- const status = {
64
- isPremium: true,
65
- expiresAt: futureDate.toISOString(),
66
- productId: 'monthly',
67
- purchasedAt: '2024-01-01T00:00:00.000Z',
68
- customerId: 'customer123',
69
- syncedAt: '2024-01-01T00:00:00.000Z',
70
- };
71
-
72
- expect(isSubscriptionValid(status)).toBe(true);
73
- });
74
-
75
- it('should return false for expired subscription', () => {
76
- const pastDate = new Date();
77
- pastDate.setDate(pastDate.getDate() - 1);
78
-
79
- const status = {
80
- isPremium: true,
81
- expiresAt: pastDate.toISOString(),
82
- productId: 'monthly',
83
- purchasedAt: '2024-01-01T00:00:00.000Z',
84
- customerId: 'customer123',
85
- syncedAt: '2024-01-01T00:00:00.000Z',
86
- };
87
-
88
- expect(isSubscriptionValid(status)).toBe(false);
89
- });
90
- });
91
-
92
- describe('calculateDaysRemaining', () => {
93
- it('should return null for null input', () => {
94
- expect(calculateDaysRemaining(null)).toBeNull();
95
- });
96
-
97
- it('should return positive days for future expiration', () => {
98
- const futureDate = new Date();
99
- futureDate.setDate(futureDate.getDate() + 5);
100
- expect(calculateDaysRemaining(futureDate.toISOString())).toBe(5);
101
- });
102
-
103
- it('should return 0 for past expiration', () => {
104
- const pastDate = new Date();
105
- pastDate.setDate(pastDate.getDate() - 5);
106
- expect(calculateDaysRemaining(pastDate.toISOString())).toBe(0);
107
- });
108
- });
109
-
110
- describe('resolveSubscriptionStatus', () => {
111
- it('should return NONE when not premium', () => {
112
- expect(resolveSubscriptionStatus({ isPremium: false })).toBe(SUBSCRIPTION_STATUS.NONE);
113
- });
114
-
115
- it('should return EXPIRED when expired', () => {
116
- expect(resolveSubscriptionStatus({ isPremium: true, isExpired: true })).toBe(SUBSCRIPTION_STATUS.EXPIRED);
117
- });
118
-
119
- it('should return TRIAL when trialing and set to renew', () => {
120
- expect(resolveSubscriptionStatus({
121
- isPremium: true,
122
- periodType: 'TRIAL',
123
- willRenew: true
124
- })).toBe(SUBSCRIPTION_STATUS.TRIAL);
125
- });
126
-
127
- it('should return TRIAL_CANCELED when trialing and will not renew', () => {
128
- expect(resolveSubscriptionStatus({
129
- isPremium: true,
130
- periodType: 'TRIAL',
131
- willRenew: false
132
- })).toBe(SUBSCRIPTION_STATUS.TRIAL_CANCELED);
133
- });
134
-
135
- it('should return ACTIVE when premium, not expired, not trial, and set to renew', () => {
136
- expect(resolveSubscriptionStatus({
137
- isPremium: true,
138
- willRenew: true
139
- })).toBe(SUBSCRIPTION_STATUS.ACTIVE);
140
- });
141
-
142
- it('should return CANCELED when premium, not expired, not trial, and will not renew', () => {
143
- expect(resolveSubscriptionStatus({
144
- isPremium: true,
145
- willRenew: false
146
- })).toBe(SUBSCRIPTION_STATUS.CANCELED);
147
- });
148
- });
149
- });
@@ -1,53 +0,0 @@
1
- # Domain Errors
2
-
3
- Domain-specific error types for subscription system.
4
-
5
- ## Location
6
-
7
- `src/domain/errors/`
8
-
9
- ## Strategy
10
-
11
- Domain errors provide typed, contextual error handling for business logic failures. They make error handling explicit and type-safe, enabling precise error management.
12
-
13
- ## Restrictions
14
-
15
- ### REQUIRED
16
-
17
- - MUST use specific error types (not generic Error)
18
- - MUST include contextual information
19
- - MUST document all error codes
20
- - MUST handle errors gracefully with user-friendly messages
21
- - MUST log errors for debugging
22
- - MUST use type guards for type-safe error handling
23
-
24
- ### PROHIBITED
25
-
26
- - MUST NOT swallow errors without handling
27
- - MUST NOT use generic Error class
28
- - MUST NOT expose sensitive information in error messages
29
- - MUST NOT ignore error conditions
30
-
31
- ### CRITICAL
32
-
33
- - Always handle or rethrow errors
34
- - Include relevant context in error objects
35
- - Use type guards to enable type-safe error handling
36
- - Show user-friendly messages while logging technical details
37
-
38
- ## AI Agent Guidelines
39
-
40
- When working with domain errors:
41
- 1. Use specific error types - don't use generic Error
42
- 2. Include context - add relevant data to errors
43
- 3. Document error codes - list all possible errors
44
- 4. Handle gracefully - show user-friendly messages
45
- 5. Log errors - track for debugging
46
- 6. Don't swallow errors - always handle or rethrow
47
- 7. Use type guards - enable type-safe error handling
48
-
49
- ## Related Documentation
50
-
51
- - [Domain Entities](../entities/README.md)
52
- - [Value Objects](../value-objects/README.md)
53
- - [Domain Layer](../README.md)