Abonnements RevenueCat : 16 cas limites et nuits blanches
Points clés : Implémenter des abonnements in-app avec RevenueCat semble simple : trois tiers, un SDK. En réalité, c'est 16 cas limites de changements de plan, des obligations légales françaises sur l'affichage TTC, et un singleton PurchaseService qui doit gérer la restauration des achats en temps réel. Voici tout ce que la documentation ne dit pas.
Quand j'ai décidé de monétiser TAMSIV, le choix de RevenueCat s'est imposé. Ils gèrent la complexité des stores pour toi — les receipts Google Play, la validation serveur, le tracking des abonnements. Enfin, en théorie.
En pratique, j'ai passé trois semaines sur l'implémentation. Pas à cause de RevenueCat lui-même (le SDK est bien fait), mais à cause de tous les cas limites que personne ne mentionne dans les tutoriels. Voici le retour d'expérience complet.
Comment structurer les tiers d'abonnement d'une app IA ?
TAMSIV propose trois plans :
- Free — Accès aux fonctions de base avec des limites quotidiennes. Assez pour découvrir l'app, pas assez pour un usage intensif.
- Pro — Tout est débloqué : génération d'images IA, STT cloud Deepgram (meilleur que le natif dans les environnements bruyants), mémos illimités, toutes les voix TTS.
- Team — La couche collaborative complète : groupes hiérarchiques sur 6 niveaux, assignations, checklists de groupe, permissions avancées.
Le choix de trois tiers n'est pas arbitraire. Les études montrent que trois options maximisent la conversion — l'effet d'ancrage pousse les utilisateurs vers le plan du milieu. Le Free attire, le Pro convertit, le Team monétise les équipes.
Le fichier planLimits.ts
Côté code, un fichier config/planLimits.ts centralise toutes les feature gates. Chaque fonctionnalité vérifie le plan actif avant de s'exécuter :
// Exemple simplifie
const PLAN_LIMITS = {
free: { dailyVoiceTasks: 5, aiImages: 0, cloudSTT: false },
pro: { dailyVoiceTasks: -1, aiImages: 20, cloudSTT: true },
team: { dailyVoiceTasks: -1, aiImages: 50, cloudSTT: true },
};
Ce fichier unique est la source de vérité. Pas de conditions if (plan === 'pro') dispersées dans le code — tout passe par les limites configurées. Quand je modifie un plan, je touche un seul fichier.
Quels sont les 16 cas limites des changements de plan ?
C'est là où ça devient cauchemardesque. Un utilisateur peut :
- Upgrader : Free → Pro, Free → Team, Pro → Team
- Downgrader : Team → Pro, Team → Free, Pro → Free
- Changer de période : Mensuel → Annuel, Annuel → Mensuel
- Combiner les deux : Upgrade + changement de période
- Annuler : Avec accès jusqu'à la fin de la période
- Réactiver : Avant ou après l'expiration
- Restaurer : Sur un nouveau téléphone
J'ai compté 16 cas distincts. Pour chacun, il faut gérer :
- Le moment d'activation : Un upgrade prend effet immédiatement. Un downgrade est différé à la fin de la période en cours.
- Le prorata : Google Play calcule automatiquement le prorata pour les upgrades. Mais tu dois l'afficher correctement dans l'UI.
- La mise à jour des feature gates en temps réel : Quand un utilisateur upgrade, les nouvelles fonctionnalités doivent se débloquer instantanément, sans redémarrage de l'app.
- L'affichage correct : Le bon plan, la bonne date d'expiration, le bon prix, le bon statut.
Trois jours de tests pour tout couvrir. C'est ennuyeux, méthodique, et absolument indispensable. Un seul cas manque, et tu reçois un email d'un utilisateur furieux qui a payé pour un Pro et qui est toujours en Free.
Comment gérer les obligations légales françaises pour les abonnements ?
Si tu vends en France, la DGCCRF impose des règles strictes :
- Prix TTC obligatoire : En France, on affiche toujours les prix TTC (Toutes Taxes Comprises) pour les consommateurs. Les stores fournissent les prix localisés, mais tu dois vérifier que l'affichage respecte la législation.
- Mention "Prix TTC" : Le texte doit être explicite.
- Lien vers les CGV : Les Conditions Générales de Vente doivent être accessibles depuis l'écran de paiement.
- Information sur le droit de rétractation : Pour les achats numériques, le droit de rétractation s'applique sous certaines conditions. Tu dois en informer l'utilisateur.
- Durée d'engagement : L'utilisateur doit savoir clairement s'il souscrit un abonnement mensuel ou annuel, et comment le résilier.
Ne pas respecter ces règles, c'est risquer un rejet de l'app par Google ou une amende de la DGCCRF. Les CGV et mentions légales de TAMSIV sont accessibles depuis le site web et depuis l'app.
Comment architecturer le PurchaseService en React Native ?
Tout passe par un singleton PurchaseService. C'est le pattern que j'utilise pour tous les services dans TAMSIV — ConversationService, CalendarService, GamificationService, tous suivent le même modèle (j'en parle dans l'article sur le refactoring clean architecture).
Le PurchaseService fait 4 choses :
- Initialise RevenueCat au démarrage de l'app avec la clé API du projet.
- Écoute les changements d'état : upgrade, downgrade, expiration, restauration. Chaque changement déclenche une mise à jour des feature gates.
- Expose le plan actif via un hook :
usePurchase()retourne le plan courant, la date d'expiration, et les fonctionnalités disponibles. - Synchronise avec Supabase : Le plan actif est aussi stocké en base de données pour que le backend puisse vérifier les permissions (par exemple, limiter le nombre de requêtes vocales pour le plan Free).
La restauration des achats
C'est le cas le plus piège. Un utilisateur change de téléphone, réinstalle l'app, et s'attend à retrouver son abonnement Pro. RevenueCat gère ça via restorePurchases(), mais le timing est critique : la restauration peut prendre quelques secondes, pendant lesquelles l'utilisateur est en mode Free. Si tu ne gères pas cet état intermédiaire, l'utilisateur voit un écran "Upgrade vers Pro" alors qu'il est déjà Pro.
La solution : un état "chargement" explicite au démarrage, qui bloque l'affichage des feature gates jusqu'à ce que RevenueCat ait confirmé le plan actif.
Quelles métriques suivre pour les abonnements ?
RevenueCat fournit un dashboard excellent. Les métriques que je suis quotidiennement :
- MRR (Monthly Recurring Revenue) : Le revenu récurrent mensuel. La métrique reine.
- Conversion Free → Pro : Quel pourcentage des utilisateurs gratuits upgradent ?
- Churn rate : Combien d'abonnés annulent chaque mois ?
- Trial conversion : Si tu offres un essai gratuit, quel pourcentage convertit ?
- Revenue per user : Le revenu moyen par utilisateur actif.
Ces métriques alimentent le dashboard admin que j'ai construit pour monitorer la santé du projet en temps réel.
Quelles erreurs éviter avec RevenueCat ?
Voici les pièges dans lesquels je suis tombé :
- Ne pas tester en sandbox : Google Play a un mode sandbox pour les achats. Utilise-le systématiquement. Un bug de paiement en production, c'est un remboursement + un utilisateur perdu.
- Oublier la migration des utilisateurs existants : Si tu ajoutes des abonnements à une app existante, les utilisateurs actuels doivent être migrés vers le plan Free proprement. Pas de downgrade silencieux.
- Ne pas gérer le mode avion : RevenueCat met en cache le plan actif localement. Mais si l'utilisateur est hors-ligne pendant un upgrade, les feature gates peuvent être désynchronisées. Prévois un mécanisme de réconciliation au retour online.
- Ignorer les webhooks : RevenueCat envoie des webhooks pour chaque événement (achat, annulation, renouvellement). Le backend doit les traiter pour garder Supabase synchronisé.
Comment le système de parrainage interagit-il avec les abonnements ?
TAMSIV a un système de parrainage qui offre un mois gratuit de Pro ou Team quand un utilisateur invite un ami. Ça ajoute une couche de complexité : le code promo RevenueCat doit être appliqué correctement, le mois gratuit doit commencer au bon moment, et le retour au plan original doit être transparent.
L'interaction parrainage + abonnement a généré 3 des 16 cas limites mentionnés plus haut. C'est un excellent levier de croissance, mais ça demande une implémentation soignée.
FAQ
Pourquoi RevenueCat plutôt que l'API native Google Play Billing ?
L'API native Google Play Billing est complexe, mal documentée, et change régulièrement. RevenueCat abstrait cette complexité avec un SDK propre, un dashboard analytique, et le support multi-plateforme (Android + iOS). Le coût (gratuit jusqu'à $2.5k MRR, puis 1% du revenu) est négligeable comparé au temps de dev économisé.
Faut-il proposer un essai gratuit ?
Ça dépend de ton modèle. Pour TAMSIV, le plan Free est déjà un essai permanent des fonctionnalités de base. Un essai gratuit du Pro pendant 7 jours est en test — les premières données montrent un taux de conversion plus élevé, mais aussi un churn plus important après l'essai.
Comment afficher les prix dans plusieurs devises ?
Google Play fournit les prix localisés via l'API. RevenueCat les expose dans offerings.current.availablePackages. Tu n'as pas à gérer la conversion de devises toi-même — le store affiche toujours le prix local. En France, c'est en euros TTC.
Que se passe-t-il si Google Play est down ?
RevenueCat met en cache le plan actif sur le device. Si Google Play est temporairement indisponible, l'utilisateur garde son accès. Le risque est minime car Google Play a un uptime de 99.99%. Mais le PurchaseService prévoit un fallback vers le cache local en cas de timeout de l'API.
Comment gérer la TVA pour les ventes internationales ?
C'est le store qui gère la TVA, pas toi. Google Play collecte et reverse la TVA selon le pays de l'acheteur. Tu reçois le montant net. Mais tu dois quand même afficher les prix TTC dans l'app et respecter les règles d'affichage du pays de l'utilisateur.