> 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/ja/3dswosuru/oobfurthaleswosuru/oobcharenjiwosuru.md).

# OOBチャレンジを処理する

## 概要

3DSサービスは、トランザクションがチャレンジされ、エンドユーザーがイシュアアプリケーションに登録済みの認証手段を持っている場合に、OOBチャレンジフローを開始します。

## ユーザー体験

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

## フロー

<figure><img src="/files/d132582aa60d5a34f6d35cdd2c6482e13ad103b5" alt=""><figcaption><p>OOBチャレンジフローの概要 - ステップ1。</p></figcaption></figure>

<figure><img src="/files/a89f2ace76db24e269ec28e05c10cfc4ba3e9324" alt=""><figcaption><p>OOBチャレンジフローの概要 - ステップ2。</p></figcaption></figure>

## シーケンス図 – バックエンドフロー

以下の図は、OOBチャレンジフローのエンドツーエンドのシーケンスを示します。

### 前提条件

* カード商品はD1バックエンドおよび決済ネットワークのディレクトリサーバーで構成されます。
* エンドユーザーとカードはD1バックエンドに登録されています。
* 認証手段がデバイスに登録されています。
* D1 SDKが初期化されています。
* イシュアアプリケーションでプッシュ通知が構成されています。

***

### 1 – AReq/ARes と最初の CReq/CRes

<figure><img src="/files/305449df83b550e9522d54456a2122e3c56ecaf8" alt=""><figcaption><p>AReq/ARes と初回の CReq/CRes。</p></figcaption></figure>

### 2 – CReq/CRes とイシュアからのOOBチャレンジ

<figure><img src="/files/f6bb69b489cddb0382fb009f827906633d96ee07" alt=""><figcaption><p>CReq/CRes とイシュアからのOOBチャレンジ。</p></figcaption></figure>

SDKの実装詳細については、こちらを参照してください [SDKフロー](/3d-secure/ja/3dswosuru/oobfurthaleswosuru/oobcharenjiwosuru.md#sequence-diagram-sdk-flow).

### 3 – 最終CReq/CResと通知

<figure><img src="/files/cbd81d9ccafd0acf5249e0c6ca357d705877a084" alt=""><figcaption><p>最終CReq/CResと通知。</p></figcaption></figure>

***

## シーケンス図 – SDKフロー

以下のシーケンスは、イシュアアプリケーションがD1 SDKを使用してOOBチャレンジを完了する方法を示します。

### 1 - イシュアアプリケーションがD1 SDK経由でFIDOチャレンジを取得

<figure><img src="/files/4c7153d33b8274d55bb12b75f403b3bdcc19ae2f" alt=""><figcaption><p>イシュアアプリケーションはD1 SDK経由でFIDOチャレンジを取得します。</p></figcaption></figure>

### 2 - D1 SDKがエンドユーザーを認証

<figure><img src="/files/5ea93de4f7eb43fce20f521eb0c1b25c33158a15" alt=""><figcaption><p>D1 SDKがエンドユーザーを認証します。</p></figcaption></figure>

プッシュ通知については、こちらを参照してください [プッシュ通知の構成](/3d-secure/ja/d1-sdkwosuru/meru/4-pusshu.md).

### トランザクション確認データ

以下の表は、D1 SDKが表示目的でイシュアアプリケーションに提供するトランザクションデータを示します。

| パラメーター名                | 説明                                                                                                                |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------- |
| acsTransId             | ACSによって割り当てられる、単一のトランザクションを識別するための一意なトランザクション識別子です。                                                               |
| purchaseDate           | 購入日時。UTC時刻形式で表されます。                                                                                               |
| purchaseExponent       | ISO 4217の通貨指数で指定される通貨の最小単位です。                                                                                     |
| threeDSRequestorAppURL | 3DSリクエスターアプリケーションが、認証アプリケーションがOOB認証完了後に3DSリクエスターアプリへ戻るために、CReqメッセージ内で自身のURLを宣言します。エンドユーザーを加盟店アプリケーションへ戻すために使用します。 |
| purchaseCurrency       | 購入金額がISO 4217形式で表される通貨です。                                                                                         |
| merchantName           | アクワイアラまたは決済ネットワークによって割り当てられた加盟店名です。                                                                               |
| purchaseAmount         | 小数点を含まない通貨の最小単位での金額です。                                                                                            |

## D1 SDKの統合

{% hint style="info" %}

* calling `login` は、このユースケースの前提条件として必要ではありません。
* イシュアアプリケーションがプッシュ通知経由で確認要求を受信しない場合、D1 SDKを呼び出して要求を取得できます `fetchAuthnRequest` API。
* SDKは強力な生体認証を使用します。複数の生体認証方式が利用可能な場合（たとえばFace IDと指紋）、オペレーティングシステムがユーザーインターフェースを管理します。
  {% endhint %}

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

```java
static String lastProcessedTransactionId = "";
public void challengeOOBFlow(
        @NonNull D1Task d1Task,
        @NonNull FragmentActivity activity,
        @NonNull CountDownLatch transactionWaiter) throws D1Exception {
    // AuthnCallbackに準拠するインターフェースを実装
    D1Authn d1Authn = d1Task.getD1Authn(activity, new AuthnCallback(){
        @Override
        public void onTransactionDataConfirmation(@NonNull Map<String, String> map, @NonNull AuthnUserConfirmationCallback authnUserConfirmationCallback) {
            // 最後に処理したトランザクションIDを確認するトランザクションハンドラーを渡す
            try {
                transactionHandler(map, transactionWaiter, authnUserConfirmationCallback);
            } catch (InterruptedException e) {
                // 例外を処理
            }
        }

        @NonNull
        @Override
        public String onBiometricPromptMessage() {
            // 認証プロンプト用のメッセージを提供します。アプリケーションで値をカスタマイズできます。
            return "生体認証プロンプト中にユーザーに表示するメッセージ";
        }
    });

    d1Authn.fetchAuthnRequest(new D1Task.Callback<Void>() {
        @Override
        public void onSuccess(Void unused) {
            // エンドユーザー確認後に後続フローを続行します。
            transactionWaiter.countDown();
        }

        @Override
        public void onError(@NonNull D1Exception e) {
            // D1 SDK統合 – エラー管理のセクションを参照してください。
            transactionWaiter.countDown();
        }
    });
}

public synchronized void transactionHandler(
        @NonNull Map<String, String> map,
        @NonNull CountDownLatch transactionWaiter,
        @NonNull AuthnUserConfirmationCallback authnUserConfirmationCallback) throws InterruptedException {
    // 'map' には認証対象のトランザクションデータが含まれます。
    //例のMap：
    // {
    //    "acsTransId": "2f37539c-d82e-4929-88db-2f9e72c7fa62",
    //    "merchantName": "CoffeeHouse2 (Dæmø)",
    //    "purchaseAmount": "100",
    //    "purchaseCurrency": "840",
    //    "purchaseExponent": "2",
    //    "purchaseDate": "20231017055638"
    // }

    // 現在のトランザクションIDを取得し、最後に処理したトランザクションIDと比較します。同じ場合はトランザクションを処理する必要がないため、トランザクションのキャンセルに進みます
    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) {}
        }

        // アプリケーションは内容を表示し、エンドユーザーに認証要求を「続行」するか「拒否」するかを促します。

        // 続行するには: ユーザーが続行を選択したとき
        // 続行後、選択されたAuthnTypeの登録内容に基づいてUIで認証が求められます。
        authnUserConfirmationCallback.proceed();

        // キャンセルするには: ユーザーがキャンセルを選択したとき
        authnUserConfirmationCallback.cancel();
    } else {
        if(transactionWaiter.await(60, TimeUnit.SECONDS)) {
            // トランザクションを処理する必要はなく、トランザクションのキャンセルに進みます
            authnUserConfirmationCallback.cancel();
        }
    }
}
```

{% endtab %}

{% tab title="Android Kotlin" %}

```kotlin
companion object {
    var lastProcessedTransactionId = ""
}
fun challengeOOBFlow(d1Task: D1Task, activity: FragmentActivity, transactionWaiter: CountDownLatch) {
    // AuthnCallbackに準拠するインターフェースを実装
    val d1Authn: D1Authn = d1Task.getD1Authn(activity, object : AuthnCallback {
        override fun onTransactionDataConfirmation(
            map: Map<String, String>,
            authnUserConfirmationCallback: AuthnUserConfirmationCallback
        ) {
            // 最後に処理したトランザクションIDを確認するトランザクションハンドラーを渡す
            try {
                transactionHandler(map, transactionWaiter, authnUserConfirmationCallback)
            } catch (e: InterruptedException) {
                // 例外を処理
            }
        }

        override fun onBiometricPromptMessage(): String {
            // 認証プロンプト用のメッセージを提供します。アプリケーションで値をカスタマイズできます。
            return "生体認証プロンプト中にユーザーに表示するメッセージ"
        }
    })

    d1Authn.fetchAuthnRequest(object : D1Task.Callback<Void?> {
        override fun onSuccess(unused: Void?) {
            // エンドユーザー確認後に後続フローを続行します。
            transactionWaiter.countDown()
        }

        override fun onError(e: D1Exception) {
            // D1 SDK統合 – エラー管理のセクションを参照してください。
            transactionWaiter.countDown()
        }
    })
}

@Synchronized
@Throws(InterruptedException::class)
fun transactionHandler(
    map: Map<String, String>,
    transactionWaiter: CountDownLatch,
    authnUserConfirmationCallback: AuthnUserConfirmationCallback
) {
    // 'map' には認証対象のトランザクションデータが含まれます。
    // 例のMap:
    // {
    //    "acsTransId": "2f37539c-d82e-4929-88db-2f9e72c7fa62",
    //    "merchantName": "CoffeeHouse2 (Dæmø)",
    //    "purchaseAmount": "100",
    //    "purchaseCurrency": "840",
    //    "purchaseExponent": "2",
    //    "purchaseDate": "20231017055638"
    // }

    // 現在のトランザクションIDを取得し、最後に処理したトランザクションIDと比較します。同じ場合はトランザクションを処理する必要がないため、トランザクションのキャンセルに進みます
    val currentTransactionId = map["acsTransId"]
    if (currentTransactionId != lastProcessedTransactionId) {
        // 最後に処理したトランザクションIDは、Kotlinではそのまま変更できないため、値を保持する変数として使用されます
        lastProcessedTransactionId = currentTransactionId ?: ""

        val json = JSONObject()
        for ((key, value) in map) {
            try {
                json.put(key, value)
            } catch (ignored: JSONException) {
                // JSON挿入中の例外は無視します
            }
        }

        // アプリケーションは内容を表示し、エンドユーザーに認証要求を「続行」するか「拒否」するかを促します。

        // 続行するには: ユーザーが続行を選択したとき
        authnUserConfirmationCallback.proceed()

        // キャンセルするには: ユーザーがキャンセルを選択したとき（この部分はユーザー操作に基づいてUIで処理する必要があります）
        authnUserConfirmationCallback.cancel()
    } else {
        if (transactionWaiter.await(60, TimeUnit.SECONDS)) {
            // トランザクションを処理する必要はなく、トランザクションのキャンセルに進みます
            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)) {
        // 'map' には認証対象のトランザクションデータが含まれます。 

        //例のMap： 
        // {
        //    "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
            }

            // アプリケーションは内容を表示し、エンドユーザーに認証要求を「続行」するか「拒否」するかを促します。
          
            // 続行するには: ユーザーが続行を選択したとき
            // 続行後、選択されたAuthnTypeの登録内容に基づいてUIで認証が求められます。

            let alertController = UIAlertController(title: "トランザクション詳細", message: message, preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "続行", style: .default, handler: { action in
                proceedHandler()
            }))
        alertController.addAction(UIAlertAction(title: "キャンセル", style: .cancel, handler: {action in
                cancelHandler()
            }))
            viewController?.present(alertController, animated: true)
            lastTransaction = transaction
        }
        else {
            cancelHandler()
        }
    }

    public func authnTouchIDOperationPrompt(_ authn: D1Authn) -> String {
        // 影響ありません。登録解除は認証なしで行われます 
    }
}
let authnConformer = AuthnConformer()
authnConformer.viewController = self?.window?.rootViewController
let d1Authn = d1Task.d1Authn(authnConformer)

d1Authn.fetchAuthnRequest({ error in
    if let error = error {
        // D1 SDK統合 – エラー管理のセクションを参照してください。
    } else {
        // エンドユーザー確認後に後続フローを続行します。
    }
})
```

{% endtab %}
{% endtabs %}

以下の例は、イシュアアプリケーションが `threeDSRequestorAppURL` トランザクション確認データからエンドユーザーを加盟店アプリケーションに戻す方法を示します。

{% 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);

            // 続行するためのUIをユーザーに表示

            // 続行する場合は、次を呼び出します:
            authnUserConfirmationCallback.proceed();

        }

        @NonNull
        @Override
        public String onBiometricPromptMessage() {
            return "生体認証プロンプトメッセージ";
        }
    });
    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) {
            // D1 SDK統合 – エラー管理のセクションを参照してください。
            launchMerchantApp(threeDSRequestorAppURL[0], activity);
        }
    });
}

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

        // URIをブラウザーまたはURIを処理できるアプリで開くIntentを作成
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        activity.startActivity(intent);
    } else {
        Toast.makeText(activity, "利用可能なトランザクション要求はありません", 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"] ?: "")

            // 続行するためのUIをユーザーに表示
            // 続行する場合は、次を呼び出します:
            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) {
            // D1 SDK統合 – エラー管理のセクションを参照してください。
        }
    })
}

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

        // URIをブラウザーまたはURIを処理できるアプリで開くIntentを作成
        val intent = Intent(Intent.ACTION_VIEW, uri)
        activity.startActivity(intent)
    } else {
        Toast.makeText(activity, "利用可能なトランザクション要求はありません", Toast.LENGTH_SHORT).show()
    }
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
// 'threeDSRequestorAppURL' は AuthnDelegate の authnDelegate.authn コールバックメソッドから受け取られます。
// 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)) {
        // transactionData から threeDSRequestorAppURL を抽出
        if let url3DS = transactionData["threeDSRequestorAppURL"] {
            threeDSRequestorAppURL = url3DS
        }
        // 続行するためのUIをユーザーに表示

        // 続行する場合は、次を呼び出します:
        proceedHandler()

    }
}

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

// 初期化時に AuthnDelegate を d1Task に登録
d1Task.d1Authn(mAuthnDelegate)

// authn delegate のコールバックを受け取るために fetchAuthnRequest API を呼び出します。
d1Authn.fetchAuthnRequest { error in
  if error == nil {
      // 取得したURLで加盟店アプリを起動します。
      self.launchMerchantApp(mAuthnDelegate.threeDSRequestorAppURL)
  }
}
```

{% endtab %}
{% endtabs %}

## イシュアバックエンド統合

処理フローの শেষেに、イシュアバックエンドは次を通じて通知されます [3DSカード操作の通知](/3d-secure/ja/d1-apiwosuru/d1-apirifarensu/autobaundoapid1kara/3ds-api.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:

```
GET https://docs.payments.thalescloud.io/3d-secure/ja/3dswosuru/oobfurthaleswosuru/oobcharenjiwosuru.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.
