Entitlements
Control access to features with entitlements
What are Entitlements?
Entitlements represent the features or content that users can unlock through purchases. Instead of checking for specific product purchases, you check if a user has a specific entitlement. This abstraction provides several benefits:
- Change products without updating app code
- Multiple products can grant the same entitlement
- Grant entitlements manually for promotions
- Cleaner code focused on features, not products
How Entitlements Work
┌─────────────────────────────────────────────────────────┐
│ Products │
├─────────────────┬─────────────────┬─────────────────────┤
│ Monthly Premium │ Annual Premium │ Lifetime Premium │
│ $9.99/mo │ $79.99/yr │ $199.99 │
└────────┬────────┴────────┬────────┴──────────┬──────────┘
│ │ │
│ All grant: │ │
▼ ▼ ▼
┌─────────────────────────────────────┐
│ "premium" entitlement │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ hasEntitlement('premium') = true │
│ │
│ • Ad-free experience │
│ • Exclusive content │
│ • Advanced features │
└─────────────────────────────────────┘Creating Entitlements
In the Dashboard
- Navigate to Products
Go to your app's Products section in the dashboard
- Click "Create Entitlement"
Find the Entitlements tab and create a new one
- Set the identifier
Use a simple, descriptive identifier like "premium" or "pro_features"
- Add display name and description
These are for your reference in the dashboard
Via API
POST /api/v1/entitlements
{
"identifier": "premium",
"displayName": "Premium Access",
"description": "Full access to all premium features"
}Linking Entitlements to Products
Each product can grant one or more entitlements when purchased. When a user purchases the product, they automatically receive all linked entitlements.
Example: Simple App
Example: Multi-Tier App
Checking Entitlements in Your App
Using the SDK
import { usePurchases } from '@croissantpay/react-native';
function MyComponent() {
const { hasEntitlement, entitlements } = usePurchases();
// Simple check
const isPremium = hasEntitlement('premium');
// Check with details
const premiumEntitlement = entitlements['premium'];
if (premiumEntitlement?.isActive) {
console.log('Expires:', premiumEntitlement.expiresAt);
console.log('From product:', premiumEntitlement.productId);
}
return (
<View>
{isPremium ? (
<PremiumContent />
) : (
<UpgradePrompt />
)}
</View>
);
}Server-Side Check
GET /api/v1/subscribers/user_123/entitlements
Authorization: X-API-Key: mx_live_xxx
// Response
{
"entitlements": {
"premium": {
"isActive": true,
"expiresAt": "2024-02-15T00:00:00Z",
"productId": "com.app.premium_monthly",
"purchaseDate": "2024-01-15T00:00:00Z"
}
}
}Granting Entitlements Manually
Sometimes you need to grant entitlements without a purchase—for promotions, customer support, or testing.
POST /api/v1/entitlements/grant
{
"appUserId": "user_123",
"entitlementId": "premium",
"expiresAt": "2024-12-31T23:59:59Z", // Optional, omit for permanent
"reason": "Customer support compensation"
}Note: Manually granted entitlements are marked as such in the subscriber record, so you can distinguish them from purchase-based entitlements.
Best Practices
Use descriptive identifiers
Choose identifiers that describe the feature, not the product. "premium_features" is better than "monthly_subscription".
Plan for growth
Create entitlements for feature groups you might separate later. It's easier to merge than to split.
Cache entitlements client-side
The SDK caches entitlements automatically. Only refresh when needed (app launch, after purchase, pull-to-refresh).
Verify server-side for sensitive operations
For actions with real consequences (unlocking content, accessing APIs), verify entitlements server-side as well.
Example: Feature Gating Pattern
import { usePurchases } from '@croissantpay/react-native';
import { useNavigation } from '@react-navigation/native';
interface FeatureGateProps {
entitlement: string;
children: React.ReactNode;
fallback?: React.ReactNode;
}
export function FeatureGate({
entitlement,
children,
fallback
}: FeatureGateProps) {
const { hasEntitlement } = usePurchases();
const navigation = useNavigation();
if (hasEntitlement(entitlement)) {
return <>{children}</>;
}
if (fallback) {
return <>{fallback}</>;
}
return (
<View style={styles.locked}>
<Lock size={24} />
<Text>This feature requires {entitlement}</Text>
<Button
title="Upgrade"
onPress={() => navigation.navigate('Paywall')}
/>
</View>
);
}
// Usage
<FeatureGate entitlement="premium">
<AdvancedAnalytics />
</FeatureGate>
<FeatureGate
entitlement="pro"
fallback={<BasicExport />}
>
<AdvancedExport />
</FeatureGate>