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

# Visa CTFのアプリ内認証

{% hint style="danger" %}
アプリ内認証フローで Thales D1 SDK を使用するには、Tokenization サービスが必要です。
{% endhint %}

Visa Cloud Token Framework（CTF）は、Tokenization 後の e コマース用デジタルカードに対する信頼を高めるために設計された Visa のフレームワークです。

CTF は、デバイスバインディングとカード所有者検証という 2 つのフローを定義しています。

これらのフローにより、加盟店はイシュアでカード所有者のステップアップ認証フローをトリガーすることで、デジタルカードへの信頼を高めることができます。

フルフローおよび Visa Cloud Token Framework（CTF）の詳細については、次のセクションを参照してください [Visa Cloud token framework](/tokenization/ja/implement-tokenization/post-tokenization-requests/visa-cloud-token-framework-ctf.md) Tokenization ドキュメント内の。

Visa CTF のアプリ内認証フローは、次で説明されているフローと非常によく似ています [アプリ内認証](/push-provisioning/ja/implement-push-provisioning/implement-view-and-control/in-app-authentication.md).

ステップ \[01] から \[10] までは基本的に同じです。

主な違いは、次の表に示すペイロード形式です。

<table><thead><tr><th>デバイスバインディング - ペイロード</th><th>カード所有者検証 - ペイロード</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>

標準の Tokenization 認証と比較すると、イシュアアプリケーションは次を確認する必要があります。

* **reasonCode**：CTF 認証の理由を識別します。
  * `TOKEN_DEVICE_BINDING`：デバイスバインディングフロー。
  * `CARDHOLDER_STEPUP`：カード所有者検証フロー。
* **deviceIndex**：デバイスバインディングフローで使用されます。これは Visa のデバイス参照です。D1 SDK では、これは次にマッピングされます `bindingReference`.

次のシーケンス図を参照する場合 [アプリ内認証](/push-provisioning/ja/implement-push-provisioning/implement-view-and-control/in-app-authentication.md)、ステップ \[11] と \[12] を新しい API 呼び出しに置き換えてください。

この場合、デジタルカードの有効化はありません。代わりに、認証結果を Visa VTS に伝達する必要があります。

D1 SDK は、イシュアアプリケーションが結果を報告できるように、次の API を公開しています。

1. 次の場合 `TOKEN_DEVICE_BINDING`、次を呼び出します `DigitalCardService.approveBinding` 次を指定して `digitalCardID` および `deviceIndex`.
2. 次の場合 `CARDHOLDER_STEPUP`、次を呼び出します `DigitalCardService.approveCardholderVerification` 次を指定して `digitalCardID`.

D1 SDK では次のことも可能です。

1. バインド済みデバイスの一覧を次として取得する `deviceBindingList` 次の場合 `getDigitalCardList()` が呼び出されます。詳細については、次を参照してください [デジタルカードの表示と管理](/push-provisioning/ja/implement-push-provisioning/implement-view-and-control/view-and-control-digital-cards.md).
2. 次を呼び出してデバイスのバインドを解除する `DigitalCardService.unbindDevice`。次を参照してください [デバイスのバインド解除](/push-provisioning/ja/implement-push-provisioning/implement-view-and-control/in-app-authentication-for-visa-ctf/unbind-a-device.md).

次の例は、ペイロードを解析してバインディング要求を承認する方法を示しています。

{% hint style="info" %}
注

次のフィールドマッピングが適用されます。

* `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) {
    // 無効な base64 ペイロード
    return
} catch (_: JSONException) {
    // 無効な JSON ペイロード
    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 {
    // その他のフロー（例：有効化）
}

private fun sendResultToTokenRequestorApp(activity: Activity, exception: D1Exception?) {
    val result = Intent()
    // 使用可能な値: "approved"、"declined"、または "failure"
    val stepupResult = if (exception == null) STEPUP_RESULT_APPROVED else STEPUP_RESULT_FAILED
    result.putExtra(VISA_STEPUP_RESULT_RESPONSE, stepupResult)
    // イシュアが認証コードによる有効化を使用している場合に適用
    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) {
    // 無効な base64/JSON ペイロード
    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 {
    // その他のフロー（例：有効化）
}

private void sendResultToTokenRequestorApp(Activity activity, D1Exception exception) {
    Intent result = new Intent ();
    // 使用可能な値: "approved"、"declined"、または "failure"
    String stepupResult = (exception == null) ? STEPUP_RESULT_APPROVED : STEPUP_RESULT_FAILED;
    result.putExtra(VISA_STEPUP_RESULT_RESPONSE, stepupResult);
    // イシュアが認証コードによる有効化を使用している場合に適用
    activity.setResult(RESULT_OK, result);
    activity.finish();
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
// 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 {
    // Visa アプリ間連携を処理するサービス名の例
    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 %}

D1 SDK への完全なアクセスについては、次を確認してください [API リファレンス](/push-provisioning/ja/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/ja/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.
