@umituz/react-native-subscription 2.27.95 → 2.27.96
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/infrastructure/CreditsRepository.ts +28 -1
- package/src/domains/subscription/application/SubscriptionInitializer.ts +1 -1
- package/src/domains/subscription/application/SubscriptionSyncService.ts +32 -5
- package/src/domains/subscription/application/SubscriptionSyncUtils.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +5 -4
- package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.utils.ts +2 -2
- package/src/types/i18next.d.ts +2 -0
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.96",
|
|
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",
|
|
@@ -12,6 +12,8 @@ import { CreditsMapper } from "../core/CreditsMapper";
|
|
|
12
12
|
import type { RevenueCatData } from "../../subscription/core/RevenueCatData";
|
|
13
13
|
import { DeductCreditsCommand } from "../application/DeductCreditsCommand";
|
|
14
14
|
import { CreditLimitCalculator } from "../application/CreditLimitCalculator";
|
|
15
|
+
import { PURCHASE_TYPE, type PurchaseType } from "../../subscription/core/SubscriptionConstants";
|
|
16
|
+
import { updateDoc } from "firebase/firestore";
|
|
15
17
|
|
|
16
18
|
export class CreditsRepository extends BaseRepository {
|
|
17
19
|
private deductCommand: DeductCreditsCommand;
|
|
@@ -47,7 +49,8 @@ export class CreditsRepository extends BaseRepository {
|
|
|
47
49
|
purchaseId: string,
|
|
48
50
|
productId: string,
|
|
49
51
|
source: PurchaseSource,
|
|
50
|
-
revenueCatData: RevenueCatData
|
|
52
|
+
revenueCatData: RevenueCatData,
|
|
53
|
+
type: PurchaseType = PURCHASE_TYPE.INITIAL
|
|
51
54
|
): Promise<CreditsResult> {
|
|
52
55
|
const db = getFirestore();
|
|
53
56
|
if (!db) {
|
|
@@ -70,6 +73,7 @@ export class CreditsRepository extends BaseRepository {
|
|
|
70
73
|
originalTransactionId: revenueCatData.originalTransactionId,
|
|
71
74
|
isPremium: revenueCatData.isPremium,
|
|
72
75
|
periodType: revenueCatData.periodType,
|
|
76
|
+
type,
|
|
73
77
|
}
|
|
74
78
|
);
|
|
75
79
|
|
|
@@ -86,4 +90,27 @@ export class CreditsRepository extends BaseRepository {
|
|
|
86
90
|
async deductCredit(userId: string, cost: number): Promise<DeductCreditsResult> {
|
|
87
91
|
return this.deductCommand.execute(userId, cost);
|
|
88
92
|
}
|
|
93
|
+
|
|
94
|
+
async hasCredits(userId: string, cost: number): Promise<boolean> {
|
|
95
|
+
const result = await this.getCredits(userId);
|
|
96
|
+
if (!result.success || !result.data) return false;
|
|
97
|
+
return result.data.credits >= cost;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async syncExpiredStatus(userId: string): Promise<void> {
|
|
101
|
+
const db = getFirestore();
|
|
102
|
+
if (!db) throw new Error("Firestore instance is not available");
|
|
103
|
+
|
|
104
|
+
const ref = this.getRef(db, userId);
|
|
105
|
+
await updateDoc(ref, {
|
|
106
|
+
isPremium: false,
|
|
107
|
+
status: "expired",
|
|
108
|
+
willRenew: false,
|
|
109
|
+
expirationDate: new Date().toISOString()
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createCreditsRepository(config: CreditsConfig): CreditsRepository {
|
|
115
|
+
return new CreditsRepository(config);
|
|
89
116
|
}
|
|
@@ -92,7 +92,7 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
|
|
|
92
92
|
showAuthModal,
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
const initializeInBackground = async (userId
|
|
95
|
+
const initializeInBackground = async (userId?: string): Promise<void> => {
|
|
96
96
|
await SubscriptionManager.initialize(userId);
|
|
97
97
|
if (__DEV__) {
|
|
98
98
|
console.log('[SubscriptionInitializer] Background init complete');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CustomerInfo } from "react-native-purchases";
|
|
2
2
|
import type { RevenueCatData } from "../core/RevenueCatData";
|
|
3
|
-
import { type PeriodType, type PurchaseSource } from "../core/SubscriptionConstants";
|
|
3
|
+
import { type PeriodType, type PurchaseSource, PURCHASE_SOURCE, PURCHASE_TYPE } from "../core/SubscriptionConstants";
|
|
4
4
|
import { getCreditsRepository } from "../../credits/infrastructure/CreditsRepositoryProvider";
|
|
5
5
|
import { extractRevenueCatData } from "./SubscriptionSyncUtils";
|
|
6
6
|
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
|
|
@@ -19,7 +19,14 @@ export class SubscriptionSyncService {
|
|
|
19
19
|
? `purchase_${revenueCatData.originalTransactionId}`
|
|
20
20
|
: `purchase_${productId}_${Date.now()}`;
|
|
21
21
|
|
|
22
|
-
await getCreditsRepository().initializeCredits(
|
|
22
|
+
await getCreditsRepository().initializeCredits(
|
|
23
|
+
userId,
|
|
24
|
+
purchaseId,
|
|
25
|
+
productId,
|
|
26
|
+
source ?? PURCHASE_SOURCE.SETTINGS, // Default to settings if source unknown
|
|
27
|
+
revenueCatData,
|
|
28
|
+
PURCHASE_TYPE.INITIAL // Default to INITIAL
|
|
29
|
+
);
|
|
23
30
|
|
|
24
31
|
// Notify listeners via Event Bus
|
|
25
32
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
|
|
@@ -37,7 +44,14 @@ export class SubscriptionSyncService {
|
|
|
37
44
|
? `renewal_${revenueCatData.originalTransactionId}_${newExpirationDate}`
|
|
38
45
|
: `renewal_${productId}_${Date.now()}`;
|
|
39
46
|
|
|
40
|
-
await getCreditsRepository().initializeCredits(
|
|
47
|
+
await getCreditsRepository().initializeCredits(
|
|
48
|
+
userId,
|
|
49
|
+
purchaseId,
|
|
50
|
+
productId,
|
|
51
|
+
PURCHASE_SOURCE.RENEWAL,
|
|
52
|
+
revenueCatData,
|
|
53
|
+
PURCHASE_TYPE.RENEWAL
|
|
54
|
+
);
|
|
41
55
|
|
|
42
56
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
|
|
43
57
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.RENEWAL_DETECTED, { userId, productId });
|
|
@@ -61,14 +75,27 @@ export class SubscriptionSyncService {
|
|
|
61
75
|
return;
|
|
62
76
|
}
|
|
63
77
|
|
|
78
|
+
// If productId is missing, we can't initialize credits fully,
|
|
79
|
+
// but if isPremium is true, we should have it.
|
|
80
|
+
// Fallback to 'unknown' if missing, but this might throw in CreditLimitCalculator.
|
|
81
|
+
const validProductId = productId ?? 'unknown_product';
|
|
82
|
+
|
|
64
83
|
const revenueCatData: RevenueCatData = {
|
|
65
84
|
expirationDate: expiresAt ?? null,
|
|
66
85
|
willRenew: willRenew ?? false,
|
|
67
86
|
isPremium,
|
|
68
|
-
periodType
|
|
87
|
+
periodType: periodType ?? null, // Fix undefined vs null
|
|
88
|
+
originalTransactionId: null // Initialize with null as we might not have it here
|
|
69
89
|
};
|
|
70
90
|
|
|
71
|
-
await getCreditsRepository().initializeCredits(
|
|
91
|
+
await getCreditsRepository().initializeCredits(
|
|
92
|
+
userId,
|
|
93
|
+
`status_sync_${Date.now()}`,
|
|
94
|
+
validProductId,
|
|
95
|
+
PURCHASE_SOURCE.SETTINGS,
|
|
96
|
+
revenueCatData,
|
|
97
|
+
PURCHASE_TYPE.INITIAL // Status sync treated as Initial or Update
|
|
98
|
+
);
|
|
72
99
|
|
|
73
100
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.CREDITS_UPDATED, userId);
|
|
74
101
|
subscriptionEventBus.emit(SUBSCRIPTION_EVENTS.PREMIUM_STATUS_CHANGED, { userId, isPremium });
|
|
@@ -12,7 +12,7 @@ export const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId:
|
|
|
12
12
|
willRenew: entitlement?.willRenew ?? false,
|
|
13
13
|
// Use latestPurchaseDate if originalPurchaseDate is missing, or a combine id
|
|
14
14
|
originalTransactionId: entitlement?.originalPurchaseDate || customerInfo.firstSeen,
|
|
15
|
-
periodType: entitlement?.periodType as PeriodType
|
|
15
|
+
periodType: (entitlement?.periodType as PeriodType) ?? null,
|
|
16
16
|
isPremium: !!customerInfo.entitlements.active[entitlementId],
|
|
17
17
|
};
|
|
18
18
|
};
|
|
@@ -53,10 +53,11 @@ class SubscriptionManagerImpl {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async initialize(userId
|
|
56
|
+
async initialize(userId?: string): Promise<boolean> {
|
|
57
57
|
this.ensureConfigured();
|
|
58
58
|
|
|
59
|
-
const
|
|
59
|
+
const actualUserId = userId ?? (await this.managerConfig!.getAnonymousUserId());
|
|
60
|
+
const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(actualUserId);
|
|
60
61
|
|
|
61
62
|
if (!shouldInit && existingPromise) {
|
|
62
63
|
return existingPromise;
|
|
@@ -71,11 +72,11 @@ class SubscriptionManagerImpl {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
this.ensurePackageHandlerInitialized();
|
|
74
|
-
const result = await this.serviceInstance.initialize(
|
|
75
|
+
const result = await this.serviceInstance.initialize(actualUserId);
|
|
75
76
|
return result.success;
|
|
76
77
|
})();
|
|
77
78
|
|
|
78
|
-
this.state.initCache.setPromise(promise,
|
|
79
|
+
this.state.initCache.setPromise(promise, actualUserId);
|
|
79
80
|
return promise;
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -44,7 +44,7 @@ export function getSubscriptionStatusType(
|
|
|
44
44
|
isPremium: boolean,
|
|
45
45
|
willRenew?: boolean,
|
|
46
46
|
expiresAt?: string | null,
|
|
47
|
-
periodType?: PeriodType
|
|
47
|
+
periodType?: PeriodType | null
|
|
48
48
|
): SubscriptionStatusType {
|
|
49
49
|
const isExpired = expiresAt ? new Date(expiresAt) < new Date() : false;
|
|
50
50
|
|
|
@@ -52,6 +52,6 @@ export function getSubscriptionStatusType(
|
|
|
52
52
|
isPremium,
|
|
53
53
|
willRenew,
|
|
54
54
|
isExpired,
|
|
55
|
-
periodType,
|
|
55
|
+
periodType: periodType ?? undefined,
|
|
56
56
|
});
|
|
57
57
|
}
|