# SDK para Android

> Instalar el SDK de detección de fraude en una app Android: dependencia Gradle, permisos, envío de transacciones en Kotlin y Java, ejemplo y registro de cambios.

URL: https://docs.ironchip.com/fraude/sdk/android/

El SDK de Android recoge las señales del dispositivo y de su entorno en cada operación sensible y las envía al motor antifraude de Ironchip. Úsalo si tu aplicación móvil es nativa de Android.

## Requisitos

| Plataforma | Versiones soportadas |
| ---------- | -------------------- |
| Android    | 5.0.0+               |

## Instalación

### Añadir la biblioteca al proyecto

Primero, en el fichero `settings.gradle`, referencia el repositorio remoto donde está la biblioteca:

```groovy title="settings.gradle"
dependencyResolutionManagement {
    ...

    repositories {
        maven { url 'https://jitpack.io' }
        ...
    }
}
```

A continuación, en el fichero `build.gradle`, añade la dependencia del SDK:

### Permisos

Añade los siguientes permisos al manifest:

```xml title="AndroidManifest.xml"
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />
```

`tools:ignore` requiere declarar el espacio de nombres `xmlns:tools="http://schemas.android.com/tools"` en la etiqueta `<manifest>`.

## Uso en una aplicación

Para aprovechar todo el potencial del SDK, concede el permiso `ACCESS_FINE_LOCATION`. Consulta [cómo solicitar permisos en Android](https://developer.android.com/training/permissions/requesting).

**Kotlin**

```kotlin
import com.ironchip.ironchiplbfraudandroidsdk.LBFraudSDK

// Sustituye APIKEY por la API key generada en la consola.
val fraud = LBFraudSDK(this, "APIKEY")
// Por defecto el SDK apunta al entorno de producción. Para apuntar a otro:
// val fraud = LBFraudSDK(this, "APIKEY", LBFraudSDK.Environment.TESTING)

val extraData = hashMapOf<String, Any>(
    "concept" to "Book august",
    "amount" to 30,
    "operation" to "booking",
)

// transactionId (obligatorio, único): identificador con el que después pedirás el informe
// userId (obligatorio): identificador del usuario
// extraData (opcional): información adicional para el análisis
fraud.sendTransaction("random_identifier_generated", "john.doe@gmail.com", extraData, {
    // Código a ejecutar cuando la transacción ha terminado.
}, { exception ->
    // Código a ejecutar si se produce un error durante la transacción.
})
```

**Java**

```java
import com.ironchip.ironchiplbfraudandroidsdk.LBFraudSDK;
...

// Sustituye APIKEY por la API key generada en la consola.
// El constructor lanza LBFraudInitializationException: gestiónala con try/catch.
LBFraudSDK fraud = new LBFraudSDK(this, "APIKEY");
// Por defecto el SDK apunta al entorno de producción. Para apuntar a otro:
// LBFraudSDK fraud = new LBFraudSDK(this, "APIKEY", LBFraudSDK.Environment.TESTING);

//public enum Environment {
//    PRODUCTION,
//    TESTING,
//    DEVELOPMENT
//}

Map<String, Object> extraData = new HashMap<>();
extraData.put("concept", "Book august");
extraData.put("amount", 30);
extraData.put("operation", "booking");

// transactionId (obligatorio, único): identificador con el que después pedirás el informe
// userId (obligatorio): identificador del usuario
// extraData (opcional): información adicional para el análisis
// sendTransaction admite dos callbacks: el primero se ejecuta al terminar la transacción
// y el segundo si se produce un error durante el proceso.
fraud.sendTransaction("random_identifier_generated", "john.doe@gmail.com", extraData, () -> {
    // Código a ejecutar cuando la transacción ha terminado.
}, exception -> {
    // Código a ejecutar si se produce un error durante la transacción.
    // exception.printStackTrace()
});
```

Además de una `Activity`, el constructor también acepta un `Context` (desde la versión 1.2.23), por lo que puedes usar el SDK desde un servicio. El constructor lanza `LBFraudInitializationException` (comprobada) si el contexto es nulo: en Java envuélvelo en `try/catch`; en Kotlin no es obligatorio.

## Gestión de errores

`sendTransaction` envía la transacción en segundo plano y recibe dos funciones de retorno: la primera (un `Runnable`) se ejecuta si el motor acepta la transacción y la segunda (un `ExceptionCallback`, con el método `onExceptionOccurred(Exception e)`) si se produce un error. Solo se ejecuta una de las dos, y en un hilo secundario: si desde ellas actualizas la interfaz, hazlo con `runOnUiThread`.

Si el error es de red o lo devuelve el servidor, la excepción es una `TransactionException`. Su método `getCode()` devuelve el código de error de la respuesta del servidor (`error_code`) o una de estas constantes:

| Código | Constante                | Significado                                              |
| ------ | ------------------------ | -------------------------------------------------------- |
| `"0"`  | `RESPONSE_PARSING_ERROR` | El servidor devolvió un error que no se pudo interpretar |
| `"1"`  | `NO_CONNECTION_ERROR`    | No se puede conectar con el servidor                     |
| `"2"`  | `TIMEOUT_ERROR`          | Se agotó el tiempo de espera                             |
| `"3"`  | `NETWORK_ERROR`          | Otro error de red                                        |

`getHttpCode()` devuelve el código de estado HTTP de la respuesta (`0` en los errores de red) y `getTraceabilityId()`, su identificador de trazabilidad (`null` si la respuesta no lo trae). Inclúyelo si abres un ticket con soporte. El formato de las respuestas de error está en [Respuestas de error](https://docs.ironchip.com/fraude/referencia/errores/).

## Configurar un proxy

Si tu app sale a Internet a través de un proxy, configúralo con `setProxy` antes de enviar transacciones. Recibe un `java.net.Proxy` y puede lanzar `IllegalArgumentException`.

**Kotlin**

```kotlin
import java.net.InetSocketAddress
import java.net.Proxy

fraud.setProxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("proxy.example.com", 8080)))
```

**Java**

```java
import java.net.InetSocketAddress;
import java.net.Proxy;
...

fraud.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)));
```

## Ejemplo

Ejemplo completo: tras un inicio de sesión correcto, se anonimiza el identificador del usuario con SHA-256 y se envía la transacción.

```java
import com.ironchip.ironchiplbfraudandroidsdk.LBFraudSDK;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

...

// Sustituye APIKEY por la API key generada en la consola.
// El constructor lanza LBFraudInitializationException: gestiónala con try/catch.
LBFraudSDK fraud = new LBFraudSDK(this, "APIKEY");

// Método asíncrono para realizar el inicio de sesión del usuario
private void login(String username, String password) {
    performLogin(username, password).thenAccept(loginSuccessful -> {
        if (loginSuccessful) {
            // Si el inicio de sesión es correcto, llama a sendTransaction con los parámetros necesarios
            String transactionID = generateRandomTransactionID(10); // Genera u obtén tu ID de transacción
            String userID = "john.doe@gmail.com"; // Identificador de usuario
            String anonymizedUserID = anonymizeUserID(userID); // ID de usuario anonimizado

            Map<String, Object> extraData = new HashMap<>(); // Información adicional para análisis
            extraData.put("amount", 30); // Usa Integer.valueOf(30) si es necesario
            extraData.put("operation", "login");

            sendTransactionAfterLogin(transactionID, anonymizedUserID, extraData);
        } else {
            // Gestiona el fallo del inicio de sesión
        }
    });
}

// Inicio de sesión asíncrono que devuelve un CompletableFuture<Boolean>
private CompletableFuture<Boolean> performLogin(String username, String password) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(2000);

            // Sustituye por tu lógica de inicio de sesión real:
            // autentica al usuario y devuelve true si es correcto
            return true;
        } catch (InterruptedException e) {
            return false; // En caso de un error, devuelve false
        }
    });
}

// Envía la transacción tras un inicio de sesión correcto
private void sendTransactionAfterLogin(String transactionID, String anonymizedUserID, Map<String, Object> extraData) {
    fraud.sendTransaction(transactionID, anonymizedUserID, extraData, () -> {
        // Código a ejecutar cuando la transacción ha terminado.
    }, exception -> {
        // Código a ejecutar si se produce un error durante la transacción.
    });
}

// Recorta, convierte a minúsculas y calcula el hash del userID
public static String anonymizeUserID(String userID) {
    String trimmedLowercaseUserID = userID.trim().toLowerCase();
    return hashString(trimmedLowercaseUserID);
}

// Calcula el hash con SHA-256
public static String hashString(String input) {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hashBytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));

        StringBuilder hexString = new StringBuilder();
        for (byte b : hashBytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Error while calculating hash", e);
    }
}

private String generateRandomTransactionID(int length) {
    // Implementa tu método para generar un ID de transacción único.
    final String chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890";
    Random rnd = new Random();
    return IntStream.range(0, length)
        .mapToObj(i -> String.valueOf(chars.charAt(rnd.nextInt(chars.length()))))
        .collect(Collectors.joining());
}
```

## Registro de cambios

### 1.2.26 (2026-09-08)

**Cambiado**

- Actualizada una biblioteca interna del SDK.

### 1.2.25 (2026-08-06)

**Cambiado**

- Actualizada una biblioteca interna del SDK.

### 1.2.23 (2026-06-25)

**Añadido**

- `LBFraudSDK` acepta también un `Context` además de una `Activity`, para poder usarlo desde componentes que no son una actividad, como los servicios.

**Cambiado**

- Actualizada una biblioteca interna del SDK.

**Corregido**

- `sendTransaction` devuelve una `TransactionException` en los errores de red, en lugar de una `IOException`. Códigos nuevos: `NO_CONNECTION_ERROR`, `TIMEOUT_ERROR` y `NETWORK_ERROR`.

### 1.2.20 (2026-03-10)

**Cambiado**

- Actualizada una biblioteca interna del SDK.

### 1.2.19 (2026-03-05)

**Cambiado**

- La generación del JSON de la transacción pasa a un hilo secundario.

### 1.2.18 (2026-03-05)

**Cambiado**

- Actualizada una biblioteca interna del SDK.

### 1.2.17 (2026-03-02)

**Cambiado**

- Actualizados los identificadores del modelo de dispositivo.

### 1.2.16 (2026-02-11)

**Añadido**

- Nuevos identificadores en el modelo de dispositivo.

### 1.2.15 (2026-02-11)

**Cambiado**

- Actualizada una biblioteca interna del SDK.

### 1.2.14

**Cambiado**

- Actualizada una biblioteca interna del SDK.

### 1.2.13 (2023-10-11)

**Añadido**

- Detección de comunicación.
- Tipo de red de la SIM (2G, 3G, 4G, 5G).

### 1.2.12 (2023-09-12)

**Añadido**

- Método de configuración del proxy.

**Cambiado**

- Ahora se utilizan conjuntos de cifrado (cipher suites).

### 1.2.11 (2023-07-05)

**Cambiado**

- Corregido el envío de los datos adicionales, que no se enviaban.

### 1.2.10 (2023-06-23)

- Esta versión es una actualización sin cambios funcionales. El número de versión se actualiza a 1.2.10 para mantener la secuencia de versiones.

### 1.2.9.2 (2023-06-23)

**Cambiado**

- Biblioteca Commons actualizada con correcciones adicionales.

### 1.2.9.1 (2023-06-22)

**Cambiado**

- Biblioteca Commons actualizada para aplicar algunas correcciones de errores.

### 1.2.9 (2023-05-18)

**Añadido**

- Se añade la versión del SDK al modelo de transacción.

**Cambiado**

- Gestión previa de errores para permitir crear el servicio de fraude aunque el proveedor de GPS no esté disponible.

### 1.2.8 (2023-05-12)

**Añadido**

- Gestión de errores adicional para evitar que la transacción falle cuando falla alguno de los pasos de construcción de la transacción.

### 1.2.7 (2023-03-15)

**Cambiado**

- Se modifica el modelo de transacción para incluir información sobre la tarjeta SIM.

### 1.2.6 (2023-02-02)

**Cambiado**

- Se modifica el modelo de transacción para incluir el estado de los permisos solicitados.
- Se modifica el modelo de transacción para incluir si los proveedores de GPS y Wi-Fi están habilitados.

### 1.2.5 (2023-01-13)

**Cambiado**

- Corregida la configuración de ProGuard que hacía que la aplicación del cliente se volviera a empaquetar en la carpeta interna de la biblioteca.

### 1.2.4 (2023-01-10)

**Cambiado**

- Los errores durante el callback de `sendTransaction` ahora se registran en el log en lugar de redirigirse al callback de excepción.

### 1.2.3 (2022-12-09)

**Cambiado**

- Corregido un problema con la identificación de la huella digital (fingerprint).

### 1.2.2 (2022-11-30)

**Cambiado**

- Corregido un error cuando el dispositivo no está conectado a una red Wi-Fi.
- Corregidos los saltos de línea en los identificadores cifrados.
- Mejora en la detección de emulación.

### 1.2.1 (2022-11-22)

**Cambiado**

- Refactorización de nombres de clase.
- Las etiquetas de los logs se cambian para que coincidan con la refactorización.
- El constructor de Fraud ya no admite un host de URL, solo permite seleccionar el entorno deseado.

### 1.2.0 (2022-10-12)

**Añadido**

- Versión del kernel en las propiedades del dispositivo.
- Registros de depuración.

**Cambiado**

- Corregido un error por el que el JSON enviaba cadenas en lugar de arrays.
- Mejora en la identificación del dispositivo, de la instalación y de la huella digital.
- El GPS y la señal ya no pueden bloquear el proceso.

### 1.1.3 (2022-09-30)

**Cambiado**

- La biblioteca pasa a publicarse en GitHub.

### 1.1.2 (2022-09-30)

**Añadido**

- Compatibilidad con Android 5 y posteriores.

### 1.1.0 (2022-09-22)

**Añadido**

- Fichero de licencia (`LICENSE`) en el proyecto.

**Cambiado**

- Refactorización del nombre del módulo.

### 1.0.2 (2022-09-10)

**Añadido**

- Excepciones personalizadas.

**Cambiado**

- Callbacks y excepciones excluidos de la ofuscación para facilitar el uso del cliente.
- Cambian los nombres y la estructura de los parámetros del modelo.

### 1.0.1 (2022-09-10)

**Cambiado**

- Callbacks y excepciones excluidos de la ofuscación para facilitar el uso del cliente.
- Cambian los nombres y la estructura de los parámetros del modelo.

### 1.0.0 (2022-09-10)

**Añadido**

- Ofuscación del código.

**Cambiado**

- Secciones de código migradas a una biblioteca común.
- Cambia el comportamiento del servicio de GPS.
