React Native SDK

Install and configure the CroissantPay SDK

The CroissantPay React Native SDK provides a simple API to manage in-app purchases, subscriptions, and entitlements in your mobile apps.

Installation

Terminal
# Using npm
npm install @croissantpay/react-native

# Using yarn
yarn add @croissantpay/react-native

# Using pnpm
pnpm add @croissantpay/react-native

iOS Setup

Install CocoaPods dependencies:

cd ios && pod install

Android Setup

No additional setup required. The SDK automatically links with React Native 0.60+.

Quick Start

1. Wrap your app with CroissantPayProvider

App.tsx
import { CroissantPayProvider } from '@croissantpay/react-native';

export default function App() {
  return (
    <CroissantPayProvider
      config={{
        apiKey: 'mx_public_your_key', // From CroissantPay dashboard
        apiUrl: 'https://api.croissantlabs.com', // Or your self-hosted URL
        appUserId: user?.id, // Your user's ID
        debugLogs: __DEV__, // Enable logs in development
      }}
    >
      <YourApp />
    </CroissantPayProvider>
  );
}

2. Use the usePurchases hook

PaywallScreen.tsx
import { usePurchases } from '@croissantpay/react-native';

export function PaywallScreen() {
  const {
    offerings,
    subscriberInfo,
    purchase,
    restore,
    hasEntitlement,
    isLoading,
  } = usePurchases();

  // Check if user has premium access
  if (hasEntitlement('premium')) {
    return <PremiumContent />;
  }

  // Get current offering products
  const currentOffering = offerings?.current;
  const products = currentOffering?.products || [];

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Unlock Premium</Text>
      
      {products.map((product) => (
        <TouchableOpacity
          key={product.identifier}
          style={styles.productButton}
          onPress={() => purchase(product.identifier)}
          disabled={isLoading}
        >
          <Text style={styles.productName}>{product.displayName}</Text>
          <Text style={styles.productPrice}>{product.priceString}</Text>
        </TouchableOpacity>
      ))}

      <TouchableOpacity onPress={restore} disabled={isLoading}>
        <Text>Restore Purchases</Text>
      </TouchableOpacity>
    </View>
  );
}

API Reference

CroissantPayProvider Props

PropTypeDescription
apiKeystringYour CroissantPay public API key (required)
apiUrlstringAPI URL (defaults to https://api.croissantlabs.com)
appUserIdstringYour app's user identifier
debugLogsbooleanEnable debug logging

usePurchases Hook

PropertyTypeDescription
isConfiguredbooleanWhether SDK is configured
isLoadingbooleanLoading state for async operations
subscriberInfoSubscriberInfo | nullCurrent subscriber data with entitlements
offeringsOfferings | nullAvailable products and packages
errorError | nullLast error that occurred

Methods

purchase(productIdentifier)Promise<PurchaseResult>

Purchase a product by its identifier. Handles native store flow and receipt validation.

restore()Promise<RestoreResult>

Restore previous purchases. Useful for users reinstalling the app or switching devices.

identify(appUserId)Promise<SubscriberInfo>

Identify or switch the current user. Call this when your user logs in.

refresh()Promise<void>

Refresh subscriber info and offerings from the server.

hasEntitlement(entitlementId)boolean

Check if user has an active entitlement. Quick way to gate premium features.

Additional Hooks

useEntitlement

Check a specific entitlement:

import { useEntitlement } from '@croissantpay/react-native';

function PremiumFeature() {
  const { isActive, entitlement, isLoading } = useEntitlement('premium');

  if (isLoading) return <Loading />;
  if (!isActive) return <UpgradePrompt />;

  return <PremiumContent expiresAt={entitlement.expiresDate} />;
}

useCurrentOffering

Get the current offering directly:

import { useCurrentOffering } from '@croissantpay/react-native';

function Paywall() {
  const { offering, isLoading } = useCurrentOffering();

  if (isLoading || !offering) return null;

  return (
    <View>
      {offering.monthly && (
        <ProductCard product={offering.monthly.product} />
      )}
      {offering.annual && (
        <ProductCard product={offering.annual.product} badge="Best Value" />
      )}
    </View>
  );
}

Helper Functions

import { 
  isSubscription, 
  isConsumable, 
  formatPeriod,
  getBestPackage 
} from '@croissantpay/react-native';

// Check product type
isSubscription(product); // true for subscription products
isConsumable(product);   // true for consumable products

// Format subscription period
formatPeriod('P1M');  // "month"
formatPeriod('P1Y');  // "year"
formatPeriod('P7D');  // "7 days"

// Get best package from offering (prefers annual > monthly > weekly)
const bestPackage = getBestPackage(offering);

Error Handling

import { CroissantPayError } from '@croissantpay/react-native';

async function handlePurchase(productId: string) {
  const result = await purchase(productId);

  if (!result.success && result.error) {
    const error = CroissantPayError.fromPurchaseError(result.error);

    if (error.isCancelled) {
      // User cancelled - don't show error
      return;
    }

    if (error.isPending) {
      // Purchase pending (e.g., parental approval)
      showPendingMessage();
      return;
    }

    if (error.isNetworkError) {
      showNetworkError();
      return;
    }

    // Show generic error
    showError(error.message);
  }
}

Next Steps