Documentation Index
Fetch the complete documentation index at: https://docs.mobile-starter.amisi.ai/llms.txt
Use this file to discover all available pages before exploring further.
@amisi/subscriptions
@amisi/subscriptions provides an adapter-based in-app subscription and purchase API for managing subscriptions across iOS, Android, and web platforms.
Adapter model
- Adapters implement a shared
SubscriptionAdapter contract
- The active adapter is selected at runtime via
initializeSubscriptions({ adapter, options })
- Built-in adapters:
revenuecat, mock
Initialize
Initialize once at app startup:
import { initializeSubscriptions } from '@amisi/subscriptions';
await initializeSubscriptions({
adapter: 'revenuecat',
options: {
apiKey: 'your_revenuecat_api_key',
// or platform-specific keys:
// iosApiKey: 'ios_key',
// androidApiKey: 'android_key',
entitlementId: 'premium', // optional
logLevel: 'DEBUG', // optional
},
});
SubscriptionsProvider
Wrap your app with the SubscriptionsProvider to provide subscription context throughout your app:
import { SubscriptionsProvider } from '@amisi/subscriptions';
export default function App() {
return (
<SubscriptionsProvider
config={{
adapter: 'revenuecat',
options: {
apiKey: 'your_revenuecat_api_key',
entitlementId: 'premium',
},
}}
>
<YourApp />
</SubscriptionsProvider>
);
}
The provider automatically:
- Initializes the subscription adapter
- Fetches products and subscription status
- Provides subscription state to all child components
- Handles refresh and purchase operations
Hook usage
useSubscriptions
Access subscription state and operations from the provider:
import { useSubscriptions } from '@amisi/subscriptions';
export function SubscriptionScreen() {
const { loading, status, products, purchase, restore, refresh } =
useSubscriptions();
if (loading) {
return <LoadingSpinner />;
}
return (
<View>
<Text>Active: {status?.isActive ? 'Yes' : 'No'}</Text>
{products.map((product) => (
<ProductCard
key={product.id}
product={product}
onPurchase={() => purchase(product.id)}
/>
))}
<Button onPress={restore}>Restore Purchases</Button>
</View>
);
}
useEntitlement
Check if a user has access to a specific product:
import { useEntitlement } from '@amisi/subscriptions';
export function PremiumFeature() {
const hasAccess = useEntitlement('premium_monthly');
if (!hasAccess) {
return <UpgradePrompt />;
}
return <PremiumContent />;
}
useHasActiveSubscription
Check if the user has any active subscription:
import { useHasActiveSubscription } from '@amisi/subscriptions';
export function Header() {
const isSubscribed = useHasActiveSubscription();
return <View>{isSubscribed ? <PremiumBadge /> : <UpgradeButton />}</View>;
}
useSubscriptionProduct
Get the current active subscription product ID:
import { useSubscriptionProduct } from '@amisi/subscriptions';
export function SubscriptionInfo() {
const productId = useSubscriptionProduct();
return <Text>Current plan: {productId || 'Free'}</Text>;
}
Hook alias usage
useSubscription is an alias of useSubscriptions and must be used inside SubscriptionsProvider:
import { useSubscription } from '@amisi/subscriptions';
export function SubscriptionScreen() {
const { loading, status, products, purchase, restore, refresh } =
useSubscription();
if (loading) {
return <LoadingSpinner />;
}
return (
<View>
<Text>Active: {status?.isActive ? 'Yes' : 'No'}</Text>
{products.map((product) => (
<ProductCard
key={product.id}
product={product}
onPurchase={() => purchase(product.id)}
/>
))}
<Button onPress={restore}>Restore Purchases</Button>
</View>
);
}
Purchase a product
const success = await purchase('monthly_subscription_id');
if (success) {
console.log('Purchase successful!');
}
Restore purchases
const restored = await restore();
if (restored) {
console.log('Purchases restored!');
}
Check subscription status
The status object contains:
interface SubscriptionStatus {
isActive: boolean;
productId?: string;
expiresAt?: Date;
willRenew?: boolean;
platform?: 'ios' | 'android' | 'web';
}
Available products
The products array contains:
interface SubscriptionProduct {
id: string;
name: string;
price: string;
currency: string;
period: 'monthly' | 'yearly' | 'lifetime';
features?: string[];
}
RevenueCat adapter
The RevenueCat adapter supports the following options:
interface RevenueCatSubscriptionsOptions {
apiKey?: string; // Universal API key
iosApiKey?: string; // iOS-specific key
androidApiKey?: string; // Android-specific key
appUserId?: string; // Custom user ID
useAmazon?: boolean; // Use Amazon App Store
logLevel?: 'VERBOSE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
entitlementId?: string; // Specific entitlement to check
}
Mock adapter
For testing and development:
await initializeSubscriptions({
adapter: 'mock',
options: {
// Mock adapter provides fake products and subscription status
},
});
Paywall component
Use the built-in Paywall component:
import { Paywall } from '@amisi/subscriptions';
export function App() {
return (
<Paywall
onPurchase={(productId) => console.log('Purchased:', productId)}
onRestore={() => console.log('Restored')}
onClose={() => console.log('Closed')}
/>
);
}
Custom adapter
Create a custom adapter by implementing the SubscriptionAdapter interface:
import { SubscriptionAdapter } from '@amisi/subscriptions';
export function createCustomAdapter(): SubscriptionAdapter {
return {
name: 'custom',
async initialize(config) {
// Initialize your subscription service
},
async getProducts() {
// Return available products
return [];
},
async getProduct(id) {
// Return specific product
return null;
},
async purchaseProduct(productId) {
// Handle purchase
return true;
},
async restorePurchases() {
// Restore purchases
return true;
},
async getSubscriptionStatus() {
// Return subscription status
return { isActive: false };
},
async cleanup() {
// Cleanup resources
},
};
}
Then register and use it:
import { registerSubscriptionAdapter } from '@amisi/subscriptions';
registerSubscriptionAdapter('custom', createCustomAdapter());
await initializeSubscriptions({
adapter: 'custom',
options: {},
});