@umituz/react-native-subscription 2.27.64 → 2.27.66
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 +3 -1
- package/src/domain/entities/Credits.ts +10 -14
- package/src/domain/entities/SubscriptionConstants.ts +67 -0
- package/src/domain/entities/SubscriptionStatus.test.ts +45 -1
- package/src/domain/entities/SubscriptionStatus.ts +12 -19
- package/src/index.ts +3 -7
- package/src/infrastructure/mappers/CreditsMapper.ts +2 -1
- package/src/infrastructure/models/UserCreditsDocument.ts +18 -8
- package/src/infrastructure/services/SubscriptionInitializer.ts +3 -4
- package/src/presentation/components/details/PremiumStatusBadge.tsx +18 -15
- package/src/utils/packageTypeDetector.ts +23 -26
- package/src/domain/constants/README.md +0 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.27.
|
|
3
|
+
"version": "2.27.66",
|
|
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",
|
|
@@ -55,7 +55,9 @@
|
|
|
55
55
|
"@tanstack/query-async-storage-persister": "^5.66.7",
|
|
56
56
|
"@tanstack/react-query": "^5.0.0",
|
|
57
57
|
"@tanstack/react-query-persist-client": "^5.66.7",
|
|
58
|
+
"@types/jest": "^30.0.0",
|
|
58
59
|
"@types/react": "~19.1.10",
|
|
60
|
+
"@types/react-native": "^0.72.8",
|
|
59
61
|
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
60
62
|
"@typescript-eslint/parser": "^8.50.1",
|
|
61
63
|
"@umituz/react-native-auth": "^3.6.14",
|
|
@@ -6,21 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { SubscriptionPackageType } from "../../utils/packageTypeDetector";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
SubscriptionStatusType,
|
|
11
|
+
PeriodType,
|
|
12
|
+
PackageType,
|
|
13
|
+
Platform,
|
|
14
|
+
PurchaseSource,
|
|
15
|
+
PurchaseType
|
|
16
|
+
} from "./SubscriptionConstants";
|
|
10
17
|
|
|
11
18
|
export type CreditType = "text" | "image";
|
|
12
19
|
|
|
13
|
-
export type PurchaseSource =
|
|
14
|
-
| "onboarding"
|
|
15
|
-
| "settings"
|
|
16
|
-
| "upgrade_prompt"
|
|
17
|
-
| "home_screen"
|
|
18
|
-
| "feature_gate"
|
|
19
|
-
| "credits_exhausted"
|
|
20
|
-
| "renewal";
|
|
21
|
-
|
|
22
|
-
export type PurchaseType = "initial" | "renewal" | "upgrade" | "downgrade";
|
|
23
|
-
|
|
24
20
|
/** Single Source of Truth for user subscription + credits data */
|
|
25
21
|
export interface UserCredits {
|
|
26
22
|
// Core subscription
|
|
@@ -35,7 +31,7 @@ export interface UserCredits {
|
|
|
35
31
|
// RevenueCat subscription details
|
|
36
32
|
willRenew: boolean;
|
|
37
33
|
productId?: string;
|
|
38
|
-
packageType?:
|
|
34
|
+
packageType?: PackageType;
|
|
39
35
|
originalTransactionId?: string;
|
|
40
36
|
|
|
41
37
|
// Trial fields
|
|
@@ -53,7 +49,7 @@ export interface UserCredits {
|
|
|
53
49
|
// Metadata
|
|
54
50
|
purchaseSource?: PurchaseSource;
|
|
55
51
|
purchaseType?: PurchaseType;
|
|
56
|
-
platform?:
|
|
52
|
+
platform?: Platform;
|
|
57
53
|
appVersion?: string;
|
|
58
54
|
}
|
|
59
55
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscription Constants and Types
|
|
3
|
+
* Centralized source of truth for subscription-related enums and types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Subscription status constants */
|
|
7
|
+
export const SUBSCRIPTION_STATUS = {
|
|
8
|
+
ACTIVE: 'active',
|
|
9
|
+
TRIAL: 'trial',
|
|
10
|
+
TRIAL_CANCELED: 'trial_canceled',
|
|
11
|
+
EXPIRED: 'expired',
|
|
12
|
+
CANCELED: 'canceled',
|
|
13
|
+
NONE: 'none',
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
export type SubscriptionStatusType = (typeof SUBSCRIPTION_STATUS)[keyof typeof SUBSCRIPTION_STATUS];
|
|
17
|
+
|
|
18
|
+
/** RevenueCat period type constants */
|
|
19
|
+
export const PERIOD_TYPE = {
|
|
20
|
+
NORMAL: 'NORMAL',
|
|
21
|
+
INTRO: 'INTRO',
|
|
22
|
+
TRIAL: 'TRIAL',
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export type PeriodType = (typeof PERIOD_TYPE)[keyof typeof PERIOD_TYPE];
|
|
26
|
+
|
|
27
|
+
/** Subscription package type constants */
|
|
28
|
+
export const PACKAGE_TYPE = {
|
|
29
|
+
WEEKLY: 'weekly',
|
|
30
|
+
MONTHLY: 'monthly',
|
|
31
|
+
YEARLY: 'yearly',
|
|
32
|
+
LIFETIME: 'lifetime',
|
|
33
|
+
UNKNOWN: 'unknown',
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
export type PackageType = (typeof PACKAGE_TYPE)[keyof typeof PACKAGE_TYPE];
|
|
37
|
+
|
|
38
|
+
/** Platform constants */
|
|
39
|
+
export const PLATFORM = {
|
|
40
|
+
IOS: 'ios',
|
|
41
|
+
ANDROID: 'android',
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
export type Platform = (typeof PLATFORM)[keyof typeof PLATFORM];
|
|
45
|
+
|
|
46
|
+
/** Purchase source constants */
|
|
47
|
+
export const PURCHASE_SOURCE = {
|
|
48
|
+
ONBOARDING: 'onboarding',
|
|
49
|
+
SETTINGS: 'settings',
|
|
50
|
+
UPGRADE_PROMPT: 'upgrade_prompt',
|
|
51
|
+
HOME_SCREEN: 'home_screen',
|
|
52
|
+
FEATURE_GATE: 'feature_gate',
|
|
53
|
+
CREDITS_EXHAUSTED: 'credits_exhausted',
|
|
54
|
+
RENEWAL: 'renewal',
|
|
55
|
+
} as const;
|
|
56
|
+
|
|
57
|
+
export type PurchaseSource = (typeof PURCHASE_SOURCE)[keyof typeof PURCHASE_SOURCE];
|
|
58
|
+
|
|
59
|
+
/** Purchase type constants */
|
|
60
|
+
export const PURCHASE_TYPE = {
|
|
61
|
+
INITIAL: 'initial',
|
|
62
|
+
RENEWAL: 'renewal',
|
|
63
|
+
UPGRADE: 'upgrade',
|
|
64
|
+
DOWNGRADE: 'downgrade',
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
export type PurchaseType = (typeof PURCHASE_TYPE)[keyof typeof PURCHASE_TYPE];
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SUBSCRIPTION_STATUS,
|
|
3
|
+
} from './SubscriptionConstants';
|
|
1
4
|
import {
|
|
2
5
|
createDefaultSubscriptionStatus,
|
|
3
6
|
isSubscriptionValid,
|
|
4
7
|
calculateDaysRemaining,
|
|
8
|
+
resolveSubscriptionStatus,
|
|
5
9
|
} from './SubscriptionStatus';
|
|
6
10
|
|
|
7
11
|
describe('SubscriptionStatus', () => {
|
|
@@ -16,7 +20,7 @@ describe('SubscriptionStatus', () => {
|
|
|
16
20
|
purchasedAt: null,
|
|
17
21
|
customerId: null,
|
|
18
22
|
syncedAt: null,
|
|
19
|
-
status:
|
|
23
|
+
status: SUBSCRIPTION_STATUS.NONE,
|
|
20
24
|
});
|
|
21
25
|
});
|
|
22
26
|
});
|
|
@@ -102,4 +106,44 @@ describe('SubscriptionStatus', () => {
|
|
|
102
106
|
expect(calculateDaysRemaining(pastDate.toISOString())).toBe(0);
|
|
103
107
|
});
|
|
104
108
|
});
|
|
109
|
+
|
|
110
|
+
describe('resolveSubscriptionStatus', () => {
|
|
111
|
+
it('should return NONE when not premium', () => {
|
|
112
|
+
expect(resolveSubscriptionStatus({ isPremium: false })).toBe(SUBSCRIPTION_STATUS.NONE);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should return EXPIRED when expired', () => {
|
|
116
|
+
expect(resolveSubscriptionStatus({ isPremium: true, isExpired: true })).toBe(SUBSCRIPTION_STATUS.EXPIRED);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should return TRIAL when trialing and set to renew', () => {
|
|
120
|
+
expect(resolveSubscriptionStatus({
|
|
121
|
+
isPremium: true,
|
|
122
|
+
periodType: 'TRIAL',
|
|
123
|
+
willRenew: true
|
|
124
|
+
})).toBe(SUBSCRIPTION_STATUS.TRIAL);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should return TRIAL_CANCELED when trialing and will not renew', () => {
|
|
128
|
+
expect(resolveSubscriptionStatus({
|
|
129
|
+
isPremium: true,
|
|
130
|
+
periodType: 'TRIAL',
|
|
131
|
+
willRenew: false
|
|
132
|
+
})).toBe(SUBSCRIPTION_STATUS.TRIAL_CANCELED);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should return ACTIVE when premium, not expired, not trial, and set to renew', () => {
|
|
136
|
+
expect(resolveSubscriptionStatus({
|
|
137
|
+
isPremium: true,
|
|
138
|
+
willRenew: true
|
|
139
|
+
})).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should return CANCELED when premium, not expired, not trial, and will not renew', () => {
|
|
143
|
+
expect(resolveSubscriptionStatus({
|
|
144
|
+
isPremium: true,
|
|
145
|
+
willRenew: false
|
|
146
|
+
})).toBe(SUBSCRIPTION_STATUS.CANCELED);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
105
149
|
});
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
import { timezoneService } from "@umituz/react-native-design-system";
|
|
2
|
+
import {
|
|
3
|
+
SUBSCRIPTION_STATUS,
|
|
4
|
+
PERIOD_TYPE,
|
|
5
|
+
type PeriodType,
|
|
6
|
+
type SubscriptionStatusType
|
|
7
|
+
} from "./SubscriptionConstants";
|
|
2
8
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
NONE: 'none',
|
|
10
|
-
} as const;
|
|
11
|
-
|
|
12
|
-
/** RevenueCat period type constants */
|
|
13
|
-
export const PERIOD_TYPE = {
|
|
14
|
-
NORMAL: 'NORMAL',
|
|
15
|
-
INTRO: 'INTRO',
|
|
16
|
-
TRIAL: 'TRIAL',
|
|
17
|
-
} as const;
|
|
18
|
-
|
|
19
|
-
export type PeriodType = (typeof PERIOD_TYPE)[keyof typeof PERIOD_TYPE];
|
|
20
|
-
|
|
21
|
-
export type SubscriptionStatusType = (typeof SUBSCRIPTION_STATUS)[keyof typeof SUBSCRIPTION_STATUS];
|
|
9
|
+
export {
|
|
10
|
+
SUBSCRIPTION_STATUS,
|
|
11
|
+
PERIOD_TYPE,
|
|
12
|
+
type PeriodType,
|
|
13
|
+
type SubscriptionStatusType
|
|
14
|
+
};
|
|
22
15
|
|
|
23
16
|
export interface SubscriptionStatus {
|
|
24
17
|
isPremium: boolean;
|
package/src/index.ts
CHANGED
|
@@ -6,15 +6,14 @@ export * from "./domains/wallet";
|
|
|
6
6
|
export * from "./domains/paywall";
|
|
7
7
|
export * from "./domains/config";
|
|
8
8
|
|
|
9
|
-
// Domain Layer
|
|
9
|
+
// Domain Layer - Constants & Types
|
|
10
|
+
export * from "./domain/entities/SubscriptionConstants";
|
|
10
11
|
export {
|
|
11
|
-
SUBSCRIPTION_STATUS,
|
|
12
|
-
PERIOD_TYPE,
|
|
13
12
|
createDefaultSubscriptionStatus,
|
|
14
13
|
isSubscriptionValid,
|
|
15
14
|
resolveSubscriptionStatus,
|
|
16
15
|
} from "./domain/entities/SubscriptionStatus";
|
|
17
|
-
export type { SubscriptionStatus,
|
|
16
|
+
export type { SubscriptionStatus, StatusResolverInput } from "./domain/entities/SubscriptionStatus";
|
|
18
17
|
export type { SubscriptionConfig } from "./domain/value-objects/SubscriptionConfig";
|
|
19
18
|
export type { ISubscriptionRepository } from "./application/ports/ISubscriptionRepository";
|
|
20
19
|
|
|
@@ -78,15 +77,12 @@ export * from "./presentation/types/SubscriptionDetailTypes";
|
|
|
78
77
|
// Presentation Layer - Stores
|
|
79
78
|
export * from "./presentation/stores";
|
|
80
79
|
|
|
81
|
-
// Credits Domain
|
|
82
80
|
export type {
|
|
83
81
|
CreditType,
|
|
84
82
|
UserCredits,
|
|
85
83
|
CreditsConfig,
|
|
86
84
|
CreditsResult,
|
|
87
85
|
DeductCreditsResult,
|
|
88
|
-
PurchaseSource,
|
|
89
|
-
PurchaseType,
|
|
90
86
|
CreditAllocation,
|
|
91
87
|
PackageAllocationMap,
|
|
92
88
|
} from "./domain/entities/Credits";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { UserCredits } from "../../domain/entities/Credits";
|
|
2
|
-
import { resolveSubscriptionStatus
|
|
2
|
+
import { resolveSubscriptionStatus } from "../../domain/entities/SubscriptionStatus";
|
|
3
|
+
import type { PeriodType, SubscriptionStatusType } from "../../domain/entities/SubscriptionConstants";
|
|
3
4
|
import type { UserCreditsDocumentRead } from "../models/UserCreditsDocument";
|
|
4
5
|
|
|
5
6
|
/** Maps Firestore document to domain entity with expiration validation */
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
PurchaseSource,
|
|
3
|
+
PurchaseType,
|
|
4
|
+
SubscriptionStatusType,
|
|
5
|
+
PeriodType,
|
|
6
|
+
PackageType,
|
|
7
|
+
Platform
|
|
8
|
+
} from "../../domain/entities/SubscriptionConstants";
|
|
3
9
|
|
|
4
|
-
export type {
|
|
5
|
-
|
|
10
|
+
export type {
|
|
11
|
+
PurchaseSource,
|
|
12
|
+
PurchaseType,
|
|
13
|
+
SubscriptionStatusType,
|
|
14
|
+
PeriodType
|
|
15
|
+
};
|
|
6
16
|
|
|
7
17
|
export interface FirestoreTimestamp {
|
|
8
18
|
toDate: () => Date;
|
|
@@ -10,11 +20,11 @@ export interface FirestoreTimestamp {
|
|
|
10
20
|
|
|
11
21
|
export interface PurchaseMetadata {
|
|
12
22
|
productId: string;
|
|
13
|
-
packageType:
|
|
23
|
+
packageType: PackageType;
|
|
14
24
|
creditLimit: number;
|
|
15
25
|
source: PurchaseSource;
|
|
16
26
|
type: PurchaseType;
|
|
17
|
-
platform:
|
|
27
|
+
platform: Platform;
|
|
18
28
|
appVersion?: string;
|
|
19
29
|
timestamp: FirestoreTimestamp;
|
|
20
30
|
}
|
|
@@ -34,7 +44,7 @@ export interface UserCreditsDocumentRead {
|
|
|
34
44
|
// RevenueCat subscription details
|
|
35
45
|
willRenew?: boolean;
|
|
36
46
|
productId?: string;
|
|
37
|
-
packageType?:
|
|
47
|
+
packageType?: PackageType;
|
|
38
48
|
originalTransactionId?: string;
|
|
39
49
|
|
|
40
50
|
// Trial fields
|
|
@@ -52,7 +62,7 @@ export interface UserCreditsDocumentRead {
|
|
|
52
62
|
// Metadata
|
|
53
63
|
purchaseSource?: PurchaseSource;
|
|
54
64
|
purchaseType?: PurchaseType;
|
|
55
|
-
platform?:
|
|
65
|
+
platform?: Platform;
|
|
56
66
|
appVersion?: string;
|
|
57
67
|
processedPurchases?: string[];
|
|
58
68
|
purchaseHistory?: PurchaseMetadata[];
|
|
@@ -13,8 +13,8 @@ import { configureCreditsRepository, getCreditsRepository } from "../repositorie
|
|
|
13
13
|
import { SubscriptionManager } from "../../revenuecat/infrastructure/managers/SubscriptionManager";
|
|
14
14
|
import { configureAuthProvider } from "../../presentation/hooks/useAuthAwarePurchase";
|
|
15
15
|
import type { RevenueCatData } from "../../domain/types/RevenueCatData";
|
|
16
|
+
import { type PeriodType, type PurchaseSource } from "../../domain/entities/SubscriptionConstants";
|
|
16
17
|
import type { SubscriptionInitConfig, FirebaseAuthLike } from "./SubscriptionInitializerTypes";
|
|
17
|
-
import type { PurchaseSource } from "../../domain/entities/Credits";
|
|
18
18
|
|
|
19
19
|
export type { FirebaseAuthLike, CreditPackageConfig, SubscriptionInitConfig } from "./SubscriptionInitializerTypes";
|
|
20
20
|
|
|
@@ -53,8 +53,7 @@ const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId: string
|
|
|
53
53
|
expirationDate: entitlement?.expirationDate ?? customerInfo.latestExpirationDate ?? null,
|
|
54
54
|
willRenew: entitlement?.willRenew ?? false,
|
|
55
55
|
originalTransactionId: entitlement?.originalPurchaseDate ?? undefined,
|
|
56
|
-
|
|
57
|
-
periodType: entitlement?.periodType as "NORMAL" | "INTRO" | "TRIAL" | undefined,
|
|
56
|
+
periodType: entitlement?.periodType as PeriodType | undefined,
|
|
58
57
|
};
|
|
59
58
|
};
|
|
60
59
|
|
|
@@ -95,7 +94,7 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
|
|
|
95
94
|
|
|
96
95
|
const onPremiumStatusChanged = async (
|
|
97
96
|
userId: string, isPremium: boolean, productId?: string,
|
|
98
|
-
expiresAt?: string, willRenew?: boolean, periodType?:
|
|
97
|
+
expiresAt?: string, willRenew?: boolean, periodType?: PeriodType
|
|
99
98
|
) => {
|
|
100
99
|
if (__DEV__) console.log('[SubscriptionInitializer] onPremiumStatusChanged:', { userId, isPremium, productId, willRenew, periodType });
|
|
101
100
|
try {
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
import React, { useMemo } from "react";
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
SUBSCRIPTION_STATUS,
|
|
11
|
+
type SubscriptionStatusType
|
|
12
|
+
} from "../../../domain/entities/SubscriptionConstants";
|
|
10
13
|
|
|
11
14
|
export type { SubscriptionStatusType };
|
|
12
15
|
|
|
@@ -30,23 +33,23 @@ export const PremiumStatusBadge: React.FC<PremiumStatusBadgeProps> = ({
|
|
|
30
33
|
}) => {
|
|
31
34
|
const tokens = useAppDesignTokens();
|
|
32
35
|
|
|
33
|
-
const labels: Record<SubscriptionStatusType, string> = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
36
|
+
const labels: Record<SubscriptionStatusType, string> = useMemo(() => ({
|
|
37
|
+
[SUBSCRIPTION_STATUS.ACTIVE]: activeLabel,
|
|
38
|
+
[SUBSCRIPTION_STATUS.TRIAL]: activeLabel,
|
|
39
|
+
[SUBSCRIPTION_STATUS.TRIAL_CANCELED]: trialCanceledLabel ?? canceledLabel,
|
|
40
|
+
[SUBSCRIPTION_STATUS.EXPIRED]: expiredLabel,
|
|
41
|
+
[SUBSCRIPTION_STATUS.NONE]: noneLabel,
|
|
42
|
+
[SUBSCRIPTION_STATUS.CANCELED]: canceledLabel,
|
|
43
|
+
}), [activeLabel, trialCanceledLabel, canceledLabel, expiredLabel, noneLabel]);
|
|
41
44
|
|
|
42
45
|
const backgroundColor = useMemo(() => {
|
|
43
46
|
const colors: Record<SubscriptionStatusType, string> = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
[SUBSCRIPTION_STATUS.ACTIVE]: tokens.colors.success,
|
|
48
|
+
[SUBSCRIPTION_STATUS.TRIAL]: tokens.colors.primary, // Blue/purple for trial
|
|
49
|
+
[SUBSCRIPTION_STATUS.TRIAL_CANCELED]: tokens.colors.warning, // Orange for trial canceled
|
|
50
|
+
[SUBSCRIPTION_STATUS.EXPIRED]: tokens.colors.error,
|
|
51
|
+
[SUBSCRIPTION_STATUS.NONE]: tokens.colors.textTertiary,
|
|
52
|
+
[SUBSCRIPTION_STATUS.CANCELED]: tokens.colors.warning,
|
|
50
53
|
};
|
|
51
54
|
return colors[status];
|
|
52
55
|
}, [status, tokens.colors]);
|
|
@@ -3,36 +3,37 @@
|
|
|
3
3
|
* Detects subscription package type from RevenueCat package identifier
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import { PACKAGE_TYPE, type PackageType } from "../domain/entities/SubscriptionConstants";
|
|
7
7
|
|
|
8
|
+
export type SubscriptionPackageType = PackageType;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if identifier is a credit package (consumable purchase)
|
|
12
|
+
* Credit packages use a different system and don't need type detection
|
|
13
|
+
*/
|
|
8
14
|
/**
|
|
9
15
|
* Check if identifier is a credit package (consumable purchase)
|
|
10
16
|
* Credit packages use a different system and don't need type detection
|
|
11
17
|
*/
|
|
12
18
|
function isCreditPackage(identifier: string): boolean {
|
|
13
|
-
|
|
19
|
+
// Matches "credit" as a word or part of a common naming pattern
|
|
20
|
+
return /\bcredit\b|_credit_|-credit-/i.test(identifier) || identifier.toLowerCase().includes("credit");
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* Detect package type from product identifier
|
|
18
|
-
* Supports common RevenueCat naming patterns
|
|
19
|
-
* - premium_weekly, weekly_premium, premium-weekly
|
|
20
|
-
* - premium_monthly, monthly_premium, premium-monthly
|
|
21
|
-
* - premium_yearly, yearly_premium, premium-yearly, premium_annual, annual_premium
|
|
22
|
-
* - preview-product-id (Preview API mode in Expo Go)
|
|
23
|
-
*
|
|
24
|
-
* Note: Credit packages (consumable purchases) are skipped silently
|
|
25
|
+
* Supports common RevenueCat naming patterns with regex for better accuracy
|
|
25
26
|
*/
|
|
26
27
|
export function detectPackageType(productIdentifier: string): SubscriptionPackageType {
|
|
27
28
|
if (!productIdentifier) {
|
|
28
|
-
return
|
|
29
|
+
return PACKAGE_TYPE.UNKNOWN;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const normalized = productIdentifier.toLowerCase();
|
|
32
33
|
|
|
33
34
|
// Skip credit packages silently - they use creditPackageConfig instead
|
|
34
35
|
if (isCreditPackage(normalized)) {
|
|
35
|
-
return
|
|
36
|
+
return PACKAGE_TYPE.UNKNOWN;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// Preview API mode (Expo Go testing)
|
|
@@ -40,40 +41,36 @@ export function detectPackageType(productIdentifier: string): SubscriptionPackag
|
|
|
40
41
|
if (__DEV__) {
|
|
41
42
|
console.log("[PackageTypeDetector] Detected: PREVIEW (monthly)");
|
|
42
43
|
}
|
|
43
|
-
return
|
|
44
|
+
return PACKAGE_TYPE.MONTHLY;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
// Weekly detection
|
|
47
|
-
if (
|
|
47
|
+
// Weekly detection: matches "weekly" or "week" as distinct parts of the ID
|
|
48
|
+
if (/\bweekly?\b|_week_|-week-|\.week\./i.test(normalized)) {
|
|
48
49
|
if (__DEV__) {
|
|
49
50
|
console.log("[PackageTypeDetector] Detected: WEEKLY");
|
|
50
51
|
}
|
|
51
|
-
return
|
|
52
|
+
return PACKAGE_TYPE.WEEKLY;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
// Monthly detection
|
|
55
|
-
if (
|
|
55
|
+
// Monthly detection: matches "monthly" or "month"
|
|
56
|
+
if (/\bmonthly?\b|_month_|-month-|\.month\./i.test(normalized)) {
|
|
56
57
|
if (__DEV__) {
|
|
57
58
|
console.log("[PackageTypeDetector] Detected: MONTHLY");
|
|
58
59
|
}
|
|
59
|
-
return
|
|
60
|
+
return PACKAGE_TYPE.MONTHLY;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
// Yearly detection
|
|
63
|
-
if (
|
|
64
|
-
normalized.includes("yearly") ||
|
|
65
|
-
normalized.includes("year") ||
|
|
66
|
-
normalized.includes("annual")
|
|
67
|
-
) {
|
|
63
|
+
// Yearly detection: matches "yearly", "year", or "annual"
|
|
64
|
+
if (/\byearly?\b|_year_|-year-|\.year\.|annual/i.test(normalized)) {
|
|
68
65
|
if (__DEV__) {
|
|
69
66
|
console.log("[PackageTypeDetector] Detected: YEARLY");
|
|
70
67
|
}
|
|
71
|
-
return
|
|
68
|
+
return PACKAGE_TYPE.YEARLY;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
if (__DEV__) {
|
|
75
72
|
console.warn("[PackageTypeDetector] Unknown package type for:", productIdentifier);
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
return
|
|
75
|
+
return PACKAGE_TYPE.UNKNOWN;
|
|
79
76
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# Domain Constants
|
|
2
|
-
|
|
3
|
-
Constants used throughout the domain layer.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This directory contains constant definitions for subscription tiers, package periods, error codes, and other domain values.
|
|
8
|
-
|
|
9
|
-
## Constants
|
|
10
|
-
|
|
11
|
-
### Subscription Tiers
|
|
12
|
-
|
|
13
|
-
### Package Periods
|
|
14
|
-
|
|
15
|
-
### Error Codes
|
|
16
|
-
|
|
17
|
-
### Credit Limits
|
|
18
|
-
|
|
19
|
-
### Time Periods
|
|
20
|
-
|
|
21
|
-
## Related
|
|
22
|
-
|
|
23
|
-
- [Domain README](../README.md)
|
|
24
|
-
- [Entities](../entities/README.md)
|