@umituz/react-native-subscription 2.31.0 → 2.31.2
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/credits/presentation/useCredits.ts +3 -3
- package/src/domains/credits/utils/creditValidation.ts +3 -3
- package/src/domains/paywall/components/PaywallModal.tsx +3 -3
- package/src/domains/subscription/application/SubscriptionSyncService.ts +7 -7
- package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +0 -15
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +0 -17
- package/src/domains/subscription/utils/authGuards.ts +1 -1
- package/src/domains/subscription/utils/syncStatus.ts +1 -1
- package/src/shared/utils/numberUtils.core.ts +0 -10
- package/src/domains/subscription/core/SubscriptionDisplayConfig.ts +0 -1
- package/src/domains/wallet/presentation/WalletDisplayConfig.ts +0 -1
- package/src/shared/types/CommonTypes.ts +0 -70
- package/src/shared/utils/typeGuards.ts +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.31.
|
|
3
|
+
"version": "2.31.2",
|
|
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",
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getCreditsConfig,
|
|
8
8
|
isCreditsRepositoryConfigured,
|
|
9
9
|
} from "../infrastructure/CreditsRepositoryManager";
|
|
10
|
-
import {
|
|
10
|
+
import { calculateSafePercentage, canAffordAmount } from "../utils/creditValidation";
|
|
11
11
|
import { isAuthenticated } from "../../subscription/utils/authGuards";
|
|
12
12
|
import { creditsQueryKeys } from "./creditsQueryKeys";
|
|
13
13
|
import type { UseCreditsResult, CreditsLoadStatus } from "./useCredits.types";
|
|
@@ -73,12 +73,12 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
73
73
|
const derivedValues = useMemo(() => {
|
|
74
74
|
const has = (credits?.credits ?? 0) > 0;
|
|
75
75
|
const limit = config?.creditLimit ?? 0;
|
|
76
|
-
const percent =
|
|
76
|
+
const percent = calculateSafePercentage(credits?.credits, limit);
|
|
77
77
|
return { hasCredits: has, creditsPercent: percent };
|
|
78
78
|
}, [credits, config?.creditLimit]);
|
|
79
79
|
|
|
80
80
|
const canAfford = useCallback(
|
|
81
|
-
(cost: number): boolean =>
|
|
81
|
+
(cost: number): boolean => canAffordAmount(credits?.credits, cost),
|
|
82
82
|
[credits]
|
|
83
83
|
);
|
|
84
84
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { isValidNumber,
|
|
1
|
+
import { isValidNumber, isNonNegativeNumber } from "../../../shared/utils/validators";
|
|
2
2
|
|
|
3
3
|
export const isValidBalance = (balance: number | null | undefined): balance is number => {
|
|
4
|
-
return isValidNumber(balance) &&
|
|
4
|
+
return isValidNumber(balance) && isNonNegativeNumber(balance);
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
export const isValidCost = (cost: number): boolean => {
|
|
8
|
-
return isValidNumber(cost) &&
|
|
8
|
+
return isValidNumber(cost) && isNonNegativeNumber(cost);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export const isValidMaxCredits = (max: number): boolean => {
|
|
@@ -21,10 +21,10 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
|
|
|
21
21
|
|
|
22
22
|
const handleLegalUrl = useCallback(async (url: string | undefined) => {
|
|
23
23
|
if (!url) return;
|
|
24
|
-
try {
|
|
25
|
-
if (await Linking.canOpenURL(url)) await Linking.openURL(url);
|
|
24
|
+
try {
|
|
25
|
+
if (await Linking.canOpenURL(url)) await Linking.openURL(url);
|
|
26
26
|
} catch (err) {
|
|
27
|
-
|
|
27
|
+
// Silently fail - legal links are non-critical
|
|
28
28
|
}
|
|
29
29
|
}, []);
|
|
30
30
|
|
|
@@ -15,7 +15,7 @@ export class SubscriptionSyncService {
|
|
|
15
15
|
await this.processor.processPurchase(userId, productId, customerInfo, source);
|
|
16
16
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.PURCHASE_COMPLETED, { userId, productId });
|
|
17
17
|
} catch (err) {
|
|
18
|
-
|
|
18
|
+
// Swallow error - event bus consumers handle failures
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -24,23 +24,23 @@ export class SubscriptionSyncService {
|
|
|
24
24
|
await this.processor.processRenewal(userId, productId, newExpirationDate, customerInfo);
|
|
25
25
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.RENEWAL_DETECTED, { userId, productId });
|
|
26
26
|
} catch (err) {
|
|
27
|
-
|
|
27
|
+
// Swallow error - event bus consumers handle failures
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
async handlePremiumStatusChanged(
|
|
32
|
-
userId: string,
|
|
33
|
-
isPremium: boolean,
|
|
32
|
+
userId: string,
|
|
33
|
+
isPremium: boolean,
|
|
34
34
|
productId?: string,
|
|
35
|
-
expiresAt?: string,
|
|
36
|
-
willRenew?: boolean,
|
|
35
|
+
expiresAt?: string,
|
|
36
|
+
willRenew?: boolean,
|
|
37
37
|
periodType?: PeriodType
|
|
38
38
|
) {
|
|
39
39
|
try {
|
|
40
40
|
await this.processor.processStatusChange(userId, isPremium, productId, expiresAt, willRenew, periodType);
|
|
41
41
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.PREMIUM_STATUS_CHANGED, { userId, isPremium });
|
|
42
42
|
} catch (err) {
|
|
43
|
-
|
|
43
|
+
// Swallow error - event bus consumers handle failures
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -75,11 +75,6 @@ export class CustomerInfoListenerManager {
|
|
|
75
75
|
customerInfo
|
|
76
76
|
);
|
|
77
77
|
} catch (error) {
|
|
78
|
-
console.error('[CustomerInfoListener] Renewal callback failed', {
|
|
79
|
-
userId: this.currentUserId,
|
|
80
|
-
productId: renewalResult.productId,
|
|
81
|
-
error
|
|
82
|
-
});
|
|
83
78
|
// Swallow error to prevent listener crash
|
|
84
79
|
}
|
|
85
80
|
}
|
|
@@ -95,12 +90,6 @@ export class CustomerInfoListenerManager {
|
|
|
95
90
|
customerInfo
|
|
96
91
|
);
|
|
97
92
|
} catch (error) {
|
|
98
|
-
console.error('[CustomerInfoListener] Plan change callback failed', {
|
|
99
|
-
userId: this.currentUserId,
|
|
100
|
-
productId: renewalResult.productId,
|
|
101
|
-
previousProductId: renewalResult.previousProductId,
|
|
102
|
-
error
|
|
103
|
-
});
|
|
104
93
|
// Swallow error to prevent listener crash
|
|
105
94
|
}
|
|
106
95
|
}
|
|
@@ -113,10 +102,6 @@ export class CustomerInfoListenerManager {
|
|
|
113
102
|
try {
|
|
114
103
|
await syncPremiumStatus(config, this.currentUserId, customerInfo);
|
|
115
104
|
} catch (error) {
|
|
116
|
-
console.error('[CustomerInfoListener] Premium status sync failed', {
|
|
117
|
-
userId: this.currentUserId,
|
|
118
|
-
error
|
|
119
|
-
});
|
|
120
105
|
// Swallow error to prevent listener crash
|
|
121
106
|
}
|
|
122
107
|
}
|
|
@@ -36,12 +36,6 @@ export async function syncPremiumStatus(
|
|
|
36
36
|
await config.onPremiumStatusChanged(userId, false, undefined, undefined, undefined, undefined);
|
|
37
37
|
}
|
|
38
38
|
} catch (error) {
|
|
39
|
-
console.error('[PremiumStatusSyncer] Premium status change callback failed', {
|
|
40
|
-
userId,
|
|
41
|
-
isPremium: !!premiumEntitlement,
|
|
42
|
-
productId: premiumEntitlement?.productIdentifier,
|
|
43
|
-
error
|
|
44
|
-
});
|
|
45
39
|
// Silently fail callback notifications to prevent crashing the main flow
|
|
46
40
|
}
|
|
47
41
|
}
|
|
@@ -60,12 +54,6 @@ export async function notifyPurchaseCompleted(
|
|
|
60
54
|
try {
|
|
61
55
|
await config.onPurchaseCompleted(userId, productId, customerInfo, source);
|
|
62
56
|
} catch (error) {
|
|
63
|
-
console.error('[PremiumStatusSyncer] Purchase completed callback failed', {
|
|
64
|
-
userId,
|
|
65
|
-
productId,
|
|
66
|
-
source,
|
|
67
|
-
error
|
|
68
|
-
});
|
|
69
57
|
// Silently fail callback notifications to prevent crashing the main flow
|
|
70
58
|
}
|
|
71
59
|
}
|
|
@@ -83,11 +71,6 @@ export async function notifyRestoreCompleted(
|
|
|
83
71
|
try {
|
|
84
72
|
await config.onRestoreCompleted(userId, isPremium, customerInfo);
|
|
85
73
|
} catch (error) {
|
|
86
|
-
console.error('[PremiumStatusSyncer] Restore completed callback failed', {
|
|
87
|
-
userId,
|
|
88
|
-
isPremium,
|
|
89
|
-
error
|
|
90
|
-
});
|
|
91
74
|
// Silently fail callback notifications to prevent crashing the main flow
|
|
92
75
|
}
|
|
93
76
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { canAffordAmount, calculateSafePercentage } from "../../domains/credits/utils/creditValidation";
|
|
2
|
-
|
|
3
1
|
export function clamp(value: number, min: number, max: number): number {
|
|
4
2
|
return Math.min(Math.max(value, min), max);
|
|
5
3
|
}
|
|
@@ -30,11 +28,3 @@ export function safeDivide(numerator: number, denominator: number): number {
|
|
|
30
28
|
export function calculateRemaining(current: number, cost: number): number {
|
|
31
29
|
return Math.max(0, current - cost);
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
export function canAfford(balance: number | null | undefined, cost: number): boolean {
|
|
35
|
-
return canAffordAmount(balance, cost);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function calculateCreditPercentage(current: number | null | undefined, max: number): number {
|
|
39
|
-
return calculateSafePercentage(current, max);
|
|
40
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const EXPIRATION_WARNING_DAYS = 7;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const TRANSACTION_LIST_MAX_HEIGHT = 400;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Common Type Definitions
|
|
3
|
-
* Shared types used across multiple domains
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Platform as SubscriptionPlatform } from "../../domains/subscription/core/SubscriptionConstants";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Purchase result from any purchase operation
|
|
10
|
-
*/
|
|
11
|
-
export interface PurchaseResult {
|
|
12
|
-
success: boolean;
|
|
13
|
-
productId?: string;
|
|
14
|
-
error?: Error;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Transaction result for repository operations
|
|
19
|
-
*/
|
|
20
|
-
export interface TransactionResult<T = unknown> {
|
|
21
|
-
success: boolean;
|
|
22
|
-
data?: T;
|
|
23
|
-
error?: Error;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Subscription status information
|
|
28
|
-
*/
|
|
29
|
-
export interface SubscriptionStatusInfo {
|
|
30
|
-
isActive: boolean;
|
|
31
|
-
isExpired: boolean;
|
|
32
|
-
isTrial: boolean;
|
|
33
|
-
willRenew: boolean;
|
|
34
|
-
expirationDate?: Date;
|
|
35
|
-
productId?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Credits information
|
|
40
|
-
*/
|
|
41
|
-
export interface CreditsInfo {
|
|
42
|
-
credits: number;
|
|
43
|
-
creditLimit: number;
|
|
44
|
-
isPremium: boolean;
|
|
45
|
-
status: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Transaction metadata for any transaction type
|
|
50
|
-
*/
|
|
51
|
-
export interface TransactionMetadata {
|
|
52
|
-
productId: string;
|
|
53
|
-
amount: number;
|
|
54
|
-
currency?: string;
|
|
55
|
-
timestamp: Date;
|
|
56
|
-
type: 'purchase' | 'renewal' | 'restore' | 'credit_purchase';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Platform information
|
|
61
|
-
*/
|
|
62
|
-
export type Platform = SubscriptionPlatform;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Purchase source tracking
|
|
66
|
-
*/
|
|
67
|
-
export type PurchaseSource = 'settings' | 'paywall' | 'upgrade_prompt' | 'auto-execution' | 'manual';
|
|
68
|
-
|
|
69
|
-
// Re-export from SubscriptionConstants to maintain compatibility
|
|
70
|
-
export { PLATFORM, PURCHASE_SOURCE, type Platform as SubscriptionPlatformType, type PurchaseSource as PurchaseSourceType } from "../../domains/subscription/core/SubscriptionConstants";
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const isDefined = <T>(value: T | null | undefined): value is T => {
|
|
2
|
-
return value !== null && value !== undefined;
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
export const isPositive = (value: number): boolean => {
|
|
6
|
-
return value > 0;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const isNonNegative = (value: number): boolean => {
|
|
10
|
-
return value >= 0;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const isValidNumber = (value: number | null | undefined): value is number => {
|
|
14
|
-
return isDefined(value) && !isNaN(value) && isFinite(value);
|
|
15
|
-
};
|