@umituz/react-native-subscription 2.37.39 → 2.37.40

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 (131) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/application/CreditLimitCalculator.ts +1 -9
  3. package/src/domains/credits/application/DeductCreditsCommand.ts +13 -6
  4. package/src/domains/credits/application/RefundCreditsCommand.ts +1 -5
  5. package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +1 -5
  6. package/src/domains/credits/application/creditDocumentHelpers.ts +2 -9
  7. package/src/domains/credits/core/Credits.ts +0 -23
  8. package/src/domains/credits/core/CreditsMapper.ts +0 -6
  9. package/src/domains/credits/core/UserCreditsDocument.ts +0 -12
  10. package/src/domains/credits/infrastructure/CreditsRepositoryManager.ts +0 -21
  11. package/src/domains/credits/infrastructure/operations/CreditsWriter.ts +2 -1
  12. package/src/domains/credits/presentation/deduct-credit/useDeductCredit.ts +2 -2
  13. package/src/domains/credits/presentation/useCredits.ts +10 -9
  14. package/src/domains/paywall/components/PaywallContainer.types.ts +0 -28
  15. package/src/domains/paywall/components/PaywallModal.styles.ts +0 -4
  16. package/src/domains/paywall/entities/types.ts +0 -5
  17. package/src/domains/paywall/hooks/usePaywallActions.ts +1 -15
  18. package/src/domains/revenuecat/core/errors/RevenueCatError.ts +0 -6
  19. package/src/domains/revenuecat/core/errors/RevenueCatErrorHandler.ts +0 -24
  20. package/src/domains/revenuecat/core/errors/RevenueCatErrorMessages.ts +0 -18
  21. package/src/domains/revenuecat/core/errors/index.ts +0 -4
  22. package/src/domains/revenuecat/core/types/RevenueCatConfig.ts +3 -7
  23. package/src/domains/revenuecat/core/types/RevenueCatData.ts +4 -9
  24. package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +5 -65
  25. package/src/domains/revenuecat/core/types/index.ts +0 -4
  26. package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +1 -24
  27. package/src/domains/subscription/application/SubscriptionAuthListener.ts +5 -21
  28. package/src/domains/subscription/application/SubscriptionInitializerTypes.ts +1 -5
  29. package/src/domains/subscription/application/SubscriptionSyncService.ts +11 -2
  30. package/src/domains/subscription/application/SubscriptionSyncUtils.ts +1 -1
  31. package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +15 -2
  32. package/src/domains/subscription/application/initializer/ServiceConfigurator.ts +9 -2
  33. package/src/domains/subscription/constants/thresholds.ts +0 -9
  34. package/src/domains/subscription/core/SubscriptionConstants.ts +0 -4
  35. package/src/domains/subscription/core/SubscriptionStatus.ts +11 -21
  36. package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +4 -7
  37. package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +1 -1
  38. package/src/domains/subscription/infrastructure/hooks/subscriptionQueryKeys.ts +0 -13
  39. package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +0 -18
  40. package/src/domains/subscription/infrastructure/hooks/useRestorePurchase.ts +3 -17
  41. package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +0 -17
  42. package/src/domains/subscription/infrastructure/hooks/useSubscriptionPackages.ts +0 -19
  43. package/src/domains/subscription/infrastructure/hooks/useSubscriptionQueries.ts +0 -6
  44. package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +0 -17
  45. package/src/domains/subscription/infrastructure/state/initializationState.ts +0 -25
  46. package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +0 -21
  47. package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +2 -7
  48. package/src/domains/subscription/infrastructure/utils/authPurchaseState.ts +0 -5
  49. package/src/domains/subscription/infrastructure/utils/renewal/PackageTierComparator.ts +1 -0
  50. package/src/domains/subscription/infrastructure/utils/trialEligibilityUtils.ts +0 -18
  51. package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.styles.ts +0 -5
  52. package/src/domains/subscription/presentation/components/details/PremiumDetailsCardTypes.ts +0 -5
  53. package/src/domains/subscription/presentation/components/feedback/paywallFeedbackStyles.ts +0 -5
  54. package/src/domains/subscription/presentation/stores/index.ts +0 -4
  55. package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +0 -13
  56. package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +30 -21
  57. package/src/domains/subscription/presentation/usePaywallVisibility.ts +0 -9
  58. package/src/domains/subscription/presentation/useSubscriptionStatus.ts +8 -11
  59. package/src/domains/subscription/utils/authGuards.ts +3 -0
  60. package/src/domains/trial/application/TrialService.ts +0 -9
  61. package/src/domains/trial/core/TrialTypes.ts +0 -8
  62. package/src/domains/wallet/domain/mappers/TransactionMapper.ts +0 -5
  63. package/src/domains/wallet/domain/types/transaction.types.ts +0 -7
  64. package/src/domains/wallet/index.ts +0 -7
  65. package/src/domains/wallet/infrastructure/config/walletConfig.ts +0 -11
  66. package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +6 -3
  67. package/src/domains/wallet/presentation/hooks/useWallet.ts +0 -7
  68. package/src/domains/wallet/utils/transactionIconMap.ts +0 -10
  69. package/src/global.d.ts +0 -6
  70. package/src/index.ts +1 -4
  71. package/src/init/createSubscriptionInitModule.ts +12 -2
  72. package/src/init/index.ts +1 -5
  73. package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +0 -11
  74. package/src/shared/application/FeedbackService.ts +3 -21
  75. package/src/shared/application/ports/ISubscriptionRepository.ts +0 -4
  76. package/src/shared/infrastructure/SubscriptionEventBus.ts +0 -13
  77. package/src/shared/infrastructure/firestore/collectionUtils.ts +1 -17
  78. package/src/shared/infrastructure/firestore/index.ts +0 -4
  79. package/src/shared/infrastructure/firestore/resultUtils.ts +0 -12
  80. package/src/shared/infrastructure/react-query/hooks/usePreviousUserCleanup.ts +0 -17
  81. package/src/shared/infrastructure/react-query/queryConfig.ts +0 -15
  82. package/src/shared/utils/BaseError.ts +0 -5
  83. package/src/shared/utils/Result.ts +0 -20
  84. package/src/shared/utils/dateConverter.ts +6 -46
  85. package/src/utils/appUtils.ts +0 -16
  86. package/src/utils/creditMapper.ts +0 -7
  87. package/src/utils/dateUtils.compare.ts +0 -24
  88. package/src/utils/dateUtils.core.ts +0 -39
  89. package/src/utils/dateUtils.format.ts +0 -41
  90. package/src/utils/dateUtils.math.ts +0 -41
  91. package/src/utils/dateUtils.ts +0 -5
  92. package/src/utils/packagePeriodUtils.ts +0 -20
  93. package/src/utils/packageTypeDetector.ts +1 -21
  94. package/src/utils/premiumStatusUtils.ts +1 -14
  95. package/src/utils/priceUtils.ts +0 -35
  96. package/src/utils/tierUtils.ts +1 -8
  97. package/src/utils/types.ts +1 -25
  98. package/src/utils/validation.ts +1 -7
  99. package/src/domains/README.md +0 -52
  100. package/src/domains/config/domain/README.md +0 -37
  101. package/src/domains/config/domain/entities/README.md +0 -41
  102. package/src/domains/paywall/README.md +0 -101
  103. package/src/domains/paywall/entities/README.md +0 -40
  104. package/src/domains/paywall/hooks/README.md +0 -41
  105. package/src/domains/subscription/application/syncConstants.ts +0 -1
  106. package/src/domains/subscription/infrastructure/README.md +0 -41
  107. package/src/domains/subscription/infrastructure/config/README.md +0 -49
  108. package/src/domains/subscription/infrastructure/handlers/README.md +0 -41
  109. package/src/domains/subscription/infrastructure/hooks/README.md +0 -50
  110. package/src/domains/subscription/infrastructure/managers/README.md +0 -41
  111. package/src/domains/subscription/infrastructure/services/README.md +0 -42
  112. package/src/domains/subscription/infrastructure/utils/README.md +0 -41
  113. package/src/domains/subscription/presentation/components/README.md +0 -155
  114. package/src/domains/subscription/presentation/components/details/CreditRow.md +0 -92
  115. package/src/domains/subscription/presentation/components/details/DetailRow.md +0 -91
  116. package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.md +0 -93
  117. package/src/domains/subscription/presentation/components/details/PremiumStatusBadge.md +0 -91
  118. package/src/domains/subscription/presentation/components/details/README.md +0 -99
  119. package/src/domains/subscription/presentation/components/feedback/PaywallFeedbackModal.md +0 -90
  120. package/src/domains/subscription/presentation/components/feedback/README.md +0 -99
  121. package/src/domains/subscription/presentation/components/paywall/PaywallModal.md +0 -94
  122. package/src/domains/subscription/presentation/components/paywall/README.md +0 -54
  123. package/src/domains/subscription/presentation/components/sections/README.md +0 -99
  124. package/src/domains/subscription/presentation/components/sections/SubscriptionSection.md +0 -94
  125. package/src/domains/subscription/presentation/utils/README.md +0 -31
  126. package/src/domains/wallet/README.md +0 -51
  127. package/src/domains/wallet/domain/README.md +0 -41
  128. package/src/domains/wallet/infrastructure/README.md +0 -41
  129. package/src/domains/wallet/presentation/components/README.md +0 -41
  130. package/src/domains/wallet/presentation/hooks/README.md +0 -41
  131. package/src/shared/application/ports/README.md +0 -48
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.37.39",
3
+ "version": "2.37.40",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -1,20 +1,14 @@
1
1
  import type { CreditsConfig } from "../core/Credits";
2
2
  import { detectPackageType } from "../../../utils/packageTypeDetector";
3
3
  import { getCreditAllocation } from "../../../utils/creditMapper";
4
- import { NO_SUBSCRIPTION_PRODUCT_ID } from "../../subscription/application/syncConstants";
5
4
 
6
5
  export function calculateCreditLimit(productId: string | undefined, config: CreditsConfig): number {
7
6
  if (!productId) {
8
7
  throw new Error("[CreditLimitCalculator] Cannot calculate credit limit without productId");
9
8
  }
10
9
 
11
- // Free tier users (no subscription) get 0 credits - strict paywall
12
- if (productId === NO_SUBSCRIPTION_PRODUCT_ID) {
13
- return 0;
14
- }
15
-
16
10
  const explicitAmount = config.creditPackageAmounts?.[productId];
17
- if (explicitAmount) return explicitAmount;
11
+ if (explicitAmount !== undefined && explicitAmount !== null) return explicitAmount;
18
12
 
19
13
  const packageType = detectPackageType(productId);
20
14
  const dynamicLimit = getCreditAllocation(packageType, config.packageAllocations);
@@ -25,5 +19,3 @@ export function calculateCreditLimit(productId: string | undefined, config: Cred
25
19
 
26
20
  return dynamicLimit;
27
21
  }
28
-
29
-
@@ -3,10 +3,6 @@ import type { DeductCreditsResult } from "../core/Credits";
3
3
  import { CREDIT_ERROR_CODES } from "../core/CreditsConstants";
4
4
  import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
5
5
 
6
- /**
7
- * Deducts credits from a user's balance.
8
- * Encapsulates the domain rules and transaction logic for credit usage.
9
- */
10
6
  export async function deductCreditsOperation(
11
7
  _db: Firestore,
12
8
  creditsRef: DocumentReference,
@@ -24,6 +20,17 @@ export async function deductCreditsOperation(
24
20
  };
25
21
  }
26
22
 
23
+ if (cost <= 0 || !Number.isFinite(cost)) {
24
+ return {
25
+ success: false,
26
+ remainingCredits: null,
27
+ error: {
28
+ message: 'Cost must be a positive finite number',
29
+ code: 'INVALID_AMOUNT'
30
+ }
31
+ };
32
+ }
33
+
27
34
  try {
28
35
  const remaining = await runTransaction(async (tx: Transaction) => {
29
36
  const docSnap = await tx.get(creditsRef);
@@ -55,8 +62,8 @@ export async function deductCreditsOperation(
55
62
  };
56
63
  } catch (e: unknown) {
57
64
  const message = e instanceof Error ? e.message : String(e);
58
- const code = (message === CREDIT_ERROR_CODES.NO_CREDITS || message === CREDIT_ERROR_CODES.CREDITS_EXHAUSTED)
59
- ? message
65
+ const code = (message === CREDIT_ERROR_CODES.NO_CREDITS || message === CREDIT_ERROR_CODES.CREDITS_EXHAUSTED)
66
+ ? message
60
67
  : CREDIT_ERROR_CODES.DEDUCT_ERR;
61
68
 
62
69
  return {
@@ -3,10 +3,6 @@ import type { DeductCreditsResult } from "../core/Credits";
3
3
  import { CREDIT_ERROR_CODES } from "../core/CreditsConstants";
4
4
  import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
5
5
 
6
- /**
7
- * Refunds credits to a user's balance.
8
- * Used for optimistic billing rollbacks when generation fails due to transient errors.
9
- */
10
6
  export async function refundCreditsOperation(
11
7
  _db: Firestore,
12
8
  creditsRef: DocumentReference,
@@ -24,7 +20,7 @@ export async function refundCreditsOperation(
24
20
  };
25
21
  }
26
22
 
27
- if (amount <= 0) {
23
+ if (amount <= 0 || !Number.isFinite(amount)) {
28
24
  return {
29
25
  success: false,
30
26
  remainingCredits: null,
@@ -2,13 +2,9 @@ import { ICreditStrategy, type CreditAllocationParams } from "./ICreditStrategy"
2
2
  import { SUBSCRIPTION_STATUS } from "../../../subscription/core/SubscriptionConstants";
3
3
  import { TRIAL_CONFIG } from "../../../trial/core/TrialTypes";
4
4
 
5
- /**
6
- * Strategy for Trial and Trial Canceled users.
7
- * Allocates credits based on trial configuration.
8
- */
9
5
  export class TrialCreditStrategy implements ICreditStrategy {
10
6
  canHandle(params: CreditAllocationParams): boolean {
11
- return params.status === SUBSCRIPTION_STATUS.TRIAL ||
7
+ return params.status === SUBSCRIPTION_STATUS.TRIAL ||
12
8
  params.status === SUBSCRIPTION_STATUS.TRIAL_CANCELED;
13
9
  }
14
10
 
@@ -1,14 +1,7 @@
1
- /**
2
- * Credit Document Helpers
3
- * Utilities for getting and creating credit documents
4
- */
5
-
6
1
  import type { UserCreditsDocumentRead } from "../core/UserCreditsDocument";
7
2
  import { serverTimestamp, type DocumentSnapshot } from "@umituz/react-native-firebase";
8
3
  import { SUBSCRIPTION_STATUS, type Platform } from "../../subscription/core/SubscriptionConstants";
9
- /**
10
- * Get existing credit document or create default
11
- */
4
+
12
5
  export function getCreditDocumentOrDefault(
13
6
  creditsDoc: DocumentSnapshot,
14
7
  platform: Platform
@@ -17,7 +10,7 @@ export function getCreditDocumentOrDefault(
17
10
  return creditsDoc.data() as UserCreditsDocumentRead;
18
11
  }
19
12
 
20
- const now = serverTimestamp() as any; // FieldValue for Firestore write
13
+ const now = serverTimestamp() as any;
21
14
 
22
15
  const defaultDocument: UserCreditsDocumentRead = {
23
16
  credits: 0,
@@ -1,12 +1,4 @@
1
- /**
2
- * Credits Domain Entities
3
- *
4
- * Generic credit system types for subscription-based apps.
5
- * Designed to be used across hundreds of apps with configurable limits.
6
- */
7
-
8
1
  import type { SubscriptionPackageType } from "../../../utils/packageTypeDetector";
9
- // Types imported from SubscriptionConstants are used directly in UserCredits interface
10
2
  import type {
11
3
  SubscriptionStatusType,
12
4
  PackageType,
@@ -17,37 +9,25 @@ import type {
17
9
 
18
10
  export type CreditType = "text" | "image";
19
11
 
20
- /** Single Source of Truth for user subscription + credits data */
21
12
  export interface UserCredits {
22
- // Core subscription
23
13
  isPremium: boolean;
24
14
  status: SubscriptionStatusType;
25
-
26
- // Dates
27
15
  purchasedAt: Date | null;
28
16
  expirationDate: Date | null;
29
17
  lastUpdatedAt: Date | null;
30
18
  lastPurchaseAt: Date | null;
31
-
32
- // RevenueCat subscription details
33
19
  willRenew: boolean | null;
34
20
  productId: string | null;
35
21
  packageType: PackageType | null;
36
22
  originalTransactionId: string | null;
37
-
38
- // Trial fields - periodType comes from RevenueCat SDK
39
23
  periodType: string | null;
40
24
  isTrialing: boolean | null;
41
25
  trialStartDate: Date | null;
42
26
  trialEndDate: Date | null;
43
27
  trialCredits: number | null;
44
28
  convertedFromTrial: boolean | null;
45
-
46
- // Credits
47
29
  credits: number;
48
30
  creditLimit: number;
49
-
50
- // Metadata
51
31
  purchaseSource: PurchaseSource | null;
52
32
  purchaseType: PurchaseType | null;
53
33
  platform: Platform;
@@ -66,11 +46,8 @@ export type PackageAllocationMap = Partial<Record<
66
46
  export interface CreditsConfig {
67
47
  collectionName: string;
68
48
  creditLimit: number;
69
- /** When true, stores credits at users/{userId}/credits instead of {collectionName}/{userId} */
70
49
  useUserSubcollection: boolean;
71
- /** Credit amounts per product ID for consumable credit packages */
72
50
  creditPackageAmounts: Record<string, number>;
73
- /** Credit allocations for different subscription types (weekly, monthly, yearly) */
74
51
  packageAllocations: PackageAllocationMap;
75
52
  }
76
53
 
@@ -3,9 +3,6 @@ import { resolveSubscriptionStatus, type SubscriptionStatusType } from "../../su
3
3
  import type { UserCreditsDocumentRead } from "./UserCreditsDocument";
4
4
  import { toSafeDate } from "../../../utils/dateUtils";
5
5
 
6
- /**
7
- * Validate subscription status against expirationDate and periodType
8
- */
9
6
  function validateSubscription(
10
7
  doc: UserCreditsDocumentRead,
11
8
  expirationDate: Date | null,
@@ -28,9 +25,6 @@ function validateSubscription(
28
25
  };
29
26
  }
30
27
 
31
- /**
32
- * Maps Firestore document to domain entity with expiration validation
33
- */
34
28
  export function mapCreditsDocumentToEntity(doc: UserCreditsDocumentRead): UserCredits {
35
29
  const expirationDate = toSafeDate(doc.expirationDate);
36
30
  const periodType = doc.periodType;
@@ -30,41 +30,29 @@ export interface PurchaseMetadata {
30
30
  timestamp: FirestoreTimestamp;
31
31
  }
32
32
 
33
- /** Single Source of Truth for user subscription data */
34
33
  export interface UserCreditsDocumentRead {
35
- // Core subscription status
36
34
  isPremium: boolean;
37
35
  status: SubscriptionStatusType;
38
-
39
- // Dates (all from RevenueCat)
40
36
  purchasedAt: FirestoreTimestamp;
41
37
  expirationDate: FirestoreTimestamp | null;
42
38
  lastUpdatedAt: FirestoreTimestamp;
43
39
  lastPurchaseAt: FirestoreTimestamp | null;
44
40
  canceledAt: FirestoreTimestamp | null;
45
41
  billingIssueDetectedAt: FirestoreTimestamp | null;
46
-
47
- // RevenueCat subscription details
48
42
  willRenew: boolean | null;
49
43
  productId: string | null;
50
44
  packageType: PackageType | null;
51
45
  originalTransactionId: string | null;
52
46
  store: Store | null;
53
47
  ownershipType: OwnershipType | null;
54
-
55
- // Trial fields
56
48
  periodType: string | null;
57
49
  isTrialing: boolean | null;
58
50
  trialStartDate: FirestoreTimestamp | null;
59
51
  trialEndDate: FirestoreTimestamp | null;
60
52
  trialCredits: number | null;
61
53
  convertedFromTrial: boolean | null;
62
-
63
- // Credits
64
54
  credits: number;
65
55
  creditLimit: number;
66
-
67
- // Metadata
68
56
  purchaseSource: PurchaseSource | null;
69
57
  purchaseType: PurchaseType | null;
70
58
  platform: Platform;
@@ -1,9 +1,3 @@
1
- /**
2
- * Credits Repository Manager
3
- * Module-level singleton for credits repository configuration
4
- * Provides a clean, testable approach for repository access
5
- */
6
-
7
1
  import type { CreditsConfig } from "../core/Credits";
8
2
  import type { CreditsRepository } from "./CreditsRepository";
9
3
  import { createCreditsRepository } from "./CreditsRepository";
@@ -11,26 +5,15 @@ import { createCreditsRepository } from "./CreditsRepository";
11
5
  let globalRepository: CreditsRepository | null = null;
12
6
  let globalConfig: CreditsConfig | null = null;
13
7
 
14
- /**
15
- * Configure credits repository for the application
16
- * Must be called once during app initialization
17
- */
18
8
  export function configureCreditsRepository(config: CreditsConfig): void {
19
9
  globalConfig = config;
20
10
  globalRepository = createCreditsRepository(globalConfig);
21
11
  }
22
12
 
23
- /**
24
- * Check if credits repository is configured
25
- */
26
13
  export function isCreditsRepositoryConfigured(): boolean {
27
14
  return globalRepository !== null;
28
15
  }
29
16
 
30
- /**
31
- * Get the configured credits repository
32
- * Throws if repository not configured
33
- */
34
17
  export function getCreditsRepository(): CreditsRepository {
35
18
  if (!globalRepository) {
36
19
  throw new Error(
@@ -40,9 +23,6 @@ export function getCreditsRepository(): CreditsRepository {
40
23
  return globalRepository;
41
24
  }
42
25
 
43
- /**
44
- * Get the current credits configuration
45
- */
46
26
  export function getCreditsConfig(): CreditsConfig {
47
27
  if (!globalConfig) {
48
28
  throw new Error(
@@ -51,4 +31,3 @@ export function getCreditsConfig(): CreditsConfig {
51
31
  }
52
32
  return globalConfig;
53
33
  }
54
-
@@ -4,6 +4,7 @@ import { serverTimestamp } from "@umituz/react-native-firebase";
4
4
  import { SUBSCRIPTION_STATUS } from "../../../subscription/core/SubscriptionConstants";
5
5
  import { resolveSubscriptionStatus } from "../../../subscription/core/SubscriptionStatus";
6
6
  import { toTimestamp } from "../../../../shared/utils/dateConverter";
7
+ import { isPast } from "../../../../utils/dateUtils";
7
8
 
8
9
  export async function syncExpiredStatus(ref: DocumentReference): Promise<void> {
9
10
  const doc = await getDoc(ref);
@@ -36,7 +37,7 @@ export async function syncPremiumMetadata(
36
37
  const doc = await getDoc(ref);
37
38
  if (!doc.exists()) return;
38
39
 
39
- const isExpired = false;
40
+ const isExpired = metadata.expirationDate ? isPast(metadata.expirationDate) : false;
40
41
  const status = resolveSubscriptionStatus({
41
42
  isPremium: metadata.isPremium,
42
43
  willRenew: metadata.willRenew,
@@ -35,9 +35,9 @@ export const useDeductCredit = ({
35
35
  console.error('[useDeductCredit] Unexpected error during credit deduction', {
36
36
  cost,
37
37
  userId,
38
- error
38
+ error: error instanceof Error ? error.message : String(error)
39
39
  });
40
- throw error;
40
+ return false;
41
41
  }
42
42
  }, [onCreditsExhausted, userId]);
43
43
 
@@ -1,6 +1,6 @@
1
1
  import { useQuery, useQueryClient } from "@umituz/react-native-design-system";
2
2
  import { useCallback, useMemo, useEffect } from "react";
3
- import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
3
+ import { useAuthStore, selectUserId, selectIsAnonymous } from "@umituz/react-native-auth";
4
4
  import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
5
5
  import { NO_CACHE_QUERY_CONFIG } from "../../../shared/infrastructure/react-query/queryConfig";
6
6
  import { usePreviousUserCleanup } from "../../../shared/infrastructure/react-query/hooks/usePreviousUserCleanup";
@@ -10,7 +10,7 @@ import {
10
10
  isCreditsRepositoryConfigured,
11
11
  } from "../infrastructure/CreditsRepositoryManager";
12
12
  import { calculateSafePercentage, canAffordAmount } from "../utils/creditValidation";
13
- import { isAuthenticated } from "../../subscription/utils/authGuards";
13
+ import { isRegisteredUser } from "../../subscription/utils/authGuards";
14
14
  import { creditsQueryKeys } from "./creditsQueryKeys";
15
15
  import type { UseCreditsResult, CreditsLoadStatus } from "./useCredits.types";
16
16
 
@@ -26,15 +26,17 @@ const deriveLoadStatus = (
26
26
 
27
27
  export const useCredits = (): UseCreditsResult => {
28
28
  const userId = useAuthStore(selectUserId);
29
+ const isAnonymous = useAuthStore(selectIsAnonymous);
29
30
  const isConfigured = isCreditsRepositoryConfigured();
30
31
 
31
32
  const config = isConfigured ? getCreditsConfig() : null;
32
- const queryEnabled = isAuthenticated(userId) && isConfigured;
33
+ const isUserRegistered = isRegisteredUser(userId, isAnonymous);
34
+ const queryEnabled = isUserRegistered && isConfigured;
33
35
 
34
36
  const { data, status, error, refetch } = useQuery({
35
37
  queryKey: creditsQueryKeys.user(userId),
36
38
  queryFn: async () => {
37
- if (!isAuthenticated(userId) || !isConfigured) return null;
39
+ if (!isUserRegistered || !isConfigured) return null;
38
40
 
39
41
  const repository = getCreditsRepository();
40
42
  const result = await repository.getCredits(userId);
@@ -51,11 +53,10 @@ export const useCredits = (): UseCreditsResult => {
51
53
 
52
54
  const queryClient = useQueryClient();
53
55
 
54
- // Clean up previous user's cache on logout/user switch
55
56
  usePreviousUserCleanup(userId, queryClient, creditsQueryKeys.user);
56
57
 
57
58
  useEffect(() => {
58
- if (!isAuthenticated(userId)) return undefined;
59
+ if (!isUserRegistered) return undefined;
59
60
 
60
61
  const unsubscribe = subscriptionEventBus.on(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, (updatedUserId) => {
61
62
  if (updatedUserId === userId) {
@@ -64,13 +65,13 @@ export const useCredits = (): UseCreditsResult => {
64
65
  });
65
66
 
66
67
  return unsubscribe;
67
- }, [userId, queryClient]);
68
+ }, [userId, isUserRegistered, queryClient]);
68
69
 
69
70
  const credits = data ?? null;
70
71
 
71
72
  const derivedValues = useMemo(() => {
72
73
  const has = (credits?.credits ?? 0) > 0;
73
- const limit = config?.creditLimit ?? 0;
74
+ const limit = credits?.creditLimit ?? config?.creditLimit ?? 0;
74
75
  const percent = calculateSafePercentage(credits?.credits, limit);
75
76
  return { hasCredits: has, creditsPercent: percent };
76
77
  }, [credits, config?.creditLimit]);
@@ -95,4 +96,4 @@ export const useCredits = (): UseCreditsResult => {
95
96
  refetch,
96
97
  canAfford,
97
98
  };
98
- };
99
+ };
@@ -1,57 +1,29 @@
1
- /**
2
- * PaywallContainer Types
3
- * Props for subscription paywall
4
- */
5
-
6
1
  import type { ImageSourcePropType } from "react-native";
7
2
  import type { PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../entities/types";
8
3
  import type { PurchaseSource } from "../../subscription/core/SubscriptionConstants";
9
4
  import type { PackageAllocationMap } from "../../credits/core/Credits";
10
5
 
11
- /**
12
- * Trial display configuration
13
- * Controls how free trial info is displayed (Apple-compliant)
14
- */
15
6
  export interface TrialConfig {
16
- /** Enable trial display (default: false) */
17
7
  readonly enabled: boolean;
18
- /** Product IDs that have trial offers (if empty, checks all via RevenueCat) */
19
8
  readonly eligibleProductIds?: readonly string[];
20
- /** Trial duration in days (default: 7) */
21
9
  readonly durationDays?: number;
22
- /** Text to show for trial (e.g., "7 days free, then billed") */
23
10
  readonly trialText?: string;
24
11
  }
25
12
 
26
13
  export interface PaywallContainerProps {
27
- /** Paywall translations - no defaults, must be provided */
28
14
  readonly translations: PaywallTranslations;
29
- /** Legal URLs for privacy and terms */
30
15
  readonly legalUrls?: PaywallLegalUrls;
31
- /** Feature list to display */
32
16
  readonly features?: readonly SubscriptionFeature[];
33
- /** Hero image for paywall header */
34
17
  readonly heroImage?: ImageSourcePropType;
35
- /** Best value package identifier for badge */
36
18
  readonly bestValueIdentifier?: string;
37
- /** Credit amounts per product identifier (takes precedence over packageAllocations) */
38
19
  readonly creditAmounts?: Record<string, number>;
39
- /** Credits label text (e.g., "credits") */
40
20
  readonly creditsLabel?: string;
41
- /** Package allocations for auto-computing creditAmounts (used when creditAmounts not provided) */
42
21
  readonly packageAllocations?: PackageAllocationMap;
43
- /** Source of the paywall - affects pending purchase handling */
44
22
  readonly source?: PurchaseSource;
45
- /** Callback when purchase succeeds */
46
23
  readonly onPurchaseSuccess?: () => void;
47
- /** Callback when purchase fails */
48
24
  readonly onPurchaseError?: (error: Error | string) => void;
49
- /** Callback when auth is required (for anonymous users) */
50
25
  readonly onAuthRequired?: () => void;
51
- /** Visibility override */
52
26
  readonly visible?: boolean;
53
- /** Callback when paywall is closed */
54
27
  readonly onClose?: () => void;
55
- /** Trial display configuration (Apple-compliant) */
56
28
  readonly trialConfig?: TrialConfig;
57
29
  }
@@ -1,7 +1,3 @@
1
- /**
2
- * PaywallModal Styles
3
- */
4
-
5
1
  import { StyleSheet } from "react-native";
6
2
 
7
3
  export const paywallModalStyles = StyleSheet.create({
@@ -1,8 +1,3 @@
1
- /**
2
- * Paywall Types
3
- * All paywall-related type definitions
4
- */
5
-
6
1
  export interface SubscriptionFeature {
7
2
  icon: string;
8
3
  text: string;
@@ -1,7 +1,3 @@
1
- /**
2
- * usePaywallActions Hook
3
- * Encapsulates purchase and restore flow for the paywall.
4
- */
5
1
  import { useState, useCallback, useRef, useEffect } from "react";
6
2
  import type { PurchasesPackage } from "react-native-purchases";
7
3
  import { usePurchaseLoadingStore } from "../../subscription/presentation/stores";
@@ -52,7 +48,6 @@ export function usePaywallActions({
52
48
  });
53
49
 
54
50
  const handlePurchase = useCallback(async () => {
55
-
56
51
  if (!selectedPlanId) {
57
52
  return;
58
53
  }
@@ -71,7 +66,7 @@ export function usePaywallActions({
71
66
 
72
67
  if (!pkg) {
73
68
  if (typeof __DEV__ !== "undefined" && __DEV__) {
74
- console.error("[usePaywallActions] Package not found", {
69
+ console.error("[usePaywallActions] Package not found", {
75
70
  selectedPlanId,
76
71
  availablePackages: packages.map(p => p.product.identifier),
77
72
  });
@@ -85,18 +80,12 @@ export function usePaywallActions({
85
80
  startPurchase(selectedPlanId, "manual");
86
81
 
87
82
  try {
88
-
89
83
  const success = await onPurchaseRef.current(pkg);
90
84
 
91
85
  if (success === true) {
92
86
  onPurchaseSuccessRef.current?.();
93
87
  onCloseRef.current?.();
94
- } else if (success === false) {
95
- if (typeof __DEV__ !== "undefined" && __DEV__) {
96
- console.warn("[usePaywallActions] ⚠️ Purchase returned false (user cancelled or failed)");
97
- }
98
88
  }
99
- // else: success is undefined/null - no action needed
100
89
  } catch (error) {
101
90
  const err = error instanceof Error ? error : new Error(String(error));
102
91
  onPurchaseErrorRef.current?.(err);
@@ -107,7 +96,6 @@ export function usePaywallActions({
107
96
  }, [selectedPlanId, packages, isProcessing, startPurchase, endPurchase]);
108
97
 
109
98
  const handleRestore = useCallback(async () => {
110
-
111
99
  if (!onRestoreRef.current) {
112
100
  const err = new Error("Restore handler not configured");
113
101
  onPurchaseErrorRef.current?.(err);
@@ -120,13 +108,11 @@ export function usePaywallActions({
120
108
 
121
109
  setIsLocalProcessing(true);
122
110
  try {
123
-
124
111
  const success = await onRestoreRef.current();
125
112
 
126
113
  if (success === true) {
127
114
  onPurchaseSuccessRef.current?.();
128
115
  }
129
- // else: success is false/undefined - restore failed or user cancelled, no action needed
130
116
  } catch (error) {
131
117
  const err = error instanceof Error ? error : new Error(String(error));
132
118
  onPurchaseErrorRef.current?.(err);
@@ -1,8 +1,3 @@
1
- /**
2
- * RevenueCat Error Classes
3
- * Domain-specific error types for RevenueCat operations
4
- */
5
-
6
1
  import { BaseError } from "../../../../shared/utils/BaseError";
7
2
 
8
3
  class RevenueCatError extends BaseError {
@@ -49,4 +44,3 @@ export class RevenueCatNetworkError extends RevenueCatError {
49
44
  this.name = "RevenueCatNetworkError";
50
45
  }
51
46
  }
52
-
@@ -1,8 +1,3 @@
1
- /**
2
- * RevenueCat Error Handler
3
- * Error code mapping and message resolution utilities
4
- */
5
-
6
1
  import Purchases from "react-native-purchases";
7
2
  import {
8
3
  ERROR_MESSAGES_MAP,
@@ -11,12 +6,7 @@ import {
11
6
  type PurchasesErrorCode,
12
7
  } from "./RevenueCatErrorMessages";
13
8
 
14
- /**
15
- * Error Code to Enum Mapping
16
- * Maps both string keys and numeric codes to Purchases error enum values
17
- */
18
9
  const ERROR_CODE_MAP = new Map<string, PurchasesErrorCode>([
19
- // String error codes
20
10
  ["PURCHASE_CANCELLED_ERROR", Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR],
21
11
  ["PURCHASE_NOT_ALLOWED_ERROR", Purchases.PURCHASES_ERROR_CODE.PURCHASE_NOT_ALLOWED_ERROR],
22
12
  ["PURCHASE_INVALID_ERROR", Purchases.PURCHASES_ERROR_CODE.PURCHASE_INVALID_ERROR],
@@ -31,7 +21,6 @@ const ERROR_CODE_MAP = new Map<string, PurchasesErrorCode>([
31
21
  ["STORE_PROBLEM_ERROR", Purchases.PURCHASES_ERROR_CODE.STORE_PROBLEM_ERROR],
32
22
  ["PAYMENT_PENDING_ERROR", Purchases.PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR],
33
23
 
34
- // Numeric error codes as fallback
35
24
  ["1", Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR],
36
25
  ["2", Purchases.PURCHASES_ERROR_CODE.STORE_PROBLEM_ERROR],
37
26
  ["3", Purchases.PURCHASES_ERROR_CODE.PURCHASE_NOT_ALLOWED_ERROR],
@@ -47,13 +36,6 @@ const ERROR_CODE_MAP = new Map<string, PurchasesErrorCode>([
47
36
  ["0", Purchases.PURCHASES_ERROR_CODE.UNKNOWN_ERROR],
48
37
  ]);
49
38
 
50
- /**
51
- * Get error message configuration for a given error code
52
- * Strategy Pattern with Map lookup - O(1) complexity
53
- *
54
- * @param errorCode - Error code string from RevenueCat error
55
- * @returns ErrorMessage configuration
56
- */
57
39
  function getErrorMessageForCode(errorCode: string | null | undefined): ErrorMessage {
58
40
  if (!errorCode) {
59
41
  return DEFAULT_ERROR_MESSAGE;
@@ -70,12 +52,6 @@ function getErrorMessageForCode(errorCode: string | null | undefined): ErrorMess
70
52
  return DEFAULT_ERROR_MESSAGE;
71
53
  }
72
54
 
73
- /**
74
- * Get error message from RevenueCat error object
75
- *
76
- * @param error - RevenueCat error object
77
- * @returns ErrorMessage configuration
78
- */
79
55
  export function getErrorMessage(error: unknown): ErrorMessage {
80
56
  if (!error || typeof error !== "object") {
81
57
  return DEFAULT_ERROR_MESSAGE;
@@ -1,28 +1,13 @@
1
- /**
2
- * RevenueCat Error Messages
3
- * User-friendly error message configurations
4
- */
5
-
6
1
  import Purchases from "react-native-purchases";
7
2
 
8
- /**
9
- * Error Message Configuration
10
- */
11
3
  export interface ErrorMessage {
12
4
  title: string;
13
5
  message: string;
14
6
  shouldShowAlert?: boolean;
15
7
  }
16
8
 
17
- /**
18
- * RevenueCat Error Code Type
19
- */
20
9
  export type PurchasesErrorCode = typeof Purchases.PURCHASES_ERROR_CODE[keyof typeof Purchases.PURCHASES_ERROR_CODE];
21
10
 
22
- /**
23
- * User-friendly error messages mapped by error code enum
24
- * Strategy Pattern: Each error code has its own message configuration
25
- */
26
11
  export const ERROR_MESSAGES_MAP = new Map<PurchasesErrorCode, ErrorMessage>([
27
12
  [
28
13
  Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR,
@@ -130,9 +115,6 @@ export const ERROR_MESSAGES_MAP = new Map<PurchasesErrorCode, ErrorMessage>([
130
115
  ],
131
116
  ]);
132
117
 
133
- /**
134
- * Default error message for unknown errors
135
- */
136
118
  export const DEFAULT_ERROR_MESSAGE: ErrorMessage = {
137
119
  title: "Error",
138
120
  message: "An error occurred. Please try again.",