@umituz/react-native-subscription 2.22.9 → 2.22.10
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.22.
|
|
3
|
+
"version": "2.22.10",
|
|
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",
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import type { UserCredits, SubscriptionStatus } from "../../domain/entities/Credits";
|
|
2
2
|
import type { UserCreditsDocumentRead } from "../models/UserCreditsDocument";
|
|
3
3
|
|
|
4
|
-
/** Maps Firestore document to domain entity */
|
|
4
|
+
/** Maps Firestore document to domain entity with expiration validation */
|
|
5
5
|
export class CreditsMapper {
|
|
6
6
|
static toEntity(doc: UserCreditsDocumentRead): UserCredits {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const expirationDate = doc.expirationDate?.toDate?.() ?? null;
|
|
8
|
+
|
|
9
|
+
// Validate isPremium against expirationDate (real-time check)
|
|
10
|
+
const { isPremium, status } = CreditsMapper.validateSubscription(doc, expirationDate);
|
|
9
11
|
|
|
10
12
|
return {
|
|
11
|
-
// Core subscription
|
|
12
|
-
isPremium
|
|
13
|
+
// Core subscription (validated)
|
|
14
|
+
isPremium,
|
|
13
15
|
status,
|
|
14
16
|
|
|
15
17
|
// Dates
|
|
16
18
|
purchasedAt: doc.purchasedAt?.toDate?.() ?? null,
|
|
17
|
-
expirationDate
|
|
19
|
+
expirationDate,
|
|
18
20
|
lastUpdatedAt: doc.lastUpdatedAt?.toDate?.() ?? null,
|
|
19
21
|
|
|
20
22
|
// RevenueCat details
|
|
@@ -35,14 +37,33 @@ export class CreditsMapper {
|
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
/**
|
|
39
|
-
private static
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
/** Validate subscription status against expirationDate */
|
|
41
|
+
private static validateSubscription(
|
|
42
|
+
doc: UserCreditsDocumentRead,
|
|
43
|
+
expirationDate: Date | null
|
|
44
|
+
): { isPremium: boolean; status: SubscriptionStatus } {
|
|
45
|
+
const docIsPremium = doc.isPremium ?? false;
|
|
46
|
+
|
|
47
|
+
// No expiration date = lifetime or free
|
|
48
|
+
if (!expirationDate) {
|
|
49
|
+
return {
|
|
50
|
+
isPremium: docIsPremium,
|
|
51
|
+
status: docIsPremium ? "active" : "free",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if subscription has expired
|
|
56
|
+
const isExpired = expirationDate < new Date();
|
|
57
|
+
|
|
58
|
+
if (isExpired) {
|
|
59
|
+
// Subscription expired - override document's isPremium
|
|
60
|
+
return { isPremium: false, status: "expired" };
|
|
45
61
|
}
|
|
46
|
-
|
|
62
|
+
|
|
63
|
+
// Subscription still active
|
|
64
|
+
return {
|
|
65
|
+
isPremium: docIsPremium,
|
|
66
|
+
status: docIsPremium ? "active" : "free",
|
|
67
|
+
};
|
|
47
68
|
}
|
|
48
69
|
}
|
|
@@ -129,6 +129,25 @@ export class CreditsRepository extends BaseRepository {
|
|
|
129
129
|
const res = await this.getCredits(userId);
|
|
130
130
|
return !!(res.success && res.data && res.data.credits >= cost);
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
/** Sync expired subscription status to Firestore (background) */
|
|
134
|
+
async syncExpiredStatus(userId: string): Promise<void> {
|
|
135
|
+
const db = getFirestore();
|
|
136
|
+
if (!db) return;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const ref = this.getRef(db, userId);
|
|
140
|
+
const { updateDoc } = await import("firebase/firestore");
|
|
141
|
+
await updateDoc(ref, {
|
|
142
|
+
isPremium: false,
|
|
143
|
+
status: "expired",
|
|
144
|
+
lastUpdatedAt: serverTimestamp(),
|
|
145
|
+
});
|
|
146
|
+
if (__DEV__) console.log("[CreditsRepository] Synced expired status for:", userId.slice(0, 8));
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (__DEV__) console.error("[CreditsRepository] Sync expired failed:", e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
export const createCreditsRepository = (c: CreditsConfig) => new CreditsRepository(c);
|
|
@@ -88,15 +88,22 @@ export const useCredits = ({
|
|
|
88
88
|
if (__DEV__) console.error("[useCredits] Query failed:", result.error?.message);
|
|
89
89
|
throw new Error(result.error?.message || "Failed to fetch credits");
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
|
|
92
|
+
// Background sync: If mapper detected expired status, sync to Firestore
|
|
93
|
+
if (result.data?.status === "expired") {
|
|
94
|
+
if (__DEV__) console.log("[useCredits] Detected expired subscription, syncing...");
|
|
95
|
+
repository.syncExpiredStatus(userId).catch(() => {});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (__DEV__) console.log("[useCredits] Query success:", { hasData: !!result.data, credits: result.data?.credits, status: result.data?.status });
|
|
92
99
|
return result.data || null;
|
|
93
100
|
},
|
|
94
101
|
enabled: queryEnabled,
|
|
95
102
|
staleTime,
|
|
96
103
|
gcTime,
|
|
97
|
-
refetchOnMount: true,
|
|
98
|
-
refetchOnWindowFocus: true,
|
|
99
|
-
refetchOnReconnect: true,
|
|
104
|
+
refetchOnMount: true,
|
|
105
|
+
refetchOnWindowFocus: true,
|
|
106
|
+
refetchOnReconnect: true,
|
|
100
107
|
});
|
|
101
108
|
|
|
102
109
|
const credits = data ?? null;
|