@umituz/react-native-subscription 2.37.12 → 2.37.14
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/PurchaseMetadataGenerator.ts +1 -1
- package/src/domains/credits/application/credit-strategies/CreditAllocationOrchestrator.ts +1 -1
- package/src/domains/credits/application/creditDocumentHelpers.ts +0 -14
- package/src/domains/credits/presentation/deduct-credit/index.ts +0 -1
- package/src/domains/credits/presentation/useCredits.ts +1 -6
- package/src/domains/credits/utils/creditValidation.ts +3 -3
- package/src/domains/paywall/components/PaywallFeatures.tsx +1 -1
- package/src/domains/paywall/hooks/usePaywallActions.ts +1 -1
- package/src/domains/revenuecat/core/errors/RevenueCatError.ts +1 -14
- package/src/domains/revenuecat/core/errors/RevenueCatErrorHandler.ts +1 -1
- package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +3 -3
- package/src/domains/revenuecat/infrastructure/services/ConfigurationStateManager.ts +1 -1
- package/src/domains/revenuecat/infrastructure/services/RevenueCatInitializer.ts +0 -2
- package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +9 -0
- package/src/domains/subscription/application/initializer/index.ts +1 -1
- package/src/domains/subscription/core/SubscriptionStatus.ts +4 -10
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +1 -1
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +0 -2
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +1 -1
- package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +1 -2
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionQueries.ts +0 -4
- package/src/domains/subscription/infrastructure/services/OfferingsFetcher.ts +1 -1
- package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +1 -1
- package/src/domains/subscription/infrastructure/services/RevenueCatService.ts +1 -2
- package/src/domains/subscription/infrastructure/services/revenueCatServiceInstance.ts +0 -4
- package/src/domains/subscription/infrastructure/utils/renewal/index.ts +1 -1
- package/src/domains/subscription/presentation/components/details/CreditRow.tsx +1 -1
- package/src/domains/subscription/presentation/components/details/DetailRow.tsx +1 -1
- package/src/domains/subscription/presentation/components/feedback/FeedbackOption.tsx +0 -2
- package/src/domains/subscription/presentation/components/feedback/FeedbackTextInput.tsx +1 -1
- package/src/domains/subscription/presentation/featureGateRefs.ts +1 -1
- package/src/domains/subscription/presentation/screens/components/CreditsList.tsx +2 -2
- package/src/domains/subscription/presentation/screens/components/SubscriptionHeader.tsx +0 -2
- package/src/domains/subscription/presentation/stores/index.ts +0 -5
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +3 -7
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +2 -8
- package/src/domains/subscription/presentation/useFeatureGate.ts +0 -2
- package/src/domains/subscription/presentation/usePaywallVisibility.ts +1 -1
- package/src/domains/subscription/utils/authGuards.ts +0 -23
- package/src/domains/subscription/utils/syncStatus.ts +1 -1
- package/src/domains/wallet/index.ts +0 -112
- package/src/domains/wallet/infrastructure/config/walletConfig.ts +1 -23
- package/src/domains/wallet/infrastructure/repositories/transaction/CollectionBuilder.ts +1 -1
- package/src/domains/wallet/infrastructure/repositories/transaction/index.ts +0 -9
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +1 -1
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +0 -2
- package/src/domains/wallet/presentation/components/TransactionList.tsx +3 -2
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +2 -2
- package/src/domains/wallet/presentation/hooks/useWallet.ts +2 -2
- package/src/shared/application/FeedbackService.ts +2 -2
- package/src/shared/infrastructure/SubscriptionEventBus.ts +1 -1
- package/src/shared/infrastructure/firestore/collectionUtils.ts +0 -7
- package/src/shared/infrastructure/firestore/resultUtils.ts +3 -39
- package/src/shared/presentation/layouts/ScreenLayout.tsx +1 -1
- package/src/shared/utils/numberUtils.core.ts +0 -27
- package/src/shared/utils/numberUtils.ts +1 -9
- package/src/shared/utils/validators.ts +0 -58
- package/src/domains/config/domain/entities/Plan.ts +0 -44
- package/src/domains/config/domain/index.ts +0 -2
- package/src/domains/config/domain/value-objects/Config.ts +0 -49
- package/src/domains/config/index.ts +0 -6
- package/src/domains/config/utils/planSelectors.ts +0 -56
- package/src/domains/paywall/components/FeatureItem.tsx +0 -50
- package/src/domains/paywall/components/FeatureList.tsx +0 -34
- package/src/domains/paywall/components/PaywallHeader.tsx +0 -112
- package/src/domains/paywall/hooks/usePaywallTranslations.ts +0 -78
- package/src/domains/paywall/hooks/useSubscriptionModal.ts +0 -45
- package/src/domains/paywall/index.ts +0 -13
- package/src/domains/revenuecat/core/constants/RevenueCatConstants.ts +0 -201
- package/src/domains/revenuecat/core/constants/index.ts +0 -5
- package/src/domains/revenuecat/core/customerInfoHelpers.ts +0 -21
- package/src/domains/revenuecat/core/index.ts +0 -7
- package/src/domains/revenuecat/index.ts +0 -7
- package/src/domains/revenuecat/infrastructure/index.ts +0 -5
- package/src/domains/revenuecat/infrastructure/services/index.ts +0 -7
- package/src/domains/subscription/infrastructure/hooks/customer-info/index.ts +0 -2
- package/src/domains/subscription/infrastructure/hooks/customer-info/types.ts +0 -9
- package/src/domains/subscription/infrastructure/hooks/customer-info/useCustomerInfo.ts +0 -52
- package/src/domains/subscription/infrastructure/hooks/useInitializeSubscription.ts +0 -30
- package/src/domains/subscription/infrastructure/hooks/usePaywallFlow.ts +0 -78
- package/src/domains/subscription/infrastructure/hooks/useRevenueCat.ts +0 -119
- package/src/domains/subscription/presentation/components/feedback/FeedbackConstants.ts +0 -6
- package/src/domains/subscription/presentation/screens/components/SubscriptionActions.tsx +0 -56
- package/src/domains/subscription/utils/dateFormatters.ts +0 -28
- package/src/domains/wallet/domain/entities/CreditCost.ts +0 -45
- package/src/domains/wallet/domain/entities/README.md +0 -41
- package/src/domains/wallet/domain/errors/README.md +0 -40
- package/src/domains/wallet/domain/errors/WalletError.ts +0 -17
- package/src/domains/wallet/domain/errors/WalletError.types.ts +0 -30
- package/src/domains/wallet/domain/errors/WalletErrorClasses.ts +0 -82
- package/src/domains/wallet/domain/errors/WalletErrorFactory.ts +0 -24
- package/src/domains/wallet/domain/errors/WalletErrorMessages.ts +0 -17
- package/src/domains/wallet/domain/types/credit-cost.types.ts +0 -86
- package/src/domains/wallet/domain/types/index.ts +0 -33
- package/src/domains/wallet/domain/types/wallet.types.ts +0 -50
- package/src/domains/wallet/infrastructure/services/product-metadata/CacheManager.ts +0 -30
- package/src/domains/wallet/infrastructure/services/product-metadata/FirebaseFetcher.ts +0 -17
- package/src/domains/wallet/infrastructure/services/product-metadata/ProductMetadataService.ts +0 -57
- package/src/domains/wallet/infrastructure/services/product-metadata/ServiceManager.ts +0 -29
- package/src/domains/wallet/infrastructure/services/product-metadata/index.ts +0 -7
- package/src/domains/wallet/presentation/hooks/index.ts +0 -8
- package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +0 -72
- package/src/domains/wallet/utils/index.ts +0 -1
- package/src/shared/application/ActivationHandler.ts +0 -108
- package/src/shared/application/ports/ISubscriptionService.ts +0 -27
- package/src/shared/infrastructure/index.ts +0 -6
- package/src/shared/infrastructure/react-query/queryInvalidation.ts +0 -46
- package/src/shared/presentation/hooks/index.ts +0 -6
- package/src/shared/presentation/hooks/useAsyncState.ts +0 -72
- package/src/shared/presentation/hooks/useServiceCall.ts +0 -77
- package/src/shared/types/ReactTypes.ts +0 -80
- package/src/shared/utils/InsufficientCreditsError.ts +0 -32
- package/src/shared/utils/Logger.ts +0 -81
- package/src/shared/utils/SubscriptionConfig.ts +0 -15
- package/src/shared/utils/SubscriptionError.ts +0 -47
- package/src/shared/utils/appValidators.ts +0 -38
- package/src/shared/utils/arrayUtils.core.ts +0 -81
- package/src/shared/utils/arrayUtils.query.ts +0 -118
- package/src/shared/utils/arrayUtils.transforms.ts +0 -116
- package/src/shared/utils/arrayUtils.ts +0 -19
- package/src/shared/utils/errorUtils.ts +0 -32
- package/src/shared/utils/index.ts +0 -14
- package/src/shared/utils/numberUtils.aggregate.ts +0 -35
- package/src/shared/utils/numberUtils.format.ts +0 -42
- package/src/shared/utils/numberUtils.math.ts +0 -48
- package/src/shared/utils/queryKeyFactory.ts +0 -9
- package/src/shared/utils/stringUtils.case.ts +0 -64
- package/src/shared/utils/stringUtils.check.ts +0 -65
- package/src/shared/utils/stringUtils.format.ts +0 -84
- package/src/shared/utils/stringUtils.generate.ts +0 -47
- package/src/shared/utils/stringUtils.modify.ts +0 -67
- package/src/shared/utils/stringUtils.ts +0 -10
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Initialize Subscription Hook
|
|
3
|
-
* TanStack mutation for initializing RevenueCat
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useMutation, useQueryClient } from "@umituz/react-native-design-system";
|
|
7
|
-
import { SubscriptionManager } from '../../infrastructure/managers/SubscriptionManager';
|
|
8
|
-
import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
|
|
9
|
-
import { requireAuthentication } from "../../utils/authGuards";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Initialize subscription with RevenueCat
|
|
13
|
-
*/
|
|
14
|
-
export const useInitializeSubscription = (userId: string | undefined) => {
|
|
15
|
-
const queryClient = useQueryClient();
|
|
16
|
-
|
|
17
|
-
return useMutation({
|
|
18
|
-
mutationFn: async () => {
|
|
19
|
-
requireAuthentication(userId);
|
|
20
|
-
return SubscriptionManager.initialize(userId);
|
|
21
|
-
},
|
|
22
|
-
onSuccess: () => {
|
|
23
|
-
if (userId) {
|
|
24
|
-
queryClient.invalidateQueries({
|
|
25
|
-
queryKey: SUBSCRIPTION_QUERY_KEYS.packages,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paywall Flow Hook
|
|
3
|
-
* Manages post-onboarding paywall state with persistence
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
7
|
-
import { useStorage } from '@umituz/react-native-design-system';
|
|
8
|
-
|
|
9
|
-
const PAYWALL_SHOWN_KEY = 'post_onboarding_paywall_shown';
|
|
10
|
-
|
|
11
|
-
export interface UsePaywallFlowOptions {
|
|
12
|
-
showAfterOnboarding?: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface UsePaywallFlowResult {
|
|
16
|
-
showPostOnboardingPaywall: boolean;
|
|
17
|
-
paywallShown: boolean;
|
|
18
|
-
closePostOnboardingPaywall: (isPremium: boolean) => Promise<void>;
|
|
19
|
-
hidePostOnboardingPaywall: () => void;
|
|
20
|
-
markPaywallShown: () => Promise<void>;
|
|
21
|
-
setShowPostOnboardingPaywall: (show: boolean) => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const usePaywallFlow = (options: UsePaywallFlowOptions = {}): UsePaywallFlowResult => {
|
|
25
|
-
const { showAfterOnboarding = false } = options;
|
|
26
|
-
const { getString, setString } = useStorage();
|
|
27
|
-
const [showPostOnboardingPaywall, setShowPostOnboardingPaywall] = useState(showAfterOnboarding);
|
|
28
|
-
const [paywallShown, setPaywallShown] = useState(false);
|
|
29
|
-
|
|
30
|
-
// Load persisted state
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
let isMounted = true;
|
|
33
|
-
|
|
34
|
-
const loadPersistedState = async () => {
|
|
35
|
-
try {
|
|
36
|
-
const value = await getString(PAYWALL_SHOWN_KEY, '');
|
|
37
|
-
if (isMounted) {
|
|
38
|
-
setPaywallShown(value === 'true');
|
|
39
|
-
}
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error('[usePaywallFlow] Failed to load paywall state', error);
|
|
42
|
-
if (isMounted) {
|
|
43
|
-
setPaywallShown(false); // Safe default
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
loadPersistedState();
|
|
49
|
-
|
|
50
|
-
return () => {
|
|
51
|
-
isMounted = false;
|
|
52
|
-
};
|
|
53
|
-
}, [getString]);
|
|
54
|
-
|
|
55
|
-
const closePostOnboardingPaywall = useCallback(async (_isPremium: boolean) => {
|
|
56
|
-
await setString(PAYWALL_SHOWN_KEY, 'true');
|
|
57
|
-
setShowPostOnboardingPaywall(false);
|
|
58
|
-
setPaywallShown(true);
|
|
59
|
-
}, [setString]);
|
|
60
|
-
|
|
61
|
-
const hidePostOnboardingPaywall = useCallback(() => {
|
|
62
|
-
setShowPostOnboardingPaywall(false);
|
|
63
|
-
}, []);
|
|
64
|
-
|
|
65
|
-
const markPaywallShown = useCallback(async () => {
|
|
66
|
-
await setString(PAYWALL_SHOWN_KEY, 'true');
|
|
67
|
-
setPaywallShown(true);
|
|
68
|
-
}, [setString]);
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
showPostOnboardingPaywall,
|
|
72
|
-
paywallShown,
|
|
73
|
-
closePostOnboardingPaywall,
|
|
74
|
-
hidePostOnboardingPaywall,
|
|
75
|
-
markPaywallShown,
|
|
76
|
-
setShowPostOnboardingPaywall,
|
|
77
|
-
};
|
|
78
|
-
};
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useRevenueCat Hook
|
|
3
|
-
* React hook for RevenueCat subscription management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useState, useCallback, useEffect, useRef } from "react";
|
|
7
|
-
import type { PurchasesOffering, PurchasesPackage } from "react-native-purchases";
|
|
8
|
-
import { getRevenueCatService } from '../../infrastructure/services/RevenueCatService';
|
|
9
|
-
import type { PurchaseResult, RestoreResult } from '../../../../shared/application/ports/IRevenueCatService';
|
|
10
|
-
|
|
11
|
-
export interface UseRevenueCatResult {
|
|
12
|
-
/** Current offering */
|
|
13
|
-
offering: PurchasesOffering | null;
|
|
14
|
-
/** Whether RevenueCat is loading */
|
|
15
|
-
loading: boolean;
|
|
16
|
-
/** Initialize RevenueCat SDK */
|
|
17
|
-
initialize: (userId: string, apiKey?: string) => Promise<void>;
|
|
18
|
-
/** Load offerings */
|
|
19
|
-
loadOfferings: () => Promise<void>;
|
|
20
|
-
/** Purchase a package */
|
|
21
|
-
purchasePackage: (pkg: PurchasesPackage, userId: string) => Promise<PurchaseResult>;
|
|
22
|
-
/** Restore purchases */
|
|
23
|
-
restorePurchases: (userId: string) => Promise<RestoreResult>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Hook for RevenueCat operations
|
|
28
|
-
* Only initialize when subscription screen is opened
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* const { offering, loading, initialize, purchasePackage } = useRevenueCat();
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export function useRevenueCat(): UseRevenueCatResult {
|
|
36
|
-
const [offering, setOffering] = useState<PurchasesOffering | null>(null);
|
|
37
|
-
const [loading, setLoading] = useState(false);
|
|
38
|
-
const isMountedRef = useRef(true);
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
isMountedRef.current = true;
|
|
42
|
-
return () => {
|
|
43
|
-
isMountedRef.current = false;
|
|
44
|
-
};
|
|
45
|
-
}, []);
|
|
46
|
-
|
|
47
|
-
const initialize = useCallback(async (userId: string, apiKey?: string) => {
|
|
48
|
-
if (!isMountedRef.current) return;
|
|
49
|
-
|
|
50
|
-
setLoading(true);
|
|
51
|
-
try {
|
|
52
|
-
const service = getRevenueCatService();
|
|
53
|
-
if (!service) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const result = await service.initialize(userId, apiKey);
|
|
57
|
-
if (result.success && isMountedRef.current) {
|
|
58
|
-
setOffering(result.offering);
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
61
|
-
// Error handling is done by service
|
|
62
|
-
} finally {
|
|
63
|
-
if (isMountedRef.current) {
|
|
64
|
-
setLoading(false);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
const loadOfferings = useCallback(async () => {
|
|
70
|
-
if (!isMountedRef.current) return;
|
|
71
|
-
|
|
72
|
-
setLoading(true);
|
|
73
|
-
try {
|
|
74
|
-
const service = getRevenueCatService();
|
|
75
|
-
if (!service) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const fetchedOffering = await service.fetchOfferings();
|
|
79
|
-
if (isMountedRef.current) {
|
|
80
|
-
setOffering(fetchedOffering);
|
|
81
|
-
}
|
|
82
|
-
} catch {
|
|
83
|
-
// Error handling is done by service
|
|
84
|
-
} finally {
|
|
85
|
-
if (isMountedRef.current) {
|
|
86
|
-
setLoading(false);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}, []);
|
|
90
|
-
|
|
91
|
-
const purchasePackage = useCallback(async (pkg: PurchasesPackage, userId: string) => {
|
|
92
|
-
const service = getRevenueCatService();
|
|
93
|
-
if (!service) {
|
|
94
|
-
throw new Error("RevenueCat service is not initialized");
|
|
95
|
-
}
|
|
96
|
-
return await service.purchasePackage(pkg, userId);
|
|
97
|
-
}, []);
|
|
98
|
-
|
|
99
|
-
const restorePurchases = useCallback(async (userId: string) => {
|
|
100
|
-
const service = getRevenueCatService();
|
|
101
|
-
if (!service) {
|
|
102
|
-
throw new Error("RevenueCat service is not initialized");
|
|
103
|
-
}
|
|
104
|
-
return await service.restorePurchases(userId);
|
|
105
|
-
}, []);
|
|
106
|
-
|
|
107
|
-
// Note: State cleanup is handled by React automatically on unmount
|
|
108
|
-
// No explicit cleanup needed for these state variables
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
offering,
|
|
112
|
-
loading,
|
|
113
|
-
initialize,
|
|
114
|
-
loadOfferings,
|
|
115
|
-
purchasePackage,
|
|
116
|
-
restorePurchases,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subscription Actions Component
|
|
3
|
-
* Displays upgrade button for non-premium users
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useMemo } from "react";
|
|
7
|
-
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
8
|
-
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
10
|
-
export interface SubscriptionActionsProps {
|
|
11
|
-
isPremium: boolean;
|
|
12
|
-
upgradeButtonLabel?: string;
|
|
13
|
-
onUpgrade?: () => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const SubscriptionActions: React.FC<SubscriptionActionsProps> = ({
|
|
17
|
-
isPremium,
|
|
18
|
-
upgradeButtonLabel,
|
|
19
|
-
onUpgrade,
|
|
20
|
-
}) => {
|
|
21
|
-
const tokens = useAppDesignTokens();
|
|
22
|
-
|
|
23
|
-
const styles = useMemo(
|
|
24
|
-
() =>
|
|
25
|
-
StyleSheet.create({
|
|
26
|
-
container: {
|
|
27
|
-
paddingBottom: tokens.spacing.xl,
|
|
28
|
-
},
|
|
29
|
-
primaryButton: {
|
|
30
|
-
paddingVertical: tokens.spacing.md,
|
|
31
|
-
borderRadius: tokens.radius.lg,
|
|
32
|
-
alignItems: "center",
|
|
33
|
-
backgroundColor: tokens.colors.primary,
|
|
34
|
-
},
|
|
35
|
-
buttonText: {
|
|
36
|
-
color: tokens.colors.onPrimary,
|
|
37
|
-
fontWeight: "700",
|
|
38
|
-
},
|
|
39
|
-
}),
|
|
40
|
-
[tokens]
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
if (isPremium || !onUpgrade || !upgradeButtonLabel) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<View style={styles.container}>
|
|
49
|
-
<TouchableOpacity style={styles.primaryButton} onPress={onUpgrade}>
|
|
50
|
-
<AtomicText type="titleMedium" style={styles.buttonText}>
|
|
51
|
-
{upgradeButtonLabel}
|
|
52
|
-
</AtomicText>
|
|
53
|
-
</TouchableOpacity>
|
|
54
|
-
</View>
|
|
55
|
-
);
|
|
56
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { timezoneService } from "@umituz/react-native-design-system";
|
|
2
|
-
|
|
3
|
-
export function convertFirestoreTimestampToISO(timestamp: unknown): string | null {
|
|
4
|
-
if (!timestamp) return null;
|
|
5
|
-
|
|
6
|
-
let date: Date;
|
|
7
|
-
if (
|
|
8
|
-
typeof timestamp === "object" &&
|
|
9
|
-
timestamp !== null &&
|
|
10
|
-
"toDate" in timestamp
|
|
11
|
-
) {
|
|
12
|
-
date = (timestamp as { toDate: () => Date }).toDate();
|
|
13
|
-
} else if (timestamp instanceof Date) {
|
|
14
|
-
date = timestamp;
|
|
15
|
-
} else {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return timezoneService.formatToISOString(date);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function formatDateForDisplay(dateStr: string | null): string | null {
|
|
23
|
-
if (!dateStr) return null;
|
|
24
|
-
const date = new Date(dateStr);
|
|
25
|
-
if (isNaN(date.getTime())) return null;
|
|
26
|
-
|
|
27
|
-
return timezoneService.formatToDisplayDate(date);
|
|
28
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credit Cost Entity
|
|
3
|
-
*
|
|
4
|
-
* Configurable credit cost configuration for AI operations.
|
|
5
|
-
* Apps can override default costs based on their pricing model.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { AICreditCosts } from "../types/credit-cost.types";
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_AI_CREDIT_COSTS,
|
|
11
|
-
createCreditCostConfig,
|
|
12
|
-
} from "../types/credit-cost.types";
|
|
13
|
-
|
|
14
|
-
export interface CreditCostEntity {
|
|
15
|
-
costs: AICreditCosts;
|
|
16
|
-
pricePerCredit: number;
|
|
17
|
-
getCost: (operation: keyof AICreditCosts) => number;
|
|
18
|
-
getDollarValue: (operation: keyof AICreditCosts) => number;
|
|
19
|
-
getCustomCost: (operation: string) => number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function createCreditCostEntity(
|
|
23
|
-
overrides?: Partial<AICreditCosts>,
|
|
24
|
-
pricePerCredit: number = 0.1
|
|
25
|
-
): CreditCostEntity {
|
|
26
|
-
const costs = createCreditCostConfig(overrides);
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
costs,
|
|
30
|
-
pricePerCredit,
|
|
31
|
-
getCost: (operation: keyof AICreditCosts): number => {
|
|
32
|
-
return costs[operation];
|
|
33
|
-
},
|
|
34
|
-
getDollarValue: (operation: keyof AICreditCosts): number => {
|
|
35
|
-
return costs[operation] * pricePerCredit;
|
|
36
|
-
},
|
|
37
|
-
getCustomCost: (operation: string): number => {
|
|
38
|
-
const costsRecord = costs as unknown as Record<string, number>;
|
|
39
|
-
return costsRecord[operation] ?? 0;
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export { DEFAULT_AI_CREDIT_COSTS, createCreditCostConfig };
|
|
45
|
-
export type { AICreditCosts };
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# Wallet Domain Entities
|
|
2
|
-
|
|
3
|
-
## Location
|
|
4
|
-
Core entities for credits and transaction management.
|
|
5
|
-
|
|
6
|
-
## Strategy
|
|
7
|
-
This directory contains domain entities representing credits, transactions, and wallet-related business concepts with strict immutability and validation.
|
|
8
|
-
|
|
9
|
-
## Restrictions
|
|
10
|
-
|
|
11
|
-
### REQUIRED
|
|
12
|
-
- Must treat entities as immutable values
|
|
13
|
-
- Must validate in entity methods
|
|
14
|
-
- Must use strict TypeScript types
|
|
15
|
-
- Must keep business logic in entities
|
|
16
|
-
|
|
17
|
-
### PROHIBITED
|
|
18
|
-
- DO NOT mutate entities after creation
|
|
19
|
-
- DO NOT bypass validation methods
|
|
20
|
-
- DO NOT leak entity internals
|
|
21
|
-
- DO NOT allow invalid state
|
|
22
|
-
|
|
23
|
-
### CRITICAL SAFETY
|
|
24
|
-
- All entities MUST be immutable
|
|
25
|
-
- Validation MUST be performed in entity methods
|
|
26
|
-
- Date serialization MUST be handled properly
|
|
27
|
-
- Business rules MUST be enforced in entities
|
|
28
|
-
|
|
29
|
-
## AI Agent Guidelines
|
|
30
|
-
1. Treat all entities as immutable values
|
|
31
|
-
2. Perform validation within entity methods
|
|
32
|
-
3. Use strict TypeScript types for all properties
|
|
33
|
-
4. Keep business logic encapsulated in entities
|
|
34
|
-
5. Handle date serialization properly for transactions
|
|
35
|
-
6. Implement proper equality methods
|
|
36
|
-
7. Provide clear validation error messages
|
|
37
|
-
|
|
38
|
-
## Related Documentation
|
|
39
|
-
- [Wallet Domain](../README.md)
|
|
40
|
-
- [Credits Repository](../../infrastructure/repositories/README.md)
|
|
41
|
-
- [useCredits Hook](../../../../presentation/hooks/useCredits.md)
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Wallet Domain Errors
|
|
2
|
-
|
|
3
|
-
## Location
|
|
4
|
-
Domain-specific errors for wallet and credit operations.
|
|
5
|
-
|
|
6
|
-
## Strategy
|
|
7
|
-
This directory contains custom error classes representing wallet-related error conditions with specific error types and recovery strategies.
|
|
8
|
-
|
|
9
|
-
## Restrictions
|
|
10
|
-
|
|
11
|
-
### REQUIRED
|
|
12
|
-
- Must use specific error types for different scenarios
|
|
13
|
-
- Must include relevant context in error properties
|
|
14
|
-
- Must handle errors gracefully at boundaries
|
|
15
|
-
- Must log errors for debugging
|
|
16
|
-
|
|
17
|
-
### PROHIBITED
|
|
18
|
-
- DO NOT use generic error types
|
|
19
|
-
- DO NOT throw errors without context
|
|
20
|
-
- DO NOT ignore error conditions
|
|
21
|
-
- DO NOT expose technical errors to users
|
|
22
|
-
|
|
23
|
-
### CRITICAL SAFETY
|
|
24
|
-
- All errors MUST be handled appropriately
|
|
25
|
-
- Error context MUST be sufficient for recovery
|
|
26
|
-
- Technical errors MUST be converted to user-friendly messages
|
|
27
|
-
- Error codes MUST be actionable
|
|
28
|
-
|
|
29
|
-
## AI Agent Guidelines
|
|
30
|
-
1. Use specific error types for different scenarios (CreditsExhaustedError, DuplicatePurchaseError, etc.)
|
|
31
|
-
2. Include relevant data in error properties for recovery
|
|
32
|
-
3. Handle errors gracefully at layer boundaries
|
|
33
|
-
4. Log all errors with sufficient context for debugging
|
|
34
|
-
5. Convert technical errors to user-friendly messages
|
|
35
|
-
6. Follow error code reference for recovery strategies
|
|
36
|
-
7. Test error handling paths thoroughly
|
|
37
|
-
|
|
38
|
-
## Related Documentation
|
|
39
|
-
- [Wallet Entities](../entities/README.md)
|
|
40
|
-
- [Credits Repository](../../infrastructure/repositories/README.md)
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export type { WalletErrorCategory } from "./WalletError.types";
|
|
2
|
-
export { WalletError } from "./WalletError.types";
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
PaymentValidationError,
|
|
6
|
-
PaymentProviderError,
|
|
7
|
-
DuplicatePaymentError,
|
|
8
|
-
UserValidationError,
|
|
9
|
-
PackageValidationError,
|
|
10
|
-
ReceiptValidationError,
|
|
11
|
-
TransactionError,
|
|
12
|
-
NetworkError,
|
|
13
|
-
CreditLimitError,
|
|
14
|
-
RefundError,
|
|
15
|
-
} from "./WalletErrorClasses";
|
|
16
|
-
|
|
17
|
-
export { handleWalletError } from "./WalletErrorFactory";
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export type WalletErrorCategory =
|
|
2
|
-
| "PAYMENT"
|
|
3
|
-
| "VALIDATION"
|
|
4
|
-
| "INFRASTRUCTURE"
|
|
5
|
-
| "BUSINESS";
|
|
6
|
-
|
|
7
|
-
export abstract class WalletError extends Error {
|
|
8
|
-
abstract readonly code: string;
|
|
9
|
-
abstract readonly userMessage: string;
|
|
10
|
-
abstract readonly category: WalletErrorCategory;
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
message: string,
|
|
14
|
-
public readonly cause?: Error
|
|
15
|
-
) {
|
|
16
|
-
super(message);
|
|
17
|
-
this.name = this.constructor.name;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
toJSON() {
|
|
21
|
-
return {
|
|
22
|
-
name: this.name,
|
|
23
|
-
code: this.code,
|
|
24
|
-
userMessage: this.userMessage,
|
|
25
|
-
category: this.category,
|
|
26
|
-
message: this.message,
|
|
27
|
-
cause: this.cause?.message,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { WalletError } from "./WalletError.types";
|
|
2
|
-
import { WALLET_ERROR_MESSAGES } from "./WalletErrorMessages";
|
|
3
|
-
|
|
4
|
-
export class PaymentValidationError extends WalletError {
|
|
5
|
-
readonly code = "PAYMENT_VALIDATION_ERROR";
|
|
6
|
-
readonly category = "PAYMENT" as const;
|
|
7
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.PAYMENT_VALIDATION_FAILED;
|
|
8
|
-
|
|
9
|
-
constructor(message: string, cause?: Error) {
|
|
10
|
-
super(message, cause);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class PaymentProviderError extends WalletError {
|
|
15
|
-
readonly code = "PAYMENT_PROVIDER_ERROR";
|
|
16
|
-
readonly category = "PAYMENT" as const;
|
|
17
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.PAYMENT_PROVIDER_ERROR;
|
|
18
|
-
|
|
19
|
-
constructor(message: string, cause?: Error) {
|
|
20
|
-
super(message, cause);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class DuplicatePaymentError extends WalletError {
|
|
25
|
-
readonly code = "DUPLICATE_PAYMENT";
|
|
26
|
-
readonly category = "PAYMENT" as const;
|
|
27
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.DUPLICATE_PAYMENT;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class UserValidationError extends WalletError {
|
|
31
|
-
readonly code = "USER_VALIDATION_ERROR";
|
|
32
|
-
readonly category = "VALIDATION" as const;
|
|
33
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.USER_VALIDATION_FAILED;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class PackageValidationError extends WalletError {
|
|
37
|
-
readonly code = "PACKAGE_VALIDATION_ERROR";
|
|
38
|
-
readonly category = "VALIDATION" as const;
|
|
39
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.PACKAGE_VALIDATION_FAILED;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export class ReceiptValidationError extends WalletError {
|
|
43
|
-
readonly code = "RECEIPT_VALIDATION_ERROR";
|
|
44
|
-
readonly category = "VALIDATION" as const;
|
|
45
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.RECEIPT_VALIDATION_FAILED;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class TransactionError extends WalletError {
|
|
49
|
-
readonly code = "TRANSACTION_ERROR";
|
|
50
|
-
readonly category = "INFRASTRUCTURE" as const;
|
|
51
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.TRANSACTION_FAILED;
|
|
52
|
-
|
|
53
|
-
constructor(message: string, cause?: Error) {
|
|
54
|
-
super(message, cause);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export class NetworkError extends WalletError {
|
|
59
|
-
readonly code = "NETWORK_ERROR";
|
|
60
|
-
readonly category = "INFRASTRUCTURE" as const;
|
|
61
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.NETWORK_ERROR;
|
|
62
|
-
|
|
63
|
-
constructor(message: string, cause?: Error) {
|
|
64
|
-
super(message, cause);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export class CreditLimitError extends WalletError {
|
|
69
|
-
readonly code = "CREDIT_LIMIT_ERROR";
|
|
70
|
-
readonly category = "BUSINESS" as const;
|
|
71
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.CREDIT_LIMIT_EXCEEDED;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class RefundError extends WalletError {
|
|
75
|
-
readonly code = "REFUND_ERROR";
|
|
76
|
-
readonly category = "BUSINESS" as const;
|
|
77
|
-
readonly userMessage = WALLET_ERROR_MESSAGES.REFUND_FAILED;
|
|
78
|
-
|
|
79
|
-
constructor(message: string, cause?: Error) {
|
|
80
|
-
super(message, cause);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { WalletError } from "./WalletError.types";
|
|
2
|
-
import { NetworkError, UserValidationError, TransactionError } from "./WalletErrorClasses";
|
|
3
|
-
|
|
4
|
-
export const handleWalletError = (error: unknown): WalletError => {
|
|
5
|
-
if (error instanceof WalletError) {
|
|
6
|
-
return error;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (error instanceof Error) {
|
|
10
|
-
const message = error.message.toLowerCase();
|
|
11
|
-
|
|
12
|
-
if (message.includes("network") || message.includes("timeout")) {
|
|
13
|
-
return new NetworkError(error.message, error);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (message.includes("permission") || message.includes("unauthorized")) {
|
|
17
|
-
return new UserValidationError("Authentication failed");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return new TransactionError(error.message, error);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return new TransactionError("Unexpected error occurred");
|
|
24
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wallet Error Messages
|
|
3
|
-
* Centralized error user messages for wallet operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const WALLET_ERROR_MESSAGES = {
|
|
7
|
-
PAYMENT_VALIDATION_FAILED: "Payment validation failed. Please try again.",
|
|
8
|
-
PAYMENT_PROVIDER_ERROR: "Payment provider error. Please try again.",
|
|
9
|
-
DUPLICATE_PAYMENT: "This payment has already been processed.",
|
|
10
|
-
USER_VALIDATION_FAILED: "Invalid user information. Please log in again.",
|
|
11
|
-
PACKAGE_VALIDATION_FAILED: "Invalid credit package. Please select a valid package.",
|
|
12
|
-
RECEIPT_VALIDATION_FAILED: "Invalid payment receipt. Please contact support.",
|
|
13
|
-
TRANSACTION_FAILED: "Transaction failed. Please try again.",
|
|
14
|
-
NETWORK_ERROR: "Network error. Please check your connection.",
|
|
15
|
-
CREDIT_LIMIT_EXCEEDED: "Credit limit exceeded. Please contact support.",
|
|
16
|
-
REFUND_FAILED: "Refund failed. Please contact support.",
|
|
17
|
-
} as const;
|