ScaleRocket/Mobile

Paiements mobiles

Achats in-app avec RevenueCat pour les abonnements iOS et Android, et pourquoi Stripe direct ne fonctionne pas sur mobile.

Paiements mobiles

Les achats in-app sur iOS et Android doivent passer par les systèmes de paiement d'Apple et Google. Vous ne pouvez pas utiliser le checkout Stripe directement pour les biens numériques vendus dans une app mobile.

Pourquoi pas Stripe direct ?

Apple et Google exigent que les biens numériques et abonnements vendus dans les apps utilisent leurs systèmes d'achat in-app (IAP) :

PlateformeExigenceCommission
iOSApple IAP requis pour les biens numériques15-30%
AndroidGoogle Play Billing requis pour les biens numériques15-30%

Stripe peut toujours être utilisé pour : les biens physiques, les services effectués hors de l'app, ou les abonnements web qui ne déverrouillent pas de contenu in-app.

RevenueCat

RevenueCat est la solution recommandée. Il encapsule les API IAP d'Apple et Google dans un SDK unique et simple :

npx expo install react-native-purchases

Configuration

import Purchases from "react-native-purchases";
import { Platform } from "react-native";

async function initRevenueCat() {
  Purchases.configure({
    apiKey: Platform.OS === "ios"
      ? "appl_your_ios_api_key"
      : "goog_your_android_api_key",
  });
}

Appelez ceci dans votre layout racine :

// app/_layout.tsx
useEffect(() => {
  initRevenueCat();
}, []);

Afficher les produits

async function loadOfferings() {
  const offerings = await Purchases.getOfferings();
  const current = offerings.current;

  if (current) {
    const monthly = current.monthly;  // Forfait mensuel
    const annual = current.annual;    // Forfait annuel

    return { monthly, annual };
  }
}

Acheter un abonnement

async function purchaseSubscription(packageToPurchase: PurchasesPackage) {
  try {
    const { customerInfo } = await Purchases.purchasePackage(packageToPurchase);
    const isPro = customerInfo.entitlements.active["pro"] !== undefined;

    if (isPro) {
      // Débloquer les fonctionnalités premium
      router.push("/dashboard");
    }
  } catch (error: any) {
    if (!error.userCancelled) {
      Alert.alert("Purchase Failed", error.message);
    }
  }
}

Vérifier le statut d'abonnement

Synchronisez le statut d'abonnement avec votre base Supabase via les webhooks RevenueCat :

// Vérifier localement d'abord
const customerInfo = await Purchases.getCustomerInfo();
const isPro = customerInfo.entitlements.active["pro"] !== undefined;

// Le webhook RevenueCat met à jour votre base Supabase
// Configurez dans : RevenueCat Dashboard > Integrations > Webhooks
// Endpoint : https://your-project.supabase.co/functions/v1/revenucat-webhook

Synchronisez le statut d'abonnement avec Convex via les webhooks RevenueCat :

// Vérifier localement d'abord
const customerInfo = await Purchases.getCustomerInfo();
const isPro = customerInfo.entitlements.active["pro"] !== undefined;

// Le webhook RevenueCat appelle votre action HTTP Convex
// Configurez dans : RevenueCat Dashboard > Integrations > Webhooks
// Endpoint : https://your-project.convex.site/revenucat-webhook

Restaurer les achats

Requis par Apple — les utilisateurs doivent pouvoir restaurer leurs achats sur de nouveaux appareils :

async function restorePurchases() {
  try {
    const customerInfo = await Purchases.restorePurchases();
    const isPro = customerInfo.entitlements.active["pro"] !== undefined;
    Alert.alert(isPro ? "Restored!" : "No active subscription found");
  } catch (error) {
    Alert.alert("Error", "Could not restore purchases");
  }
}

Écran Paywall

Construisez un écran paywall :

function PaywallScreen() {
  const [offerings, setOfferings] = useState<PurchasesOfferings | null>(null);

  useEffect(() => {
    Purchases.getOfferings().then(setOfferings);
  }, []);

  return (
    <ScreenLayout>
      <Text style={styles.title}>Upgrade to Pro</Text>
      <Text style={styles.subtitle}>Unlock all features</Text>

      {offerings?.current?.availablePackages.map((pkg) => (
        <TouchableOpacity key={pkg.identifier} onPress={() => purchaseSubscription(pkg)}>
          <Text>{pkg.product.title}</Text>
          <Text>{pkg.product.priceString}/month</Text>
        </TouchableOpacity>
      ))}

      <Button title="Restore Purchases" variant="ghost" onPress={restorePurchases} />
    </ScreenLayout>
  );
}

Configuration App Store

  1. Créez les abonnements dans App Store Connect (iOS) et Google Play Console (Android)
  2. Ajoutez les IDs de produit dans le dashboard RevenueCat
  3. Créez les Entitlements (ex. "pro") et liez-les aux produits
  4. Configurez les endpoints webhook pour la validation côté serveur

On this page