@umituz/react-native-subscription 2.37.109 → 2.37.110

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.109",
3
+ "version": "2.37.110",
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",
@@ -41,14 +41,14 @@ export async function initializeCreditsTransaction(
41
41
 
42
42
  // Global cross-user deduplication: prevent the same Apple/Google transaction
43
43
  // from allocating credits under multiple Firebase UIDs.
44
- if (metadata.originalTransactionId) {
45
- const globalRef = doc(_db, GLOBAL_TRANSACTION_COLLECTION, metadata.originalTransactionId);
44
+ if (metadata.storeTransactionId) {
45
+ const globalRef = doc(_db, GLOBAL_TRANSACTION_COLLECTION, metadata.storeTransactionId);
46
46
  const globalDoc = await transaction.get(globalRef);
47
47
  if (globalDoc.exists()) {
48
48
  const globalData = globalDoc.data();
49
49
  if (globalData?.ownerUserId && globalData.ownerUserId !== userId) {
50
50
  console.warn(
51
- `[CreditsInitializer] Transaction ${metadata.originalTransactionId} already processed by user ${globalData.ownerUserId}, skipping for ${userId}`
51
+ `[CreditsInitializer] Transaction ${metadata.storeTransactionId} already processed by user ${globalData.ownerUserId}, skipping for ${userId}`
52
52
  );
53
53
  return {
54
54
  credits: existingData.credits,
@@ -89,8 +89,8 @@ export async function initializeCreditsTransaction(
89
89
  transaction.set(creditsRef, creditsData, { merge: true });
90
90
 
91
91
  // Register transaction globally so other UIDs cannot claim the same purchase.
92
- if (metadata.originalTransactionId) {
93
- const globalRef = doc(_db, GLOBAL_TRANSACTION_COLLECTION, metadata.originalTransactionId);
92
+ if (metadata.storeTransactionId) {
93
+ const globalRef = doc(_db, GLOBAL_TRANSACTION_COLLECTION, metadata.storeTransactionId);
94
94
  transaction.set(globalRef, {
95
95
  ownerUserId: userId,
96
96
  purchaseId,
@@ -38,7 +38,7 @@ export function getCreditDocumentOrDefault(
38
38
  willRenew: false,
39
39
  productId: null,
40
40
  packageType: null,
41
- originalTransactionId: null,
41
+ storeTransactionId: null,
42
42
  store: null,
43
43
  ownershipType: null,
44
44
  appVersion: null,
@@ -68,7 +68,7 @@ export function buildCreditsData({
68
68
  ...(isPurchaseOrRenewal && { lastPurchaseAt: serverTimestamp() }),
69
69
  ...(expirationTimestamp && { expirationDate: expirationTimestamp }),
70
70
  ...(metadata.willRenew !== undefined && { willRenew: metadata.willRenew }),
71
- ...(metadata.originalTransactionId && { originalTransactionId: metadata.originalTransactionId }),
71
+ ...(metadata.storeTransactionId && { storeTransactionId: metadata.storeTransactionId }),
72
72
  ...(canceledAtTimestamp && { canceledAt: canceledAtTimestamp }),
73
73
  ...(billingIssueTimestamp && { billingIssueDetectedAt: billingIssueTimestamp }),
74
74
  ...(metadata.store && { store: metadata.store }),
@@ -19,7 +19,7 @@ export interface UserCredits {
19
19
  willRenew: boolean | null;
20
20
  productId: string | null;
21
21
  packageType: PackageType | null;
22
- originalTransactionId: string | null;
22
+ storeTransactionId: string | null;
23
23
  periodType: string | null;
24
24
  credits: number;
25
25
  creditLimit: number;
@@ -41,7 +41,7 @@ export function mapCreditsDocumentToEntity(doc: UserCreditsDocumentRead): UserCr
41
41
  willRenew: doc.willRenew,
42
42
  productId: doc.productId,
43
43
  packageType: doc.packageType,
44
- originalTransactionId: doc.originalTransactionId,
44
+ storeTransactionId: doc.storeTransactionId,
45
45
  periodType,
46
46
  credits: doc.credits,
47
47
  creditLimit: doc.creditLimit,
@@ -42,7 +42,7 @@ export interface UserCreditsDocumentRead {
42
42
  willRenew: boolean | null;
43
43
  productId: string | null;
44
44
  packageType: PackageType | null;
45
- originalTransactionId: string | null;
45
+ storeTransactionId: string | null;
46
46
  store: Store | null;
47
47
  ownershipType: OwnershipType | null;
48
48
  periodType: string | null;
@@ -92,7 +92,7 @@ export class CreditsRepository extends BaseRepository {
92
92
  willRenew: boolean,
93
93
  expirationDate: string | null,
94
94
  periodType: string | null,
95
- originalTransactionId?: string | null,
95
+ storeTransactionId?: string | null,
96
96
  ): Promise<boolean> {
97
97
  const db = requireFirestore();
98
98
  const creditLimit = calculateCreditLimit(productId, this.config);
@@ -105,7 +105,7 @@ export class CreditsRepository extends BaseRepository {
105
105
  periodType,
106
106
  db,
107
107
  userId,
108
- originalTransactionId,
108
+ storeTransactionId,
109
109
  );
110
110
  }
111
111
  }
@@ -63,7 +63,7 @@ export async function initializeCreditsWithRetry(params: InitializeCreditsParams
63
63
  source,
64
64
  expirationDate: revenueCatData.expirationDate,
65
65
  willRenew: revenueCatData.willRenew,
66
- originalTransactionId: revenueCatData.originalTransactionId,
66
+ storeTransactionId: revenueCatData.storeTransactionId,
67
67
  isPremium: revenueCatData.isPremium,
68
68
  periodType: revenueCatData.periodType,
69
69
  unsubscribeDetectedAt: revenueCatData.unsubscribeDetectedAt,
@@ -78,7 +78,7 @@ export async function syncPremiumMetadata(
78
78
  * This handles edge cases like test store purchases, reinstalls, or failed initializations.
79
79
  * Returns true if a new document was created, false if one already existed.
80
80
  *
81
- * Cross-user guard: if originalTransactionId is provided and already registered
81
+ * Cross-user guard: if storeTransactionId is provided and already registered
82
82
  * to a different user in the global processedTransactions collection, the recovery
83
83
  * document is NOT created (the subscription belongs to another UID).
84
84
  */
@@ -91,22 +91,22 @@ export async function createRecoveryCreditsDocument(
91
91
  periodType: string | null,
92
92
  db?: Firestore,
93
93
  userId?: string,
94
- originalTransactionId?: string | null,
94
+ storeTransactionId?: string | null,
95
95
  ): Promise<boolean> {
96
96
  const existingDoc = await getDoc(ref);
97
97
  if (existingDoc.exists()) return false;
98
98
 
99
99
  // Cross-user deduplication: if this transaction was already processed by another
100
100
  // user, skip recovery to prevent double credit allocation across UIDs.
101
- if (db && userId && originalTransactionId) {
101
+ if (db && userId && storeTransactionId) {
102
102
  try {
103
- const globalRef = doc(db, GLOBAL_TRANSACTION_COLLECTION, originalTransactionId);
103
+ const globalRef = doc(db, GLOBAL_TRANSACTION_COLLECTION, storeTransactionId);
104
104
  const globalDoc = await getDoc(globalRef);
105
105
  if (globalDoc.exists()) {
106
106
  const globalData = globalDoc.data();
107
107
  if (globalData?.ownerUserId && globalData.ownerUserId !== userId) {
108
108
  console.warn(
109
- `[CreditsWriter] Recovery skipped: transaction ${originalTransactionId} belongs to user ${globalData.ownerUserId}, not ${userId}`
109
+ `[CreditsWriter] Recovery skipped: transaction ${storeTransactionId} belongs to user ${globalData.ownerUserId}, not ${userId}`
110
110
  );
111
111
  return false;
112
112
  }
@@ -3,7 +3,7 @@ import type { Store, OwnershipType, PackageType } from "./RevenueCatTypes";
3
3
  export interface RevenueCatData {
4
4
  expirationDate: string | null;
5
5
  willRenew: boolean | null;
6
- originalTransactionId: string | null;
6
+ storeTransactionId: string | null;
7
7
  isPremium: boolean;
8
8
  periodType: string | null;
9
9
  packageType: PackageType | null;
@@ -196,7 +196,7 @@ export async function handleInitialConfiguration(
196
196
  expiresAt: premiumEntitlement.expirationDate ?? undefined,
197
197
  willRenew: premiumEntitlement.willRenew,
198
198
  periodType: premiumEntitlement.periodType as PeriodType | undefined,
199
- originalTransactionId: subscription?.storeTransactionId ?? undefined,
199
+ storeTransactionId: subscription?.storeTransactionId ?? undefined,
200
200
  });
201
201
  } else {
202
202
  await deps.config.onPremiumStatusChanged({
@@ -34,7 +34,7 @@ export interface InitializeCreditsMetadata {
34
34
  type: PurchaseType;
35
35
  expirationDate: string | null;
36
36
  willRenew: boolean | null;
37
- originalTransactionId: string | null;
37
+ storeTransactionId: string | null;
38
38
  isPremium: boolean;
39
39
  periodType: string | null;
40
40
  unsubscribeDetectedAt: string | null;
@@ -110,7 +110,7 @@ export class SubscriptionSyncProcessor {
110
110
  revenueCatData.packageType = event.packageType ?? null;
111
111
  const revenueCatAppUserId = await this.getRevenueCatAppUserId();
112
112
  revenueCatData.revenueCatUserId = revenueCatAppUserId ?? event.userId;
113
- const purchaseId = generatePurchaseId(revenueCatData.originalTransactionId, event.productId);
113
+ const purchaseId = generatePurchaseId(revenueCatData.storeTransactionId, event.productId);
114
114
 
115
115
  const creditsUserId = await this.getCreditsUserId(event.userId);
116
116
 
@@ -140,7 +140,7 @@ export class SubscriptionSyncProcessor {
140
140
  revenueCatData.expirationDate = event.newExpirationDate ?? revenueCatData.expirationDate;
141
141
  const revenueCatAppUserId = await this.getRevenueCatAppUserId();
142
142
  revenueCatData.revenueCatUserId = revenueCatAppUserId ?? event.userId;
143
- const purchaseId = generateRenewalId(revenueCatData.originalTransactionId, event.productId, event.newExpirationDate);
143
+ const purchaseId = generateRenewalId(revenueCatData.storeTransactionId, event.productId, event.newExpirationDate);
144
144
 
145
145
  const creditsUserId = await this.getCreditsUserId(event.userId);
146
146
 
@@ -222,7 +222,7 @@ export class SubscriptionSyncProcessor {
222
222
  event.willRenew ?? false,
223
223
  event.expiresAt ?? null,
224
224
  event.periodType ?? null,
225
- event.originalTransactionId,
225
+ event.storeTransactionId,
226
226
  );
227
227
  if (__DEV__ && created) {
228
228
  console.log('[SubscriptionSyncProcessor] Recovery: created missing credits document for premium user', {
@@ -23,7 +23,7 @@ export const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId:
23
23
  return {
24
24
  expirationDate: null,
25
25
  willRenew: null,
26
- originalTransactionId: null,
26
+ storeTransactionId: null,
27
27
  periodType: null,
28
28
  packageType: null,
29
29
  isPremium: false,
@@ -39,11 +39,11 @@ export const extractRevenueCatData = (customerInfo: CustomerInfo, entitlementId:
39
39
  return {
40
40
  expirationDate: entitlement.expirationDate ?? null,
41
41
  willRenew: entitlement.willRenew ?? null,
42
- // Maps SDK's `storeTransactionId` (current transaction ID from Apple/Google)
43
- // to our domain's `originalTransactionId`. Used as dedup key in processedTransactions.
44
- // Note: The SDK does not expose `original_store_transaction_id` (the first purchase's
45
- // ID that stays constant across renewals) only available via server-side webhooks.
46
- originalTransactionId: subscription?.storeTransactionId ?? null,
42
+ // Current transaction ID from Apple/Google — changes with each renewal.
43
+ // Used as dedup key in processedTransactions collection.
44
+ // Note: original_store_transaction_id (stable across renewals) is only
45
+ // available server-side (webhooks/REST API), not in the client SDK.
46
+ storeTransactionId: subscription?.storeTransactionId ?? null,
47
47
  periodType: validatePeriodType(entitlement.periodType),
48
48
  packageType: null,
49
49
  isPremium: true,
@@ -1,13 +1,13 @@
1
1
  const uniqueSuffix = (): string => `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2
2
 
3
- export const generatePurchaseId = (originalTransactionId: string | null, productId: string): string => {
4
- return originalTransactionId
5
- ? `purchase_${originalTransactionId}`
3
+ export const generatePurchaseId = (storeTransactionId: string | null, productId: string): string => {
4
+ return storeTransactionId
5
+ ? `purchase_${storeTransactionId}`
6
6
  : `purchase_${productId}_${uniqueSuffix()}`;
7
7
  };
8
8
 
9
- export const generateRenewalId = (originalTransactionId: string | null, productId: string, expirationDate: string): string => {
10
- return originalTransactionId
11
- ? `renewal_${originalTransactionId}_${expirationDate}`
9
+ export const generateRenewalId = (storeTransactionId: string | null, productId: string, expirationDate: string): string => {
10
+ return storeTransactionId
11
+ ? `renewal_${storeTransactionId}_${expirationDate}`
12
12
  : `renewal_${productId}_${uniqueSuffix()}`;
13
13
  };
@@ -24,7 +24,7 @@ export interface PremiumStatusChangedEvent {
24
24
  expiresAt?: string;
25
25
  willRenew?: boolean;
26
26
  periodType?: PeriodType;
27
- originalTransactionId?: string;
27
+ storeTransactionId?: string;
28
28
  }
29
29
 
30
30
  export interface PlanChangedEvent {
@@ -39,7 +39,7 @@ export async function syncPremiumStatus(
39
39
  expiresAt: premiumEntitlement.expirationDate ?? undefined,
40
40
  willRenew: premiumEntitlement.willRenew,
41
41
  periodType: premiumEntitlement.periodType as PeriodType | undefined,
42
- originalTransactionId: subscription?.storeTransactionId ?? undefined,
42
+ storeTransactionId: subscription?.storeTransactionId ?? undefined,
43
43
  });
44
44
  } else {
45
45
  await config.onPremiumStatusChanged({ userId, isPremium: false });