@umituz/react-native-subscription 2.27.42 → 2.27.44

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.27.42",
3
+ "version": "2.27.44",
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,8 +8,6 @@ import React, { useMemo, useEffect } from "react";
8
8
  import { usePaywallVisibility } from "../../../presentation/hooks/usePaywallVisibility";
9
9
  import { useSubscriptionPackages } from "../../../revenuecat/presentation/hooks/useSubscriptionPackages";
10
10
  import { useRevenueCatTrialEligibility } from "../../../revenuecat/presentation/hooks/useRevenueCatTrialEligibility";
11
- import { filterPackagesByMode } from "../../../utils/packageFilter";
12
- import { createCreditAmountsFromPackages } from "../../../utils/creditMapper";
13
11
  import { PaywallModal, type TrialEligibilityInfo } from "./PaywallModal";
14
12
  import { usePaywallActions } from "../hooks/usePaywallActions";
15
13
  import type { PaywallContainerProps } from "./PaywallContainer.types";
@@ -17,15 +15,12 @@ import type { PaywallContainerProps } from "./PaywallContainer.types";
17
15
  export const PaywallContainer: React.FC<PaywallContainerProps> = (props) => {
18
16
  const {
19
17
  translations,
20
- mode = "subscription",
21
18
  legalUrls,
22
19
  features,
23
20
  heroImage,
24
21
  bestValueIdentifier,
25
- creditsLabel,
26
22
  creditAmounts,
27
- packageAllocations,
28
- packageFilterConfig,
23
+ creditsLabel,
29
24
  source,
30
25
  onPurchaseSuccess,
31
26
  onPurchaseError,
@@ -41,7 +36,7 @@ export const PaywallContainer: React.FC<PaywallContainerProps> = (props) => {
41
36
 
42
37
  const purchaseSource = source ?? currentSource ?? "settings";
43
38
 
44
- const { data: allPackages = [], isLoading } = useSubscriptionPackages();
39
+ const { data: packages = [], isLoading } = useSubscriptionPackages();
45
40
  const { eligibilityMap, checkEligibility } = useRevenueCatTrialEligibility();
46
41
  const { handlePurchase, handleRestore } = usePaywallActions({
47
42
  source: purchaseSource,
@@ -54,10 +49,10 @@ export const PaywallContainer: React.FC<PaywallContainerProps> = (props) => {
54
49
  // Check trial eligibility only if trialConfig is enabled
55
50
  useEffect(() => {
56
51
  if (!trialConfig?.enabled) return;
57
- if (allPackages.length === 0) return;
52
+ if (packages.length === 0) return;
58
53
 
59
54
  // Get all actual product IDs from packages
60
- const allProductIds = allPackages.map((pkg) => pkg.product.identifier);
55
+ const allProductIds = packages.map((pkg) => pkg.product.identifier);
61
56
 
62
57
  // If eligibleProductIds are provided, filter to matching packages (partial match)
63
58
  // e.g., "yearly" matches "futureus.yearly"
@@ -75,7 +70,7 @@ export const PaywallContainer: React.FC<PaywallContainerProps> = (props) => {
75
70
  if (productIdsToCheck.length > 0) {
76
71
  checkEligibility(productIdsToCheck);
77
72
  }
78
- }, [allPackages, checkEligibility, trialConfig?.enabled, trialConfig?.eligibleProductIds]);
73
+ }, [packages, checkEligibility, trialConfig?.enabled, trialConfig?.eligibleProductIds]);
79
74
 
80
75
  // Convert eligibility map to format expected by PaywallModal
81
76
  // Only process if trial is enabled
@@ -92,13 +87,6 @@ export const PaywallContainer: React.FC<PaywallContainerProps> = (props) => {
92
87
  return result;
93
88
  }, [eligibilityMap, trialConfig?.enabled, trialConfig?.durationDays]);
94
89
 
95
- const { filteredPackages, computedCreditAmounts } = useMemo(() => ({
96
- filteredPackages: filterPackagesByMode(allPackages, mode, packageFilterConfig),
97
- computedCreditAmounts: mode !== "subscription" && !creditAmounts
98
- ? createCreditAmountsFromPackages(allPackages, packageAllocations)
99
- : creditAmounts
100
- }), [allPackages, mode, packageFilterConfig, creditAmounts, packageAllocations]);
101
-
102
90
  if (!isVisible) return null;
103
91
 
104
92
  return (
@@ -106,14 +94,14 @@ export const PaywallContainer: React.FC<PaywallContainerProps> = (props) => {
106
94
  visible={isVisible}
107
95
  onClose={handleClose}
108
96
  translations={translations}
109
- packages={filteredPackages}
97
+ packages={packages}
110
98
  isLoading={isLoading}
111
99
  legalUrls={legalUrls}
112
100
  features={features ? [...features] : undefined}
113
101
  heroImage={heroImage}
114
102
  bestValueIdentifier={bestValueIdentifier}
103
+ creditAmounts={creditAmounts}
115
104
  creditsLabel={creditsLabel}
116
- creditAmounts={computedCreditAmounts}
117
105
  onPurchase={handlePurchase}
118
106
  onRestore={handleRestore}
119
107
  trialEligibility={trialEligibility}
@@ -1,12 +1,11 @@
1
1
  /**
2
2
  * PaywallContainer Types
3
- * Props for package-driven paywall with mode-based filtering
3
+ * Props for subscription paywall
4
4
  */
5
5
 
6
6
  import type { ImageSourcePropType } from "react-native";
7
- import type { PaywallMode, PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../entities";
8
- import type { PackageFilterConfig } from "../../../utils/packageFilter";
9
- import type { PurchaseSource, PackageAllocationMap } from "../../../domain/entities/Credits";
7
+ import type { PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../entities";
8
+ import type { PurchaseSource } from "../../../domain/entities/Credits";
10
9
 
11
10
  /**
12
11
  * Trial display configuration
@@ -26,8 +25,6 @@ export interface TrialConfig {
26
25
  export interface PaywallContainerProps {
27
26
  /** Paywall translations - no defaults, must be provided */
28
27
  readonly translations: PaywallTranslations;
29
- /** Paywall mode - subscription, credits, or hybrid */
30
- readonly mode?: PaywallMode;
31
28
  /** Legal URLs for privacy and terms */
32
29
  readonly legalUrls?: PaywallLegalUrls;
33
30
  /** Feature list to display */
@@ -36,14 +33,10 @@ export interface PaywallContainerProps {
36
33
  readonly heroImage?: ImageSourcePropType;
37
34
  /** Best value package identifier for badge */
38
35
  readonly bestValueIdentifier?: string;
39
- /** Credits label text */
40
- readonly creditsLabel?: string;
41
- /** Credit amounts per package identifier */
36
+ /** Credit amounts per product identifier */
42
37
  readonly creditAmounts?: Record<string, number>;
43
- /** Package allocations for automatic credit amount computation */
44
- readonly packageAllocations?: PackageAllocationMap;
45
- /** Custom filter config for package categorization */
46
- readonly packageFilterConfig?: PackageFilterConfig;
38
+ /** Credits label text (e.g., "credits") */
39
+ readonly creditsLabel?: string;
47
40
  /** Source of the paywall - affects pending purchase handling */
48
41
  readonly source?: PurchaseSource;
49
42
  /** Callback when purchase succeeds */
@@ -59,4 +52,3 @@ export interface PaywallContainerProps {
59
52
  /** Trial display configuration (Apple-compliant) */
60
53
  readonly trialConfig?: TrialConfig;
61
54
  }
62
-
@@ -33,7 +33,9 @@ export interface PaywallModalProps {
33
33
  isLoading?: boolean;
34
34
  legalUrls?: PaywallLegalUrls;
35
35
  bestValueIdentifier?: string;
36
+ /** Credit amounts per product identifier */
36
37
  creditAmounts?: Record<string, number>;
38
+ /** Credits label text (e.g., "credits") */
37
39
  creditsLabel?: string;
38
40
  heroImage?: ImageSourcePropType;
39
41
  onPurchase?: (pkg: PurchasesPackage) => Promise<void | boolean>;
@@ -21,7 +21,9 @@ interface PlanCardProps {
21
21
  onSelect: () => void;
22
22
  /** Badge text (e.g., "Best Value") - NOT for trial */
23
23
  badge?: string;
24
+ /** Credit amount for this plan */
24
25
  creditAmount?: number;
26
+ /** Credits label text (e.g., "credits") */
25
27
  creditsLabel?: string;
26
28
  /** Whether this plan has a free trial (Apple-compliant display) */
27
29
  hasFreeTrial?: boolean;
@@ -9,9 +9,7 @@ export { PaywallModal } from "./PaywallModal";
9
9
  export type { PaywallModalProps } from "./PaywallModal";
10
10
 
11
11
  export { PaywallHeader } from "./PaywallHeader";
12
- export { PaywallTabBar } from "./PaywallTabBar";
13
12
  export { PaywallFooter } from "./PaywallFooter";
14
13
  export { FeatureList } from "./FeatureList";
15
14
  export { FeatureItem } from "./FeatureItem";
16
15
  export { PlanCard } from "./PlanCard";
17
- export { CreditCard } from "./CreditCard";
@@ -3,25 +3,6 @@
3
3
  * All paywall-related type definitions
4
4
  */
5
5
 
6
- export type PaywallMode = "subscription" | "credits" | "hybrid";
7
-
8
- export type PaywallTabType = "credits" | "subscription";
9
-
10
- export interface PaywallTab {
11
- id: PaywallTabType;
12
- label: string;
13
- }
14
-
15
- export interface CreditsPackage {
16
- id: string;
17
- credits: number;
18
- price: number;
19
- currency: string;
20
- bonus?: number;
21
- badge?: string;
22
- description?: string;
23
- }
24
-
25
6
  export interface SubscriptionFeature {
26
7
  icon: string;
27
8
  text: string;
@@ -30,10 +11,7 @@ export interface SubscriptionFeature {
30
11
  export interface PaywallTranslations {
31
12
  title: string;
32
13
  subtitle?: string;
33
- creditsTabLabel?: string;
34
- subscriptionTabLabel?: string;
35
14
  purchaseButtonText: string;
36
- subscribeButtonText?: string;
37
15
  restoreButtonText: string;
38
16
  loadingText: string;
39
17
  emptyText: string;
@@ -2,7 +2,6 @@
2
2
  * Paywall Hooks Index
3
3
  */
4
4
 
5
- export { usePaywall } from './usePaywall';
6
5
  export { useSubscriptionModal } from './useSubscriptionModal';
7
6
  export { usePaywallActions } from './usePaywallActions';
8
7
  export {
@@ -2,7 +2,6 @@ export * from "./aiCreditHelpers";
2
2
  export * from "./authUtils";
3
3
  export * from "./creditChecker";
4
4
  export * from "./creditMapper";
5
- export * from "./packageFilter";
6
5
  export * from "./packagePeriodUtils";
7
6
  export * from "./packageTypeDetector";
8
7
  export * from "./premiumStatusUtils";
@@ -1,120 +0,0 @@
1
- /**
2
- * Credit Card
3
- * Credit package selection card
4
- */
5
-
6
- import React from "react";
7
- import { View, TouchableOpacity, StyleSheet } from "react-native";
8
- import { AtomicText, AtomicIcon, AtomicBadge, useAppDesignTokens } from "@umituz/react-native-design-system";
9
- import type { CreditsPackage } from '../entities';
10
-
11
- import { formatPrice } from '../../../utils/priceUtils';
12
-
13
- interface CreditCardProps {
14
- pkg: CreditsPackage;
15
- isSelected: boolean;
16
- onSelect: () => void;
17
- }
18
-
19
- export const CreditCard: React.FC<CreditCardProps> = React.memo(({ pkg, isSelected, onSelect }) => {
20
- const tokens = useAppDesignTokens();
21
- const totalCredits = pkg.credits + (pkg.bonus ?? 0);
22
- const price = formatPrice(pkg.price, pkg.currency);
23
-
24
- return (
25
- <TouchableOpacity onPress={onSelect} activeOpacity={0.7} style={styles.touchable}>
26
- <View
27
- style={[
28
- styles.container,
29
- {
30
- backgroundColor: tokens.colors.surface,
31
- borderColor: isSelected ? tokens.colors.primary : tokens.colors.border,
32
- borderWidth: isSelected ? 2 : 1,
33
- },
34
- ]}
35
- >
36
- {pkg.badge && (
37
- <View style={styles.badgeContainer}>
38
- <AtomicBadge text={pkg.badge} variant="warning" size="sm" />
39
- </View>
40
- )}
41
-
42
- <View style={styles.content}>
43
- <View style={styles.leftSection}>
44
- <AtomicIcon name="flash" size="md" color={isSelected ? "primary" : "secondary"} />
45
- <AtomicText
46
- type="headlineSmall"
47
- style={[styles.credits, { color: isSelected ? tokens.colors.primary : tokens.colors.textPrimary }]}
48
- >
49
- {totalCredits.toLocaleString()}
50
- </AtomicText>
51
- </View>
52
-
53
- <View style={styles.rightSection}>
54
- <AtomicText
55
- type="titleLarge"
56
- style={[styles.price, { color: isSelected ? tokens.colors.primary : tokens.colors.textPrimary }]}
57
- >
58
- {price}
59
- </AtomicText>
60
- {isSelected && <AtomicIcon name="checkmark-circle" size="md" color="primary" />}
61
- </View>
62
- </View>
63
-
64
- {(pkg.bonus ?? 0) > 0 && (
65
- <View style={styles.bonusRow}>
66
- <AtomicIcon name="gift-outline" size="sm" color="success" />
67
- <AtomicText type="bodySmall" style={{ color: tokens.colors.success, marginLeft: 4 }}>
68
- +{pkg.bonus}
69
- </AtomicText>
70
- </View>
71
- )}
72
- </View>
73
- </TouchableOpacity>
74
- );
75
- });
76
-
77
- CreditCard.displayName = "CreditCard";
78
-
79
- const styles = StyleSheet.create({
80
- touchable: {
81
- marginBottom: 10,
82
- marginHorizontal: 24,
83
- },
84
- container: {
85
- borderRadius: 16,
86
- padding: 16,
87
- position: "relative",
88
- },
89
- badgeContainer: {
90
- position: "absolute",
91
- top: -10,
92
- right: 16,
93
- },
94
- content: {
95
- flexDirection: "row",
96
- justifyContent: "space-between",
97
- alignItems: "center",
98
- },
99
- leftSection: {
100
- flexDirection: "row",
101
- alignItems: "center",
102
- },
103
- credits: {
104
- fontWeight: "700",
105
- marginLeft: 8,
106
- },
107
- rightSection: {
108
- flexDirection: "row",
109
- alignItems: "center",
110
- },
111
- price: {
112
- fontWeight: "700",
113
- marginRight: 8,
114
- },
115
- bonusRow: {
116
- flexDirection: "row",
117
- alignItems: "center",
118
- marginTop: 8,
119
- },
120
- });
@@ -1,77 +0,0 @@
1
- /**
2
- * Paywall Tab Bar
3
- * Segmented control for hybrid mode
4
- */
5
-
6
- import React from "react";
7
- import { View, TouchableOpacity, StyleSheet } from "react-native";
8
- import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
9
- import type { PaywallTabType } from '../entities';
10
-
11
- interface PaywallTabBarProps {
12
- activeTab: PaywallTabType;
13
- onTabChange: (tab: PaywallTabType) => void;
14
- creditsLabel: string;
15
- subscriptionLabel: string;
16
- }
17
-
18
- export const PaywallTabBar: React.FC<PaywallTabBarProps> = React.memo(
19
- ({ activeTab, onTabChange, creditsLabel, subscriptionLabel }) => {
20
- const tokens = useAppDesignTokens();
21
-
22
- const renderTab = (tab: PaywallTabType, label: string) => {
23
- const isActive = activeTab === tab;
24
-
25
- return (
26
- <TouchableOpacity
27
- key={tab}
28
- style={[
29
- styles.tab,
30
- isActive ? { backgroundColor: tokens.colors.surface } : undefined,
31
- ]}
32
- onPress={() => onTabChange(tab)}
33
- activeOpacity={0.7}
34
- >
35
- <AtomicText
36
- type="labelLarge"
37
- style={[
38
- styles.tabText,
39
- { color: isActive ? tokens.colors.primary : tokens.colors.textSecondary },
40
- ]}
41
- >
42
- {label}
43
- </AtomicText>
44
- </TouchableOpacity>
45
- );
46
- };
47
-
48
- return (
49
- <View style={[styles.container, { backgroundColor: tokens.colors.surfaceSecondary }]}>
50
- {renderTab("credits", creditsLabel)}
51
- {renderTab("subscription", subscriptionLabel)}
52
- </View>
53
- );
54
- }
55
- );
56
-
57
- PaywallTabBar.displayName = "PaywallTabBar";
58
-
59
- const styles = StyleSheet.create({
60
- container: {
61
- flexDirection: "row",
62
- borderRadius: 12,
63
- padding: 4,
64
- marginHorizontal: 24,
65
- marginBottom: 16,
66
- height: 44,
67
- },
68
- tab: {
69
- flex: 1,
70
- alignItems: "center",
71
- justifyContent: "center",
72
- borderRadius: 8,
73
- },
74
- tabText: {
75
- fontWeight: "600",
76
- },
77
- });
@@ -1,54 +0,0 @@
1
- import { useState, useCallback } from "react";
2
- import type { PurchasesPackage } from "react-native-purchases";
3
- import type { PaywallTabType } from "../entities";
4
-
5
- interface UsePaywallProps {
6
- initialTab?: PaywallTabType;
7
- onCreditsPurchase: (packageId: string) => Promise<void>;
8
- onSubscriptionPurchase: (pkg: PurchasesPackage) => Promise<void>;
9
- }
10
-
11
- export const usePaywall = ({
12
- initialTab = "credits",
13
- onCreditsPurchase,
14
- onSubscriptionPurchase,
15
- }: UsePaywallProps) => {
16
- const [activeTab, setActiveTab] = useState<PaywallTabType>(initialTab);
17
- const [selectedCreditsPackageId, setSelectedCreditsPackageId] = useState<string | null>(null);
18
- const [selectedSubscriptionPkg, setSelectedSubscriptionPkg] = useState<PurchasesPackage | null>(null);
19
-
20
- const handleTabChange = useCallback((tab: PaywallTabType) => {
21
- setActiveTab(tab);
22
- }, []);
23
-
24
- const handleCreditsPackageSelect = useCallback((packageId: string) => {
25
- setSelectedCreditsPackageId(packageId);
26
- }, []);
27
-
28
- const handleSubscriptionPackageSelect = useCallback((pkg: PurchasesPackage) => {
29
- setSelectedSubscriptionPkg(pkg);
30
- }, []);
31
-
32
- const handleCreditsPurchase = useCallback(async () => {
33
- if (selectedCreditsPackageId) {
34
- await onCreditsPurchase(selectedCreditsPackageId);
35
- }
36
- }, [selectedCreditsPackageId, onCreditsPurchase]);
37
-
38
- const handleSubscriptionPurchase = useCallback(async () => {
39
- if (selectedSubscriptionPkg) {
40
- await onSubscriptionPurchase(selectedSubscriptionPkg);
41
- }
42
- }, [selectedSubscriptionPkg, onSubscriptionPurchase]);
43
-
44
- return {
45
- activeTab,
46
- selectedCreditsPackageId,
47
- selectedSubscriptionPkg,
48
- handleTabChange,
49
- handleCreditsPackageSelect,
50
- handleSubscriptionPackageSelect,
51
- handleCreditsPurchase,
52
- handleSubscriptionPurchase,
53
- };
54
- };
@@ -1,62 +0,0 @@
1
- /**
2
- * Package Filter Utility
3
- * Filters RevenueCat packages by type (credits vs subscription)
4
- */
5
-
6
- import type { PurchasesPackage } from "react-native-purchases";
7
-
8
- export type PackageCategory = "credits" | "subscription";
9
-
10
- export interface PackageFilterConfig {
11
- creditIdentifierPattern?: RegExp;
12
- subscriptionIdentifierPattern?: RegExp;
13
- }
14
-
15
- const DEFAULT_CONFIG: PackageFilterConfig = {
16
- creditIdentifierPattern: /credit/i,
17
- subscriptionIdentifierPattern: /(monthly|yearly|annual|weekly|premium|subscription)/i,
18
- };
19
-
20
- export function getPackageCategory(
21
- pkg: PurchasesPackage,
22
- config: PackageFilterConfig = DEFAULT_CONFIG
23
- ): PackageCategory {
24
- const identifier = pkg.identifier.toLowerCase();
25
- const productIdentifier = pkg.product.identifier.toLowerCase();
26
-
27
- const isCreditPackage =
28
- config.creditIdentifierPattern?.test(identifier) ||
29
- config.creditIdentifierPattern?.test(productIdentifier);
30
-
31
- return isCreditPackage ? "credits" : "subscription";
32
- }
33
-
34
- export function filterPackagesByMode(
35
- packages: PurchasesPackage[],
36
- mode: "credits" | "subscription" | "hybrid",
37
- config: PackageFilterConfig = DEFAULT_CONFIG
38
- ): PurchasesPackage[] {
39
- if (mode === "hybrid") {
40
- return packages;
41
- }
42
-
43
- return packages.filter((pkg) => getPackageCategory(pkg, config) === mode);
44
- }
45
-
46
- export function separatePackages(
47
- packages: PurchasesPackage[],
48
- config: PackageFilterConfig = DEFAULT_CONFIG
49
- ): { creditPackages: PurchasesPackage[]; subscriptionPackages: PurchasesPackage[] } {
50
- const creditPackages: PurchasesPackage[] = [];
51
- const subscriptionPackages: PurchasesPackage[] = [];
52
-
53
- for (const pkg of packages) {
54
- if (getPackageCategory(pkg, config) === "credits") {
55
- creditPackages.push(pkg);
56
- } else {
57
- subscriptionPackages.push(pkg);
58
- }
59
- }
60
-
61
- return { creditPackages, subscriptionPackages };
62
- }