Skip to main content

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: {},
});