@umituz/react-native-subscription 1.0.4 → 1.0.6
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/index.ts +8 -3
- package/src/utils/{subscriptionUtils.ts → dateUtils.ts} +10 -114
- package/src/utils/periodUtils.ts +104 -0
- package/src/utils/priceUtils.ts +31 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Subscription management system for React Native apps - Database-first approach with secure validation",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
package/src/index.ts
CHANGED
|
@@ -64,14 +64,19 @@ export type { UseSubscriptionResult } from './presentation/hooks/useSubscription
|
|
|
64
64
|
// UTILS
|
|
65
65
|
// =============================================================================
|
|
66
66
|
|
|
67
|
+
// Date utilities
|
|
67
68
|
export {
|
|
68
69
|
isSubscriptionExpired,
|
|
69
70
|
getDaysUntilExpiration,
|
|
70
71
|
formatExpirationDate,
|
|
71
72
|
calculateExpirationDate,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
} from './utils/dateUtils';
|
|
74
|
+
|
|
75
|
+
// Price utilities
|
|
76
|
+
export { formatPrice } from './utils/priceUtils';
|
|
77
|
+
|
|
78
|
+
// Period utilities
|
|
79
|
+
export { getPeriodText } from './utils/periodUtils';
|
|
75
80
|
|
|
76
81
|
export {
|
|
77
82
|
SUBSCRIPTION_PLAN_TYPES,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Date Utilities
|
|
3
|
+
* Subscription date-related helper functions
|
|
4
4
|
*
|
|
5
5
|
* Following SOLID, DRY, KISS principles:
|
|
6
|
-
* - Single Responsibility:
|
|
6
|
+
* - Single Responsibility: Only date-related operations
|
|
7
7
|
* - DRY: No code duplication
|
|
8
8
|
* - KISS: Simple, clear implementations
|
|
9
9
|
*/
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
MIN_SUBSCRIPTION_DURATIONS_DAYS,
|
|
15
15
|
SUBSCRIPTION_PERIOD_DAYS,
|
|
16
16
|
DATE_CONSTANTS,
|
|
17
|
-
SUBSCRIPTION_PERIOD_UNITS,
|
|
18
17
|
PRODUCT_ID_KEYWORDS,
|
|
19
18
|
type SubscriptionPlanType,
|
|
20
19
|
} from './subscriptionConstants';
|
|
@@ -47,7 +46,9 @@ function extractPlanFromProductId(
|
|
|
47
46
|
/**
|
|
48
47
|
* Check if subscription is expired
|
|
49
48
|
*/
|
|
50
|
-
export function isSubscriptionExpired(
|
|
49
|
+
export function isSubscriptionExpired(
|
|
50
|
+
status: SubscriptionStatus | null,
|
|
51
|
+
): boolean {
|
|
51
52
|
if (!status || !status.isPremium) {
|
|
52
53
|
return true;
|
|
53
54
|
}
|
|
@@ -109,23 +110,23 @@ export function formatExpirationDate(
|
|
|
109
110
|
|
|
110
111
|
/**
|
|
111
112
|
* Calculate expiration date based on subscription plan
|
|
112
|
-
*
|
|
113
|
+
*
|
|
113
114
|
* This function handles:
|
|
114
115
|
* - RevenueCat sandbox accelerated timers (detects and recalculates)
|
|
115
116
|
* - Production dates (trusts RevenueCat's date if valid)
|
|
116
117
|
* - Monthly subscriptions: Same day next month (e.g., Nov 10 → Dec 10)
|
|
117
118
|
* - Yearly subscriptions: Same day next year (e.g., Nov 10, 2024 → Nov 10, 2025)
|
|
118
119
|
* - Weekly subscriptions: +7 days
|
|
119
|
-
*
|
|
120
|
+
*
|
|
120
121
|
* @param productId - Product identifier (e.g., "com.umituz.app.monthly")
|
|
121
122
|
* @param revenueCatExpiresAt - Optional expiration date from RevenueCat API
|
|
122
123
|
* @returns ISO date string for expiration, or null if invalid
|
|
123
|
-
*
|
|
124
|
+
*
|
|
124
125
|
* @example
|
|
125
126
|
* // Monthly subscription purchased on Nov 10, 2024
|
|
126
127
|
* calculateExpirationDate('com.umituz.app.monthly', null)
|
|
127
128
|
* // Returns: '2024-12-10T...' (Dec 10, 2024)
|
|
128
|
-
*
|
|
129
|
+
*
|
|
129
130
|
* @example
|
|
130
131
|
* // Yearly subscription purchased on Nov 10, 2024
|
|
131
132
|
* calculateExpirationDate('com.umituz.app.yearly', null)
|
|
@@ -208,108 +209,3 @@ export function calculateExpirationDate(
|
|
|
208
209
|
return calculatedDate.toISOString();
|
|
209
210
|
}
|
|
210
211
|
|
|
211
|
-
/**
|
|
212
|
-
* Format price for display
|
|
213
|
-
* Formats a price value with currency code using Intl.NumberFormat
|
|
214
|
-
*
|
|
215
|
-
* @param price - Price value (e.g., 9.99)
|
|
216
|
-
* @param currencyCode - ISO 4217 currency code (e.g., "USD", "EUR", "TRY")
|
|
217
|
-
* @returns Formatted price string (e.g., "$9.99", "€9.99", "₺9.99")
|
|
218
|
-
*
|
|
219
|
-
* @example
|
|
220
|
-
* formatPrice(9.99, "USD") // Returns: "$9.99"
|
|
221
|
-
* formatPrice(229.99, "TRY") // Returns: "₺229.99"
|
|
222
|
-
*/
|
|
223
|
-
export function formatPrice(price: number, currencyCode: string): string {
|
|
224
|
-
return new Intl.NumberFormat(DATE_CONSTANTS.DEFAULT_LOCALE, {
|
|
225
|
-
style: 'currency',
|
|
226
|
-
currency: currencyCode,
|
|
227
|
-
}).format(price);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Get subscription period text from RevenueCat package
|
|
232
|
-
* Extracts readable period text from PurchasesPackage or subscription period object
|
|
233
|
-
*
|
|
234
|
-
* @param input - RevenueCat PurchasesPackage or subscription period object
|
|
235
|
-
* @returns Human-readable period text (e.g., "month", "year", "2 months")
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* // From PurchasesPackage
|
|
239
|
-
* getPeriodText(pkg) // Returns: "month"
|
|
240
|
-
*
|
|
241
|
-
* @example
|
|
242
|
-
* // From subscription period object
|
|
243
|
-
* getPeriodText({ unit: "MONTH", numberOfUnits: 1 }) // Returns: "month"
|
|
244
|
-
* getPeriodText({ unit: "YEAR", numberOfUnits: 1 }) // Returns: "year"
|
|
245
|
-
* getPeriodText({ unit: "MONTH", numberOfUnits: 3 }) // Returns: "3 months"
|
|
246
|
-
*/
|
|
247
|
-
export function getPeriodText(
|
|
248
|
-
input:
|
|
249
|
-
| {
|
|
250
|
-
product?: {
|
|
251
|
-
subscriptionPeriod?: {
|
|
252
|
-
unit: string;
|
|
253
|
-
numberOfUnits: number;
|
|
254
|
-
} | null;
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
| {
|
|
258
|
-
unit: string;
|
|
259
|
-
numberOfUnits: number;
|
|
260
|
-
}
|
|
261
|
-
| null
|
|
262
|
-
| undefined,
|
|
263
|
-
): string {
|
|
264
|
-
if (!input) return '';
|
|
265
|
-
|
|
266
|
-
// Extract subscription period from PurchasesPackage or use directly
|
|
267
|
-
let subscriptionPeriod:
|
|
268
|
-
| {
|
|
269
|
-
unit: string;
|
|
270
|
-
numberOfUnits: number;
|
|
271
|
-
}
|
|
272
|
-
| null
|
|
273
|
-
| undefined;
|
|
274
|
-
|
|
275
|
-
// Check if input is PurchasesPackage (has product property)
|
|
276
|
-
if ('product' in input && input.product?.subscriptionPeriod) {
|
|
277
|
-
const period = input.product.subscriptionPeriod;
|
|
278
|
-
// Type guard: check if period is an object with unit and numberOfUnits
|
|
279
|
-
if (
|
|
280
|
-
typeof period === 'object' &&
|
|
281
|
-
'unit' in period &&
|
|
282
|
-
'numberOfUnits' in period
|
|
283
|
-
) {
|
|
284
|
-
subscriptionPeriod = {
|
|
285
|
-
unit: period.unit as string,
|
|
286
|
-
numberOfUnits: period.numberOfUnits as number,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
} else if ('unit' in input && 'numberOfUnits' in input) {
|
|
290
|
-
// Input is already a subscription period object
|
|
291
|
-
subscriptionPeriod = {
|
|
292
|
-
unit: input.unit,
|
|
293
|
-
numberOfUnits: input.numberOfUnits,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (!subscriptionPeriod) return '';
|
|
298
|
-
|
|
299
|
-
const { unit, numberOfUnits } = subscriptionPeriod;
|
|
300
|
-
|
|
301
|
-
if (unit === SUBSCRIPTION_PERIOD_UNITS.MONTH) {
|
|
302
|
-
return numberOfUnits === 1 ? 'month' : `${numberOfUnits} months`;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (unit === SUBSCRIPTION_PERIOD_UNITS.YEAR) {
|
|
306
|
-
return numberOfUnits === 1 ? 'year' : `${numberOfUnits} years`;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (unit === SUBSCRIPTION_PERIOD_UNITS.WEEK) {
|
|
310
|
-
return numberOfUnits === 1 ? 'week' : `${numberOfUnits} weeks`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return '';
|
|
314
|
-
}
|
|
315
|
-
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
import { SUBSCRIPTION_PERIOD_UNITS } from './subscriptionConstants';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get subscription period text from RevenueCat package
|
|
15
|
+
* Extracts readable period text from PurchasesPackage or subscription period object
|
|
16
|
+
*
|
|
17
|
+
* @param input - RevenueCat PurchasesPackage or subscription period object
|
|
18
|
+
* @returns Human-readable period text (e.g., "month", "year", "2 months")
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // From PurchasesPackage
|
|
22
|
+
* getPeriodText(pkg) // Returns: "month"
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // From subscription period object
|
|
26
|
+
* getPeriodText({ unit: "MONTH", numberOfUnits: 1 }) // Returns: "month"
|
|
27
|
+
* getPeriodText({ unit: "YEAR", numberOfUnits: 1 }) // Returns: "year"
|
|
28
|
+
* getPeriodText({ unit: "MONTH", numberOfUnits: 3 }) // Returns: "3 months"
|
|
29
|
+
*/
|
|
30
|
+
export function getPeriodText(
|
|
31
|
+
input:
|
|
32
|
+
| {
|
|
33
|
+
product?: {
|
|
34
|
+
subscriptionPeriod?:
|
|
35
|
+
| {
|
|
36
|
+
unit: string;
|
|
37
|
+
numberOfUnits: number;
|
|
38
|
+
}
|
|
39
|
+
| string
|
|
40
|
+
| null;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
unit: string;
|
|
45
|
+
numberOfUnits: number;
|
|
46
|
+
}
|
|
47
|
+
| null
|
|
48
|
+
| undefined,
|
|
49
|
+
): string {
|
|
50
|
+
if (!input) return '';
|
|
51
|
+
|
|
52
|
+
// Extract subscription period from PurchasesPackage or use directly
|
|
53
|
+
let subscriptionPeriod:
|
|
54
|
+
| {
|
|
55
|
+
unit: string;
|
|
56
|
+
numberOfUnits: number;
|
|
57
|
+
}
|
|
58
|
+
| null
|
|
59
|
+
| undefined;
|
|
60
|
+
|
|
61
|
+
// Check if input is PurchasesPackage (has product property)
|
|
62
|
+
if ('product' in input && input.product?.subscriptionPeriod) {
|
|
63
|
+
const period = input.product.subscriptionPeriod;
|
|
64
|
+
// Type guard: check if period is an object with unit and numberOfUnits
|
|
65
|
+
// RevenueCat's subscriptionPeriod can be string | null | object
|
|
66
|
+
if (
|
|
67
|
+
typeof period === 'object' &&
|
|
68
|
+
period !== null &&
|
|
69
|
+
'unit' in period &&
|
|
70
|
+
'numberOfUnits' in period
|
|
71
|
+
) {
|
|
72
|
+
subscriptionPeriod = {
|
|
73
|
+
unit: period.unit as string,
|
|
74
|
+
numberOfUnits: period.numberOfUnits as number,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// If period is string, we can't extract unit/numberOfUnits, return empty
|
|
78
|
+
} else if ('unit' in input && 'numberOfUnits' in input) {
|
|
79
|
+
// Input is already a subscription period object
|
|
80
|
+
subscriptionPeriod = {
|
|
81
|
+
unit: input.unit,
|
|
82
|
+
numberOfUnits: input.numberOfUnits,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!subscriptionPeriod) return '';
|
|
87
|
+
|
|
88
|
+
const { unit, numberOfUnits } = subscriptionPeriod;
|
|
89
|
+
|
|
90
|
+
if (unit === SUBSCRIPTION_PERIOD_UNITS.MONTH) {
|
|
91
|
+
return numberOfUnits === 1 ? 'month' : `${numberOfUnits} months`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (unit === SUBSCRIPTION_PERIOD_UNITS.YEAR) {
|
|
95
|
+
return numberOfUnits === 1 ? 'year' : `${numberOfUnits} years`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (unit === SUBSCRIPTION_PERIOD_UNITS.WEEK) {
|
|
99
|
+
return numberOfUnits === 1 ? 'week' : `${numberOfUnits} weeks`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
import { DATE_CONSTANTS } from './subscriptionConstants';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format price for display
|
|
15
|
+
* Formats a price value with currency code using Intl.NumberFormat
|
|
16
|
+
*
|
|
17
|
+
* @param price - Price value (e.g., 9.99)
|
|
18
|
+
* @param currencyCode - ISO 4217 currency code (e.g., "USD", "EUR", "TRY")
|
|
19
|
+
* @returns Formatted price string (e.g., "$9.99", "€9.99", "₺9.99")
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* formatPrice(9.99, "USD") // Returns: "$9.99"
|
|
23
|
+
* formatPrice(229.99, "TRY") // Returns: "₺229.99"
|
|
24
|
+
*/
|
|
25
|
+
export function formatPrice(price: number, currencyCode: string): string {
|
|
26
|
+
return new Intl.NumberFormat(DATE_CONSTANTS.DEFAULT_LOCALE, {
|
|
27
|
+
style: 'currency',
|
|
28
|
+
currency: currencyCode,
|
|
29
|
+
}).format(price);
|
|
30
|
+
}
|
|
31
|
+
|