@umituz/react-native-subscription 2.14.85 → 2.14.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/domains/wallet/domain/errors/WalletError.ts +0 -20
- package/src/presentation/components/feedback/PaywallFeedbackModal.tsx +1 -1
- package/src/presentation/hooks/useFeatureGate.ts +1 -51
- package/src/presentation/hooks/useSubscriptionSettingsConfig.ts +2 -18
- package/src/presentation/hooks/useUserTierWithRepository.ts +1 -21
- package/src/utils/creditMapper.ts +3 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.86",
|
|
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",
|
|
@@ -62,40 +62,24 @@ export class DuplicatePaymentError extends WalletError {
|
|
|
62
62
|
readonly code = "DUPLICATE_PAYMENT";
|
|
63
63
|
readonly category = "PAYMENT" as const;
|
|
64
64
|
readonly userMessage = WALLET_ERROR_MESSAGES.DUPLICATE_PAYMENT;
|
|
65
|
-
|
|
66
|
-
constructor(message: string) {
|
|
67
|
-
super(message);
|
|
68
|
-
}
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
export class UserValidationError extends WalletError {
|
|
72
68
|
readonly code = "USER_VALIDATION_ERROR";
|
|
73
69
|
readonly category = "VALIDATION" as const;
|
|
74
70
|
readonly userMessage = WALLET_ERROR_MESSAGES.USER_VALIDATION_FAILED;
|
|
75
|
-
|
|
76
|
-
constructor(message: string) {
|
|
77
|
-
super(message);
|
|
78
|
-
}
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
export class PackageValidationError extends WalletError {
|
|
82
74
|
readonly code = "PACKAGE_VALIDATION_ERROR";
|
|
83
75
|
readonly category = "VALIDATION" as const;
|
|
84
76
|
readonly userMessage = WALLET_ERROR_MESSAGES.PACKAGE_VALIDATION_FAILED;
|
|
85
|
-
|
|
86
|
-
constructor(message: string) {
|
|
87
|
-
super(message);
|
|
88
|
-
}
|
|
89
77
|
}
|
|
90
78
|
|
|
91
79
|
export class ReceiptValidationError extends WalletError {
|
|
92
80
|
readonly code = "RECEIPT_VALIDATION_ERROR";
|
|
93
81
|
readonly category = "VALIDATION" as const;
|
|
94
82
|
readonly userMessage = WALLET_ERROR_MESSAGES.RECEIPT_VALIDATION_FAILED;
|
|
95
|
-
|
|
96
|
-
constructor(message: string) {
|
|
97
|
-
super(message);
|
|
98
|
-
}
|
|
99
83
|
}
|
|
100
84
|
|
|
101
85
|
export class TransactionError extends WalletError {
|
|
@@ -122,10 +106,6 @@ export class CreditLimitError extends WalletError {
|
|
|
122
106
|
readonly code = "CREDIT_LIMIT_ERROR";
|
|
123
107
|
readonly category = "BUSINESS" as const;
|
|
124
108
|
readonly userMessage = WALLET_ERROR_MESSAGES.CREDIT_LIMIT_EXCEEDED;
|
|
125
|
-
|
|
126
|
-
constructor(message: string) {
|
|
127
|
-
super(message);
|
|
128
|
-
}
|
|
129
109
|
}
|
|
130
110
|
|
|
131
111
|
export class RefundError extends WalletError {
|
|
@@ -1,36 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useFeatureGate Hook
|
|
3
|
-
*
|
|
4
3
|
* Combines auth, subscription, and credits gates into a unified feature gate.
|
|
5
|
-
* Uses composition of smaller, single-responsibility hooks.
|
|
6
|
-
*
|
|
7
|
-
* Flow:
|
|
8
|
-
* 1. Auth check → Show auth modal if not authenticated
|
|
9
|
-
* 2. Subscription check → If subscribed, bypass credits and execute
|
|
10
|
-
* 3. Credits check → Show paywall if no credits
|
|
11
|
-
* 4. Execute action
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* const { requireFeature } = useFeatureGate({
|
|
16
|
-
* // Auth config
|
|
17
|
-
* isAuthenticated: !!user && !user.isAnonymous,
|
|
18
|
-
* onShowAuthModal: (cb) => showAuthModal(cb),
|
|
19
|
-
*
|
|
20
|
-
* // Subscription config (optional)
|
|
21
|
-
* hasSubscription: isPremium,
|
|
22
|
-
*
|
|
23
|
-
* // Credits config
|
|
24
|
-
* hasCredits: canAfford(cost),
|
|
25
|
-
* creditBalance: credits,
|
|
26
|
-
* requiredCredits: cost,
|
|
27
|
-
* onShowPaywall: (cost) => showPaywall(cost),
|
|
28
|
-
* });
|
|
29
|
-
*
|
|
30
|
-
* const handleGenerate = () => {
|
|
31
|
-
* requireFeature(() => generate());
|
|
32
|
-
* };
|
|
33
|
-
* ```
|
|
34
4
|
*/
|
|
35
5
|
|
|
36
6
|
import { useCallback } from "react";
|
|
@@ -38,7 +8,7 @@ import { useAuthGate } from "./useAuthGate";
|
|
|
38
8
|
import { useSubscriptionGate } from "./useSubscriptionGate";
|
|
39
9
|
import { useCreditsGate } from "./useCreditsGate";
|
|
40
10
|
|
|
41
|
-
|
|
11
|
+
|
|
42
12
|
|
|
43
13
|
export interface UseFeatureGateParams {
|
|
44
14
|
/** Whether user is authenticated (not guest/anonymous) */
|
|
@@ -105,15 +75,6 @@ export function useFeatureGate(
|
|
|
105
75
|
|
|
106
76
|
const requireFeature = useCallback(
|
|
107
77
|
(action: () => void | Promise<void>) => {
|
|
108
|
-
if (__DEV__) {
|
|
109
|
-
|
|
110
|
-
console.log("[useFeatureGate] Checking gates", {
|
|
111
|
-
isAuthenticated,
|
|
112
|
-
hasSubscription,
|
|
113
|
-
hasCredits,
|
|
114
|
-
creditBalance,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
78
|
|
|
118
79
|
// Step 1: Auth check
|
|
119
80
|
if (!authGate.requireAuth(() => {})) {
|
|
@@ -123,10 +84,6 @@ export function useFeatureGate(
|
|
|
123
84
|
|
|
124
85
|
// Step 2: Subscription check (bypasses credits if subscribed)
|
|
125
86
|
if (hasSubscription) {
|
|
126
|
-
if (__DEV__) {
|
|
127
|
-
|
|
128
|
-
console.log("[useFeatureGate] Has subscription, executing action");
|
|
129
|
-
}
|
|
130
87
|
action();
|
|
131
88
|
return;
|
|
132
89
|
}
|
|
@@ -137,19 +94,12 @@ export function useFeatureGate(
|
|
|
137
94
|
}
|
|
138
95
|
|
|
139
96
|
// Step 4: All checks passed, execute action
|
|
140
|
-
if (__DEV__) {
|
|
141
|
-
|
|
142
|
-
console.log("[useFeatureGate] All gates passed, executing action");
|
|
143
|
-
}
|
|
144
97
|
action();
|
|
145
98
|
},
|
|
146
99
|
[
|
|
147
100
|
authGate,
|
|
148
101
|
creditsGate,
|
|
149
102
|
hasSubscription,
|
|
150
|
-
isAuthenticated,
|
|
151
|
-
hasCredits,
|
|
152
|
-
creditBalance,
|
|
153
103
|
onShowAuthModal,
|
|
154
104
|
]
|
|
155
105
|
);
|
|
@@ -39,7 +39,6 @@ export const useSubscriptionSettingsConfig = (
|
|
|
39
39
|
): SubscriptionSettingsConfig => {
|
|
40
40
|
const {
|
|
41
41
|
userId,
|
|
42
|
-
isAnonymous = false,
|
|
43
42
|
currentLanguage = "en",
|
|
44
43
|
translations,
|
|
45
44
|
getCreditLimit,
|
|
@@ -75,14 +74,7 @@ export const useSubscriptionSettingsConfig = (
|
|
|
75
74
|
? statusExpirationDate.toISOString()
|
|
76
75
|
: null);
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
console.log("[useSubscriptionSettingsConfig] Date sources:", {
|
|
80
|
-
entitlementExpirationDate,
|
|
81
|
-
statusExpirationDate: statusExpirationDate?.toISOString() || null,
|
|
82
|
-
finalExpiresAtIso: expiresAtIso,
|
|
83
|
-
premiumEntitlementKeys: premiumEntitlement ? Object.keys(premiumEntitlement) : null,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
77
|
+
|
|
86
78
|
|
|
87
79
|
const willRenew = premiumEntitlement?.willRenew || false;
|
|
88
80
|
const purchasedAtIso = convertPurchasedAt(credits?.purchasedAt);
|
|
@@ -167,15 +159,7 @@ export const useSubscriptionSettingsConfig = (
|
|
|
167
159
|
]
|
|
168
160
|
);
|
|
169
161
|
|
|
170
|
-
|
|
171
|
-
console.log("[useSubscriptionSettingsConfig]", {
|
|
172
|
-
enabled: config.enabled,
|
|
173
|
-
isPremium,
|
|
174
|
-
isAnonymous,
|
|
175
|
-
hasCredits: !!credits,
|
|
176
|
-
userId: userId || "ANONYMOUS",
|
|
177
|
-
});
|
|
178
|
-
}
|
|
162
|
+
|
|
179
163
|
|
|
180
164
|
return config;
|
|
181
165
|
};
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useUserTierWithRepository Hook
|
|
3
|
-
*
|
|
4
|
-
* Complete hook that automatically fetches premium status from repository
|
|
5
|
-
* and provides user tier information. This eliminates the need for app-specific
|
|
6
|
-
* useUserTier wrappers.
|
|
7
|
-
*
|
|
8
|
-
* This hook combines:
|
|
9
|
-
* - Auth state (from AuthProvider)
|
|
10
|
-
* - Premium status fetching (from ISubscriptionRepository)
|
|
11
|
-
* - Tier logic (from useUserTier)
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* import { useUserTierWithRepository } from '@umituz/react-native-premium';
|
|
16
|
-
* import { useAuth } from '../../domains/auth';
|
|
17
|
-
* import { premiumRepository } from '@/infrastructure/repositories/PremiumRepository';
|
|
18
|
-
*
|
|
19
|
-
* const { tier, isPremium, isGuest, isLoading, refresh } = useUserTierWithRepository({
|
|
20
|
-
* auth: useAuth(),
|
|
21
|
-
* repository: premiumRepository,
|
|
22
|
-
* });
|
|
23
|
-
* ```
|
|
3
|
+
* Automatically fetches premium status and provides user tier information.
|
|
24
4
|
*/
|
|
25
5
|
|
|
26
6
|
import { useEffect, useState, useCallback } from 'react';
|
|
@@ -44,22 +44,8 @@ export const CREDIT_ALLOCATIONS: Record<
|
|
|
44
44
|
export function getCreditAllocation(
|
|
45
45
|
packageType: SubscriptionPackageType
|
|
46
46
|
): CreditAllocation | null {
|
|
47
|
-
if (packageType === "unknown")
|
|
48
|
-
|
|
49
|
-
console.warn(
|
|
50
|
-
"[CreditMapper] Cannot allocate credits for unknown package type"
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const allocation = CREDIT_ALLOCATIONS[packageType];
|
|
57
|
-
|
|
58
|
-
if (__DEV__) {
|
|
59
|
-
console.log("[CreditMapper] Credit allocation for", packageType, ":", allocation);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return allocation;
|
|
47
|
+
if (packageType === "unknown") return null;
|
|
48
|
+
return CREDIT_ALLOCATIONS[packageType];
|
|
63
49
|
}
|
|
64
50
|
|
|
65
51
|
/**
|
|
@@ -97,53 +83,18 @@ export function createCreditAmountsFromPackages(
|
|
|
97
83
|
): Record<string, number> {
|
|
98
84
|
const result: Record<string, number> = {};
|
|
99
85
|
|
|
100
|
-
if (__DEV__) {
|
|
101
|
-
console.log("[CreditMapper] Input packages count:", packages?.length || 0);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!packages || packages.length === 0) {
|
|
105
|
-
if (__DEV__) {
|
|
106
|
-
console.log("[CreditMapper] No packages provided, returning empty object");
|
|
107
|
-
}
|
|
108
|
-
return result;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
86
|
for (const pkg of packages) {
|
|
112
87
|
const identifier = pkg?.product?.identifier;
|
|
113
88
|
|
|
114
|
-
if (
|
|
115
|
-
console.log("[CreditMapper] Processing package:", {
|
|
116
|
-
hasProduct: !!pkg?.product,
|
|
117
|
-
identifier,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (!identifier) {
|
|
122
|
-
if (__DEV__) {
|
|
123
|
-
console.warn("[CreditMapper] Package missing product.identifier:", pkg);
|
|
124
|
-
}
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
89
|
+
if (!identifier) continue;
|
|
127
90
|
|
|
128
91
|
const packageType = detectPackageType(identifier);
|
|
129
92
|
const credits = getImageCreditsForPackage(packageType);
|
|
130
93
|
|
|
131
|
-
if (__DEV__) {
|
|
132
|
-
console.log("[CreditMapper] Package mapping:", {
|
|
133
|
-
identifier,
|
|
134
|
-
packageType,
|
|
135
|
-
credits,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
94
|
if (credits !== null) {
|
|
140
95
|
result[identifier] = credits;
|
|
141
96
|
}
|
|
142
97
|
}
|
|
143
98
|
|
|
144
|
-
if (__DEV__) {
|
|
145
|
-
console.log("[CreditMapper] Final credit amounts:", result);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
99
|
return result;
|
|
149
100
|
}
|