📌 Documentación del SDK Milio para React Native (iOS)
🚀 Requisitos Previos
Antes de comenzar, asegúrate de cumplir con los siguientes requisitos:
- ✅ React Native
>=0.60
(se recomienda usar versiones actuales). - ✅ Xcode
>=14.0
instalado. - ✅ CocoaPods (
pod --version
debería devolver una versión válida). - ✅ Token de autenticación proporcionado por Milio.
1. Integración del SDK de iOS en React Native
✅ 1️⃣ Instalar la Dependencia del SDK con CocoaPods
Asegúrate de que CocoaPods esté instalado:
sudo gem install cocoapods
Luego, abre la carpeta ios y actualiza las dependencias:
cd ios
pod install --repo-update
Si el SDK de Milio se distribuye a través de un repositorio privado de CocoaPods, agrégalo en tu Podfile (ios/Podfile):
source 'https://github.com/CocoaPods/Specs.git'
source 'org.bitbucket.miliopay:sdk-mobile-android:v0.1.2'
target 'TuApp' do
use_frameworks!
pod 'MilioSDK', '0.1.1'
end
Finalmente, instala las dependencias:
pod install
2. Crear el Bridge entre React Native y el SDK de iOS
Para exponer el SDK de iOS a React Native, crea el archivo:
**ios/MilioSdkModule.m**
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#import <MilioSDK/MilioSDK.h>
@interface RCT_EXTERN_MODULE(MilioSdk, RCTEventEmitter)
RCT_EXTERN_METHOD(initializeSdk:(NSString *)token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(openTransferenciaQr:(NSString *)jsonData resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(openTransferenciaManual:(NSString *)jsonData resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(openTransferenciaInmediata:(NSString *)jsonData resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(openRecargaBilletera:(NSString *)jsonData resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@end
Ahora, crea el archivo Swift para implementar los métodos nativos:
ios/MilioSdkModule.swift
import Foundation
import React
@objc(MilioSdkModule)
class MilioSdkModule: NSObject, RCTBridgeModule {
private var authToken: String?
static func moduleName() -> String {
return "MilioSdk"
}
@objc
func initializeSdk(_ token: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
authToken = token
print("SDK Inicializado con token: \(authToken ?? "N/A")")
resolve("SDK Inicializado correctamente")
// Configurar el callback de transacción
TransactionConfigSdk.setTransactionCallback { (amount, message) in
print("✅ Transacción exitosa: \(amount) - \(message)")
} onCompletion: { (amount, isAddition) in
print("🔹 Transacción completada: amount=\(amount), isAddition=\(isAddition)")
} onError: { (errorCode, errorMessage) in
print("❌ Error en la transacción: Código \(errorCode) - \(errorMessage)")
self.sendErrorToReactNative(errorCode: errorCode, errorMessage: errorMessage)
}
}
@objc
func setThemeColors(_ config: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
guard let colors = config as? [String: String] else {
reject("ERROR", "Configuración de colores inválida", nil)
return
}
let btnColorEnabled = colors["btnColorEnabled"] ?? "#0e6655"
let btnColorDisabled = colors["btnColorDisabled"] ?? "#D3D3D3"
let btnTextColorEnabled = colors["btnTextColorEnabled"] ?? "#ffffff"
let btnTextColorDisabled = colors["btnTextColorDisabled"] ?? "#888888"
let layoutBackgroundColor = colors["layoutBackgroundColor"] ?? "#a2d9ce"
let textViewColor = colors["textViewColor"] ?? "#0b5345"
TransactionConfigSdk.setThemeColors(
btnColorEnabled,
btnColorDisabled,
btnTextColorEnabled,
btnTextColorDisabled,
layoutBackgroundColor,
textViewColor
)
print("✅ Colores aplicados correctamente")
resolve("Colores aplicados correctamente")
}
@objc
func openMilioSdkQr(_ jsonData: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
guard let authToken = authToken else {
reject("ERROR", "Token no disponible", nil)
return
}
TransactionConfigSdk.OpenMilioSdkQr(jsonData)
print("✅ Escáner QR abierto correctamente")
resolve("Escáner QR abierto correctamente")
}
@objc
func openMilioPayManualCard(_ jsonData: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
guard let authToken = authToken else {
reject("ERROR", "Token no disponible", nil)
return
}
TransactionConfigSdk.OpenMilioPayManualCard(authToken, jsonData)
print("✅ Transacción iniciada con éxito")
resolve("Transacción iniciada con éxito")
}
@objc
func openMilioPayAddFoundManualCard(_ jsonData: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
guard let authToken = authToken else {
reject("ERROR", "Token no disponible", nil)
return
}
let jsonData1 = """
{
"amount": 1500.00,
"type": "OUT",
"thirdBankUUID": "dfb56715-ed55-4839-b5d0-18645da02dd0"
}
"""
TransactionConfigSdk.OpenMilioPayAddFoundManualCard(authToken, jsonData1)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
resolve("Pago de fondos iniciado con éxito")
}
}
@objc
func openRecargaBilletera(_ jsonData: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
guard let authToken = authToken else {
reject("ERROR", "Token no disponible", nil)
return
}
TransactionConfigSdk.OpenMilioPayRechargeWallet(authToken, jsonData)
print("✅ Recarga de billetera iniciada correctamente")
resolve("Recarga de billetera iniciada correctamente")
}
private func sendErrorToReactNative(errorCode: Int, errorMessage: String) {
if let bridge = self.bridge {
bridge.eventDispatcher().sendAppEvent(withName: "onTransactionError", body: [
"errorCode": errorCode,
"errorMessage": errorMessage
])
}
}
}
3. Enlazar el Módulo en React Native
MilioSdkClient.js
import { NativeModules } from 'react-native';
const { MilioSdk } = NativeModules;
if (!MilioSdk) {
throw new Error("MilioSdkModule no se encuentra disponible. ¿Está correctamente enlazado?");
}
export default MilioSdk;
instanciar y utilizar la clase MilioSdkClientel cual se comunica MilioSdkModule.swift.
import React, { useEffect, useState } from 'react';
import { Alert, NativeEventEmitter } from 'react-native';
import MilioSdk from './MilioSdk';
const MilioSdkEmitter = new NativeEventEmitter(MilioSdk);
const App = () => {
const [authToken, setAuthToken] = useState(null);
const handleLogin = async () => {
try {
const token = await loginUser({
clientId: "",
clientSecretKey: ""
});
setAuthToken(token);
await setThemeColors();
} catch (error) {
Alert.alert('Error', 'No se pudo iniciar sesión');
}
};
const setThemeColors = async () => {
try {
const colors = {
btnColorEnabled: "#3498db",
btnColorDisabled: "#95a5a6",
btnTextColorEnabled: "#ffffff",
btnTextColorDisabled: "#7f8c8d",
layoutBackgroundColor: "#ecf0f1",
textViewColor: "#2c3e50"
};
await MilioSdk.setThemeColors(colors);
} catch (error) {
Alert.alert('Error', 'No se pudieron aplicar los colores');
}
};
useEffect(() => {
handleLogin();
const subscription = MilioSdkEmitter.addListener('onTransactionError', (event) => {
Alert.alert('Error en la transacción', `${event.errorMessage} (Código: ${event.errorCode})`);
});
return () => {
subscription.remove();
};
}, []);
const initializeSdkAndExecute = async (execute) => {
if (!authToken) {
Alert.alert('Error', 'Token no disponible');
return;
}
try {
await MilioSdk.initializeSdk(authToken);
await execute();
} catch (error) {
Alert.alert('Error', 'Hubo un problema con el SDK');
}
};
const handleTransferenciaQr = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openMilioSdkQr(JSON.stringify({
cardName: "Buffy Lanz",
cardNumber: "4364749491074401",
cardExpiry: "220801",
cardCVV: "652"
}));
});
};
const handleTransferenciaManual = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openMilioPayManualCard(JSON.stringify({
amount: 1500.00,
type: "OUT",
thirdBankUUID: ""
}));
});
};
const handleTransferenciaInmediata = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openMilioPayAddFoundManualCard(JSON.stringify({
amount: 1500.00,
type: "OUT",
thirdBankUUID: ""
}));
});
};
const handleRecargarBilletera = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openRecargaBilletera(JSON.stringify({
amount: 1500.00,
type: "IN",
thirdBankUUID: ""
}));
});
};
};
export default App;
Personalización del SDK con Colores
El SDK de Milio permite personalizar la apariencia de los elementos de la interfaz de usuario mediante la función setThemeColors. Esto permite adaptar el diseño del SDK a la identidad visual de cada aplicación.
🎨 ¿Qué puedes cambiar?
Al llamar a setThemeColors, puedes definir los siguientes colores:
Propiedad | Descripción |
---|---|
btnColorEnabled | Color del botón cuando está habilitado. |
btnColorDisabled | Color del botón cuando está deshabilitado. |
btnTextColorEnabled | Color del texto cuando el botón está habilitado. |
btnTextColorDisabled | Color del texto cuando el botón está deshabilitado. |
layoutBackgroundColor | Color de fondo del SDK. |
textViewColor | Color del texto en la interfaz del SDK. |
📌 Ejemplo de personalización
En el código, la función setThemeColors se encarga de aplicar estos cambios:
const setThemeColors = async () => {
try {
const colors = {
btnColorEnabled: "#3498db", // Azul brillante
btnColorDisabled: "#95a5a6", // Gris claro
btnTextColorEnabled: "#ffffff", // Blanco
btnTextColorDisabled: "#7f8c8d", // Gris oscuro
layoutBackgroundColor: "#ecf0f1", // Fondo gris claro
textViewColor: "#2c3e50" // Azul oscuro
};
await MilioSdk.setThemeColors(colors);
} catch (error) {
Alert.alert('Error', 'No se pudieron aplicar los colores');
}
};
📌 Métodos Disponibles
🔐 Proceso de Encriptación y Seguridad en el SDK
El sistema de transferencias y recargas del SDK utiliza un mecanismo de encriptación de datos basado en criptografía de clave pública para garantizar la seguridad de la información en cada transacción.
🔑 1. ¿Por qué se usa una llave pública?
La llave pública, generada por un endpoint especializado, es la herramienta que permite cifrar la información antes de enviarla a través del SDK. Al encriptar los datos con esta llave, aseguramos que solo el servidor que tiene la llave privada correspondiente pueda descifrarlos, evitando accesos no autorizados o modificaciones en la información.
📲 2. Procesos que requieren encriptación con la llave pública
Cada una de las transacciones en el SDK debe pasar por este proceso de encriptación antes de ser enviada al servidor:
1️⃣ Transferencia con QR
Se escanea un código QR con los datos de la transacción.
La información se encripta con la llave pública antes de enviarla.
El servidor recibe los datos cifrados y los desencripta con su llave privada.
2️⃣ Transferencia Manual
El usuario ingresa manualmente los datos de la transacción (monto, cuenta destino, etc.).
Antes de enviarlos al SDK, los datos se cifran con la llave pública.
El servidor procesa la información tras desencriptarla.
3️⃣ Transferencia Inmediata
Similar a la transferencia manual, pero con una ejecución más rápida.
La información viaja encriptada con la llave pública para evitar manipulación en el proceso.
4️⃣ Recarga de Billetera
Para recargar fondos en la billetera digital, los datos (monto, origen, destino) se protegen con encriptación.
Solo el servidor, con su llave privada, podrá acceder a la información real y procesar la recarga.
🚀 3. Beneficios de este proceso de seguridad
✅ Protección de datos sensibles en cada transacción.
✅ Evita que terceros accedan o modifiquen la información.
✅ Cumple con estándares de seguridad y auditoría en encriptación.
Cada vez que se realiza una transferencia o recarga en el SDK, los datos deben encriptarse con la llave pública antes de enviarse. Solo el servidor con la llave privada correcta podrá descifrarlos y procesar la transacción, garantizando un entorno seguro y confiable para el usuario. 🔒
🔹 Inicializar el SDK
Importante: La inicialización del SDK solo debe hacerse una vez antes de utilizar cualquier flujo de transacción.
const initializeSdkAndExecute = async (execute) => {
if (!authToken) {
Alert.alert('Error', 'Token no disponible');
return;
}
try {
await MilioSdk.initializeSdk(authToken);
await execute();
} catch (error) {
Alert.alert('Error', 'Hubo un problema con el SDK');
}
};
🔄 Flujos de Transferencia Disponibles
Una vez que el SDK está inicializado, puedes escoger cualquiera de los siguientes flujos para realizar transacciones:
Importante: Recuerda utilizar los metodos criptográficos correspondientes, puedes encontrarlos en Gestión de llaves para encriptación de datos
Recuerda que para activar el SDK, es necesario siempre encriptar los datos del lado del servidor. Este proceso implica encriptar de manera segura diversos datos sensibles, como la información de la tarjeta, los datos del titular o tercero, y tarjetas tokenizadas, entre otros. La encriptación es fundamental para garantizar la protección de la información durante su transmisión y evitar accesos no autorizados. A continuación, se muestran algunos ejemplos de los datos que deberías encriptar en el servidor:
- Información de la tarjeta (número, fecha de vencimiento, código de seguridad).
- Datos personales del titular o tercero.
- Tarjetas tokenizadas u otros identificadores sensibles.
Este procedimiento asegura que, aun en caso de una brecha de seguridad, la información permanezca protegida y cumpla con los estándares de seguridad exigidos.
🔹 1. Transferencia con QR
Antes de levantar el SDK, es necesario generar el QR correspondiente. Luego, el SDK podrá procesar la transferencia utilizando ese QR.
Este flujo permite realizar una transferencia escaneando un código QR y verificando los datos del cliente antes de completar la transacción.
Este flujo permite realizar una transferencia escaneando un código QR y verificando los datos del cliente antes de completar la transacción.
const handleTransferenciaQr = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openMilioSdkQr(JSON.stringify({
cardName: "Buffy Lanz",
cardNumber: "4364749491074401",
cardExpiry: "220801",
cardCVV: "652"
}));
});
};
🔹 2. Transferencia Manual
Permite realizar una transferencia ingresando manualmente los datos del destinatario, incluyendo el monto y la cuenta destino.
const handleTransferenciaManual = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openMilioPayManualCard(JSON.stringify({
amount: 1500.00,
type: "OUT",
thirdBankUUID: ""
}));
});
};
🔹 3. Transferencia Inmediata
Realiza una transferencia en tiempo real a otra cuenta, sin necesidad de validaciones adicionales.
const handleTransferenciaInmediata = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openMilioPayAddFoundManualCard(JSON.stringify({
amount: 1500.00,
type: "OUT",
thirdBankUUID: ""
}));
});
};
🔹 4. Recarga de Billetera
Permite añadir saldo a la billetera del usuario, facilitando la disponibilidad de fondos para futuras transacciones.
const handleRecargarBilletera = async () => {
await initializeSdkAndExecute(async () => {
await MilioSdk.openRecargaBilletera(JSON.stringify({
amount: 1500.00,
type: "IN",
thirdBankUUID: ""
}));
});
};
Resumen (iOS)
1️⃣ Inicializa el SDK una sola vez con initializeSdk().
2️⃣ Luego puedes ejecutar cualquier flujo de transacción, como QR, manual, inmediata o recarga de billetera.
📌 Beneficios en iOS
✅ Integración directa con el SDK nativo de iOS.
✅ Uso de JSON para estructurar y enviar datos de transacción fácilmente.
✅ Soporte para pagos manuales y tokenizados dentro de la app.
🚀 ¡Con esta implementación, React Native puede comunicarse con el SDK de Milio en iOS para ejecutar transacciones de forma rápida y eficiente! 🎉