@umituz/react-native-subscription 2.37.71 → 2.37.73

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.37.71",
3
+ "version": "2.37.73",
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",
@@ -10,6 +10,8 @@ import { handleExpiredSubscription, handlePremiumStatusSync } from "./statusChan
10
10
  import type { PackageType } from "../../revenuecat/core/types";
11
11
 
12
12
  export class SubscriptionSyncProcessor {
13
+ private purchaseInProgress = false;
14
+
13
15
  constructor(
14
16
  private entitlementId: string,
15
17
  private getAnonymousUserId: () => Promise<string>
@@ -35,43 +37,53 @@ export class SubscriptionSyncProcessor {
35
37
  }
36
38
 
37
39
  async processPurchase(userId: string, productId: string, customerInfo: CustomerInfo, source?: PurchaseSource, packageType?: PackageType | null) {
38
- const revenueCatData = extractRevenueCatData(customerInfo, this.entitlementId);
39
- revenueCatData.packageType = packageType ?? null;
40
- revenueCatData.revenueCatUserId = await this.getRevenueCatAppUserId();
41
- const purchaseId = generatePurchaseId(revenueCatData.originalTransactionId, productId);
42
-
43
- const creditsUserId = await this.getCreditsUserId(userId);
44
-
45
- await getCreditsRepository().initializeCredits(
46
- creditsUserId,
47
- purchaseId,
48
- productId,
49
- source ?? PURCHASE_SOURCE.SETTINGS,
50
- revenueCatData,
51
- PURCHASE_TYPE.INITIAL
52
- );
53
-
54
- emitCreditsUpdated(creditsUserId);
40
+ this.purchaseInProgress = true;
41
+ try {
42
+ const revenueCatData = extractRevenueCatData(customerInfo, this.entitlementId);
43
+ revenueCatData.packageType = packageType ?? null;
44
+ revenueCatData.revenueCatUserId = await this.getRevenueCatAppUserId();
45
+ const purchaseId = generatePurchaseId(revenueCatData.originalTransactionId, productId);
46
+
47
+ const creditsUserId = await this.getCreditsUserId(userId);
48
+
49
+ await getCreditsRepository().initializeCredits(
50
+ creditsUserId,
51
+ purchaseId,
52
+ productId,
53
+ source ?? PURCHASE_SOURCE.SETTINGS,
54
+ revenueCatData,
55
+ PURCHASE_TYPE.INITIAL
56
+ );
57
+
58
+ emitCreditsUpdated(creditsUserId);
59
+ } finally {
60
+ this.purchaseInProgress = false;
61
+ }
55
62
  }
56
63
 
57
64
  async processRenewal(userId: string, productId: string, newExpirationDate: string, customerInfo: CustomerInfo) {
58
- const revenueCatData = extractRevenueCatData(customerInfo, this.entitlementId);
59
- revenueCatData.expirationDate = newExpirationDate ?? revenueCatData.expirationDate;
60
- revenueCatData.revenueCatUserId = await this.getRevenueCatAppUserId();
61
- const purchaseId = generateRenewalId(revenueCatData.originalTransactionId, productId, newExpirationDate);
62
-
63
- const creditsUserId = await this.getCreditsUserId(userId);
64
-
65
- await getCreditsRepository().initializeCredits(
66
- creditsUserId,
67
- purchaseId,
68
- productId,
69
- PURCHASE_SOURCE.RENEWAL,
70
- revenueCatData,
71
- PURCHASE_TYPE.RENEWAL
72
- );
73
-
74
- emitCreditsUpdated(creditsUserId);
65
+ this.purchaseInProgress = true;
66
+ try {
67
+ const revenueCatData = extractRevenueCatData(customerInfo, this.entitlementId);
68
+ revenueCatData.expirationDate = newExpirationDate ?? revenueCatData.expirationDate;
69
+ revenueCatData.revenueCatUserId = await this.getRevenueCatAppUserId();
70
+ const purchaseId = generateRenewalId(revenueCatData.originalTransactionId, productId, newExpirationDate);
71
+
72
+ const creditsUserId = await this.getCreditsUserId(userId);
73
+
74
+ await getCreditsRepository().initializeCredits(
75
+ creditsUserId,
76
+ purchaseId,
77
+ productId,
78
+ PURCHASE_SOURCE.RENEWAL,
79
+ revenueCatData,
80
+ PURCHASE_TYPE.RENEWAL
81
+ );
82
+
83
+ emitCreditsUpdated(creditsUserId);
84
+ } finally {
85
+ this.purchaseInProgress = false;
86
+ }
75
87
  }
76
88
 
77
89
  async processStatusChange(
@@ -82,6 +94,16 @@ export class SubscriptionSyncProcessor {
82
94
  willRenew?: boolean,
83
95
  periodType?: PeriodType
84
96
  ) {
97
+ // Skip if a purchase is already handling the credits document.
98
+ // Both PurchaseExecutor and CustomerInfoListener fire after a purchase —
99
+ // the purchase handler writes credits + metadata, so the status handler can skip.
100
+ if (this.purchaseInProgress) {
101
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
102
+ console.log("[SubscriptionSyncProcessor] Skipping status change - purchase in progress");
103
+ }
104
+ return;
105
+ }
106
+
85
107
  const creditsUserId = await this.getCreditsUserId(userId);
86
108
 
87
109
  if (!isPremium && productId) {
@@ -90,6 +112,9 @@ export class SubscriptionSyncProcessor {
90
112
  }
91
113
 
92
114
  if (!isPremium && !productId) {
115
+ // Cancellation: RevenueCat removed entitlement, no productId available.
116
+ // Must still update Firestore to reflect expired/canceled status.
117
+ await handleExpiredSubscription(creditsUserId);
93
118
  return;
94
119
  }
95
120