Hoy os voy a enseñar cómo agregar compras de aplicacion (APPPurcharses) en tu aplicación con React Native.
Lo primero que vamos a hacer es instalar la dependencia que vamos a utilizar para agregar esta característica: react-native-iap
npm install --save react-native-iap
Una vez instalada, vamos a configurar un componente para compras:
import React, { useEffect, useState } from "react"; import { StyleSheet, Text, Platform, View } from "react-native"; import * as RNIap from 'react-native-iap';
Importamos los componentes y la libreria RNIap de react-native-iap.
const productosSolicitados = Platform.select({ ios: [ 'codigo.compra', 'codigo.compra.2' ], android: [ 'codigo.compra', 'codigo.compra.2' ] });
Creamos un array que depende de la plataforma (ios/android) y dentro añadimos los códigos de compras que configuramos en Google Play Console o iOS developer.
Ahora vamos al render y creamos la conexión con el API de compras:
const Compras = (props) => { const [comprado, setComprado] = useState(false); const [productos, setProductos] = useState({}); useEffect(() => { RNIap.initConnection().catch((error) => { console.log("Error conectando IAP: " + error); }).then(() => { console.log("Conectado IAP"); RNIap.getProducts({ skus: productosSolicitados}).then((products) => { console.log("Productos: " + JSON.stringify(products)); setProductos(products); }).catch((error) => { console.log("Error buscando productos: " + error); } ); }); }, []); return ( <View> <Text>Prueba compras</Text> </View> ); };
Primero hemos creado dos estados:
const [comprado, setComprado] = useState(false); const [productos, setProductos] = useState({});
En ellos vamos a añadir lo que se ha comprado y los productos que llegan de la tienda de aplicaciones.
Dentro del primer método que se ejecuta (useEffect) vamos a añadir la conexión con RNIap
RNIap.initConnection().catch((error) => { console.log("Error conectando IAP: " + error); }).then(() => { console.log("Conectado IAP");
Después añadimos el código para obtener los productos configurados:
RNIap.getProducts({ skus: productosSolicitados}).then((products) => { console.log("Productos: " + JSON.stringify(products)); setProductos(products); }).catch((error) => { console.log("Error buscando productos: " + error); } );
Si queremos obtener suscripciones tendremos que añadir este código:
RNIap.getSubscriptions({ skus: productosSolicitados}).then((products) => { console.log("Productos: " + JSON.stringify(products)); setProductos(products); }).catch((error) => { console.log("Error buscando productos: " + error); } );
Antes de ejecutar la APP, tenemos que ir a la carpeta de Android>app>src>main>manifest.xml y añadir este permiso (si no tienes la carpeta app generada puedes generarla siguiendo este tutorial https://devcodelight.com/generar-carpetas-de-codigo-nativo-android-ios-en-react-native-con-expo/) :
<uses-permission android:name="com.android.vending.BILLING" />
Si ejecutamos la APP ahora mismo, nos devolverá el error: Error: E_IAP_NOT_AVAILABLE (https://devcodelight.com/solucionar-error-e_iap_not_available-con-la-libreria-react-native-iap-en-react-native/(abre en una nueva pestaña), ya que esta librería es nativa y necesita ser instalada.
PARA EVITAR HACERLO A MANO:
Creamos un archivo llamado ComprasInAPPConfigBuild.js
Con este contenido:
const { withAppBuildGradle, withAndroidManifest } = require('@expo/config-plugins'); const withCustomAndroidConfig = (config) => { // Modificar build.gradle para agregar missingDimensionStrategy config = withAppBuildGradle(config, (config) => { if (config.modResults.contents) { if (!config.modResults.contents.includes("missingDimensionStrategy 'store', 'play'")) { config.modResults.contents = config.modResults.contents.replace( /defaultConfig\s*{([\s\S]*?)}/, `defaultConfig { $1 missingDimensionStrategy 'store', 'play' }` ); } } return config; }); // Modificar AndroidManifest.xml para agregar permiso de facturación config = withAndroidManifest(config, (config) => { const billingPermission = { $: { 'android:name': 'com.android.vending.BILLING', }, }; const androidManifest = config.modResults; const manifest = androidManifest.manifest; // Asegúrate de que 'uses-permission' esté definido if (!manifest['uses-permission']) { manifest['uses-permission'] = []; } // Comprobar si el permiso ya existe const existingPermissionIndex = manifest['uses-permission'].findIndex( (permission) => permission.$['android:name'] === 'com.android.vending.BILLING' ); // Agregar el permiso si no existe if (existingPermissionIndex === -1) { manifest['uses-permission'].push(billingPermission); } return config; }); return config; }; module.exports = withCustomAndroidConfig;
Ahora para que se aplique, añadimos este plugin en app.json:
{ "expo": { "plugins": [ "./ComprasInAPPConfigBuild" ] } }
Y usamos el comando:
npx expo prebuild
Ahora tenemos que generar el build para más información sobre como generar un build de desarrollo usa este artículo: Como crear un build development usando Expo EAS con React Native
eas build --profile development --platform android
Si da error al generar el build, tendremos que hacer esto: https://devcodelight.com/solucionar-error-could-not-resolve-project-react-native-iap-al-generar-build-dev-con-la-libreria-react-native-iap-en-react-native/(abre en una nueva pestaña)
Una vez generado el build, podremos ejecutarlo con este comando:
npx expo start --dev-client
Y ya nos devuleve las compras configuradas:
return ( <View> <Text>Prueba Premium</Text> { productos.map((pkg) => { return <Button style={styles.boton} mode="contained" onPress={() => comprar(pkg.productId)}> <Text>Compra 1 {pkg.oneTimePurchaseOfferDetails.formattedPrice}</Text> </Button> }) } </View> );
Recorremos el array de compras disponibles y además asignamos una función que vamos a crear, llamada comprar();. Para obtener el id de producto accedemos pkg.productId y el para el precio pkg.oneTimePurchaseOfferDetails.formattedPrice
Creamos la función de comprar:
function comprar(product_id) { console.log("Comprar: " + product_id); RNIap.requestPurchase({ skus: [product_id] }).then((purchase) => { console.log("Comprado: " + JSON.stringify(purchase)); setComprado(true); }).catch((error) => { console.log("Error: " + error); }); }
Si queremos comprar suscripciones tendremos que poner:
function comprar(product_id) { console.log("Comprar: " + product_id); RNIap.requestSubscription({ skus: [product_id] }).then((purchase) => { console.log("Comprado: " + JSON.stringify(purchase)); setComprado(true); }).catch((error) => { console.log("Error: " + error); }); }
Este código funciona para Android, si queremos que funcione en iOS, tenemos que modificarlo de esta forma ({ sku: product_id }):
function comprar(product_id) { console.log("Comprar: " + product_id); RNIap.requestSubscription({ sku: product_id }).then((purchase) => { console.log("Comprado: " + JSON.stringify(purchase)); setComprado(true); }).catch((error) => { console.log("Error: " + error); }); }
Para poder utilizar los dos códigos, podemos poner un if dependiendo de la plataforma:
function comprar(product_id) { if (Platform.OS === 'android') { console.log("Comprar: " + product_id); RNIap.requestSubscription({ skus: [product_id] }).then((purchase) => { console.log("Comprado: " + JSON.stringify(purchase)); setComprado(true); }).catch((error) => { console.log("Error: " + error); }); } else if (Platform.OS === 'ios') { console.log("Comprar: " + product_id); RNIap.requestSubscription({ sku: product_id }).then((purchase) => { console.log("Comprado: " + JSON.stringify(purchase)); setComprado(true); }).catch((error) => { console.log("Error: " + error); }); } }
Ahora nos falta obtener la validación de la compra. Para ello creamos dos variables nuevas debajo de nuestro array de productos que hemos llamado productosSolicitados:
let retornoCompraUpdate; let retornoCompraError;
Y lo asignamos dentro de usseffect justo al final:
useEffect(() => { .... retornoCompraError = RNIap.purchaseErrorListener((error) => { console.log("Error compra: " + JSON.stringify(error)); if (error.code == "E_USER_CANCELLED") { console.log("Compra cancelada"); } if (error.code == "E_UNKNOWN") { console.log("Error desconocido"); } }); retornoCompraUpdate = RNIap.purchaseUpdatedListener((purchase) => { console.log("Compra actualizada: " + JSON.stringify(purchase)); setComprado(true); //Aquí deberíamos validar con un servidor si la compra es válida } ); });
Quedando todo junto de esta forma:
useEffect(() => { RNIap.initConnection().catch((error) => { console.log("Error conectando IAP: " + error); }).then(() => { console.log("Conectado IAP"); RNIap.getProducts({ skus: productosSolicitados}).then((products) => { console.log("Productos: " + JSON.stringify(products)); setProductos(products); }).catch((error) => { console.log("Error buscando productos: " + error); } ); }); retornoCompraError = RNIap.purchaseErrorListener((error) => { console.log("Error compra: " + JSON.stringify(error)); if (error.code == "E_USER_CANCELLED") { console.log("Compra cancelada"); } if (error.code == "E_UNKNOWN") { console.log("Error desconocido"); } }); retornoCompraUpdate = RNIap.purchaseUpdatedListener((purchase) => { console.log("Compra actualizada: " + JSON.stringify(purchase)); setComprado(true); //Aquí deberíamos validar con un servidor si la compra es válida } ); }, []);
Una vez realizada la compra, podemos validarla. Aunque recomiendo validarla con un servidor dedicado o firebase, aquí os pongo un ejemplo de cómo validarla de forma local.
Para ello añadimos este código en el purchaseUpdatedListener:
RNIap.acknowledgePurchaseAndroid({ token: purchase.purchaseToken }).then(() => { console.log('acknowledgePurchaseAndroid success'); }).catch(err => { console.log('acknowledgePurchaseAndroid error', err); });
De esta forma primero aceptamos la compra y finalmente finaliza la compra. Si nuestra compra es consumible, es decir que permite comprar de nuevo, añadiremos esta línea de código para que permita de nuevo volver a comprar:
RNIap.finishTransaction({ purchase: purchase, isConsumable: true });
Quedando el código así:
RNIap.acknowledgePurchaseAndroid({ token: purchase.purchaseToken }).then(() => { console.log('acknowledgePurchaseAndroid success'); RNIap.finishTransaction({ purchase: purchase, isConsumable: true }); }).catch(err => { console.log('acknowledgePurchaseAndroid error', err); });
Este código consumirá las compras en Android, si queremos consumir las compras de iOS, tendremos que poner solo esta línea:
RNIap.finishTransaction({ purchase: purchase, isConsumable: true });
Si estamos añadiendo compras a una app iOS, tendremos que activar la capacidad de compra en nuestra APP dentro de Xcode. Para eso hacemos lo siguiente
- Generamos los ficheros de APP Build:
expo prebuild
- Abrimos con Xcode nuestro proyecto (abrimos la carpeta iOS de nuestro proyecto)
- Seleccionamos nuestro proyecto y pulsamos en Signin & Capabilities.
- Ahora pulsamos en + Capability
- Elegimos de la lista la de In-App Purcharse
- Y se añade a nuestro proyecto.
- Añadir las compras de aplicación en la web de APPLE DEVELOPER (abajo del todo al abrir nuestra APP):
Es importante que rellenes todos los campos de la compra en aplicación y que el estado sea Lista para enviar. Para que aparezcan en la APP de pruebas.
- Firmar los acuerdos, seleccionar impuestos y banca para poder recibir los pagos.
Para que las compras funcionen, hay que crear una cuenta Sandbox.
Ingeniero en Informática, 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.
1 comentario en «Añadir compras de aplicación (iOS/Android) en React Native»