@umituz/react-native-premium
v1.7.2
Published
Premium subscription management system for React Native apps - Centralized premium/freemium/guest tier LOGICAL determination (no database operations)
Maintainers
Readme
@umituz/react-native-premium
Centralized premium/freemium/guest tier determination for React Native apps
🎯 Philosophy
This package provides ONLY tier logic determination. It does NOT handle:
- ❌ Database operations
- ❌ API calls
- ❌ State management
- ❌ Authentication logic
Why? This makes the package:
- ✅ Database-agnostic (works with Firebase, Supabase, MongoDB, etc.)
- ✅ Framework-agnostic (works with any state management)
- ✅ Simple and lightweight (~2KB gzipped)
- ✅ Easy to test (pure functions)
- ✅ Reusable across 100+ apps (single source of truth)
✨ Features
- 🎯 Centralized Tier Logic: Single source of truth for tier determination
- 🚫 No Database Operations: Apps handle their own data fetching
- ⚡ Lightweight: Only ~2KB gzipped
- 🎨 Type-Safe: Full TypeScript support
- 🔄 Framework Agnostic: Works with any state management solution
- 🧪 Testable: Pure functions, easy to test
📦 Installation
npm install @umituz/react-native-premium🎭 User Tiers
The package defines three user tiers:
| Tier | Description | isPremium | isGuest |
|------|-------------|-----------|---------|
| Guest | Not authenticated | false | true |
| Freemium | Authenticated, no premium | false | false |
| Premium | Authenticated, has premium | true | false |
Key Rule: Guest users are ALWAYS freemium and NEVER premium, regardless of any other factors.
🚀 Quick Start
1. Basic Usage (Utility Functions)
import { getUserTierInfo, checkPremiumAccess } from '@umituz/react-native-premium';
// Determine tier from auth state and premium status
const tierInfo = getUserTierInfo(
isGuest, // boolean: is user a guest?
userId, // string | null: user ID (null for guests)
isPremium // boolean: does user have premium? (from your database)
);
// tierInfo: { tier: 'premium' | 'freemium' | 'guest', isPremium, isGuest, isAuthenticated, userId }
// Check premium access (guest users always return false)
const hasAccess = checkPremiumAccess(isGuest, userId, isPremium);2. React Hook Usage (Recommended)
✅ Recommended: useUserTierWithRepository - Automatically fetches premium status
import { useUserTierWithRepository } from '@umituz/react-native-premium';
import { useAuth } from '@domains/auth'; // Your auth hook
import { premiumRepository } from '@/infrastructure/repositories/PremiumRepository'; // Your repository
function MyComponent() {
const auth = useAuth();
const { tier, isPremium, isGuest, isLoading, refresh } = useUserTierWithRepository({
auth,
repository: premiumRepository,
});
if (tier === 'guest') {
return <GuestUpgradeCard />;
}
if (tier === 'freemium') {
return <FreemiumLimits />;
}
return <PremiumFeatures />;
}Alternative: useUserTier - Manual premium status fetching
import { useUserTier } from '@umituz/react-native-premium';
import { useAuth } from './useAuth'; // Your auth hook
import { usePremiumStatus } from './usePremiumStatus'; // Your premium status hook
function MyComponent() {
const { user, isGuest } = useAuth();
const { isPremium, isLoading, error } = usePremiumStatus(user?.uid);
const { tier, isPremium: hasPremium, isGuest: isGuestUser } = useUserTier({
isGuest,
userId: user?.uid ?? null,
isPremium: isPremium ?? false,
isLoading,
error,
});
if (tier === 'guest') {
return <GuestUpgradeCard />;
}
if (tier === 'freemium') {
return <FreemiumLimits />;
}
return <PremiumFeatures />;
}3. Async Usage with Fetcher
import { getUserTierInfoAsync, getIsPremium } from '@umituz/react-native-premium';
import type { PremiumStatusFetcher } from '@umituz/react-native-premium';
// Define your premium status fetcher
const premiumStatusFetcher: PremiumStatusFetcher = {
isPremium: async (userId: string) => {
// Fetch from your database
const userDoc = await getDoc(doc(db, 'users', userId));
return userDoc.data()?.isPremium ?? false;
},
};
// Get tier info asynchronously
const tierInfo = await getUserTierInfoAsync(
isGuest,
userId,
premiumStatusFetcher
);
// Or just check premium status
const isPremium = await getIsPremium(isGuest, userId, premiumStatusFetcher);📚 API Reference
Core Functions
getUserTierInfo(isGuest, userId, isPremium)
Determines user tier from auth state and premium status.
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)isPremium: boolean- Whether user has active premium subscription
Returns: UserTierInfo
{
tier: 'guest' | 'freemium' | 'premium';
isPremium: boolean;
isGuest: boolean;
isAuthenticated: boolean;
userId: string | null;
}Examples:
// Guest user
getUserTierInfo(true, null, false);
// { tier: 'guest', isPremium: false, isGuest: true, isAuthenticated: false, userId: null }
// Premium user
getUserTierInfo(false, 'user123', true);
// { tier: 'premium', isPremium: true, isGuest: false, isAuthenticated: true, userId: 'user123' }
// Freemium user
getUserTierInfo(false, 'user123', false);
// { tier: 'freemium', isPremium: false, isGuest: false, isAuthenticated: true, userId: 'user123' }checkPremiumAccess(isGuest, userId, isPremium)
Checks if user has premium access. Guest users NEVER have premium access.
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)isPremium: boolean- Whether user has active premium subscription
Returns: boolean
Examples:
// Guest user - always false
checkPremiumAccess(true, null, true); // false
// Authenticated premium user
checkPremiumAccess(false, 'user123', true); // true
// Authenticated freemium user
checkPremiumAccess(false, 'user123', false); // falseisAuthenticated(isGuest, userId)
Checks if user is authenticated. Single source of truth for authentication check.
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)
Returns: boolean
isGuest(isGuest, userId)
Checks if user is a guest. Single source of truth for guest check.
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)
Returns: boolean
Type Guards
isValidUserTier(value)
Type guard to check if a value is a valid UserTier.
Parameters:
value: unknown- Value to check
Returns: value is UserTier
isUserTierInfo(value)
Type guard to check if an object is a valid UserTierInfo.
Parameters:
value: unknown- Value to check
Returns: value is UserTierInfo
Tier Comparison Utilities
hasTierAccess(tier1, tier2)
Compare two tiers to determine if first tier has higher or equal access than second.
Parameters:
tier1: UserTier- First tier to comparetier2: UserTier- Second tier to compare
Returns: boolean
Tier hierarchy: guest < freemium < premium
isTierPremium(tier)
Check if tier is premium.
Parameters:
tier: UserTier- Tier to check
Returns: boolean
isTierFreemium(tier)
Check if tier is freemium.
Parameters:
tier: UserTier- Tier to check
Returns: boolean
isTierGuest(tier)
Check if tier is guest.
Parameters:
tier: UserTier- Tier to check
Returns: boolean
Async Functions
getIsPremium(isGuest, userId, isPremiumOrFetcher)
Get isPremium value with centralized logic. Guest users NEVER have premium.
Two usage modes:
Sync mode - When you already have isPremium value:
const isPremium = getIsPremium(false, 'user123', true); // booleanAsync mode - When you need to fetch from database:
const isPremium = await getIsPremium( false, 'user123', { isPremium: async (userId) => await premiumService.isPremium(userId) } ); // Promise<boolean>
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)isPremiumOrFetcher: boolean | PremiumStatusFetcher- Either boolean (sync) or fetcher (async)
Returns: boolean (sync) or Promise<boolean> (async)
getUserTierInfoAsync(isGuest, userId, fetcher)
Gets tier info asynchronously with fetcher.
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)fetcher: PremiumStatusFetcher- Premium status fetcher interface
Returns: Promise<UserTierInfo>
checkPremiumAccessAsync(isGuest, userId, fetcher)
Checks premium access asynchronously with fetcher.
Parameters:
isGuest: boolean- Whether user is a guestuserId: string | null- User ID (null for guests)fetcher: PremiumStatusFetcher- Premium status fetcher interface
Returns: Promise<boolean>
React Hook
useUserTier(params)
React hook for tier determination.
Parameters:
{
isGuest: boolean;
userId: string | null;
isPremium: boolean; // App should fetch from database
isLoading?: boolean; // Optional: loading state from app
error?: string | null; // Optional: error state from app
}Returns: UseUserTierResult (extends UserTierInfo with isLoading and error)
🔧 Integration Examples
Example 1: Firebase Firestore
import { getUserTierInfo, getUserTierInfoAsync } from '@umituz/react-native-premium';
import type { PremiumStatusFetcher } from '@umituz/react-native-premium';
import { doc, getDoc } from 'firebase/firestore';
import { db } from './firebase';
// Option 1: Sync version (if you already have isPremium)
async function checkUserTier(userId: string | null, isGuest: boolean) {
if (isGuest || !userId) {
return getUserTierInfo(true, null, false);
}
// Fetch from your database
const userDoc = await getDoc(doc(db, 'users', userId));
const isPremium = userDoc.data()?.isPremium ?? false;
return getUserTierInfo(false, userId, isPremium);
}
// Option 2: Async version with fetcher
const premiumStatusFetcher: PremiumStatusFetcher = {
isPremium: async (userId: string) => {
const userDoc = await getDoc(doc(db, 'users', userId));
return userDoc.data()?.isPremium ?? false;
},
};
const tierInfo = await getUserTierInfoAsync(isGuest, userId, premiumStatusFetcher);Example 2: Custom API
import { getUserTierInfoAsync } from '@umituz/react-native-premium';
import type { PremiumStatusFetcher } from '@umituz/react-native-premium';
const premiumStatusFetcher: PremiumStatusFetcher = {
isPremium: async (userId: string) => {
const response = await fetch(`/api/users/${userId}/premium`);
const { isPremium } = await response.json();
return isPremium;
},
};
const tierInfo = await getUserTierInfoAsync(isGuest, userId, premiumStatusFetcher);Example 3: React Component with Zustand Store
import { useUserTier } from '@umituz/react-native-premium';
import { useAuthStore } from './stores/authStore';
import { usePremiumStore } from './stores/premiumStore';
function MyComponent() {
const { user, isGuest } = useAuthStore();
const { isPremium, isLoading, error } = usePremiumStore();
const { tier, isPremium: hasPremium, isGuest: isGuestUser } = useUserTier({
isGuest,
userId: user?.uid ?? null,
isPremium: isPremium ?? false,
isLoading,
error,
});
if (tier === 'guest') {
return <GuestUpgradeCard />;
}
if (tier === 'freemium') {
return <FreemiumLimits />;
}
return <PremiumFeatures />;
}🎯 Core Logic
The tier determination logic is simple and consistent:
// Guest users are ALWAYS freemium, NEVER premium
if (isGuest || !userId) {
return { tier: 'guest', isPremium: false };
}
// Authenticated users: premium or freemium
return {
tier: isPremium ? 'premium' : 'freemium',
isPremium,
};📝 Best Practices
- Always use this package for tier determination - Don't implement your own logic
- Fetch premium status in your app - This package doesn't handle database operations
- Pass loading/error states - Use the optional
isLoadinganderrorparams inuseUserTier - Cache premium status - Use your state management (Zustand, Redux, etc.) to cache results
- Use fetcher pattern for async operations - Implement
PremiumStatusFetcherinterface
🔍 TypeScript Types
type UserTier = 'guest' | 'freemium' | 'premium';
interface UserTierInfo {
tier: UserTier;
isPremium: boolean;
isGuest: boolean;
isAuthenticated: boolean;
userId: string | null;
}
interface PremiumStatusFetcher {
isPremium(userId: string): Promise<boolean>;
}
interface UseUserTierParams {
isGuest: boolean;
userId: string | null;
isPremium: boolean;
isLoading?: boolean;
error?: string | null;
}
interface UseUserTierResult extends UserTierInfo {
isLoading: boolean;
error: string | null;
}🧪 Testing
All functions are pure and easy to test:
import { getUserTierInfo, checkPremiumAccess } from '@umituz/react-native-premium';
describe('getUserTierInfo', () => {
it('should return guest tier for guest users', () => {
const result = getUserTierInfo(true, null, false);
expect(result.tier).toBe('guest');
expect(result.isPremium).toBe(false);
});
it('should return premium tier for authenticated premium users', () => {
const result = getUserTierInfo(false, 'user123', true);
expect(result.tier).toBe('premium');
expect(result.isPremium).toBe(true);
});
it('should return freemium tier for authenticated non-premium users', () => {
const result = getUserTierInfo(false, 'user123', false);
expect(result.tier).toBe('freemium');
expect(result.isPremium).toBe(false);
});
});📄 License
MIT
🤝 Contributing
This package is designed to be used across 100+ apps. When making changes:
- Keep it simple - only tier logic, no database operations
- Maintain backward compatibility - don't break existing APIs
- Add tests - all functions should be tested
- Update documentation - keep README and examples up to date
🧪 Test Coverage
This package has 100% test coverage with comprehensive test suite:
- ✅ 60+ test cases
- ✅ 100% code coverage
- ✅ Edge case handling
- ✅ Input validation tests
- ✅ Error handling tests
Run tests:
npm test
npm run test:coverage🔄 Version History
1.4.0
- ✅ No wrapper needed! -
getIsPremiumnow supports both sync and async modes - ✅ Sync mode:
getIsPremium(isGuest, userId, isPremium: boolean)- when you already have the value - ✅ Async mode:
getIsPremium(isGuest, userId, fetcher: PremiumStatusFetcher)- when you need to fetch - ✅ Function overloads for type safety
- ✅ Works directly from package - no app-specific wrapper required
1.3.0
- ✅ 100% test coverage with comprehensive test suite
- ✅ Input validation for all functions
- ✅ Type guards (
isValidUserTier,isUserTierInfo) - ✅ Enhanced error handling with detailed messages
- ✅ Additional utility functions (
hasTierAccess,isTierPremium,isTierFreemium,isTierGuest) - ✅ Professional test setup with Jest
- ✅ Edge case handling (empty strings, special characters, etc.)
1.2.0
- Improved documentation and examples
- Better TypeScript types
- Enhanced async fetcher pattern
1.1.1
- Initial stable release
- Core tier determination logic
- React hook support
- Async fetcher pattern
