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, tenemos que indicar que la app contiene compras.
Para ello podemos añadir esta propiedad dentro de app.json
"entitlements": {
"com.apple.InAppPurchase": true
}
O bien:
También podemos hacerlo activando 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, 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.
1 comentario en «Añadir compras de aplicación (iOS/Android) en React Native»