API Reference

Instalación Android

🚀 Requisitos Previos

Antes de comenzar, asegúrate de cumplir con los siguientes requisitos:

  • React Native >=0.60 (se recomienda usar versiones actuales).
  • Android SDK y herramientas de desarrollo configuradas.
  • Gradle y Kotlin en versiones compatibles (1.6.21+).
  • Token de autenticación proporcionado por Milio.

Integración del SDK de Android en React Native usando JitPack

Fase 1: Integrar el SDK en React Native

Agregar JitPack en android/build.gradle

Asegúrate de que en android/build.gradle la configuración sea compatible:

compileSdkVersion y targetSdkVersion deben ser lo más altos posible para compatibilidad con Google Play.
minSdkVersion depende del público objetivo (reducirlo permite más compatibilidad, pero aumenta complejidad).
Si hay errores de compilación, verifica que el SDK correspondiente esté instalado en Android Studio.

ext {
    buildToolsVersion = "34.0.0"
    minSdkVersion = 24
    compileSdkVersion = 33
    targetSdkVersion = 33
}

Abre el archivo android/build.gradle y agrega JitPack en la sección repositories:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

google() es obligatorio para AndroidX y Google Services.
mavenCentral() es la opción principal para bibliotecas de terceros.
jitpack.io permite usar dependencias directamente desde GitHub.

🔹 Si tienes problemas con dependencias, verifica que el repositorio correcto esté incluido. 🚀🔥


✅ 2️⃣ Agregar la Dependencia del SDK en android/app/build.gradle

Abre android/app/build.gradle y en la sección dependencies agrega tu SDK desde JitPack:

dependencies {
    implementation 'org.bitbucket.miliopay:sdk-mobile-android:v0.1.1'
}

📌 Documentación del SDK Milio para React Native

📍 Descripción

Este módulo permite a las aplicaciones React Native interactuar con el SDK de Milio para realizar diversas operaciones financieras, como transferencias con QR , manuales, inmediatas y recargas de billetera.

El módulo está basado en una implementación nativa en Android y se expone a JavaScript a través del puente de React Native.


🔹 1. Vincular el módulo nativo en MainApplication.java

Agregar android/settings.gradle

Para utilizar el SDK de Milio en un proyecto React Native, es necesario configurarlo en settings.gradle y en el sistema de dependencias de Android.

include ':sdkmilio'
project(':sdkmilio').projectDir = new File(rootProject.projectDir, '../node_modules/milio-sdk/android')

 
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        google()
        mavenCentral()
        maven {
            url "https://jitpack.io"
            credentials {
                username authToken
            }
        }
    }
}

Gradle usa authToken como credencial de acceso. Sin este token, el proyecto no podrá descargar el SDK ni resolver sus dependencias. authToken debe ser proporcionado por Milio, asegurando que solo clientes autorizados puedan integrar el SDK.

Abre android/app/src/main/java/com/tuempresa/MilioSdkPackage.kt y agrega:

import com.tuempresa.MilioSdkPackage; // Importa el paquete

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class MilioSdkPackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(MilioSdkModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

Abre android/app/src/main/java/com/tuempresa/MainApplication.java y agrega:


@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
        new MilioSdkPackage() // Agrega el paquete de Milio SDK
    );
}

MainActivity es la actividad principal que conecta React Native con el código nativo de Android, la inicialización del SDK en este punto garantiza que:
✅ El SDK esté listo antes de que cualquier componente de React Native lo use.
✅ Se eviten errores de falta de inicialización cuando se intente invocar el SDK.
✅ Se mantenga una única instancia del SDK durante la ejecución de la app.

Abre android/app/src/main/java/com/tuempresa/MainActivity.java y agrega TransactionConfigSdk.initialize(this)

class MainActivity : ReactActivity() { 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        TransactionConfigSdk.initialize(this) // inicializacion del sdk
    }
}

📌 Implementación en Android (Kotlin)


📍 Código de MilioSdkModule.kt

package com.tempproject

import android.app.Activity
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.milio.sdkmilio.TransactionCallback
import com.milio.sdkmilio.TransactionConfigSdk
import android.os.Handler
import android.os.Looper



class MilioSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

    companion object {
        private const val TAG = "MilioSdkModule"
    }

    private var authToken: String? = null

    override fun getName(): String {
        return "MilioSdk"
    }

    @ReactMethod
    fun initializeSdk(token: String, promise: Promise) {
        authToken = token
        Log.d(TAG, "SDK Inicializado con token: $authToken")
        promise.resolve("SDK Inicializado correctamente")
 
        TransactionConfigSdk.setTransactionCallback(object : TransactionCallback {
            override fun onTransactionSuccess(amount: Double, message: String) {
                Log.d(TAG, "✅ Transacción exitosa: $amount - $message")
            }

            override fun onTransactionCompleted(amount: Double, isAddition: Boolean) {
                Log.d(TAG, "🔹 Transacción completada: amount=$amount, isAddition=$isAddition")
            }

            override fun onTransactionError(errorCode: Int, errorMessage: String) {
                Log.e(TAG, "❌ Error en la transacción: Código $errorCode - $errorMessage")
                sendErrorToReactNative(errorCode, errorMessage) // 🔥 Enviar error a React Native
            }
        })
    }

    @ReactMethod
fun setThemeColors(config: ReadableMap, promise: Promise) {
    val activity = currentActivity
    if (activity == null) {
        promise.reject("ERROR", "Actividad nula o incompatible")
        return
    }

    try {
        // Obtener los colores desde React Native
        val btnColorEnabled = config.getString("btnColorEnabled") ?: "#0e6655"
        val btnColorDisabled = config.getString("btnColorDisabled") ?: "#D3D3D3"
        val btnTextColorEnabled = config.getString("btnTextColorEnabled") ?: "#ffffff"
        val btnTextColorDisabled = config.getString("btnTextColorDisabled") ?: "#888888"
        val layoutBackgroundColor = config.getString("layoutBackgroundColor") ?: "#a2d9ce"
        val textViewColor = config.getString("textViewColor") ?: "#0b5345"

        // Aplicar los colores en el SDK
        TransactionConfigSdk.setThemeColors(
            activity,
            btnColorEnabled,
            btnColorDisabled,
            btnTextColorEnabled,
            btnTextColorDisabled,
            layoutBackgroundColor,
            textViewColor
        )

        Log.d("MilioSdkModule", "✅ Colores aplicados correctamente")
        promise.resolve("Colores aplicados correctamente")
    } catch (e: Exception) {
        Log.e("MilioSdkModule", "❌ Error al aplicar colores", e)
        promise.reject("ERROR", "Error al aplicar colores: ${e.localizedMessage}")
    }
}


    

    @ReactMethod
    fun openMilioSdkQr(jsonData: String, promise: Promise) {
        val activity = currentActivity as? AppCompatActivity
        if (activity == null) {
            promise.reject("ERROR", "Actividad nula o incompatible")
            Log.e(TAG, "Activity is null or not AppCompatActivity")
            return
        }

        if (authToken.isNullOrEmpty()) {
            promise.reject("ERROR", "Token no disponible")
            Log.e(TAG, "authToken is null or empty")
            return
        }

        try {
            TransactionConfigSdk.OpenMilioSdkQr(activity, jsonData)
            Log.d(TAG, "Escáner QR abierto correctamente")
            promise.resolve("Escáner QR abierto correctamente")
        } catch (e: Exception) {
            Log.e(TAG, "Error al abrir el escáner QR", e)
            promise.reject("ERROR", "Error al abrir el escáner QR: ${e.localizedMessage}")
        }
    }



    @ReactMethod
    fun openMilioPayManualCard(jsonData: String, promise: Promise) {
        val activity = currentActivity as? AppCompatActivity
        if (activity == null) {
            promise.reject("ERROR", "Actividad nula o incompatible")
            Log.e(TAG, "Activity is null or not AppCompatActivity")
            return
        }

        // if (authToken.isNullOrEmpty()) {
        //     promise.reject("ERROR", "Token no disponible")
        //     Log.e(TAG, "authToken is null or empty")
        //     return
        // }
        
        try {
            TransactionConfigSdk.OpenMilioPayManualCard(activity, authToken!!, jsonData)
            Log.d(TAG, "Transacción iniciada con éxito")
            promise.resolve("Transacción iniciada con éxito")
        } catch (e: Exception) {
            Log.e(TAG, "Error en la transacción", e)
            promise.reject("ERROR", "Error en la transacción: ${e.localizedMessage}")
        }
    }


    @ReactMethod
    fun openMilioPayAddFoundManualCard(jsonData: String, promise: Promise) {


        val jsonData1 = """
            {
                "amount": 1500.00,
                "type": "OUT",
                "thirdBankUUID": "dfb56715-ed55-4839-b5d0-18645da02dd0"
            }
        """.trimIndent()

        val activity = currentActivity as? AppCompatActivity
        if (activity == null) {
            promise.reject("ERROR", "Actividad nula o incompatible")
            Log.e("MilioSdkModule", "❌ Activity is null or not AppCompatActivity")
            return
        }

        if (authToken.isNullOrEmpty()) {
            promise.reject("ERROR", "Token no disponible")
            Log.e("MilioSdkModule", "❌ authToken is null or empty")
            return
        }
 
         Log.e("MilioSdkModule", "❌  tokenssssssssssss "+authToken)
         

        try {
            TransactionConfigSdk.OpenMilioPayAddFoundManualCard(activity, authToken!!, jsonData1)
           
            Handler(Looper.getMainLooper()).postDelayed({
                promise.resolve("Pago de fondos iniciado con éxito")
            }, 1000) // Ajusta el tiempo según la lógica de la transacción
        } catch (e: Exception) {
            Log.e("MilioSdkModule", "❌ Error al iniciar el pago de fondos", e)
            promise.reject("ERROR", "Error al iniciar el pago de fondos: ${e.localizedMessage}")
        }
    }


    @ReactMethod
    fun openRecargaBilletera(jsonData: String, promise: Promise) {
        val activity = currentActivity as? AppCompatActivity
        if (activity == null) {
            promise.reject("ERROR", "Actividad nula o incompatible")
            Log.e(TAG, "❌ Activity is null or not AppCompatActivity")
            return
        }

        if (authToken.isNullOrEmpty()) {
            promise.reject("ERROR", "Token no disponible")
            Log.e(TAG, "❌ authToken is null or empty")
            return
        }

        try {
            TransactionConfigSdk.OpenMilioPayRechargeWallet(activity, activity.supportFragmentManager,             authToken!!, jsonData)
            Log.d(TAG, "✅ Recarga de billetera iniciada correctamente")
            promise.resolve("Recarga de billetera iniciada correctamente")
        } catch (e: Exception) {
            Log.e(TAG, "❌ Error al iniciar recarga de billetera", e)
            promise.reject("ERROR", "Error al iniciar recarga de billetera: ${e.localizedMessage}")
        }
    }



    private fun sendErrorToReactNative(errorCode: Int, errorMessage: String) {
        val params = Arguments.createMap()
        params.putInt("errorCode", errorCode)
        params.putString("errorMessage", errorMessage)

        reactApplicationContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            .emit("onTransactionError", params) // 🔥 Envía evento "onTransactionError"
    }
}


instanciar y utilizar la clase MilioSdk el cual se comunica MilioSdkModule.

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:

PropiedadDescripción
btnColorEnabledColor del botón cuando está habilitado.
btnColorDisabledColor del botón cuando está deshabilitado.
btnTextColorEnabledColor del texto cuando el botón está habilitado.
btnTextColorDisabledColor del texto cuando el botón está deshabilitado.
layoutBackgroundColorColor de fondo del SDK.
textViewColorColor 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:
1️⃣ Inicializa el SDK una sola vez con initializeSdk().
2️⃣ Luego puedes elegir cualquier flujo de transacción: QR, manual, inmediata o recarga de billetera.

📌 Beneficios

Conexión directa con el SDK nativo de Android.
Uso de JSON para facilitar las transacciones.
Soporte para pagos manuales y tokenizados.

🚀 ¡Con esta implementación, React Native puede ejecutar transferencias y recargas con el SDK de Milio de manera eficiente! 🎉