import { useEffect, useState } from 'react';
import { Platform } from 'react-native';
import Purchases, { CustomerInfo, PurchasesOffering } from 'react-native-purchases';
const API_KEYS = {
apple: 'appl_xxxxxxxxxxxxxxxxxxxxxx',
google: 'goog_xxxxxxxxxxxxxxxxxxxxxx',
};
export function useRevenueCat(userId?: string) {
const [currentOffering, setCurrentOffering] = useState<PurchasesOffering | null>(null);
const [isPro, setIsPro] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
const setupPurchases = async () => {
try {
Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG);
const apiKey = Platform.select({ ios: API_KEYS.apple, android: API_KEYS.google })!;
Purchases.configure({ apiKey, appUserID: userId });
const offerings = await Purchases.getOfferings();
if (offerings.current !== null) {
setCurrentOffering(offerings.current);
}
const customerInfo = await Purchases.getCustomerInfo();
setIsPro(typeof customerInfo.entitlements.active['pro_access'] !== 'undefined');
} catch (error) {
console.error('Eroare la inițializarea RevenueCat:', error);
} finally {
setLoading(false);
}
};
setupPurchases();
const listener = (info: CustomerInfo) => {
setIsPro(typeof info.entitlements.active['pro_access'] !== 'undefined');
};
Purchases.addCustomerInfoUpdateListener(listener);
}, [userId]);
return { currentOffering, isPro, loading };
}Am băgat RevenueCat pe vreo trei proiecte React Native până acum, dar ultimul în Expo Managed Workflow (SDK 50+) a fost cel mai rapid setup pe care l-am făcut vreodată. Dacă vrei să vinzi abonamente sau paywalls dinamice fără să-ți scrii propriul backend pentru validarea chitanțelor Apple și Google, combinația asta e practic standardul în industrie. Hai să-ți arăt exact unde se rupe filmul la configurare și ce trucuri am învățat pe pielea mea.
De ce să nu scrii cod custom de IAP?
La un proiect anterior am încercat să procesăm abonamentele direct cu StoreKit 2 și Google Play Billing Library v5, plus o infrastructură proprie în Node.js cu webhook-uri. Am pierdut peste 80 de ore de dev doar gestionând cazuri speciale: grace periods, billing issues, refunds, family sharing și sincronizare între dispozitive.
RevenueCat rezolvă asta out of the box. Evident, există un trade-off clar: costul. E moca până la $2.5k MRA (Monthly Revenue Tracked), dar după aia plătești 1% din venituri. Pentru 90% din startup-uri sau aplicații indie, tradeoff-ul merită fiecare cent. Economisești sute de ore de mentenanță de backend pe care altfel le-ai pierde de fiecare dată când Apple sau Google schimbă API-urile de billing.
Setup-ul în Expo: Adio Expo Go
Prima chestie de care te lovești: nu poți testa In-App Purchases în aplicația Expo Go. Pachetul react-native-purchases conține cod nativ și are nevoie de un Expo Config Plugin.
Trebuie să adaugi plugin-ul în app.json, apoi să generezi un Development Build cu EAS (eas build --profile development --platform ios/android). Eu folosesc un simulator de iOS pentru UI basic, dar pentru fluxul real de plată Sandbox ai nevoie neapărat de un dispozitiv fizic sau de un emulator configurat corect.
{
"expo": {
"plugins": [
"react-native-purchases"
]
}
}
Entitlements, Offerings și setup-ul în magazine
Magia în RevenueCat stă în abstractizare. Nu legi codul din aplicație direct de ID-ul produsului (com.app.monthly_499). În schimb, lucrezi cu trei concepte:
- Products: ID-urile brute create în App Store Connect și Google Play Console.
- Entitlements: Ce primește userul (ex:
pro_access). - Offerings: Setul de opțiuni pe care le afișezi în Paywall (ex:
defaultcu pachetemonthlyșiannual).
O capcană mare pe Android: pe Google Play Console trebuie să creezi o aplicație de test, să uploadezi un AAB pe un track de Closed Testing și să adaugi adresa de mail a testerului în lista de testeri licențiați. Altfel, când apelezi Purchases.getOfferings(), Google îți va întoarce o listă goală fără niciun eroare explicită.
Pe iOS lucrurile merg mai lin cu StoreKit Configuration Files (.storekit) în Xcode pentru local testing, dar când treci pe Sandbox-ul real din App Store Connect, asigură-te că ai acceptat toate acordurile de Paid Apps în contul de Apple Developer. Altfel stai și te întrebi de ce produsele sunt invalid.
Testing în Sandbox: Lecții învățate pe banii (și timpul) meu
Pe iOS, un abonament lunar în Sandbox expiră în 5 minute și se reînnoiește de maxim 5 ori pe zi. Pe Android, o lună înseamnă tot cam 5 minute. Nu uita să tratezi asta când testezi logica de UI – starea de isSubscribed se va schimba foarte repede.
Altă chestie: apelează Purchases.configure() cât mai devreme în ciclul de viață al aplicației, ideal în _layout.tsx (dacă folosești Expo Router) sau în App.tsx. Dacă ai un sistem propriu de auth, pasează appUserID-ul din backend-ul tău în configure ca să nu ajungi cu utilizatori anonimi dublați în dashboard-ul RevenueCat.
Unde ați întâmpinat cele mai mari blocaje la integrarea plăților pe mobil? Preferați un wrapper ca RevenueCat/Adapty sau mergeți pe soluții custom?