@umituz/react-native-subscription 2.37.110 → 2.37.111

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.110",
3
+ "version": "2.37.111",
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",
@@ -8,7 +8,8 @@ import { refundCreditsOperation } from "../application/RefundCreditsCommand";
8
8
  import { PURCHASE_TYPE, type PurchaseType } from "../../subscription/core/SubscriptionConstants";
9
9
  import { requireFirestore, buildDocRef, type CollectionConfig } from "../../../shared/infrastructure/firestore";
10
10
  import { fetchCredits, checkHasCredits, documentExists } from "./operations/CreditsFetcher";
11
- import { syncExpiredStatus, syncPremiumMetadata, createRecoveryCreditsDocument, type PremiumMetadata } from "./operations/CreditsWriter";
11
+ import { syncExpiredStatus, syncPremiumMetadata, createRecoveryCreditsDocument } from "./operations/CreditsWriter";
12
+ import type { SubscriptionMetadata } from "../../subscription/core/types";
12
13
  import { initializeCreditsWithRetry } from "./operations/CreditsInitializer";
13
14
  import { calculateCreditLimit } from "../application/CreditLimitCalculator";
14
15
 
@@ -81,7 +82,7 @@ export class CreditsRepository extends BaseRepository {
81
82
  await syncExpiredStatus(this.getRef(db, userId));
82
83
  }
83
84
 
84
- async syncPremiumMetadata(userId: string, metadata: PremiumMetadata): Promise<void> {
85
+ async syncPremiumMetadata(userId: string, metadata: SubscriptionMetadata): Promise<void> {
85
86
  const db = requireFirestore();
86
87
  await syncPremiumMetadata(this.getRef(db, userId), metadata);
87
88
  }
@@ -3,6 +3,7 @@ import { runTransaction, serverTimestamp } from "@umituz/react-native-firebase";
3
3
  import { doc, getDoc, setDoc } from "firebase/firestore";
4
4
  import { SUBSCRIPTION_STATUS } from "../../../subscription/core/SubscriptionConstants";
5
5
  import { resolveSubscriptionStatus } from "../../../subscription/core/SubscriptionStatus";
6
+ import type { SubscriptionMetadata } from "../../../subscription/core/types";
6
7
  import { toTimestamp } from "../../../../shared/utils/dateConverter";
7
8
  import { isPast } from "../../../../utils/dateUtils";
8
9
  import { getAppVersion, validatePlatform } from "../../../../utils/appUtils";
@@ -25,22 +26,10 @@ export async function syncExpiredStatus(ref: DocumentReference): Promise<void> {
25
26
  });
26
27
  }
27
28
 
28
- export interface PremiumMetadata {
29
- isPremium: boolean;
30
- willRenew: boolean;
31
- expirationDate: string | null;
32
- productId: string;
33
- periodType: string | null;
34
- unsubscribeDetectedAt: string | null;
35
- billingIssueDetectedAt: string | null;
36
- store: string | null;
37
- ownershipType: string | null;
38
- }
39
-
40
29
  // Fix: was getDoc+setDoc (non-atomic) — now uses runTransaction.
41
30
  export async function syncPremiumMetadata(
42
31
  ref: DocumentReference,
43
- metadata: PremiumMetadata
32
+ metadata: SubscriptionMetadata
44
33
  ): Promise<void> {
45
34
  await runTransaction(async (tx: Transaction) => {
46
35
  const doc = await tx.get(ref);
@@ -1,15 +1,9 @@
1
- import type { Store, OwnershipType, PackageType } from "./RevenueCatTypes";
1
+ import type { SubscriptionMetadata } from "../../../subscription/core/types";
2
+ import type { PackageType } from "./RevenueCatTypes";
2
3
 
3
- export interface RevenueCatData {
4
- expirationDate: string | null;
4
+ export interface RevenueCatData extends Omit<SubscriptionMetadata, 'willRenew' | 'productId'> {
5
5
  willRenew: boolean | null;
6
6
  storeTransactionId: string | null;
7
- isPremium: boolean;
8
- periodType: string | null;
9
7
  packageType: PackageType | null;
10
- unsubscribeDetectedAt: string | null;
11
- billingIssueDetectedAt: string | null;
12
- store: Store | null;
13
- ownershipType: OwnershipType | null;
14
8
  revenueCatUserId?: string | null;
15
9
  }
@@ -193,10 +193,14 @@ export async function handleInitialConfiguration(
193
193
  userId: normalizedUserId,
194
194
  isPremium: true,
195
195
  productId: premiumEntitlement.productIdentifier,
196
- expiresAt: premiumEntitlement.expirationDate ?? undefined,
196
+ expirationDate: premiumEntitlement.expirationDate ?? null,
197
197
  willRenew: premiumEntitlement.willRenew,
198
198
  periodType: premiumEntitlement.periodType as PeriodType | undefined,
199
199
  storeTransactionId: subscription?.storeTransactionId ?? undefined,
200
+ unsubscribeDetectedAt: premiumEntitlement.unsubscribeDetectedAt ?? null,
201
+ billingIssueDetectedAt: premiumEntitlement.billingIssueDetectedAt ?? null,
202
+ store: premiumEntitlement.store ?? null,
203
+ ownershipType: premiumEntitlement.ownershipType ?? null,
200
204
  });
201
205
  } else {
202
206
  await deps.config.onPremiumStatusChanged({
@@ -1,7 +1,7 @@
1
1
  import type { CreditsConfig } from "../../credits/core/Credits";
2
2
  import type { UserCreditsDocumentRead } from "../../credits/core/UserCreditsDocument";
3
3
  import type { PurchaseSource, PurchaseType } from "../core/SubscriptionConstants";
4
- import type { Store, OwnershipType } from "../../revenuecat/core/types";
4
+ import type { SubscriptionMetadata } from "../core/types";
5
5
 
6
6
  export interface FirebaseAuthLike {
7
7
  currentUser: { uid: string; isAnonymous: boolean } | null;
@@ -28,19 +28,11 @@ export interface SubscriptionInitConfig {
28
28
  authStateTimeoutMs?: number;
29
29
  }
30
30
 
31
- export interface InitializeCreditsMetadata {
32
- productId: string;
31
+ export interface InitializeCreditsMetadata extends Omit<SubscriptionMetadata, 'willRenew'> {
33
32
  source: PurchaseSource;
34
33
  type: PurchaseType;
35
- expirationDate: string | null;
36
34
  willRenew: boolean | null;
37
35
  storeTransactionId: string | null;
38
- isPremium: boolean;
39
- periodType: string | null;
40
- unsubscribeDetectedAt: string | null;
41
- billingIssueDetectedAt: string | null;
42
- store: Store | null;
43
- ownershipType: OwnershipType | null;
44
36
  revenueCatUserId?: string | null;
45
37
  }
46
38
 
@@ -220,7 +220,7 @@ export class SubscriptionSyncProcessor {
220
220
  userId,
221
221
  event.productId!,
222
222
  event.willRenew ?? false,
223
- event.expiresAt ?? null,
223
+ event.expirationDate ?? null,
224
224
  event.periodType ?? null,
225
225
  event.storeTransactionId,
226
226
  );
@@ -235,13 +235,13 @@ export class SubscriptionSyncProcessor {
235
235
  await repo.syncPremiumMetadata(userId, {
236
236
  isPremium: event.isPremium,
237
237
  willRenew: event.willRenew ?? false,
238
- expirationDate: event.expiresAt ?? null,
238
+ expirationDate: event.expirationDate ?? null,
239
239
  productId: event.productId!,
240
240
  periodType: event.periodType ?? null,
241
- unsubscribeDetectedAt: null,
242
- billingIssueDetectedAt: null,
243
- store: null,
244
- ownershipType: null,
241
+ unsubscribeDetectedAt: event.unsubscribeDetectedAt ?? null,
242
+ billingIssueDetectedAt: event.billingIssueDetectedAt ?? null,
243
+ store: event.store ?? null,
244
+ ownershipType: event.ownershipType ?? null,
245
245
  });
246
246
  this.emitCreditsUpdated(userId);
247
247
  }
@@ -1,5 +1,6 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
- import type { PeriodType, PurchaseSource } from "./SubscriptionConstants";
2
+ import type { PurchaseSource } from "./SubscriptionConstants";
3
+ import type { SubscriptionMetadata } from "./types";
3
4
  import type { PackageType } from "../../revenuecat/core/types/RevenueCatTypes";
4
5
 
5
6
  export interface PurchaseCompletedEvent {
@@ -17,13 +18,9 @@ export interface RenewalDetectedEvent {
17
18
  customerInfo: CustomerInfo;
18
19
  }
19
20
 
20
- export interface PremiumStatusChangedEvent {
21
+ export interface PremiumStatusChangedEvent extends Partial<SubscriptionMetadata> {
21
22
  userId: string;
22
23
  isPremium: boolean;
23
- productId?: string;
24
- expiresAt?: string;
25
- willRenew?: boolean;
26
- periodType?: PeriodType;
27
24
  storeTransactionId?: string;
28
25
  }
29
26
 
@@ -12,7 +12,7 @@ export type { SubscriptionStatusType };
12
12
 
13
13
  export interface SubscriptionStatus {
14
14
  isPremium: boolean;
15
- expiresAt: string | null;
15
+ expirationDate: string | null;
16
16
  productId: string | null;
17
17
  purchasedAt?: string | null;
18
18
  customerId?: string | null;
@@ -23,7 +23,7 @@ export interface SubscriptionStatus {
23
23
 
24
24
  export const createDefaultSubscriptionStatus = (): SubscriptionStatus => ({
25
25
  isPremium: false,
26
- expiresAt: null,
26
+ expirationDate: null,
27
27
  productId: null,
28
28
  purchasedAt: null,
29
29
  customerId: null,
@@ -33,8 +33,8 @@ export const createDefaultSubscriptionStatus = (): SubscriptionStatus => ({
33
33
 
34
34
  export const isSubscriptionValid = (status: SubscriptionStatus | null): boolean => {
35
35
  if (!status || !status.isPremium) return false;
36
- if (!status.expiresAt) return true;
37
- return timezoneService.isFuture(new Date(status.expiresAt));
36
+ if (!status.expirationDate) return true;
37
+ return timezoneService.isFuture(new Date(status.expirationDate));
38
38
  };
39
39
 
40
40
  export interface StatusResolverInput {
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Credit usage information for display in subscription UI components.
3
+ * Single source of truth — used by SubscriptionDetailScreen and PremiumDetailsCard.
4
+ */
5
+ export interface CreditInfo {
6
+ id: string;
7
+ label: string;
8
+ current: number;
9
+ total: number;
10
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolved premium status from RevenueCat CustomerInfo.
3
+ * Uses Date objects (presentation-ready).
4
+ *
5
+ * Extended by SubscriptionStatusResult which adds hook state (isLoading, error, refetch).
6
+ */
7
+ export interface PremiumStatus {
8
+ isPremium: boolean;
9
+ expirationDate: Date | null;
10
+ willRenew: boolean;
11
+ productIdentifier: string | null;
12
+ originalPurchaseDate: Date | null;
13
+ latestPurchaseDate: Date | null;
14
+ billingIssuesDetected: boolean;
15
+ isSandbox: boolean;
16
+ periodType: string | null;
17
+ packageType: string | null;
18
+ store: string | null;
19
+ gracePeriodExpiresDate: Date | null;
20
+ unsubscribeDetectedAt: Date | null;
21
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Base subscription metadata — the single source of truth for subscription state fields.
3
+ *
4
+ * All subscription-related types (RevenueCatData, PremiumStatusChangedEvent,
5
+ * InitializeCreditsMetadata) extend or compose from this base to eliminate
6
+ * field duplication and ensure consistent naming.
7
+ */
8
+ export interface SubscriptionMetadata {
9
+ isPremium: boolean;
10
+ willRenew: boolean;
11
+ expirationDate: string | null;
12
+ productId: string;
13
+ periodType: string | null;
14
+ unsubscribeDetectedAt: string | null;
15
+ billingIssueDetectedAt: string | null;
16
+ store: string | null;
17
+ ownershipType: string | null;
18
+ }
@@ -0,0 +1,3 @@
1
+ export type { SubscriptionMetadata } from "./SubscriptionMetadata";
2
+ export type { PremiumStatus } from "./PremiumStatus";
3
+ export type { CreditInfo } from "./CreditInfo";
@@ -2,22 +2,9 @@ import type { CustomerInfo } from "react-native-purchases";
2
2
  import { getPremiumEntitlement } from "../../../revenuecat/core/types";
3
3
  import { toDate } from "../../../../shared/utils/dateConverter";
4
4
  import { detectPackageType } from "../../../../utils/packageTypeDetector";
5
+ import type { PremiumStatus } from "../../core/types";
5
6
 
6
- export interface PremiumStatus {
7
- isPremium: boolean;
8
- expirationDate: Date | null;
9
- willRenew: boolean;
10
- productIdentifier: string | null;
11
- originalPurchaseDate: Date | null;
12
- latestPurchaseDate: Date | null;
13
- billingIssuesDetected: boolean;
14
- isSandbox: boolean;
15
- periodType: string | null;
16
- packageType: string | null;
17
- store: string | null;
18
- gracePeriodExpiresDate: Date | null;
19
- unsubscribeDetectedAt: Date | null;
20
- }
7
+ export type { PremiumStatus };
21
8
 
22
9
  export class PurchaseStatusResolver {
23
10
  static resolve(customerInfo: CustomerInfo, entitlementId: string): PremiumStatus {
@@ -1,5 +1,5 @@
1
1
  import type { RevenueCatConfig } from "../../../revenuecat/core/types";
2
- import type { PremiumStatus } from "../handlers/PurchaseStatusResolver";
2
+ import type { PremiumStatus } from "../../core/types";
3
3
  import type { RestoreResultInfo } from "../handlers/package-operations/types";
4
4
 
5
5
  export interface SubscriptionManagerConfig {
@@ -36,10 +36,14 @@ export async function syncPremiumStatus(
36
36
  userId,
37
37
  isPremium: true,
38
38
  productId: premiumEntitlement.productIdentifier,
39
- expiresAt: premiumEntitlement.expirationDate ?? undefined,
39
+ expirationDate: premiumEntitlement.expirationDate ?? null,
40
40
  willRenew: premiumEntitlement.willRenew,
41
41
  periodType: premiumEntitlement.periodType as PeriodType | undefined,
42
42
  storeTransactionId: subscription?.storeTransactionId ?? undefined,
43
+ unsubscribeDetectedAt: premiumEntitlement.unsubscribeDetectedAt ?? null,
44
+ billingIssueDetectedAt: premiumEntitlement.billingIssueDetectedAt ?? null,
45
+ store: premiumEntitlement.store ?? null,
46
+ ownershipType: premiumEntitlement.ownershipType ?? null,
43
47
  });
44
48
  } else {
45
49
  await config.onPremiumStatusChanged({ userId, isPremium: false });
@@ -1,11 +1,7 @@
1
1
  import type { SubscriptionStatusType } from "./PremiumStatusBadge";
2
+ import type { CreditInfo } from "../../../core/types";
2
3
 
3
- export interface CreditInfo {
4
- id: string;
5
- label: string;
6
- current: number;
7
- total: number;
8
- }
4
+ export type { CreditInfo };
9
5
 
10
6
  export interface PremiumDetailsTranslations {
11
7
  title: string;
@@ -1,4 +1,7 @@
1
1
  import type { SubscriptionStatusType } from "../../core/SubscriptionConstants";
2
+ import type { CreditInfo } from "../../core/types";
3
+
4
+ export type { CreditInfo };
2
5
 
3
6
  export interface SubscriptionDisplayFlags {
4
7
  showHeader: boolean;
@@ -40,13 +43,6 @@ export interface UpgradePromptConfig {
40
43
  onUpgrade?: () => void;
41
44
  }
42
45
 
43
- export interface CreditInfo {
44
- id: string;
45
- label: string;
46
- current: number;
47
- total: number;
48
- }
49
-
50
46
  export interface SubscriptionDetailConfig {
51
47
  display: SubscriptionDisplayFlags;
52
48
  statusType: SubscriptionStatusType;
@@ -1,17 +1,6 @@
1
- export interface SubscriptionStatusResult {
2
- isPremium: boolean;
3
- expirationDate: Date | null;
4
- willRenew: boolean;
5
- productIdentifier: string | null;
6
- originalPurchaseDate: Date | null;
7
- latestPurchaseDate: Date | null;
8
- billingIssuesDetected: boolean;
9
- isSandbox: boolean;
10
- periodType: string | null;
11
- packageType: string | null;
12
- store: string | null;
13
- gracePeriodExpiresDate: Date | null;
14
- unsubscribeDetectedAt: Date | null;
1
+ import type { PremiumStatus } from "../core/types";
2
+
3
+ export interface SubscriptionStatusResult extends PremiumStatus {
15
4
  isLoading: boolean;
16
5
  error: Error | null;
17
6
  refetch: () => void;
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Domain Layer - Constants & Types
2
2
  export * from "./domains/subscription/core/SubscriptionConstants";
3
+ export type { SubscriptionMetadata, PremiumStatus, CreditInfo } from "./domains/subscription/core/types";
3
4
  export {
4
5
  createDefaultSubscriptionStatus,
5
6
  isSubscriptionValid,
@@ -57,7 +58,6 @@ export type {
57
58
  SubscriptionDetailTranslations,
58
59
  SubscriptionDisplayFlags,
59
60
  UpgradePromptConfig,
60
- CreditInfo,
61
61
  } from "./domains/subscription/presentation/screens/SubscriptionDetailScreen.types";
62
62
  export * from "./domains/paywall/components/PaywallContainer";
63
63