@umituz/react-native-subscription 2.13.19 → 2.13.21
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.13.
|
|
3
|
+
"version": "2.13.21",
|
|
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",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Follows "Package Driven Design" by accepting dynamic props.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { useState, useCallback } from "react";
|
|
9
|
+
import { useState, useCallback, useRef, useEffect } from "react";
|
|
10
10
|
import { Alert } from "react-native";
|
|
11
11
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
12
12
|
import { usePremium } from "./usePremium";
|
|
@@ -53,6 +53,18 @@ export function usePaywallOperations({
|
|
|
53
53
|
const { purchasePackage, restorePurchase, closePaywall } = usePremium(userId);
|
|
54
54
|
const [pendingPackage, setPendingPackage] = useState<PurchasesPackage | null>(null);
|
|
55
55
|
|
|
56
|
+
// Ref to always have latest purchasePackage function (avoids stale closure)
|
|
57
|
+
const purchasePackageRef = useRef(purchasePackage);
|
|
58
|
+
const onPurchaseSuccessRef = useRef(onPurchaseSuccess);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
purchasePackageRef.current = purchasePackage;
|
|
62
|
+
}, [purchasePackage]);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
onPurchaseSuccessRef.current = onPurchaseSuccess;
|
|
66
|
+
}, [onPurchaseSuccess]);
|
|
67
|
+
|
|
56
68
|
/**
|
|
57
69
|
* Check if action requires authentication
|
|
58
70
|
* @returns true if authenticated, false if auth required
|
|
@@ -174,10 +186,12 @@ export function usePaywallOperations({
|
|
|
174
186
|
const pkg = pendingPackage;
|
|
175
187
|
setPendingPackage(null);
|
|
176
188
|
|
|
177
|
-
|
|
189
|
+
// Use ref to get latest purchasePackage (avoids stale closure after auth)
|
|
190
|
+
const success = await purchasePackageRef.current(pkg);
|
|
178
191
|
|
|
179
192
|
if (success) {
|
|
180
|
-
|
|
193
|
+
// Use ref to get latest callback
|
|
194
|
+
if (onPurchaseSuccessRef.current) onPurchaseSuccessRef.current();
|
|
181
195
|
} else {
|
|
182
196
|
Alert.alert(
|
|
183
197
|
t("premium.purchaseError"),
|
|
@@ -186,7 +200,7 @@ export function usePaywallOperations({
|
|
|
186
200
|
}
|
|
187
201
|
|
|
188
202
|
return success;
|
|
189
|
-
}, [pendingPackage,
|
|
203
|
+
}, [pendingPackage, t]);
|
|
190
204
|
|
|
191
205
|
const clearPendingPackage = useCallback(() => {
|
|
192
206
|
setPendingPackage(null);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Purchase Package Hook
|
|
3
3
|
* TanStack mutation for purchasing subscription packages
|
|
4
|
-
*
|
|
4
|
+
* Credits are initialized by CustomerInfoListener (not here to avoid duplicates)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
} from "@umituz/react-native-sentry";
|
|
14
14
|
import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
|
|
15
15
|
import { creditsQueryKeys } from "../../../presentation/hooks/useCredits";
|
|
16
|
-
import { getCreditsRepository } from "../../../infrastructure/repositories/CreditsRepositoryProvider";
|
|
17
16
|
|
|
18
17
|
interface PurchaseResult {
|
|
19
18
|
success: boolean;
|
|
@@ -22,7 +21,7 @@ interface PurchaseResult {
|
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
* Purchase a subscription package
|
|
25
|
-
*
|
|
24
|
+
* Credits are initialized by CustomerInfoListener when entitlement becomes active
|
|
26
25
|
*/
|
|
27
26
|
export const usePurchasePackage = (userId: string | undefined) => {
|
|
28
27
|
const queryClient = useQueryClient();
|
|
@@ -49,35 +48,7 @@ export const usePurchasePackage = (userId: string | undefined) => {
|
|
|
49
48
|
productId,
|
|
50
49
|
userId,
|
|
51
50
|
});
|
|
52
|
-
|
|
53
|
-
// Initialize credits immediately after purchase
|
|
54
|
-
const repository = getCreditsRepository();
|
|
55
|
-
const creditResult = await repository.initializeCredits(
|
|
56
|
-
userId,
|
|
57
|
-
pkg.identifier,
|
|
58
|
-
productId
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
if (creditResult.success) {
|
|
62
|
-
addPackageBreadcrumb("subscription", "Credits initialized", {
|
|
63
|
-
productId,
|
|
64
|
-
userId,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Update cache immediately for instant UI update
|
|
68
|
-
if (creditResult.data) {
|
|
69
|
-
queryClient.setQueryData(
|
|
70
|
-
creditsQueryKeys.user(userId),
|
|
71
|
-
creditResult.data
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
addPackageBreadcrumb("subscription", "Credits initialization failed", {
|
|
76
|
-
productId,
|
|
77
|
-
userId,
|
|
78
|
-
error: creditResult.error?.message,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
51
|
+
// Credits will be initialized by CustomerInfoListener
|
|
81
52
|
} else {
|
|
82
53
|
addPackageBreadcrumb("subscription", "Purchase cancelled", {
|
|
83
54
|
packageId: pkg.identifier,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Restore Purchase Hook
|
|
3
3
|
* TanStack mutation for restoring previous purchases
|
|
4
|
-
*
|
|
4
|
+
* Credits are initialized by CustomerInfoListener (not here to avoid duplicates)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "@umituz/react-native-sentry";
|
|
13
13
|
import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
|
|
14
14
|
import { creditsQueryKeys } from "../../../presentation/hooks/useCredits";
|
|
15
|
-
import { getCreditsRepository } from "../../../infrastructure/repositories/CreditsRepositoryProvider";
|
|
16
15
|
|
|
17
16
|
interface RestoreResult {
|
|
18
17
|
success: boolean;
|
|
@@ -21,7 +20,7 @@ interface RestoreResult {
|
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* Restore previous purchases
|
|
24
|
-
*
|
|
23
|
+
* Credits are initialized by CustomerInfoListener when entitlement becomes active
|
|
25
24
|
*/
|
|
26
25
|
export const useRestorePurchase = (userId: string | undefined) => {
|
|
27
26
|
const queryClient = useQueryClient();
|
|
@@ -43,37 +42,7 @@ export const useRestorePurchase = (userId: string | undefined) => {
|
|
|
43
42
|
userId,
|
|
44
43
|
productId: result.productId,
|
|
45
44
|
});
|
|
46
|
-
|
|
47
|
-
// Initialize credits if we have a product ID
|
|
48
|
-
if (result.productId) {
|
|
49
|
-
const repository = getCreditsRepository();
|
|
50
|
-
const creditResult = await repository.initializeCredits(
|
|
51
|
-
userId,
|
|
52
|
-
undefined,
|
|
53
|
-
result.productId
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
if (creditResult.success) {
|
|
57
|
-
addPackageBreadcrumb("subscription", "Credits initialized after restore", {
|
|
58
|
-
productId: result.productId,
|
|
59
|
-
userId,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Update cache immediately for instant UI update
|
|
63
|
-
if (creditResult.data) {
|
|
64
|
-
queryClient.setQueryData(
|
|
65
|
-
creditsQueryKeys.user(userId),
|
|
66
|
-
creditResult.data
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
addPackageBreadcrumb("subscription", "Credits initialization failed after restore", {
|
|
71
|
-
productId: result.productId,
|
|
72
|
-
userId,
|
|
73
|
-
error: creditResult.error?.message,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
45
|
+
// Credits will be initialized by CustomerInfoListener
|
|
77
46
|
} else {
|
|
78
47
|
addPackageBreadcrumb("subscription", "Restore failed - no premium found", {
|
|
79
48
|
userId,
|