@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.19",
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
- const success = await purchasePackage(pkg);
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
- if (onPurchaseSuccess) onPurchaseSuccess();
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, purchasePackage, onPurchaseSuccess, t]);
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
- * Automatically initializes credits after successful purchase
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
- * After successful purchase, automatically initializes credits
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
- * Automatically initializes credits after successful restore
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
- * After successful restore, automatically initializes credits
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,