Integrando Pagos con Stripe en React Native: Guía Completa para iOS y Android
Este tutorial detallado te guiará paso a paso en la integración de Stripe para procesar pagos en tus aplicaciones React Native, tanto para iOS como para Android. Aprenderás desde la configuración inicial hasta la implementación del flujo de pago completo, asegurando transacciones seguras y eficientes.
Introducción a los Pagos Móviles con Stripe en React Native ✨
En el mundo actual del desarrollo móvil, ofrecer una experiencia de pago fluida y segura es crucial para el éxito de cualquier aplicación de comercio electrónico o servicio. Stripe es una de las plataformas de pago más populares y robustas, conocida por su facilidad de integración, potentes APIs y seguridad de primera clase. En este tutorial, te sumergirás en la implementación de pagos con Stripe en tus aplicaciones React Native, cubriendo todos los aspectos necesarios para tener un sistema de pago funcional en iOS y Android.
¿Por qué Stripe para React Native? 🤔
Stripe ofrece una serie de ventajas que lo hacen ideal para aplicaciones móviles:
- API Amigable para Desarrolladores: Documentación clara y SDKs bien diseñados.
- Seguridad: Manejo de la complejidad PCI DSS para proteger los datos de tus usuarios.
- Flexibilidad: Permite pagos con tarjetas de crédito/débito, Apple Pay, Google Pay y otros métodos.
- Escalabilidad: Diseñado para manejar desde pequeñas startups hasta grandes empresas.
- Global: Soporte para múltiples divisas y métodos de pago internacionales.
🛠️ Configuración Inicial y Prerrequisitos
Antes de sumergirnos en el código, necesitamos configurar nuestro entorno y nuestras cuentas.
1. Crear una Cuenta de Stripe y Obtener Claves API 🔑
Si aún no tienes una cuenta de Stripe, dirígete a stripe.com y regístrate. Una vez dentro del Dashboard de Stripe:
- Navega a Developers > API keys.
- Encontrarás tu Publishable key (clave publicable, que comienza con
pk_test_...para modo de prueba) y tu Secret key (clave secreta, que comienza consk_test_...para modo de prueba). - La clave publicable se usará en el frontend de tu aplicación React Native, mientras que la clave secreta DEBE usarse SOLAMENTE en tu backend seguro.
2. Instalar el SDK de Stripe para React Native 📦
Utilizaremos la biblioteca oficial de Stripe para React Native: @stripe/stripe-react-native.
En tu proyecto React Native, ejecuta:
npm install @stripe/stripe-react-native
or
yarn add @stripe/stripe-react-native
3. Configuración para iOS (React Native CLI) 🍎
Para proyectos react-native init, debes enlazar las dependencias nativas:
cd ios && pod install && cd ..
Además, asegúrate de que tu proyecto iOS tenga una configuración de deployment target de al menos iOS 11.0. Esto lo puedes verificar en Xcode, en la configuración de tu target.
4. Configuración para Android (React Native CLI) 🤖
Para Android, no se requiere configuración manual adicional con la mayoría de las versiones de React Native, ya que el autolinking se encarga de todo. Sin embargo, asegúrate de que tu minSdkVersion en android/app/build.gradle sea al menos 21 (Android 5.0 Lollipop).
// android/app/build.gradle
android {
defaultConfig {
minSdkVersion 21 // Asegúrate de que sea al menos 21
// ...
}
// ...
}
🚀 Inicializando Stripe en tu Aplicación
Una vez instalada la librería, necesitamos inicializar Stripe en el nivel superior de nuestra aplicación.
StripeProvider Componente ✅
El componente StripeProvider debe envolver la parte de tu aplicación donde usarás la funcionalidad de Stripe. Se encarga de inicializar el SDK de Stripe con tu clave publicable.
Modifica tu archivo App.js o index.js (o tu componente raíz):
// App.js
import React from 'react';
import { SafeAreaView, StyleSheet, Text, View } from 'react-native';
import { StripeProvider } from '@stripe/stripe-react-native';
import PaymentScreen from './PaymentScreen'; // Crearemos este componente pronto
const App = () => {
return (
<StripeProvider
publishableKey="pk_test_YOUR_PUBLISHABLE_KEY" // Reemplaza con tu clave publicable de prueba
// urlScheme="your-app-scheme" // Requerido para 3D Secure y otros métodos de pago que redirigen
// merchantIdentifier="merchant.com.your_app_name" // Requerido para Apple Pay
>
<SafeAreaView style={styles.container}>
<Text style={styles.header}>Mi Tienda RN</Text>
<PaymentScreen />
</SafeAreaView>
</StripeProvider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F5F5',
paddingTop: 50,
},
header: {
fontSize: 28,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#333',
},
});
export default App;
Explicación de propiedades:
publishableKey: Tu clave publicable de Stripe. Obligatoria.urlScheme: Un esquema URL personalizado para tu aplicación. Es crucial si planeas usar métodos de pago que requieren redirección, como 3D Secure o ciertos métodos de pago bancarios. Configura esto en tuInfo.plist(iOS) yAndroidManifest.xml(Android). Por ejemplo,<data android:scheme="your-app-scheme" />.merchantIdentifier: Esencial si planeas integrar Apple Pay. Debes registrar un ID de comerciante en tu cuenta de desarrollador de Apple.
🌐 Configurando el Backend para Pagos Seguros
La parte más crítica para la seguridad de los pagos es que las operaciones sensibles (como la creación de PaymentIntent y la confirmación de pagos) se realicen en tu servidor backend, no directamente desde la aplicación móvil. Esto protege tu clave secreta de Stripe y previene fraudes.
Para este tutorial, usaremos un backend simple basado en Node.js y Express. Puedes adaptar estos conceptos a cualquier lenguaje de backend que prefieras (Python, PHP, Ruby, Java, Go, etc.).
1. Inicializar un Proyecto Backend (Node.js) 💻
Crea una nueva carpeta para tu backend (fuera de tu proyecto React Native) e inicialízala:
mkdir stripe-backend
cd stripe-backend
npm init -y
npm install express stripe dotenv cors
Crea un archivo .env en la raíz de tu proyecto backend para almacenar tu clave secreta:
STRIPE_SECRET_KEY=sk_test_YOUR_SECRET_KEY
PORT=5000
2. Implementar el Servidor Backend 🔐
Crea un archivo server.js (o index.js) con el siguiente contenido:
// server.js
require('dotenv').config();
const express = require('express');
const app = express();
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const cors = require('cors');
const PORT = process.env.PORT || 5000;
app.use(cors({ origin: '*' })); // Permite solicitudes desde cualquier origen para desarrollo
app.use(express.json()); // Permite analizar cuerpos de solicitud JSON
app.get('/', (req, res) => {
res.send('Stripe backend is running!');
});
app.post('/create-payment-intent', async (req, res) => {
const { amount, currency, paymentMethodType } = req.body;
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: amount, // en la unidad más pequeña de la moneda (ej. centavos)
currency: currency,
payment_method_types: [paymentMethodType || 'card'],
// Para producción, se recomienda añadir un `metadata` o `description`
// para identificar la transacción. Por ejemplo:
// metadata: { userId: 'user_123', orderId: 'order_abc' }
});
res.json({
clientSecret: paymentIntent.client_secret,
});
} catch (error) {
console.error('Error creating payment intent:', error.message);
res.status(500).json({ error: error.message });
}
});
app.listen(PORT, () => console.log(`Backend server running on port ${PORT}`));
Puntos clave del backend:
dotenv: Para cargar variables de entorno (tu clave secreta).stripe: El SDK de Stripe para Node.js, inicializado con tu clave secreta./create-payment-intentendpoint: Este es el endpoint que tu aplicación React Native llamará. Recibe elamount,currencyypaymentMethodTypede la aplicación.stripe.paymentIntents.create(): Crea unPaymentIntent, que es el objeto central de Stripe para manejar el ciclo de vida de un pago. Devuelve unclient_secretque la aplicación cliente usará para confirmar el pago.
Inicia tu servidor backend:
node server.js
Verás Backend server running on port 5000 en tu consola.
💳 Implementando la UI de Pago en React Native
Ahora que nuestro backend puede crear PaymentIntents, podemos construir la interfaz de usuario en React Native para recolectar la información de pago y procesar la transacción.
Crea un nuevo archivo PaymentScreen.js en tu proyecto React Native:
// PaymentScreen.js
import React, { useState } from 'react';
import { View, Text, Button, Alert, ActivityIndicator, StyleSheet, TextInput } from 'react-native';
import { useStripe, CardField, confirmPayment, createPaymentMethod } from '@stripe/stripe-react-native';
const API_URL = 'http://localhost:5000'; // Reemplaza con la IP de tu máquina si usas un dispositivo físico
const PaymentScreen = () => {
const { initPaymentSheet, presentPaymentSheet, confirmPayment: confirmStripePayment } = useStripe();
const [loading, setLoading] = useState(false);
const [paymentReady, setPaymentReady] = useState(false);
const [cardDetails, setCardDetails] = useState(null); // Para CardField si lo usamos
// Opcional: Para el PaymentSheet completo
const initializePaymentSheet = async () => {
setLoading(true);
try {
const response = await fetch(`${API_URL}/create-payment-intent`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 1000, // $10.00 en centavos
currency: 'usd',
paymentMethodType: 'card', // Asegúrate de que coincida con el backend
}),
});
const { clientSecret, error: backendError } = await response.json();
if (backendError) {
Alert.alert('Error del Backend', backendError);
setLoading(false);
return;
}
const { error } = await initPaymentSheet({
customerId: 'customer-id-optional', // Si tienes un ID de cliente de Stripe
customerEphemeralKeySecret: 'ephemeral-key-secret-optional', // Para guardar métodos de pago
paymentIntentClientSecret: clientSecret,
merchantDisplayName: 'Mi Tienda RN',
allowsDelayedPaymentMethods: true,
// Para temas de UI personalizados:
// style: 'alwaysDark' // 'alwaysLight' o 'automatic'
});
if (error) {
Alert.alert(`Error: ${error.code}`, error.message);
} else {
setPaymentReady(true);
}
} catch (e) {
console.error('Error inicializando Payment Sheet:', e);
Alert.alert('Error', 'No se pudo inicializar la hoja de pagos.');
} finally {
setLoading(false);
}
};
const openPaymentSheet = async () => {
if (!paymentReady) return;
setLoading(true);
try {
const { error } = await presentPaymentSheet();
if (error) {
Alert.alert(`Error: ${error.code}`, error.message);
} else {
Alert.alert('¡Éxito!', 'Tu pago fue procesado correctamente.');
// Aquí puedes navegar a una pantalla de confirmación o resetear el estado
}
} catch (e) {
console.error('Error abriendo Payment Sheet:', e);
Alert.alert('Error', 'No se pudo abrir la hoja de pagos.');
} finally {
setLoading(false);
setPaymentReady(false); // Reset para un nuevo pago
}
};
// --- Alternativa: Usar CardField para recolectar detalles de la tarjeta directamente ---
const handleCardChange = (details) => {
setCardDetails(details);
};
const createPayment = async () => {
setLoading(true);
try {
if (!cardDetails?.complete) {
Alert.alert('Error', 'Por favor, introduce los detalles completos de la tarjeta.');
setLoading(false);
return;
}
// 1. Obtener el clientSecret de tu backend
const response = await fetch(`${API_URL}/create-payment-intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: 2000, // $20.00 en centavos
currency: 'usd',
paymentMethodType: 'card',
}),
});
const { clientSecret, error: backendError } = await response.json();
if (backendError) {
Alert.alert('Error del Backend', backendError);
setLoading(false);
return;
}
// 2. Confirmar el pago con el clientSecret y los detalles de la tarjeta
const { error: confirmError, paymentIntent } = await confirmStripePayment(clientSecret, {
paymentMethodType: {
type: 'Card',
billingDetails: { /* Opcional: detalles de facturación */ },
},
});
if (confirmError) {
Alert.alert(`Error: ${confirmError.code}`, confirmError.message);
if (confirmError.paymentIntent) {
console.log('Payment Intent Status:', confirmError.paymentIntent.status);
}
} else if (paymentIntent) {
Alert.alert('¡Pago Confirmado!', `Estado: ${paymentIntent.status}`);
// Aquí puedes actualizar tu UI o navegar a la pantalla de confirmación
}
} catch (e) {
console.error('Error durante el pago:', e);
Alert.alert('Error', 'Ha ocurrido un error inesperado durante el pago.');
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Realizar Pago</Text>
{/* Opción 1: Usar PaymentSheet de Stripe (recomendado) */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Opción 1: Stripe PaymentSheet</Text>
<Button
title="Inicializar PaymentSheet ($10.00)"
onPress={initializePaymentSheet}
disabled={loading}
color="#6200EE"
/>
<View style={{ marginTop: 15 }} />
<Button
title="Abrir PaymentSheet"
onPress={openPaymentSheet}
disabled={!paymentReady || loading}
color="#03DAC6"
/>
{loading && <ActivityIndicator size="large" color="#0000ff" style={{ marginTop: 20 }} />}
</View>
<View style={styles.separator} />
{/* Opción 2: Usar CardField (personalizado, más control) */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Opción 2: Formulario de Tarjeta Manual ($20.00)</Text>
<View style={styles.cardFieldContainer}>
<CardField
postalCodeEnabled={false} // Puedes habilitar si necesitas el código postal
placeholders={{
number: 'Número de tarjeta',
}}
cardStyle={{
backgroundColor: '#FFFFFF',
textColor: '#000000',
placeholderColor: '#888888',
borderColor: '#CCCCCC',
borderWidth: 1,
borderRadius: 5,
textErrorColor: '#FF0000',
}}
style={styles.cardField}
onCardChange={handleCardChange}
/>
</View>
<Button
title="Pagar con Tarjeta"
onPress={createPayment}
disabled={!cardDetails?.complete || loading}
color="#6200EE"
/>
{loading && <ActivityIndicator size="large" color="#0000ff" style={{ marginTop: 20 }} />}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#F9F9F9',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 30,
textAlign: 'center',
color: '#333',
},
section: {
marginBottom: 30,
backgroundColor: '#FFFFFF',
padding: 15,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 5,
elevation: 3,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
marginBottom: 15,
color: '#555',
},
cardFieldContainer: {
height: 50,
marginBottom: 20,
},
cardField: {
width: '100%',
height: 50,
},
input: {
height: 50,
borderColor: '#CCC',
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 10,
marginBottom: 15,
backgroundColor: '#FFF',
},
separator: {
height: 1,
backgroundColor: '#E0E0E0',
marginVertical: 20,
},
});
export default PaymentScreen;
Explicación del Componente PaymentScreen 📖
Hemos implementado dos enfoques para la UI de pago:
-
Stripe
PaymentSheet(Recomendado):useStripe()hook: Proporciona acceso a las funciones de Stripe, comoinitPaymentSheet,presentPaymentSheetyconfirmPayment.initializePaymentSheet(): Llama a tu backend para obtener unclientSecrety luego usainitPaymentSheetpara preparar la hoja de pagos. Esta hoja de pagos gestiona la recopilación de datos de la tarjeta, Apple Pay/Google Pay, y otros métodos de pago.openPaymentSheet(): Muestra la interfaz de usuario de la hoja de pagos. Una vez que el usuario ingresa sus detalles y confirma, el SDK de Stripe maneja el resto del flujo de pago.- Ventajas: Proporciona una experiencia de usuario completa y segura, adaptada a la plataforma, con soporte para múltiples métodos de pago (tarjetas, wallets, etc.). Menos código para ti.
-
CardFieldconconfirmPayment:CardField: Un componente UI que Stripe te proporciona para recolectar de forma segura los detalles de la tarjeta. Maneja la validación y el tokenizado de forma automática.handleCardChange: Captura los detalles de la tarjeta a medida que el usuario los introduce.createPayment(): Primero, obtiene unclientSecretde tu backend (igual que conPaymentSheet). Luego, usaconfirmPaymentjunto con los detalles de la tarjeta recolectados porCardFieldpara procesar el pago.- Ventajas: Mayor control sobre la UI y la experiencia de usuario si necesitas un diseño muy específico.
- Desventajas: Requiere más manejo manual de errores y flujos de pago, y no incluye de forma nativa la misma amplitud de métodos de pago que
PaymentSheet.
🧪 Probando la Integración
Para probar tu implementación de Stripe, puedes usar las tarjetas de prueba que Stripe proporciona. Asegúrate de que tu backend y frontend estén corriendo.
1. Iniciar el Backend
cd stripe-backend
node server.js
2. Iniciar la Aplicación React Native
npm run ios
// o
npm run android
3. Usar Tarjetas de Prueba de Stripe 💳
Stripe proporciona números de tarjeta de prueba para simular diferentes escenarios:
| Número de Tarjeta | CVV | Fecha de Caducidad | Resultado |
|---|---|---|---|
| --- | --- | --- | --- |
4242 4242 4242 4242 | 123 | Cualquier fecha futura | Pago exitoso |
4000 0000 0000 0002 | 123 | Cualquier fecha futura | Fallo: Fondos insuficientes |
| --- | --- | --- | --- |
4000 0000 0000 0000 | 123 | Cualquier fecha futura | Error de validación |
Diagrama de Flujo de Pago 📈
✅ Buenas Prácticas y Consideraciones Avanzadas
Webhooks para Confirmación Asíncrona 🎣
Aunque presentPaymentSheet y confirmPayment te dan una respuesta inmediata, es crucial implementar webhooks en tu backend para confirmar el estado final de un PaymentIntent. Algunos pagos pueden requerir autenticación adicional (3D Secure) o ser procesados de forma asíncrona, lo que significa que el estado final no se conoce en el momento de la confirmación inicial en la aplicación.
Ejemplo de un endpoint de Webhook
// server.js (continuación)
// Asegúrate de que este middleware se aplique SÓLO a las rutas de webhook
// para que el cuerpo de la solicitud no sea parseado como JSON prematuramente.
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
// Verifica la firma del webhook para asegurar que es de Stripe
event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
return res.sendStatus(400);
}
// Maneja el evento
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntentSucceeded = event.data.object;
console.log(`PaymentIntent for ${paymentIntentSucceeded.amount} was successful!`);
// Aquí actualiza tu base de datos, envía un correo de confirmación, etc.
break;
case 'payment_intent.payment_failed':
const paymentIntentFailed = event.data.object;
console.log(`PaymentIntent failed: ${paymentIntentFailed.last_payment_error?.message}`);
// Aquí notifica al usuario, gestiona el error, etc.
break;
// ... maneja otros eventos relevantes
default:
console.log(`Unhandled event type ${event.type}`);
}
// Devuelve una respuesta 200 para que Stripe sepa que el evento fue recibido
res.json({ received: true });
});
Para probar webhooks en desarrollo, puedes usar la CLI de Stripe: stripe listen --forward-to localhost:5000/webhook.
Gestión de Errores y UI/UX 🎨
- Mensajes Claros: Proporciona mensajes de error informativos al usuario si un pago falla.
- Estados de Carga: Usa indicadores de carga (
ActivityIndicator) para informar al usuario que la aplicación está procesando el pago. - Reintentos: Permite a los usuarios reintentar un pago fallido, especialmente si el problema fue temporal (ej. error de red).
- Validación del lado del cliente: Aunque Stripe
CardFieldhace gran parte del trabajo, puedes añadir validaciones adicionales para mejorar la experiencia.
Guardar Métodos de Pago para Futuro (Customers) 👤
Stripe te permite guardar los métodos de pago de los usuarios asociados a un Customer para futuras transacciones. Esto mejora la experiencia del usuario al no tener que introducir sus datos de tarjeta cada vez.
- Crea un
Customeren tu backend:
const customer = await stripe.customers.create({
email: 'customer@example.com',
});
- Usa el
customerIdal inicializarPaymentSheety también uncustomerEphemeralKeySecret(que también se genera en el backend) para permitir que el cliente realice acciones seguras con suCustomerID.
// Backend para Ephemeral Key
const ephemeralKey = await stripe.ephemeralKeys.create(
{ customer: customer.id },
{ apiVersion: '2023-10-16' }
);
res.json({ ephemeralKey: ephemeralKey.secret, customerId: customer.id, paymentIntentClientSecret });
Luego pásalo a `initPaymentSheet`.
Apple Pay y Google Pay 🍎🤖
@stripe/stripe-react-native ofrece soporte nativo para Apple Pay y Google Pay a través de PaymentSheet. Necesitarás configuraciones adicionales específicas para cada plataforma (ID de comerciante en Apple Developer, configuración de Google Pay en la consola de Google Developer). La PaymentSheet simplifica enormemente la integración de estos métodos.
Seguridad y PCI DSS 🔒
Al usar Stripe (especialmente PaymentSheet o CardField), la mayor parte de la carga de cumplir con PCI DSS se transfiere a Stripe. Esto se debe a que tu aplicación nunca toca directamente la información sensible de la tarjeta. Sin embargo, siempre debes seguir las mejores prácticas de seguridad en tu backend y en tu aplicación para proteger otros datos de usuario.
Conclusión 🎉
¡Felicidades! Has completado un tutorial exhaustivo sobre cómo integrar pagos con Stripe en tu aplicación React Native. Hemos cubierto desde la configuración inicial, la implementación de un backend seguro, hasta la creación de una interfaz de usuario de pago y las mejores prácticas. Ahora tienes las herramientas para habilitar transacciones seguras y eficientes en tus aplicaciones móviles.
La integración de pagos es un componente vital para muchas aplicaciones, y Stripe, junto con React Native, proporciona una combinación potente y flexible para lograrlo. Recuerda siempre priorizar la seguridad y probar a fondo tu implementación con tarjetas de prueba antes de pasar a producción.
Tutoriales relacionados
- React Native con TypeScript: Desarrollo de Aplicaciones Robustas y Escalablesintermediate25 min
- Integrando Realidad Aumentada en React Native con ViroReact: Guía Completaintermediate20 min
- Navegación Avanzada en React Native: React Navigation v6intermediate20 min
- React Native con Expo: Desarrollo de Apps Multiplataforma de Manera Eficienteintermediate25 min
- Integrando Notificaciones Push en React Native con Firebase Cloud Messaging (FCM)intermediate18 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!