Hoy vamos a aprender cómo podemos añadir suscripciones en nuestra aplicación desarrollada con React Native para Android o iOS.

Para realizar las compras en aplicación tenemos que instalar la siguiente librería en nuestro proyecto:
npm install react-native-iap
Os recomiendo instalar la versión (a fecha de hoy):
"react-native-iap": "12.16.3",
Referencias de compras de suscripción en Android:
https://developer.android.com/google/play/billing/rtdn-reference?hl=es-419
Referencias de compras de suscripción en iOS:
https://developer.apple.com/documentation/appstoreservernotifications
Y ahora implementamos el siguiente código qué nos permitirá realizar la compra:
import React, { useEffect, useState } from 'react'; import { EmitterSubscription, Linking, Platform, SafeAreaView, ScrollView, StyleSheet, Text, View, Button, TouchableOpacity, } from 'react-native'; import { useTranslation } from 'react-i18next'; import { useAuthContext } from '@/context/AuthContext'; import { AxiosResponse } from 'axios'; import * as RNIap from 'react-native-iap'; import { ComprasObj } from '@/objects/ComprasIAP'; import { useServices } from '@/context/ServiceContext'; const test = false; const productosSolicitados = Platform.select({ ios: ['suscripcion.mes.1'], android: ['suscripcion.mes.1'], }); const ComprarSuscripcionScreen: React.FC = () => { const { t } = useTranslation(); const { userData } = useAuthContext(); const { comprasInAppQueries } = useServices(); const [productos, setProductos] = useState<RNIap.Subscription[]>([]); useEffect(() => { let retornoCompraError: EmitterSubscription | null = null; let retornoCompraUpdate: EmitterSubscription | null = null; RNIap.initConnection() .then(async () => { console.log('Conectado IAP'); try { const products = await RNIap.getSubscriptions({ skus: productosSolicitados!, }); products.sort((a, b) => { const aPrice = (a as any)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList?.[0]?.priceAmountMicros ?? 0; const bPrice = (b as any)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList?.[0]?.priceAmountMicros ?? 0; return aPrice - bPrice; }); setProductos(products); } catch (error) { console.log('Error buscando suscripciones: ' + error); } retornoCompraError = RNIap.purchaseErrorListener((error) => { console.log('Error compra: ' + JSON.stringify(error)); }); retornoCompraUpdate = RNIap.purchaseUpdatedListener(async (purchase) => { try { if (Platform.OS === 'android' && !purchase.isAcknowledgedAndroid && purchase.purchaseToken) { await RNIap.acknowledgePurchaseAndroid({ token: purchase.purchaseToken }); } await RNIap.finishTransaction({ purchase, isConsumable: false }); const objCompra: ComprasObj = { user_uuid: userData!.user_uuid, purchaseToken: purchase.purchaseToken ?? purchase.transactionReceipt, packageName: purchase.packageNameAndroid ?? '', productId: purchase.productId, transactionId: purchase.transactionId ?? '', purchaseTime: purchase.transactionDate, platform: Platform.OS, test: test, }; comprasInAppQueries?.sendSubscriptionFinished(objCompra) .then((response: AxiosResponse<string>) => { console.log('Compra enviada al backend: ', response.data); }) .catch((error) => { console.log('Error enviando compra al backend: ', error); }); } catch (err) { console.warn('Error procesando compra:', err); } }); }) .catch((error) => { console.log('Error conectando IAP: ' + error); }); return () => { retornoCompraUpdate?.remove(); retornoCompraError?.remove(); RNIap.endConnection().catch((e) => console.log('Error cerrando conexión:', e)); }; }, []); const comprar = (producto: RNIap.Subscription) => { const offerToken = (producto as any)?.subscriptionOfferDetails?.[0]?.offerToken; RNIap.requestSubscription({ sku: producto.productId, ...(offerToken && { subscriptionOffers: [{ sku: producto.productId, offerToken }], }), }).catch((error) => { console.log('❌ Error en la compra: ', error); }); }; const cancelarSuscripcion = (productId: string) => { const url = Platform.select({ android: `https://play.google.com/store/account/subscriptions?sku=${productId}&package=com.tuapp.package`, ios: 'https://apps.apple.com/account/subscriptions', }); Linking.openURL(url!); }; return ( <SafeAreaView style={styles.contenedorGeneral}> <ScrollView> <View style={styles.contenedorPremium}> <View style={styles.contenedorBotones}> {productos.map((pkg) => { const price = (pkg as any)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList?.[0]?.formattedPrice ?? ''; return ( <TouchableOpacity key={pkg.productId} style={styles.boton} onPress={() => comprar(pkg)} > <Text style={styles.botonTexto}>{`${t(pkg.productId)} ${price}`}</Text> </TouchableOpacity> ); })} </View> </View> </ScrollView> </SafeAreaView> ); }; export default ComprarSuscripcionScreen; const styles = StyleSheet.create({ contenedorGeneral: { flex: 1, width: '100%', height: '100%', backgroundColor: '#fff', }, contenedorPremium: { padding: 20, alignItems: 'center', }, card: { backgroundColor: '#f2f2f2', borderRadius: 10, padding: 20, width: '100%', marginBottom: 20, }, premiumTitulo: { fontSize: 20, fontWeight: 'bold', textAlign: 'center', marginBottom: 10, }, premiumSubtitulo: { fontSize: 16, fontWeight: '400', marginBottom: 10, textAlign: 'justify', }, premiumTexto: { fontSize: 15, marginBottom: 5, }, contenedorBotones: { width: '100%', alignItems: 'center', }, boton: { backgroundColor: '#007bff', paddingVertical: 12, paddingHorizontal: 20, borderRadius: 8, marginVertical: 6, width: '100%', alignItems: 'center', }, botonTexto: { color: '#fff', fontSize: 16, fontWeight: '600', }, });
NOTA: uso las i18n para devolver el valor en texto de los elementos de compra.
Con esto tendremos las compras implementadas.
Cómo se informa al backend de actualizaciones de suscripciones (renovaciones, cancelaciones, etc.)
1. Cuando el usuario compra o renueva dentro de la app
- El cliente (tu app React Native) recibe la notificación en tiempo real vía
purchaseUpdatedListener
. - Entonces tú mandas el recibo o token al backend para validar y actualizar el estado del usuario.
Esto ya lo tienes implementado con purchaseUpdatedListener
.
2. Pero las suscripciones pueden renovarse, cancelarse o expirar fuera de la app
Por ejemplo:
- Renovación automática mensual
- Cancelación desde Google Play o App Store (fuera de la app)
- Cambios de plan, reembolsos, etc.
Tu backend no sabe automáticamente cuándo esto ocurre si solo confía en el cliente.

Ingeniero en Informática, Investigador, me encanta crear cosas o arreglarlas y darles una nueva vida. Escritor y poeta. Más de 20 APPs publicadas y un libro en Amazon.