> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/3d-secure/es/implementar-3ds/implementar-el-flujo-oob-autenticacion-thales/gestionar-el-desafio-oob.md).

# Gestionar el desafío OOB

## Resumen

El servicio 3DS activa el flujo de desafío OOB cuando una transacción es desafiada y el usuario final tiene un autenticador registrado en la aplicación del emisor.

## Experiencia del usuario

<figure><img src="/files/f2b6d92d0227c5e62dfafa0c133629853265cc14" alt=""><figcaption></figcaption></figure>

## Flujo

<figure><img src="/files/d3e2b205c71ac7c8e15bddbd0ed104db02fe6f14" alt=""><figcaption><p>Resumen del flujo de desafío OOB - paso 1.</p></figcaption></figure>

<figure><img src="/files/e803c29b3c66e5cd08619498acacbca7ab6bde59" alt=""><figcaption><p>Resumen del flujo de desafío OOB - paso 2.</p></figcaption></figure>

## Diagrama de secuencia – Flujo de backend

Los diagramas a continuación muestran la secuencia de extremo a extremo para un flujo de desafío OOB.

### Requisitos previos

* Los productos de tarjeta están configurados en el backend de D1 y en el servidor de directorio de la red de pagos.
* El usuario final y la tarjeta están registrados en el backend de D1.
* Se registró un autenticador en el dispositivo.
* El SDK de D1 está inicializado.
* La aplicación del emisor tiene configuradas las notificaciones push.

***

### 1 – AReq/ARes y primer CReq/CRes

<figure><img src="/files/a75ed2bdc2f149465a44c7481e5ab93e23e5b86a" alt=""><figcaption><p>AReq/ARes y CReq/CRes inicial.</p></figcaption></figure>

### 2 – CReq/CRes y desafío OOB del emisor

<figure><img src="/files/b0036c5a55979009a9590eee0eb7de27d92991e0" alt=""><figcaption><p>CReq/CRes y desafío OOB del emisor.</p></figcaption></figure>

Para obtener detalles de implementación del SDK, consulte [flujo del SDK](/3d-secure/es/implementar-3ds/implementar-el-flujo-oob-autenticacion-thales/gestionar-el-desafio-oob.md#sequence-diagram-sdk-flow).

### 3 – CReq/CRes final y notificación

<figure><img src="/files/d2ad6852d6b16f28cee8ac4d799a27688732f76e" alt=""><figcaption><p>CReq/CRes final y notificación.</p></figcaption></figure>

***

## Diagrama de secuencia – Flujo del SDK

La secuencia a continuación muestra cómo la aplicación del emisor utiliza el SDK de D1 para completar el desafío OOB.

### 1 - La aplicación del emisor obtiene el desafío FIDO a través del SDK de D1

<figure><img src="/files/cf6f773d792548dc69d1f16b0df434744b0c335a" alt=""><figcaption><p>La aplicación del emisor obtiene el desafío FIDO a través del SDK de D1.</p></figcaption></figure>

### 2 - El SDK de D1 autentica al usuario final

<figure><img src="/files/fe4e1da7a5d8f35972585e97e11654e97836a04a" alt=""><figcaption><p>El SDK de D1 autentica al usuario final.</p></figcaption></figure>

Para las notificaciones push, consulte [configuración de notificaciones push](/3d-secure/es/integrar-el-sdk-de-d1/comenzar/4.-configuracion-de-notificaciones-push.md).

### Datos de confirmación de la transacción

La siguiente tabla describe los datos de la transacción que el SDK de D1 proporciona a la aplicación del emisor para fines de visualización.

| Nombre del parámetro   | Descripción                                                                                                                                                                                                                                                                            |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| acsTransId             | Un identificador de transacción universalmente único asignado por ACS para identificar una sola transacción.                                                                                                                                                                           |
| purchaseDate           | Fecha y hora de la compra, expresadas en formato de hora UTC.                                                                                                                                                                                                                          |
| purchaseExponent       | Unidades menores de la moneda según lo especificado en el exponente de moneda ISO 4217.                                                                                                                                                                                                |
| threeDSRequestorAppURL | La aplicación solicitante de 3DS declara su URL dentro del mensaje CReq para que la aplicación de autenticación pueda llamar a la aplicación solicitante de 3DS una vez completada la autenticación OOB. Úselo para redirigir al usuario final de vuelta a la aplicación del comercio. |
| purchaseCurrency       | La moneda en la que se expresa el importe de la compra en formato ISO 4217.                                                                                                                                                                                                            |
| merchantName           | El nombre del comercio asignado por el adquirente o la red de pagos.                                                                                                                                                                                                                   |
| purchaseAmount         | El importe en unidades menores de la moneda, sin decimales.                                                                                                                                                                                                                            |

## Integración del SDK de D1

{% hint style="info" %}

* Llamar a `login` no es necesario como requisito previo para este caso de uso.
* Si la aplicación del emisor no recibe una solicitud de verificación mediante notificación push, puede obtener la solicitud llamando al SDK de D1 `fetchAuthnRequest` API.
* El SDK utiliza autenticación biométrica fuerte. Si hay varios métodos biométricos disponibles (por ejemplo, Face ID y huella dactilar), el sistema operativo administra la interfaz de usuario.
  {% endhint %}

{% tabs %}
{% tab title="Android Java" %}

```java
static String lastProcessedTransactionId = "";
public void challengeOOBFlow(
        @NonNull D1Task d1Task,
        @NonNull FragmentActivity activity,
        @NonNull CountDownLatch transactionWaiter) throws D1Exception {
    // Implementar la interfaz conforme a AuthnCallback
    D1Authn d1Authn = d1Task.getD1Authn(activity, new AuthnCallback(){
        @Override
        public void onTransactionDataConfirmation(@NonNull Map<String, String> map, @NonNull AuthnUserConfirmationCallback authnUserConfirmationCallback) {
            //Pase el controlador de transacciones que comprueba el último ID de transacción procesado
            try {
                transactionHandler(map, transactionWaiter, authnUserConfirmationCallback);
            } catch (InterruptedException e) {
                //Procesar la excepción
            }
        }

        @NonNull
        @Override
        public String onBiometricPromptMessage() {
            // Proporcione un mensaje para la solicitud de autenticación. La aplicación puede personalizar el valor.
            return "Mensaje que se mostrará al usuario durante la solicitud biométrica";
        }
    });

    d1Authn.fetchAuthnRequest(new D1Task.Callback<Void>() {
        @Override
        public void onSuccess(Void unused) {
            // Continúe con los flujos posteriores después de la verificación del usuario final.
            transactionWaiter.countDown();
        }

        @Override
        public void onError(@NonNull D1Exception e) {
            // Consulte la sección Integración del SDK de D1 – Gestión de errores.
            transactionWaiter.countDown();
        }
    });
}

public synchronized void transactionHandler(
        @NonNull Map<String, String> map,
        @NonNull CountDownLatch transactionWaiter,
        @NonNull AuthnUserConfirmationCallback authnUserConfirmationCallback) throws InterruptedException {
    // El 'map' contiene los datos de la transacción que se van a autenticar.
    //Mapa de ejemplo :
    // {
    //    "acsTransId": "2f37539c-d82e-4929-88db-2f9e72c7fa62",
    //    "merchantName": "CoffeeHouse2 (Dæmø)",
    //    "purchaseAmount": "100",
    //    "purchaseCurrency": "840",
    //    "purchaseExponent": "2",
    //    "purchaseDate": "20231017055638"
    // }

    //Obtenga el ID de la transacción actual y compárelo con el último ID de transacción procesado. Si es el mismo, no es necesario procesar la transacción y continúe cancelando la transacción
    if(map.get("acsTransId") != lastProcessedTransactionId) {
        lastProcessedTransactionId = map.get("acsTransId");
        JSONObject json = new JSONObject();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            try {
                json.put(entry.getKey(), entry.getValue());
            } catch (JSONException ignored) {}
        }

        // Aplicación para mostrar el contenido, solicitar al usuario final que 'proceda' o 'deniegue' la solicitud de autenticación.

        // Para proceder: cuando el usuario seleccione proceder
        // una vez que proceda, se solicitará autenticación en la interfaz según el AuthnType seleccionado registrado.
        authnUserConfirmationCallback.proceed();

        // Para cancelar: cuando el usuario seleccione cancelar
        authnUserConfirmationCallback.cancel();
    } else {
        if(transactionWaiter.await(60, TimeUnit.SECONDS)) {
            //No es necesario procesar la transacción y continúe cancelando la transacción
            authnUserConfirmationCallback.cancel();
        }
    }
}
```

{% endtab %}

{% tab title="Android Kotlin" %}

```kotlin
companion object {
    var lastProcessedTransactionId = ""
}
fun challengeOOBFlow(d1Task: D1Task, activity: FragmentActivity, transactionWaiter: CountDownLatch) {
    // Implementar la interfaz conforme a AuthnCallback
    val d1Authn: D1Authn = d1Task.getD1Authn(activity, object : AuthnCallback {
        override fun onTransactionDataConfirmation(
            map: Map<String, String>,
            authnUserConfirmationCallback: AuthnUserConfirmationCallback
        ) {
            // Pase el controlador de transacciones que comprueba el último ID de transacción procesado
            try {
                transactionHandler(map, transactionWaiter, authnUserConfirmationCallback)
            } catch (e: InterruptedException) {
                // Procese la excepción
            }
        }

        override fun onBiometricPromptMessage(): String {
            // Proporcione un mensaje para la solicitud de autenticación. La aplicación puede personalizar el valor.
            return "Mensaje que se mostrará al usuario durante la solicitud biométrica"
        }
    })

    d1Authn.fetchAuthnRequest(object : D1Task.Callback<Void?> {
        override fun onSuccess(unused: Void?) {
            // Continúe con los flujos posteriores después de la verificación del usuario final.
            transactionWaiter.countDown()
        }

        override fun onError(e: D1Exception) {
            // Consulte la sección Integración del SDK de D1 – Gestión de errores.
            transactionWaiter.countDown()
        }
    })
}

@Synchronized
@Throws(InterruptedException::class)
fun transactionHandler(
    map: Map<String, String>,
    transactionWaiter: CountDownLatch,
    authnUserConfirmationCallback: AuthnUserConfirmationCallback
) {
    // El 'map' contiene los datos de la transacción que se van a autenticar.
    // Mapa de ejemplo:
    // {
    //    "acsTransId": "2f37539c-d82e-4929-88db-2f9e72c7fa62",
    //    "merchantName": "CoffeeHouse2 (Dæmø)",
    //    "purchaseAmount": "100",
    //    "purchaseCurrency": "840",
    //    "purchaseExponent": "2",
    //    "purchaseDate": "20231017055638"
    // }

    // Obtenga el ID de la transacción actual y compárelo con el último ID de transacción procesado. Si es el mismo, no es necesario procesar la transacción y continúe cancelando la transacción
    val currentTransactionId = map["acsTransId"]
    if (currentTransactionId != lastProcessedTransactionId) {
        // El ID de la última transacción procesada se usa como variable para mantener su valor, ya que no puede modificarse tal cual en Kotlin
        lastProcessedTransactionId = currentTransactionId ?: ""

        val json = JSONObject()
        for ((key, value) in map) {
            try {
                json.put(key, value)
            } catch (ignored: JSONException) {
                // Ignorar las excepciones durante la inserción en JSON
            }
        }

        // Aplicación para mostrar el contenido, solicitar al usuario final que 'proceda' o 'deniegue' la solicitud de autenticación.

        // Para proceder: cuando el usuario seleccione proceder
        authnUserConfirmationCallback.proceed()

        // Para cancelar: cuando el usuario seleccione cancelar (esta parte debe manejarse en la UI según la interacción del usuario)
        authnUserConfirmationCallback.cancel()
    } else {
        if (transactionWaiter.await(60, TimeUnit.SECONDS)) {
            // No es necesario procesar la transacción y continúe cancelando la transacción
            authnUserConfirmationCallback.cancel()
        }
    }
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
public class AuthnConformer {
    weak var viewController: UIViewController?
    private var lastTransaction:[String:String] = [:]
}

extension AuthnConformer: AuthnDelegate {
    public func authn(_ authn: D1Authn, shouldConfirmTransactionData transactionData: [String : String], proceedHandler: (@escaping() -> Void), cancelHandler: (@escaping() -> Void)) {
        // El 'map' contiene los datos de la transacción que se van a autenticar. 

        //Mapa de ejemplo : 
        // {
        //    "acsTransId": "2f37539c-d82e-4929-88db-2f9e72c7fa62",
        //    "merchantName": "CoffeeHouse2 (Dæmø)",
        //    "purchaseAmount": "100",
        //    "purchaseCurrency": "840",
        //    "purchaseExponent": "2",
        //    "purchaseDate": "20231017055638"
        // }


        if (lastTransaction != transaction) {
            var message = ""
            if let json = try? JSONSerialization.data(withJSONObject: transactionData, options: .prettyPrinted),
               let jsonString = String(data: json, encoding: .utf8) {
                message = jsonString
            }

            // Aplicación para mostrar el contenido, solicitar al usuario final que 'proceda' o 'deniegue' la solicitud de autenticación.
          
            // Para proceder: cuando el usuario seleccione proceder
            // una vez que proceda, se solicitará autenticación en la interfaz según el AuthnType seleccionado registrado.

            let alertController = UIAlertController(title: "Detalles de la transacción", message: message, preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "Proceder", style: .default, handler: { action in
                proceedHandler()
            }))
        alertController.addAction(UIAlertAction(title: "Cancelar", style: .cancel, handler: {action in
                cancelHandler()
            }))
            viewController?.present(alertController, animated: true)
            lastTransaction = transaction
        }
        else {
            cancelHandler()
        }
    }

    public func authnTouchIDOperationPrompt(_ authn: D1Authn) -> String {
        //No importa. El desenrolamiento se realiza sin autenticación 
    }
}
let authnConformer = AuthnConformer()
authnConformer.viewController = self?.window?.rootViewController
let d1Authn = d1Task.d1Authn(authnConformer)

d1Authn.fetchAuthnRequest({ error in
    if let error = error {
        // Consulte la sección Integración del SDK de D1 – Gestión de errores.
    } else {
        // Continúe con los flujos posteriores después de la verificación del usuario final.
    }
})
```

{% endtab %}
{% endtabs %}

El siguiente ejemplo muestra cómo la aplicación del emisor usa `threeDSRequestorAppURL` de los datos de confirmación de la transacción para devolver al usuario final a la aplicación del comercio.

{% tabs fullWidth="false" %}
{% tab title="Android Java" %}

```java
public interface ITransactionConfirmation {
    void transactionConfirmationPayload(@NonNull String threeDSRequestorAppURL);
}

public D1Authn configureAuthn(@NonNull D1Task d1Task, @NonNull FragmentActivity activity, @NonNull ITransactionConfirmation transactionConfirmation) throws D1Exception {
    D1Authn d1Authn = d1Task.getD1Authn(activity, new AuthnCallback() {
        @Override
        public void onTransactionDataConfirmation(@NonNull Map<String, String> map, @NonNull AuthnUserConfirmationCallback authnUserConfirmationCallback) {
            String appUrl = map.get("threeDSRequestorAppURL");
            transactionConfirmation.transactionConfirmationPayload((appUrl == null)? "": appUrl);

            //Mostrar la interfaz al usuario para continuar

            //Si procede, llame a:
            authnUserConfirmationCallback.proceed();

        }

        @NonNull
        @Override
        public String onBiometricPromptMessage() {
            return "biometricPromtMessage";
        }
    });
    return d1Authn;
}

public void fetchAllRequest(@NonNull D1Task d1Task, @NonNull FragmentActivity activity) throws D1Exception {
    final String[] threeDSRequestorAppURL = new String[1];
    D1Authn d1Authn = configureAuthn(d1Task, activity, new ITransactionConfirmation() {
        @Override
        public void transactionConfirmationPayload(@NonNull String url3DS) {
            threeDSRequestorAppURL[0] = url3DS;
        }
    });

    d1Authn.fetchAuthnRequest(new D1Task.Callback<Void>() {
        @Override
        public void onSuccess(Void unused) {
            launchMerchantApp(threeDSRequestorAppURL[0], activity);
        }

        @Override
        public void onError(@NonNull D1Exception e) {
            // Consulte la sección Integración del SDK de D1 – Gestión de errores.
            launchMerchantApp(threeDSRequestorAppURL[0], activity);
        }
    });
}

public void launchMerchantApp(@NonNull String threeDSRequestorAppURL, @NonNull FragmentActivity activity) {
    if (!threeDSRequestorAppURL.isEmpty()) {
        Uri uri = Uri.parse(threeDSRequestorAppURL);

        // Cree un Intent para abrir la URI en un navegador o una aplicación que pueda manejar la URI
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        activity.startActivity(intent);
    } else {
        Toast.makeText(activity, "No hay solicitudes de transacción disponibles", Toast.LENGTH_SHORT).show();
    }
}
```

{% endtab %}

{% tab title="Android Kotlin" %}

```kotlin
interface ITransactionConfirmation {
    fun transactionConfirmationPayload(threeDSRequestorAppURL: String)
}

@Throws(D1Exception::class)
fun configureAuthn(d1Task: D1Task, activity: FragmentActivity, transactionConfirmation: ITransactionConfirmation): D1Authn {
    return d1Task.getD1Authn(activity, object : AuthnCallback {
        override fun onTransactionDataConfirmation(map: Map<String, String>, authnUserConfirmationCallback: AuthnUserConfirmationCallback) {
            transactionConfirmation.transactionConfirmationPayload(map["threeDSRequestorAppURL"] ?: "")

            // Mostrar la interfaz al usuario para continuar
            // Si procede, llame a:
            authnUserConfirmationCallback.proceed()
        }

        override fun onBiometricPromptMessage(): String {
            return "biometricPromptMessage"
        }
    })
}

@Throws(D1Exception::class)
fun fetchAllRequest(d1Task: D1Task, activity: FragmentActivity) {
    var threeDSRequestorAppURL : String = ""
    val d1Authn = configureAuthn(d1Task, activity, object : ITransactionConfirmation {
        override fun transactionConfirmationPayload(url3DS: String) {
            threeDSRequestorAppURL = url3DS
        }
    })

    d1Authn.fetchAuthnRequest(object : D1Task.Callback<Void?> {
        override fun onSuccess(unused: Void?) {
            launchMerchantApp(threeDSRequestorAppURL, activity)
        }

        override fun onError(e: D1Exception) {
            // Consulte la sección Integración del SDK de D1 – Gestión de errores.
        }
    })
}

fun launchMerchantApp(threeDSRequestorAppURL: String, activity: FragmentActivity) {
    if (threeDSRequestorAppURL.isNotEmpty()) {
        val uri = Uri.parse(threeDSRequestorAppURL)

        // Cree un Intent para abrir la URI en un navegador o una aplicación que pueda manejar la URI
        val intent = Intent(Intent.ACTION_VIEW, uri)
        activity.startActivity(intent)
    } else {
        Toast.makeText(activity, "No hay solicitudes de transacción disponibles", Toast.LENGTH_SHORT).show()
    }
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
//'threeDSRequestorAppURL' se recibe desde el método de devolución de llamada authnDelegate.authn de AuthnDelegate.
// Se recibirá como transactionData: [String : String].
public class MAuthnDelegate: AuthnDelegate {
    var threeDSRequestorAppURL: String?
    public func authn(_ authn: D1Authn, shouldConfirmTransactionData transactionData: [String : String], proceedHandler: (@escaping() -> Void), cancelHandler: (@escaping() -> Void)) {
        // Extraer threeDSRequestorAppURL de transactionData
        if let url3DS = transactionData["threeDSRequestorAppURL"] {
            threeDSRequestorAppURL = url3DS
        }
        //Mostrar la interfaz al usuario para continuar

        //Si procede, llame a:
        proceedHandler()

    }
}

private func launchMerchantApp(_ threeDSRequestorAppURL: String) {
    if let appURL = URL(string: threeDSRequestorAppURL) {
        UIApplication.shared.open(appURL, options: [:], completionHandler: nil)
    }
}

//Registrar AuthnDelegate en d1Task durante la inicialización
d1Task.d1Authn(mAuthnDelegate)

//Llame a la API fetchAuthnRequest para recibir la devolución de llamada del delegado de autenticación.
d1Authn.fetchAuthnRequest { error in
  if error == nil {
      //iniciar la aplicación del comercio con la URL obtenida.
      self.launchMerchantApp(mAuthnDelegate.threeDSRequestorAppURL)
  }
}
```

{% endtab %}
{% endtabs %}

## Integración del backend del emisor

Se notifica al backend del emisor al final del flujo de procesamiento a través de la [Notificar operación de tarjeta 3DS](/3d-secure/es/integrar-la-api-de-d1/referencia-de-la-api-de-d1/api-de-salida-desde-d1/api-de-3ds.md#post-notifications-d1-v1-issuers-issuerid-cards-cardid-3ds-notifications) API.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.payments.thalescloud.io/3d-secure/es/implementar-3ds/implementar-el-flujo-oob-autenticacion-thales/gestionar-el-desafio-oob.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
