@umituz/react-native-subscription 1.0.6 → 1.1.0

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 (83) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +10 -0
  3. package/lib/application/ports/ISubscriptionRepository.d.ts +25 -0
  4. package/lib/application/ports/ISubscriptionRepository.d.ts.map +1 -0
  5. package/lib/application/ports/ISubscriptionRepository.js +9 -0
  6. package/lib/application/ports/ISubscriptionRepository.js.map +1 -0
  7. package/lib/application/ports/ISubscriptionService.d.ts +28 -0
  8. package/lib/application/ports/ISubscriptionService.d.ts.map +1 -0
  9. package/lib/application/ports/ISubscriptionService.js +6 -0
  10. package/lib/application/ports/ISubscriptionService.js.map +1 -0
  11. package/lib/domain/entities/SubscriptionStatus.d.ts +31 -0
  12. package/lib/domain/entities/SubscriptionStatus.d.ts.map +1 -0
  13. package/lib/domain/entities/SubscriptionStatus.js +39 -0
  14. package/lib/domain/entities/SubscriptionStatus.js.map +1 -0
  15. package/lib/domain/errors/SubscriptionError.d.ts +18 -0
  16. package/lib/domain/errors/SubscriptionError.d.ts.map +1 -0
  17. package/lib/domain/errors/SubscriptionError.js +30 -0
  18. package/lib/domain/errors/SubscriptionError.js.map +1 -0
  19. package/lib/domain/value-objects/SubscriptionConfig.d.ts +15 -0
  20. package/lib/domain/value-objects/SubscriptionConfig.d.ts.map +1 -0
  21. package/lib/domain/value-objects/SubscriptionConfig.js +6 -0
  22. package/lib/domain/value-objects/SubscriptionConfig.js.map +1 -0
  23. package/lib/index.d.ts +33 -0
  24. package/lib/index.d.ts.map +1 -0
  25. package/lib/index.js +43 -0
  26. package/lib/index.js.map +1 -0
  27. package/lib/infrastructure/services/ActivationHandler.d.ts +20 -0
  28. package/lib/infrastructure/services/ActivationHandler.d.ts.map +1 -0
  29. package/lib/infrastructure/services/ActivationHandler.js +71 -0
  30. package/lib/infrastructure/services/ActivationHandler.js.map +1 -0
  31. package/lib/infrastructure/services/SubscriptionService.d.ts +22 -0
  32. package/lib/infrastructure/services/SubscriptionService.d.ts.map +1 -0
  33. package/lib/infrastructure/services/SubscriptionService.js +110 -0
  34. package/lib/infrastructure/services/SubscriptionService.js.map +1 -0
  35. package/lib/presentation/hooks/useSubscription.d.ts +33 -0
  36. package/lib/presentation/hooks/useSubscription.d.ts.map +1 -0
  37. package/lib/presentation/hooks/useSubscription.js +129 -0
  38. package/lib/presentation/hooks/useSubscription.js.map +1 -0
  39. package/lib/utils/dateUtils.d.ts +39 -0
  40. package/lib/utils/dateUtils.d.ts.map +1 -0
  41. package/lib/utils/dateUtils.js +117 -0
  42. package/lib/utils/dateUtils.js.map +1 -0
  43. package/lib/utils/dateValidationUtils.d.ts +20 -0
  44. package/lib/utils/dateValidationUtils.d.ts.map +1 -0
  45. package/lib/utils/dateValidationUtils.js +39 -0
  46. package/lib/utils/dateValidationUtils.js.map +1 -0
  47. package/lib/utils/periodUtils.d.ts +38 -0
  48. package/lib/utils/periodUtils.d.ts.map +1 -0
  49. package/lib/utils/periodUtils.js +70 -0
  50. package/lib/utils/periodUtils.js.map +1 -0
  51. package/lib/utils/planDetectionUtils.d.ts +17 -0
  52. package/lib/utils/planDetectionUtils.d.ts.map +1 -0
  53. package/lib/utils/planDetectionUtils.js +31 -0
  54. package/lib/utils/planDetectionUtils.js.map +1 -0
  55. package/lib/utils/priceUtils.d.ts +23 -0
  56. package/lib/utils/priceUtils.d.ts.map +1 -0
  57. package/lib/utils/priceUtils.js +29 -0
  58. package/lib/utils/priceUtils.js.map +1 -0
  59. package/lib/utils/subscriptionConstants.d.ts +62 -0
  60. package/lib/utils/subscriptionConstants.d.ts.map +1 -0
  61. package/lib/utils/subscriptionConstants.js +61 -0
  62. package/lib/utils/subscriptionConstants.js.map +1 -0
  63. package/package.json +13 -3
  64. package/src/application/ports/ISubscriptionRepository.ts +10 -0
  65. package/src/application/ports/ISubscriptionService.ts +10 -0
  66. package/src/domain/entities/SubscriptionStatus.test.ts +106 -0
  67. package/src/domain/entities/SubscriptionStatus.ts +10 -0
  68. package/src/domain/errors/SubscriptionError.ts +10 -0
  69. package/src/domain/value-objects/SubscriptionConfig.ts +0 -0
  70. package/src/index.ts +9 -2
  71. package/src/infrastructure/services/ActivationHandler.ts +108 -0
  72. package/src/infrastructure/services/SubscriptionService.ts +58 -177
  73. package/src/presentation/hooks/useSubscription.ts +22 -2
  74. package/src/utils/dateUtils.test.ts +116 -0
  75. package/src/utils/dateUtils.ts +12 -76
  76. package/src/utils/dateValidationUtils.test.ts +142 -0
  77. package/src/utils/dateValidationUtils.ts +53 -0
  78. package/src/utils/periodUtils.ts +0 -0
  79. package/src/utils/planDetectionUtils.test.ts +47 -0
  80. package/src/utils/planDetectionUtils.ts +40 -0
  81. package/src/utils/priceUtils.test.ts +35 -0
  82. package/src/utils/priceUtils.ts +0 -0
  83. package/src/utils/subscriptionConstants.ts +0 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * useSubscription Hook
3
+ * React hook for subscription management
4
+ */
5
+ import type { SubscriptionStatus } from '../../domain/entities/SubscriptionStatus';
6
+ export interface UseSubscriptionResult {
7
+ /** Current subscription status */
8
+ status: SubscriptionStatus | null;
9
+ /** Whether subscription is loading */
10
+ loading: boolean;
11
+ /** Error if any */
12
+ error: string | null;
13
+ /** Whether user has active subscription */
14
+ isPremium: boolean;
15
+ /** Load subscription status */
16
+ loadStatus: (userId: string) => Promise<void>;
17
+ /** Refresh subscription status */
18
+ refreshStatus: (userId: string) => Promise<void>;
19
+ /** Activate subscription */
20
+ activateSubscription: (userId: string, productId: string, expiresAt: string | null) => Promise<void>;
21
+ /** Deactivate subscription */
22
+ deactivateSubscription: (userId: string) => Promise<void>;
23
+ }
24
+ /**
25
+ * Hook for subscription operations
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const { status, isPremium, loadStatus } = useSubscription();
30
+ * ```
31
+ */
32
+ export declare function useSubscription(): UseSubscriptionResult;
33
+ //# sourceMappingURL=useSubscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSubscription.d.ts","sourceRoot":"","sources":["../../../src/presentation/hooks/useSubscription.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,mBAAmB;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,kCAAkC;IAClC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,4BAA4B;IAC5B,oBAAoB,EAAE,CACpB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,IAAI,KACrB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,8BAA8B;IAC9B,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAsIvD"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * useSubscription Hook
3
+ * React hook for subscription management
4
+ */
5
+ import { useState, useCallback } from 'react';
6
+ import { getSubscriptionService } from '../../infrastructure/services/SubscriptionService';
7
+ /**
8
+ * Hook for subscription operations
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const { status, isPremium, loadStatus } = useSubscription();
13
+ * ```
14
+ */
15
+ export function useSubscription() {
16
+ const [status, setStatus] = useState(null);
17
+ const [loading, setLoading] = useState(false);
18
+ const [error, setError] = useState(null);
19
+ const loadStatus = useCallback(async (userId) => {
20
+ if (!userId) {
21
+ setError('User ID is required');
22
+ return;
23
+ }
24
+ const service = getSubscriptionService();
25
+ if (!service) {
26
+ setError('Subscription service is not initialized');
27
+ return;
28
+ }
29
+ setLoading(true);
30
+ setError(null);
31
+ try {
32
+ const subscriptionStatus = await service.getSubscriptionStatus(userId);
33
+ setStatus(subscriptionStatus);
34
+ }
35
+ catch (err) {
36
+ const errorMessage = err instanceof Error ? err.message : 'Failed to load subscription status';
37
+ setError(errorMessage);
38
+ }
39
+ finally {
40
+ setLoading(false);
41
+ }
42
+ }, []);
43
+ const refreshStatus = useCallback(async (userId) => {
44
+ if (!userId) {
45
+ setError('User ID is required');
46
+ return;
47
+ }
48
+ const service = getSubscriptionService();
49
+ if (!service) {
50
+ setError('Subscription service is not initialized');
51
+ return;
52
+ }
53
+ setLoading(true);
54
+ setError(null);
55
+ try {
56
+ const subscriptionStatus = await service.getSubscriptionStatus(userId);
57
+ setStatus(subscriptionStatus);
58
+ }
59
+ catch (err) {
60
+ const errorMessage = err instanceof Error ? err.message : 'Failed to refresh subscription status';
61
+ setError(errorMessage);
62
+ }
63
+ finally {
64
+ setLoading(false);
65
+ }
66
+ }, []);
67
+ const activateSubscription = useCallback(async (userId, productId, expiresAt) => {
68
+ if (!userId || !productId) {
69
+ setError('User ID and Product ID are required');
70
+ return;
71
+ }
72
+ const service = getSubscriptionService();
73
+ if (!service) {
74
+ setError('Subscription service is not initialized');
75
+ return;
76
+ }
77
+ setLoading(true);
78
+ setError(null);
79
+ try {
80
+ const updatedStatus = await service.activateSubscription(userId, productId, expiresAt);
81
+ setStatus(updatedStatus);
82
+ }
83
+ catch (err) {
84
+ const errorMessage = err instanceof Error ? err.message : 'Failed to activate subscription';
85
+ setError(errorMessage);
86
+ throw err;
87
+ }
88
+ finally {
89
+ setLoading(false);
90
+ }
91
+ }, []);
92
+ const deactivateSubscription = useCallback(async (userId) => {
93
+ if (!userId) {
94
+ setError('User ID is required');
95
+ return;
96
+ }
97
+ const service = getSubscriptionService();
98
+ if (!service) {
99
+ setError('Subscription service is not initialized');
100
+ return;
101
+ }
102
+ setLoading(true);
103
+ setError(null);
104
+ try {
105
+ const updatedStatus = await service.deactivateSubscription(userId);
106
+ setStatus(updatedStatus);
107
+ }
108
+ catch (err) {
109
+ const errorMessage = err instanceof Error ? err.message : 'Failed to deactivate subscription';
110
+ setError(errorMessage);
111
+ throw err;
112
+ }
113
+ finally {
114
+ setLoading(false);
115
+ }
116
+ }, []);
117
+ const isPremium = status?.isPremium && (status.expiresAt === null || new Date(status.expiresAt).getTime() > Date.now()) || false;
118
+ return {
119
+ status,
120
+ loading,
121
+ error,
122
+ isPremium,
123
+ loadStatus,
124
+ refreshStatus,
125
+ activateSubscription,
126
+ deactivateSubscription,
127
+ };
128
+ }
129
+ //# sourceMappingURL=useSubscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSubscription.js","sourceRoot":"","sources":["../../../src/presentation/hooks/useSubscription.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Date Utilities
3
+ * Subscription date-related helper functions
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only date formatting and calculation
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ /**
11
+ * Format expiration date for display
12
+ */
13
+ export declare function formatExpirationDate(expiresAt: string | null, locale?: string): string | null;
14
+ /**
15
+ * Calculate expiration date based on subscription plan
16
+ *
17
+ * This function handles:
18
+ * - RevenueCat sandbox accelerated timers (detects and recalculates)
19
+ * - Production dates (trusts RevenueCat's date if valid)
20
+ * - Monthly subscriptions: Same day next month (e.g., Nov 10 → Dec 10)
21
+ * - Yearly subscriptions: Same day next year (e.g., Nov 10, 2024 → Nov 10, 2025)
22
+ * - Weekly subscriptions: +7 days
23
+ *
24
+ * @param productId - Product identifier (e.g., "com.company.app.monthly")
25
+ * @param revenueCatExpiresAt - Optional expiration date from RevenueCat API
26
+ * @returns ISO date string for expiration, or null if invalid
27
+ *
28
+ * @example
29
+ * // Monthly subscription purchased on Nov 10, 2024
30
+ * calculateExpirationDate('com.company.app.monthly', null)
31
+ * // Returns: '2024-12-10T...' (Dec 10, 2024)
32
+ *
33
+ * @example
34
+ * // Yearly subscription purchased on Nov 10, 2024
35
+ * calculateExpirationDate('com.company.app.yearly', null)
36
+ * // Returns: '2025-11-10T...' (Nov 10, 2025)
37
+ */
38
+ export declare function calculateExpirationDate(productId: string | null | undefined, revenueCatExpiresAt?: string | null): string | null;
39
+ //# sourceMappingURL=dateUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateUtils.d.ts","sourceRoot":"","sources":["../../src/utils/dateUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,GAAE,MAAsC,GAC7C,MAAM,GAAG,IAAI,CAkBf;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,GAClC,MAAM,GAAG,IAAI,CA0Ef"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Date Utilities
3
+ * Subscription date-related helper functions
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only date formatting and calculation
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ import { DATE_CONSTANTS } from './subscriptionConstants';
11
+ import { extractPlanFromProductId } from './planDetectionUtils';
12
+ import { SUBSCRIPTION_PLAN_TYPES, MIN_SUBSCRIPTION_DURATIONS_DAYS, SUBSCRIPTION_PERIOD_DAYS, } from './subscriptionConstants';
13
+ /**
14
+ * Format expiration date for display
15
+ */
16
+ export function formatExpirationDate(expiresAt, locale = DATE_CONSTANTS.DEFAULT_LOCALE) {
17
+ if (!expiresAt) {
18
+ return null;
19
+ }
20
+ try {
21
+ const date = new Date(expiresAt);
22
+ if (isNaN(date.getTime())) {
23
+ return null;
24
+ }
25
+ return date.toLocaleDateString(locale, {
26
+ year: 'numeric',
27
+ month: 'long',
28
+ day: 'numeric',
29
+ });
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ /**
36
+ * Calculate expiration date based on subscription plan
37
+ *
38
+ * This function handles:
39
+ * - RevenueCat sandbox accelerated timers (detects and recalculates)
40
+ * - Production dates (trusts RevenueCat's date if valid)
41
+ * - Monthly subscriptions: Same day next month (e.g., Nov 10 → Dec 10)
42
+ * - Yearly subscriptions: Same day next year (e.g., Nov 10, 2024 → Nov 10, 2025)
43
+ * - Weekly subscriptions: +7 days
44
+ *
45
+ * @param productId - Product identifier (e.g., "com.company.app.monthly")
46
+ * @param revenueCatExpiresAt - Optional expiration date from RevenueCat API
47
+ * @returns ISO date string for expiration, or null if invalid
48
+ *
49
+ * @example
50
+ * // Monthly subscription purchased on Nov 10, 2024
51
+ * calculateExpirationDate('com.company.app.monthly', null)
52
+ * // Returns: '2024-12-10T...' (Dec 10, 2024)
53
+ *
54
+ * @example
55
+ * // Yearly subscription purchased on Nov 10, 2024
56
+ * calculateExpirationDate('com.company.app.yearly', null)
57
+ * // Returns: '2025-11-10T...' (Nov 10, 2025)
58
+ */
59
+ export function calculateExpirationDate(productId, revenueCatExpiresAt) {
60
+ if (!productId)
61
+ return null;
62
+ const plan = extractPlanFromProductId(productId);
63
+ const now = new Date();
64
+ // Check if RevenueCat's date is valid and not sandbox accelerated
65
+ if (revenueCatExpiresAt) {
66
+ try {
67
+ const rcDate = new Date(revenueCatExpiresAt);
68
+ // Only trust if date is in the future
69
+ if (rcDate > now) {
70
+ // Detect sandbox accelerated timers by checking duration
71
+ const durationMs = rcDate.getTime() - now.getTime();
72
+ const durationDays = durationMs / DATE_CONSTANTS.MILLISECONDS_PER_DAY;
73
+ // Get minimum expected duration for this plan type
74
+ const minDurationMap = {
75
+ [SUBSCRIPTION_PLAN_TYPES.WEEKLY]: MIN_SUBSCRIPTION_DURATIONS_DAYS.WEEKLY,
76
+ [SUBSCRIPTION_PLAN_TYPES.MONTHLY]: MIN_SUBSCRIPTION_DURATIONS_DAYS.MONTHLY,
77
+ [SUBSCRIPTION_PLAN_TYPES.YEARLY]: MIN_SUBSCRIPTION_DURATIONS_DAYS.YEARLY,
78
+ [SUBSCRIPTION_PLAN_TYPES.UNKNOWN]: MIN_SUBSCRIPTION_DURATIONS_DAYS.UNKNOWN,
79
+ };
80
+ const minDuration = minDurationMap[plan];
81
+ // If duration is reasonable, trust RevenueCat's date (production)
82
+ if (durationDays >= minDuration) {
83
+ return rcDate.toISOString();
84
+ }
85
+ // Otherwise, fall through to manual calculation (sandbox accelerated)
86
+ }
87
+ }
88
+ catch {
89
+ // Invalid date, fall through to calculation
90
+ }
91
+ }
92
+ // Calculate production-equivalent expiration date
93
+ // Use current date as base to preserve the day of month/year
94
+ const calculatedDate = new Date(now);
95
+ switch (plan) {
96
+ case SUBSCRIPTION_PLAN_TYPES.WEEKLY:
97
+ // Weekly: +7 days
98
+ calculatedDate.setDate(calculatedDate.getDate() + SUBSCRIPTION_PERIOD_DAYS.WEEKLY);
99
+ break;
100
+ case SUBSCRIPTION_PLAN_TYPES.MONTHLY:
101
+ // Monthly: Same day next month
102
+ // This handles edge cases like Jan 31 → Feb 28/29 correctly
103
+ calculatedDate.setMonth(calculatedDate.getMonth() + 1);
104
+ break;
105
+ case SUBSCRIPTION_PLAN_TYPES.YEARLY:
106
+ // Yearly: Same day next year
107
+ // This handles leap years correctly (Feb 29 → Feb 28/29)
108
+ calculatedDate.setFullYear(calculatedDate.getFullYear() + 1);
109
+ break;
110
+ default:
111
+ // Unknown plan type - default to 1 month
112
+ calculatedDate.setMonth(calculatedDate.getMonth() + 1);
113
+ break;
114
+ }
115
+ return calculatedDate.toISOString();
116
+ }
117
+ //# sourceMappingURL=dateUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateUtils.js","sourceRoot":"","sources":["../../src/utils/dateUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Date Validation Utilities
3
+ * Utilities for validating and checking subscription dates
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only date validation logic
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ import type { SubscriptionStatus } from '../domain/entities/SubscriptionStatus';
11
+ /**
12
+ * Check if subscription is expired
13
+ */
14
+ export declare function isSubscriptionExpired(status: SubscriptionStatus | null): boolean;
15
+ /**
16
+ * Get days until subscription expires
17
+ * Returns null for lifetime subscriptions
18
+ */
19
+ export declare function getDaysUntilExpiration(status: SubscriptionStatus | null): number | null;
20
+ //# sourceMappingURL=dateValidationUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateValidationUtils.d.ts","sourceRoot":"","sources":["../../src/utils/dateValidationUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAEhF;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAChC,OAAO,CAcT;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAChC,MAAM,GAAG,IAAI,CAaf"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Date Validation Utilities
3
+ * Utilities for validating and checking subscription dates
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only date validation logic
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ /**
11
+ * Check if subscription is expired
12
+ */
13
+ export function isSubscriptionExpired(status) {
14
+ if (!status || !status.isPremium) {
15
+ return true;
16
+ }
17
+ if (!status.expiresAt) {
18
+ // Lifetime subscription (no expiration)
19
+ return false;
20
+ }
21
+ const expirationDate = new Date(status.expiresAt);
22
+ const now = new Date();
23
+ return expirationDate.getTime() <= now.getTime();
24
+ }
25
+ /**
26
+ * Get days until subscription expires
27
+ * Returns null for lifetime subscriptions
28
+ */
29
+ export function getDaysUntilExpiration(status) {
30
+ if (!status || !status.expiresAt) {
31
+ return null;
32
+ }
33
+ const expirationDate = new Date(status.expiresAt);
34
+ const now = new Date();
35
+ const diffMs = expirationDate.getTime() - now.getTime();
36
+ const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
37
+ return Math.max(0, diffDays);
38
+ }
39
+ //# sourceMappingURL=dateValidationUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateValidationUtils.js","sourceRoot":"","sources":["../../src/utils/dateValidationUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAiC;IAEjC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,wCAAwC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,OAAO,cAAc,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAiC;IAEjC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,MAAM,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAC/B,CAAC;IAEF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Period Utilities
3
+ * Subscription period-related helper functions
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only period-related operations
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ /**
11
+ * Get subscription period text from RevenueCat package
12
+ * Extracts readable period text from PurchasesPackage or subscription period object
13
+ *
14
+ * @param input - RevenueCat PurchasesPackage or subscription period object
15
+ * @returns Human-readable period text (e.g., "month", "year", "2 months")
16
+ *
17
+ * @example
18
+ * // From PurchasesPackage
19
+ * getPeriodText(pkg) // Returns: "month"
20
+ *
21
+ * @example
22
+ * // From subscription period object
23
+ * getPeriodText({ unit: "MONTH", numberOfUnits: 1 }) // Returns: "month"
24
+ * getPeriodText({ unit: "YEAR", numberOfUnits: 1 }) // Returns: "year"
25
+ * getPeriodText({ unit: "MONTH", numberOfUnits: 3 }) // Returns: "3 months"
26
+ */
27
+ export declare function getPeriodText(input: {
28
+ product?: {
29
+ subscriptionPeriod?: {
30
+ unit: string;
31
+ numberOfUnits: number;
32
+ } | string | null;
33
+ };
34
+ } | {
35
+ unit: string;
36
+ numberOfUnits: number;
37
+ } | null | undefined): string;
38
+ //# sourceMappingURL=periodUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"periodUtils.d.ts","sourceRoot":"","sources":["../../src/utils/periodUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EACD;IACE,OAAO,CAAC,EAAE;QACR,kBAAkB,CAAC,EACf;YACE,IAAI,EAAE,MAAM,CAAC;YACb,aAAa,EAAE,MAAM,CAAC;SACvB,GACD,MAAM,GACN,IAAI,CAAC;KACV,CAAC;CACH,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,GACD,IAAI,GACJ,SAAS,GACZ,MAAM,CAsDR"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Period Utilities
3
+ * Subscription period-related helper functions
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only period-related operations
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ import { SUBSCRIPTION_PERIOD_UNITS } from './subscriptionConstants';
11
+ /**
12
+ * Get subscription period text from RevenueCat package
13
+ * Extracts readable period text from PurchasesPackage or subscription period object
14
+ *
15
+ * @param input - RevenueCat PurchasesPackage or subscription period object
16
+ * @returns Human-readable period text (e.g., "month", "year", "2 months")
17
+ *
18
+ * @example
19
+ * // From PurchasesPackage
20
+ * getPeriodText(pkg) // Returns: "month"
21
+ *
22
+ * @example
23
+ * // From subscription period object
24
+ * getPeriodText({ unit: "MONTH", numberOfUnits: 1 }) // Returns: "month"
25
+ * getPeriodText({ unit: "YEAR", numberOfUnits: 1 }) // Returns: "year"
26
+ * getPeriodText({ unit: "MONTH", numberOfUnits: 3 }) // Returns: "3 months"
27
+ */
28
+ export function getPeriodText(input) {
29
+ if (!input)
30
+ return '';
31
+ // Extract subscription period from PurchasesPackage or use directly
32
+ let subscriptionPeriod;
33
+ // Check if input is PurchasesPackage (has product property)
34
+ if ('product' in input && input.product?.subscriptionPeriod) {
35
+ const period = input.product.subscriptionPeriod;
36
+ // Type guard: check if period is an object with unit and numberOfUnits
37
+ // RevenueCat's subscriptionPeriod can be string | null | object
38
+ if (typeof period === 'object' &&
39
+ period !== null &&
40
+ 'unit' in period &&
41
+ 'numberOfUnits' in period) {
42
+ subscriptionPeriod = {
43
+ unit: period.unit,
44
+ numberOfUnits: period.numberOfUnits,
45
+ };
46
+ }
47
+ // If period is string, we can't extract unit/numberOfUnits, return empty
48
+ }
49
+ else if ('unit' in input && 'numberOfUnits' in input) {
50
+ // Input is already a subscription period object
51
+ subscriptionPeriod = {
52
+ unit: input.unit,
53
+ numberOfUnits: input.numberOfUnits,
54
+ };
55
+ }
56
+ if (!subscriptionPeriod)
57
+ return '';
58
+ const { unit, numberOfUnits } = subscriptionPeriod;
59
+ if (unit === SUBSCRIPTION_PERIOD_UNITS.MONTH) {
60
+ return numberOfUnits === 1 ? 'month' : `${numberOfUnits} months`;
61
+ }
62
+ if (unit === SUBSCRIPTION_PERIOD_UNITS.YEAR) {
63
+ return numberOfUnits === 1 ? 'year' : `${numberOfUnits} years`;
64
+ }
65
+ if (unit === SUBSCRIPTION_PERIOD_UNITS.WEEK) {
66
+ return numberOfUnits === 1 ? 'week' : `${numberOfUnits} weeks`;
67
+ }
68
+ return '';
69
+ }
70
+ //# sourceMappingURL=periodUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"periodUtils.js","sourceRoot":"","sources":["../../src/utils/periodUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEpE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAC3B,KAiBa;IAEb,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,oEAAoE;IACpE,IAAI,kBAMS,CAAC;IAEd,4DAA4D;IAC5D,IAAI,SAAS,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,kBAAkB,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAChD,uEAAuE;QACvE,gEAAgE;QAChE,IACE,OAAO,MAAM,KAAK,QAAQ;YAC1B,MAAM,KAAK,IAAI;YACf,MAAM,IAAI,MAAM;YAChB,eAAe,IAAI,MAAM,EACzB,CAAC;YACD,kBAAkB,GAAG;gBACnB,IAAI,EAAE,MAAM,CAAC,IAAc;gBAC3B,aAAa,EAAE,MAAM,CAAC,aAAuB;aAC9C,CAAC;QACJ,CAAC;QACD,yEAAyE;IAC3E,CAAC;SAAM,IAAI,MAAM,IAAI,KAAK,IAAI,eAAe,IAAI,KAAK,EAAE,CAAC;QACvD,gDAAgD;QAChD,kBAAkB,GAAG;YACnB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,aAAa,EAAE,KAAK,CAAC,aAAa;SACnC,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,kBAAkB;QAAE,OAAO,EAAE,CAAC;IAEnC,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,kBAAkB,CAAC;IAEnD,IAAI,IAAI,KAAK,yBAAyB,CAAC,KAAK,EAAE,CAAC;QAC7C,OAAO,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa,SAAS,CAAC;IACnE,CAAC;IAED,IAAI,IAAI,KAAK,yBAAyB,CAAC,IAAI,EAAE,CAAC;QAC5C,OAAO,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,aAAa,QAAQ,CAAC;IACjE,CAAC;IAED,IAAI,IAAI,KAAK,yBAAyB,CAAC,IAAI,EAAE,CAAC;QAC5C,OAAO,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,aAAa,QAAQ,CAAC;IACjE,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Plan Detection Utilities
3
+ * Utilities for detecting subscription plan types from product IDs
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only plan detection logic
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ import { type SubscriptionPlanType } from './subscriptionConstants';
11
+ /**
12
+ * Extract subscription plan type from product ID
13
+ * Example: "com.company.app.weekly" → "weekly"
14
+ * @internal
15
+ */
16
+ export declare function extractPlanFromProductId(productId: string | null | undefined): SubscriptionPlanType;
17
+ //# sourceMappingURL=planDetectionUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planDetectionUtils.d.ts","sourceRoot":"","sources":["../../src/utils/planDetectionUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACnC,oBAAoB,CAgBtB"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Plan Detection Utilities
3
+ * Utilities for detecting subscription plan types from product IDs
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only plan detection logic
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ import { SUBSCRIPTION_PLAN_TYPES, PRODUCT_ID_KEYWORDS, } from './subscriptionConstants';
11
+ /**
12
+ * Extract subscription plan type from product ID
13
+ * Example: "com.company.app.weekly" → "weekly"
14
+ * @internal
15
+ */
16
+ export function extractPlanFromProductId(productId) {
17
+ if (!productId)
18
+ return SUBSCRIPTION_PLAN_TYPES.UNKNOWN;
19
+ const lower = productId.toLowerCase();
20
+ if (PRODUCT_ID_KEYWORDS.WEEKLY.some((keyword) => lower.includes(keyword))) {
21
+ return SUBSCRIPTION_PLAN_TYPES.WEEKLY;
22
+ }
23
+ if (PRODUCT_ID_KEYWORDS.MONTHLY.some((keyword) => lower.includes(keyword))) {
24
+ return SUBSCRIPTION_PLAN_TYPES.MONTHLY;
25
+ }
26
+ if (PRODUCT_ID_KEYWORDS.YEARLY.some((keyword) => lower.includes(keyword))) {
27
+ return SUBSCRIPTION_PLAN_TYPES.YEARLY;
28
+ }
29
+ return SUBSCRIPTION_PLAN_TYPES.UNKNOWN;
30
+ }
31
+ //# sourceMappingURL=planDetectionUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planDetectionUtils.js","sourceRoot":"","sources":["../../src/utils/planDetectionUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,uBAAuB,EACvB,mBAAmB,GAEpB,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,SAAoC;IAEpC,IAAI,CAAC,SAAS;QAAE,OAAO,uBAAuB,CAAC,OAAO,CAAC;IAEvD,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;IAEtC,IAAI,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC1E,OAAO,uBAAuB,CAAC,MAAM,CAAC;IACxC,CAAC;IACD,IAAI,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC3E,OAAO,uBAAuB,CAAC,OAAO,CAAC;IACzC,CAAC;IACD,IAAI,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC1E,OAAO,uBAAuB,CAAC,MAAM,CAAC;IACxC,CAAC;IAED,OAAO,uBAAuB,CAAC,OAAO,CAAC;AACzC,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Price Utilities
3
+ * Subscription price-related helper functions
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only price-related operations
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ /**
11
+ * Format price for display
12
+ * Formats a price value with currency code using Intl.NumberFormat
13
+ *
14
+ * @param price - Price value (e.g., 9.99)
15
+ * @param currencyCode - ISO 4217 currency code (e.g., "USD", "EUR", "TRY")
16
+ * @returns Formatted price string (e.g., "$9.99", "€9.99", "₺9.99")
17
+ *
18
+ * @example
19
+ * formatPrice(9.99, "USD") // Returns: "$9.99"
20
+ * formatPrice(229.99, "TRY") // Returns: "₺229.99"
21
+ */
22
+ export declare function formatPrice(price: number, currencyCode: string): string;
23
+ //# sourceMappingURL=priceUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"priceUtils.d.ts","sourceRoot":"","sources":["../../src/utils/priceUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAKvE"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Price Utilities
3
+ * Subscription price-related helper functions
4
+ *
5
+ * Following SOLID, DRY, KISS principles:
6
+ * - Single Responsibility: Only price-related operations
7
+ * - DRY: No code duplication
8
+ * - KISS: Simple, clear implementations
9
+ */
10
+ import { DATE_CONSTANTS } from './subscriptionConstants';
11
+ /**
12
+ * Format price for display
13
+ * Formats a price value with currency code using Intl.NumberFormat
14
+ *
15
+ * @param price - Price value (e.g., 9.99)
16
+ * @param currencyCode - ISO 4217 currency code (e.g., "USD", "EUR", "TRY")
17
+ * @returns Formatted price string (e.g., "$9.99", "€9.99", "₺9.99")
18
+ *
19
+ * @example
20
+ * formatPrice(9.99, "USD") // Returns: "$9.99"
21
+ * formatPrice(229.99, "TRY") // Returns: "₺229.99"
22
+ */
23
+ export function formatPrice(price, currencyCode) {
24
+ return new Intl.NumberFormat(DATE_CONSTANTS.DEFAULT_LOCALE, {
25
+ style: 'currency',
26
+ currency: currencyCode,
27
+ }).format(price);
28
+ }
29
+ //# sourceMappingURL=priceUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"priceUtils.js","sourceRoot":"","sources":["../../src/utils/priceUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,YAAoB;IAC7D,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,cAAc,EAAE;QAC1D,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,YAAY;KACvB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC"}