> 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/push-provisioning/es/implement-push-provisioning/implement-view-and-control/in-app-authentication-for-visa-ctf.md).

# Autenticación en la app para Visa CTF

{% hint style="danger" %}
Se requiere un servicio de tokenización para usar el SDK Thales D1 en el flujo de autenticación dentro de la app.
{% endhint %}

Visa Cloud Token Framework (CTF) es un marco de Visa diseñado para aumentar la confianza en las tarjetas digitales de comercio electrónico después de la tokenización.

CTF define dos flujos: vinculación de dispositivo y verificación del titular de la tarjeta.

Estos flujos permiten al comerciante aumentar la confianza en una tarjeta digital al activar un flujo de autenticación reforzada del titular de la tarjeta con el emisor.

Para más detalles sobre los flujos completos y Visa Cloud Token Framework (CTF), consulta la sección [marco de tokens en la nube de Visa](/tokenization/es/implement-tokenization/post-tokenization-requests/marco-de-tokens-en-la-nube-de-visa-ctf.md) en la documentación de Tokenización.

El flujo de autenticación dentro de la app para Visa CTF es muy similar al flujo descrito en [Autenticación en la app](/push-provisioning/es/implement-push-provisioning/implement-view-and-control/in-app-authentication.md).

Los pasos \[01] a \[10] son esencialmente los mismos.

La principal diferencia es el formato del payload, mostrado en la siguiente tabla:

<table><thead><tr><th>Vinculación de dispositivo - payload</th><th>Verificación del titular de la tarjeta - payload</th></tr></thead><tbody><tr><td><pre><code>{
  "panReferenceID": "V-3815023863409817870482",
  "tokenRequestorID": "42301999123",
  "tokenReferenceID": "DNITHE381502386342002358",
  "panLast4": "1234",
  "deviceID": "DEiOiJBMjU2R_0NNS1-ciLCJiI",
  "walletAccountID": "AiOiJBMjU-2_R0NNS1ciLCJiI6",
  "deviceIndex": "1",
  "reasonCode": "TOKEN_DEVICE_BINDING"
}
</code></pre></td><td><pre><code>{
  "panReferenceID": "V-3815023863409817870482",
  "tokenRequestorID": "42301999123",
  "tokenReferenceID": "DNITHE381502386342002358",
  "panLast4": "1234",
  "deviceID": "null",
  "walletAccountID": "AiOiJBMjU-2_R0NNS1ciLCJiI6",
  "deviceIndex": "null",
  "reasonCode": "CARDHOLDER_STEPUP"
}
</code></pre></td></tr></tbody></table>

En comparación con la autenticación estándar de tokenización, la aplicación del emisor debe inspeccionar:

* **reasonCode**: identifica el motivo de la autenticación CTF.
  * `TOKEN_DEVICE_BINDING`: flujo de vinculación de dispositivo.
  * `CARDHOLDER_STEPUP`: flujo de verificación del titular de la tarjeta.
* **deviceIndex**: se usa para el flujo de vinculación de dispositivo. Esta es la referencia del dispositivo de Visa. En el SDK D1, se mapea a `bindingReference`.

Cuando te refieras al diagrama de secuencia en [Autenticación en la app](/push-provisioning/es/implement-push-provisioning/implement-view-and-control/in-app-authentication.md), reemplaza los pasos \[11] y \[12] con una nueva llamada a la API.

En este caso, no hay activación de la tarjeta digital. En su lugar, el resultado de la autenticación debe propagarse a Visa VTS.

El SDK D1 expone las siguientes APIs para que la aplicación del emisor pueda informar el resultado:

1. Para `TOKEN_DEVICE_BINDING`, llama a `DigitalCardService.approveBinding` con `digitalCardID` y `deviceIndex`.
2. Para `CARDHOLDER_STEPUP`, llama a `DigitalCardService.approveCardholderVerification` con `digitalCardID`.

El SDK D1 también te permite:

1. Recuperar la lista de dispositivos vinculados como `deviceBindingList` cuando se llama a `getDigitalCardList()` . Para más detalles, consulta [Ver y controlar tarjetas digitales](/push-provisioning/es/implement-push-provisioning/implement-view-and-control/view-and-control-digital-cards.md).
2. Desvincular un dispositivo llamando a `DigitalCardService.unbindDevice`. Consulta [Desvinculación de dispositivo](/push-provisioning/es/implement-push-provisioning/implement-view-and-control/in-app-authentication-for-visa-ctf/unbind-a-device.md).

Los siguientes ejemplos muestran cómo analizar el payload y aprobar una solicitud de vinculación.

{% hint style="info" %}
Nota

Se aplica el siguiente mapeo de campos:

* `tokenReferenceID` = `digitalCardID`
* `deviceIndex` = `bindingReference`
  {% endhint %}

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

```kotlin
private const val VISA_STEPUP_RESULT_RESPONSE = "STEP_UP_RESPONSE"
private const val STEPUP_RESULT_APPROVED = "approved"
private const val STEPUP_RESULT_DECLINED = "declined"
private const val STEPUP_RESULT_FAILED = "failure"

val visaPayload = intent.getStringExtra(Intent.EXTRA_TEXT) ?: return

val payloadJson = try {
    val decodedPayload = String(Base64.decode(visaPayload, Base64.DEFAULT), Charsets.UTF_8)
    JSONObject(decodedPayload)
} catch (_: IllegalArgumentException) {
    // Payload base64 no válido
    return
} catch (_: JSONException) {
    // Payload JSON no válido
    return
}

val reasonCode = payloadJson.optString("reasonCode", "")
val digitalCardID = payloadJson.optString("tokenReferenceID", "") // tokenReferenceID -> digitalCardID
val bindingReference = payloadJson.optString("deviceIndex", "") // deviceIndex -> bindingReference

if ("TOKEN_DEVICE_BINDING" == reasonCode &&
    digitalCardID.isNotEmpty() &&
    bindingReference.isNotEmpty()) {

    d1Task.digitalCardService().approveBinding(
        digitalCardID,
        bindingReference,
        DigitalCardService.BindingApprovalReason.USER_DECISION,
        object : D1Task.Callback<Void> {
            override fun onSuccess(data: Void?) {
                sendResultToTokenRequestorApp(activity, null)
            }

            override fun onError(e: D1Exception) {
                sendResultToTokenRequestorApp(activity, e)
            }
        }
    )
} else {
    // Otros flujos (por ejemplo, activación)
}

private fun sendResultToTokenRequestorApp(activity: Activity, exception: D1Exception?) {
    val result = Intent()
    // valores posibles: "approved", "declined" o "failure"
    val stepupResult = if (exception == null) STEPUP_RESULT_APPROVED else STEPUP_RESULT_FAILED
    result.putExtra(VISA_STEPUP_RESULT_RESPONSE, stepupResult)
    // aplicable si el emisor está usando activación mediante código de autenticación
    activity.setResult(Activity.RESULT_OK, result)
    activity.finish()
}
```

```java
private static final String VISA_STEPUP_RESULT_RESPONSE = "STEP_UP_RESPONSE";
private static final String STEPUP_RESULT_APPROVED = "approved";
private static final String STEPUP_RESULT_DECLINED = "declined";
private static final String STEPUP_RESULT_FAILED = "failure";

String visaPayload = getIntent().getStringExtra(Intent.EXTRA_TEXT);
if (visaPayload == null) {
    return;
}

JSONObject payloadJson;
try {
    byte[] decodedPayloadBytes = Base64.decode(visaPayload, Base64.DEFAULT);
    String decodedPayload = new String(decodedPayloadBytes, StandardCharsets.UTF_8);
    payloadJson = new JSONObject(decodedPayload);
} catch (IllegalArgumentException | JSONException e) {
    // Payload base64/JSON no válido
    return;
}

String reasonCode = payloadJson.optString("reasonCode", "");
String digitalCardID = payloadJson.optString("tokenReferenceID", ""); // tokenReferenceID -> digitalCardID
String bindingReference = payloadJson.optString("deviceIndex", ""); // deviceIndex -> bindingReference

if ("TOKEN_DEVICE_BINDING".equals(reasonCode) &&
        !digitalCardID.isEmpty() &&
        !bindingReference.isEmpty()) {

    d1Task.digitalCardService().approveBinding(
            digitalCardID,
            bindingReference,
            DigitalCardService.BindingApprovalReason.USER_DECISION,
            new D1Task.Callback<Void>() {
                @Override
                public void onSuccess(Void data) {
                    sendResultToTokenRequestorApp(activity, null);
                }

                @Override
                public void onError(@NonNull D1Exception exception) {
                    sendResultToTokenRequestorApp(activity, exception);
                }
            });
} else {
    // Otros flujos (por ejemplo, activación)
}

private void sendResultToTokenRequestorApp(Activity activity, D1Exception exception) {
    Intent result = new Intent ();
    // valores posibles: "approved", "declined" o "failure"
    String stepupResult = (exception == null) ? STEPUP_RESULT_APPROVED : STEPUP_RESULT_FAILED;
    result.putExtra(VISA_STEPUP_RESULT_RESPONSE, stepupResult);
    // aplicable si el emisor está usando activación mediante código de autenticación
    activity.setResult(RESULT_OK, result);
    activity.finish();
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
// Para admitir la redirección de la app desde la app Wallet.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return false }

    let queryDict = components.queryItems?.reduce(into: [String: String]()) { dict, item in
        dict[item.name] = item.value
    } ?? [:]

    switch components.host {
    // Nombre de servicio de ejemplo que maneja Visa app-to-app
    case "visaCTF":
        guard let wpCallback = queryDict["wpcallback"]?.removingPercentEncoding,
              let encodedPayload = queryDict["a2apayload"],
              let payloadData = Data(base64Encoded: encodedPayload.replacingOccurrences(of: " ", with: "+")),
              let json = try? JSONSerialization.jsonObject(with: payloadData) as? [String: Any],
              let reasonCode = json["reasonCode"] as? String else {
            return false
        }

        switch reasonCode {
        case "TOKEN_DEVICE_BINDING":
            guard let digitalCardID = json["tokenReferenceID"] as? String,
                  let bindingReference = json["deviceIndex"] as? String else {
                return false
            }

            Task {
                do {
                    try await d1Task.digitalCardService().approveBinding(
                        digitalCardID,
                        bindingReference: bindingReference,
                        reason: nil
                    )
                    sendResultToApp(wpCallback: wpCallback, stepupResult: "Approved")
                } catch {
                    sendResultToApp(wpCallback: wpCallback, stepupResult: "Failure")
                }
            }

        default:
            return false
        }

        return true
    default:
        return false
    }
}

private func sendResultToApp(wpCallback: String, stepupResult: String) {
    guard var components = URLComponents(string: wpCallback) else { return }

    var items = components.queryItems ?? []
    items.append(URLQueryItem(name: "stepupresponse", value: stepupResult))
    components.queryItems = items

    if let callbackURL = components.url {
        UIApplication.shared.open(callbackURL)
    }
}
```

{% endtab %}
{% endtabs %}

Para tener acceso completo al SDK D1, consulta [referencia de API](/push-provisioning/es/integrate-the-d1-sdk/api-reference.md).


---

# 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:

```
GET https://docs.payments.thalescloud.io/push-provisioning/es/implement-push-provisioning/implement-view-and-control/in-app-authentication-for-visa-ctf.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
