@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.
- package/package.json +1 -1
- package/src/domains/credits/application/CreditLimitCalculator.ts +17 -0
- package/src/domains/credits/application/CreditsInitializer.ts +85 -0
- package/src/domains/credits/application/DeductCreditsCommand.ts +52 -0
- package/src/domains/credits/application/PurchaseMetadataGenerator.ts +59 -0
- package/src/domains/credits/application/credit-strategies/CreditAllocationContext.ts +35 -0
- package/src/domains/credits/application/credit-strategies/ICreditStrategy.ts +18 -0
- package/src/domains/credits/application/credit-strategies/StandardPurchaseCreditStrategy.ts +16 -0
- package/src/domains/credits/application/credit-strategies/SyncCreditStrategy.ts +15 -0
- package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +18 -0
- package/src/{infrastructure/mappers → domains/credits/core}/CreditsMapper.ts +4 -4
- package/src/domains/credits/infrastructure/CreditsRepository.ts +102 -0
- package/src/{presentation/hooks → domains/credits/presentation}/useCredits.ts +21 -4
- package/src/domains/subscription/application/SubscriptionAuthListener.ts +26 -0
- package/src/domains/subscription/application/SubscriptionInitializer.ts +77 -0
- package/src/{infrastructure/services → domains/subscription/application}/SubscriptionInitializerTypes.ts +21 -1
- package/src/domains/subscription/application/SubscriptionSyncService.ts +71 -0
- package/src/domains/subscription/application/SubscriptionSyncUtils.ts +16 -0
- package/src/{revenuecat/domain/value-objects → domains/subscription/core}/RevenueCatConfig.ts +1 -1
- package/src/{domain/types → domains/subscription/core}/RevenueCatData.ts +1 -1
- package/src/{domain/entities → domains/subscription/core}/SubscriptionStatus.ts +13 -21
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +51 -0
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +67 -0
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +27 -0
- package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCat.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/SubscriptionInternalState.ts +12 -0
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +110 -0
- package/src/{revenuecat → domains/subscription}/infrastructure/services/PurchaseHandler.ts +1 -1
- package/src/{revenuecat → domains/subscription}/infrastructure/services/RestoreHandler.ts +1 -1
- package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatInitializer.ts +1 -1
- package/src/{revenuecat → domains/subscription}/infrastructure/services/RevenueCatService.ts +2 -2
- package/src/{presentation/hooks → domains/subscription/presentation}/usePremium.ts +7 -4
- package/src/domains/trial/application/TrialEligibilityService.ts +25 -0
- package/src/domains/trial/application/TrialService.ts +69 -0
- package/src/{infrastructure/services → domains/trial/core}/TrialTypes.ts +1 -1
- package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +30 -0
- package/src/index.ts +28 -59
- package/src/init/createSubscriptionInitModule.ts +1 -1
- package/src/presentation/components/details/PremiumStatusBadge.tsx +1 -3
- package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +1 -1
- package/src/presentation/hooks/index.ts +11 -11
- package/src/shared/application/ports/IRevenueCatService.ts +32 -0
- package/src/shared/infrastructure/SubscriptionEventBus.ts +51 -0
- package/src/application/README.md +0 -50
- package/src/domain/README.md +0 -54
- package/src/domain/entities/README.md +0 -50
- package/src/domain/entities/SubscriptionStatus.test.ts +0 -149
- package/src/domain/errors/README.md +0 -53
- package/src/domain/value-objects/README.md +0 -50
- package/src/infrastructure/README.md +0 -55
- package/src/infrastructure/mappers/README.md +0 -21
- package/src/infrastructure/models/README.md +0 -26
- package/src/infrastructure/repositories/CreditsRepository.ts +0 -132
- package/src/infrastructure/repositories/README.md +0 -99
- package/src/infrastructure/services/CreditsInitializer.ts +0 -170
- package/src/infrastructure/services/README.md +0 -99
- package/src/infrastructure/services/SubscriptionInitializer.ts +0 -176
- package/src/infrastructure/services/SubscriptionService.ts +0 -133
- package/src/infrastructure/services/TrialService.ts +0 -197
- package/src/infrastructure/services/app-service-helpers.ts +0 -111
- package/src/revenuecat/README.md +0 -104
- package/src/revenuecat/application/README.md +0 -43
- package/src/revenuecat/application/ports/IRevenueCatService.ts +0 -76
- package/src/revenuecat/application/ports/README.md +0 -41
- package/src/revenuecat/domain/README.md +0 -48
- package/src/revenuecat/domain/constants/README.md +0 -41
- package/src/revenuecat/domain/entities/README.md +0 -42
- package/src/revenuecat/domain/errors/README.md +0 -53
- package/src/revenuecat/domain/types/README.md +0 -41
- package/src/revenuecat/domain/value-objects/README.md +0 -41
- package/src/revenuecat/index.ts +0 -13
- package/src/revenuecat/infrastructure/handlers/PackageHandler.ts +0 -161
- package/src/revenuecat/infrastructure/managers/SubscriptionManager.ts +0 -165
- package/src/revenuecat/presentation/README.md +0 -42
- /package/src/{domain/entities → domains/credits/core}/Credits.ts +0 -0
- /package/src/{infrastructure/models → domains/credits/core}/UserCreditsDocument.ts +0 -0
- /package/src/{infrastructure/repositories → domains/credits/infrastructure}/CreditsRepositoryProvider.ts +0 -0
- /package/src/{presentation/hooks → domains/credits/presentation}/useDeductCredit.ts +0 -0
- /package/src/{revenuecat/domain/constants → domains/subscription/core}/RevenueCatConstants.ts +0 -0
- /package/src/{revenuecat/domain/errors → domains/subscription/core}/RevenueCatError.ts +0 -0
- /package/src/{revenuecat/domain/types → domains/subscription/core}/RevenueCatTypes.ts +0 -0
- /package/src/{domain/entities → domains/subscription/core}/SubscriptionConstants.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/config/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/handlers/README.md +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/README.md +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/subscriptionQueryKeys.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useCustomerInfo.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useInitializeSubscription.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePaywallFlow.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/usePurchasePackage.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRestorePurchase.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useRevenueCatTrialEligibility.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionPackages.ts +0 -0
- /package/src/{revenuecat/presentation → domains/subscription/infrastructure}/hooks/useSubscriptionQueries.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/managers/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/CustomerInfoListenerManager.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/OfferingsFetcher.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/services/ServiceStateManager.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/ApiKeyResolver.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/InitializationCache.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/PremiumStatusSyncer.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/README.md +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/RenewalDetector.ts +0 -0
- /package/src/{revenuecat → domains/subscription}/infrastructure/utils/UserIdProvider.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthAwarePurchase.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useAuthSubscriptionSync.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useFeatureGate.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/usePaywallVisibility.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/usePremiumGate.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSavedPurchaseAutoExecution.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionSettingsConfig.utils.ts +0 -0
- /package/src/{presentation/hooks → domains/subscription/presentation}/useSubscriptionStatus.ts +0 -0
- /package/src/{infrastructure/services → shared/application}/ActivationHandler.ts +0 -0
- /package/src/{infrastructure/services → shared/application}/FeedbackService.ts +0 -0
- /package/src/{application → shared/application}/ports/ISubscriptionRepository.ts +0 -0
- /package/src/{application → shared/application}/ports/ISubscriptionService.ts +0 -0
- /package/src/{application → shared/application}/ports/README.md +0 -0
- /package/src/{domain/errors → shared/utils}/InsufficientCreditsError.ts +0 -0
- /package/src/{infrastructure → shared}/utils/Logger.ts +0 -0
- /package/src/{domain/value-objects → shared/utils}/Result.ts +0 -0
- /package/src/{domain/value-objects → shared/utils}/SubscriptionConfig.ts +0 -0
- /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
|
-
|
|
6
|
-
export * from "./domains/
|
|
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 "./
|
|
16
|
-
export type { SubscriptionStatus, StatusResolverInput } from "./
|
|
17
|
-
|
|
18
|
-
|
|
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 (
|
|
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 "./
|
|
34
|
-
export type { Result, Success, Failure } from "./
|
|
31
|
+
} from "./shared/utils/Result";
|
|
32
|
+
export type { Result, Success, Failure } from "./shared/utils/Result";
|
|
35
33
|
|
|
36
|
-
// Infrastructure Layer
|
|
37
|
-
export {
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} from "./infrastructure/
|
|
56
|
-
export {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
} from "./
|
|
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 '../
|
|
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 "../../../
|
|
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 "../../../
|
|
11
|
+
} from "../../../shared/application/FeedbackService";
|
|
12
12
|
|
|
13
13
|
export interface UsePaywallFeedbackSubmitOptions {
|
|
14
14
|
onSuccess?: () => void;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export * from "
|
|
2
|
-
export * from "
|
|
3
|
-
export * from "
|
|
4
|
-
export * from "
|
|
5
|
-
export * from "
|
|
6
|
-
export * from "
|
|
7
|
-
export * from "
|
|
8
|
-
export * from "
|
|
9
|
-
export * from "
|
|
10
|
-
export * from "
|
|
11
|
-
export * from "
|
|
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)
|
package/src/domain/README.md
DELETED
|
@@ -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)
|