Skip to content

フロントエンド実装

このセクションでは、サンプルコードを使用して加盟店アプリケーションのためのフロントエンドの実装方法を示します。

サンプルコードパッケージ内の以下のファイルはフロントエンド内の3DS2認証に必須です。必要な場合はディレクトリツリーで詳細を確認してください。

  • process.html - すべての認証シーケンスを実装しています。
  • 3ds-web-adapter.js - 3DS2データをフロントエンドからバックエンドへ渡し、コールバックURLの為に必要なiframeを生成する3DSクライアントの重要なコンポーネントです。
  • notify_3ds_events.html - ActiveServerの為に使用されるコールバックページで、認証において(ステップ 7ステップ 18(C))を開始する際に必要になります。このページはチェックアウト処理に認証イベントを渡します。

以下は認証処理認証シーケンスに基づいたフロントエンド実装の詳細です。

処理1: 認証の初期化

認証を初期化する為に、フロントエンドが必要な事:

ユーザーがチェックアウトページ内のチェックアウトを続けるボタンをクリックすると、ブラウザは必要なデータをセッションストレージに保存しprocess.htmlページに移動します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//checkout.html
function checkout() {
  var sessionData = {};
  sessionData.channel = "brw";
  sessionData.messageCategory = "pa";
  sessionData.authData = genAuthData();
  sessionData.backButtonType = "toShop";

  sessionStorage.setItem("sessionData", JSON.stringify(sessionData));
  window.location.href = "/process";
}

注目

sessionDataが含むもの:

  • channelbrw3ri、またはenrol。上記の例では、ブラウザベースの認証を実行するためにbrwチャンネルを使用しました。

  • messageCategorypa-決済認証または npa-非決済認証のいずれか。上記の例では、paを使用しました。

  • authData:JSON形式のすべての必要なデータ。データ構造については、APIドキュメントを参照してください。

  • backButtonType:プロセスページのbackボタンタイプを示します。上記の例では戻るボタンをショップに戻るとして作成します。

注意点:ページ間通信にsessionStorageを使用するのは、デモ用です。 3DSリクエスターの実際の実装では、既存のチェックアウトプロセスと統合するために、ページ間でパラメーターを転送するための最適なアプローチを選択しましょう。

process.htmlページ内で、sessionData受信し3DS2処理を開始します。まず、認証を初期化する為の情報を3ds-web-adapterに送信します (ステップ 1)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//process.html
var sessionData = JSON.parse(sessionStorage.getItem("sessionData"));
...
switch (sessionData.channel) {
  case "brw":
    var container = $('#iframeDiv');
    brw(sessionData.authData, container, _callbackFn, sessionData.messageCategory,
        sessionData.options);
    break;
  ...     

情報

3ds-web-adapter内のbrw()関数は以下のパラメータを含みます:

  • authData:JSON形式のすべての必要なデータ。データ構造については、APIドキュメントを参照してください。
  • container:複数のiframeを生成する3ds-web-adapterの事前定義されたコンテナー。
  • callbackFn:認証結果を処理するコールバック関数。
  • messageCategorypa-決済認証またはnpa-非決済認証。
  • options: optional parameters for 3DS Requestor. For example, the 3DS Requestor can choose to cancel the challenge by setting options.cancelChallenge=true.

次に3ds-web-adapter内のbrw()メソッドから3DSリクエスターバックエンドに認証を初期化する為の情報を送信します(ステップ 2)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//3ds-web-adapter.js
function brw(authData, container, callbackFn, messageCategory, options) {

  _callbackFn = callbackFn;
  iframeContainer = container;
  if (options) {
    _options = options;
  }
  //iframeのためにランダムな番号を作成する
  iframeId = String(Math.floor(100000 + Math.random() * 900000));

  //認証を初期化するための3DSリクエスターのURL
  var initAuthUrl;
  if (messageCategory) {
    if (messageCategory === "pa" || messageCategory === "npa") {
      initAuthUrl = "/auth/init/" + messageCategory;
    } else {
      _onError({"Error": "Invalid messageCategory"});
    }
  } else {
    initAuthUrl = "/auth/init/" + "pa";
  }


  console.log('init authentication', authData);

  // /auth/init/{messageCategory}に認証を初期化するためにデータを送信する(ステップ 2)
  doPost(initAuthUrl, authData, _onInitAuthSuccess, _onError);
}

brw()関数はバックエンド/api/v1/auth/init/{messageCategory} POSTリクエストを送信します。APIメッセージオブジェクトはJSON形式で送信されます。バックエンドがどのようにこのリクエストを処理するかを確認するにはこちらを参照してください。

3ds-web-adapterは成功した応答を処理する為に_onInitAuthSuccess()関数を使用します(ステップ 5)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//3ds-web-adapter.js
function _onInitAuthSuccess(data) {
  console.log('init auth returns:', data);
  if (data.threeDSServerCallbackUrl) {

    serverTransId = data.threeDSServerTransID;
    $('<iframe id="' + "3ds_" + iframeId
        + '" width="0" height="0" style="visibility: hidden;" src="'
        + data.threeDSServerCallbackUrl + '"></iframe>')
      .appendTo(iframeContainer);

  } else {
    _onError(data);
  }

  if (data.monUrl) {
      // 任意でモニタリング用iframeを追加する
    $('<iframe id="' + "mon_" + iframeId
        + '" width="0" height="0" style="visibility: hidden;" src="'
        + data.monUrl + '"></iframe>')
    .appendTo(iframeContainer);

  }
}

3ds-web-adapterは2つの隠しiframeをチェックアウトページに挿入します (ステップ 6)。1つ目のiframe(ステップ 7) 用で、threeDSServerCallbackUrlを使用してブラウザの情報がACSとActiveServerにより収集出来るようにします。

2つ目はオプションのモニタリングiframeで、ブラウザ情報を収集する間もしくは3DSメソッドの処理中に何かしらのエラーが発生した時に、InitAuthTimedOutイベントをActiveServer受信できるようになります。このイベントのタイムアウトは15秒です。

注目

シーケンス図のステップ 1からステップ 7はこの処理で実装されています。

処理 2: 認証の実行

認証を実行するためにはフロントエンドはブラウザ情報の収集、3DSメソッドデータの収集(収集が可能な場合のみ)を終える必要があります。これらの2つの処理が完了した後、notify_3ds_events.html3ds-web-adapterに認証を続けるよう通知します。この処理内で、何らかの理由によりデータ収集が失敗したか、もしくは完了出来なかった場合、InitAuth処理内でセットアップした別のモニタリングiframeによってInitAuthTimedOutのイベントが3ds-web-adapterに通知され、認証処理は終了されます。

以下は認証を実行するためのステップです。

  • notify_3ds_events.htmlを実装もしくは提供されたものを再利用し、データ収集についてのイベントを受信します。
  • _on3DSMethodSkippedもしくは_on3DSMethodFinishedイベントが通知された後、認証の実行メッセージを3DSリクエスターに送信します (ステップ 8ステップ 9) 。
  • 認証結果をフリクションレスフローもしくはチャレンジフローで処理します。(ステップ 13に続き、ステップ 14(F)またはステップ 14(C)) 。

notify_3ds_events.htmlは認証処理を開始するのに使用されます (ステップ 8)3DSリクエスターnotify_3ds_events.htmltransIdcallbackNameとオプションのparam変数を提供します。バックエンド実装を確認するにはこちらを参照してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!--notify_3ds_events.html-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <title>3DSecure 2.0 Authentication</title>
</head>
<body>

<form>
  <input type="hidden" id="notifyCallback" name="notifyCallback" value={{callbackName}}>
  <input type="hidden" id="transId" name="transId" value={{transId}}>
  <input type="hidden" id="param" name="param" value={{callbackParam}}>
</form>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"
        integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
<script>
  //チェックアウトページに通知を送り、以降の処理を実行する。

  var callbackFn = parent[$('#notifyCallback').val()];
  //callbackFnは3ds-notifyハンドラーメソッドにより定義されています。
  if (typeof callbackFn === 'function') {
    callbackFn($('#transId').val(), $('#param').val());
  }
</script>

</body>
</html>

callbackNameに応じて3ds-web-adapter内の異なるメソッドを呼んでいる事が分かります(ステップ 8)callbackNameの値は_onThreeDSMethodFinished_onThreeDSMethodSkipped_onAuthResult、もしくは_onInitAuthTimedOutです。それぞれのメソッドの説明は以下になります:

イベント説明
_onThreeDSMethodFinished             3DSメソッドがACSにより終了し、_doAuth()を呼び出す時である事を通知します
_onThreeDSMethodSkipped3DSメソッドが(利用出来ない、もしくは別の理由により)スキップされ、_doAuth()を呼び出す時である事を通知します3DSメソッドがスキップされたか否かに関わらず、3DSサーバーのブラウザ情報収集は3DSメソッドよりも前に実行される事に注意してください。
_onAuthResultこのイベントは認証結果がActiveServerから取得可能になったことを通知します。フリクションレスフローとチャレンジフローで使用されます (ステップ 17(F) と 19(C))
_onInitAuthTimedOut3DSメソッド中もしくはブラウザ情報収集中にエラーが発生した事を通知します。デモ内ではこのイベントが発生した場合、認証処理は終了されます。

timeout result

ここステップ 8ActiveServerにより返されるcallbackName_onThreeDSMethodFinishedもしくは _onThreeDSMethodSkippedのどちらかです。3ds-web-adapterはこれらのイベントを受信すると、POSTリクエストを送信し認証を実行する為に _doAuth()を呼び出します (ステップ 9)

そしてバックエンドはAPI /api/v1/auth/brwの呼び出しを作成します。バックエンドがどのようにそのリクエストを処理するか確認するにはこちらを参照してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//3ds-web-adapter.js
function _doAuth(transId) {

  console.log('Do Authentication for transId', transId);

  //最初に3DSメソッド用のiframeを取り除く
  $("#3ds_" + iframeId).remove();
  var authData = {};
  authData.threeDSRequestorTransID = transId;
  authData.threeDSServerTransID = serverTransId;

  doPost("/auth", authData, _onDoAuthSuccess, _onError);
}

3ds-web-adapterは成功した応答を処理する為に_onDoAuthSuccess()関数を使用します(ステップ 13)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//3ds-web-adapter.js
function _onDoAuthSuccess(data) {
  console.log('auth returns:', data);

  if (data.transStatus) {
    if (data.transStatus === "C") {
      // 3Dリクエスターはチャレンジフローに移行するか否かを選択できます。
      if (_options.cancelChallenge) {
          if (_options.cancelReason) {
            var sendData = {};
            sendData.threeDSServerTransID = serverTransId;
            sendData.status = _options.cancelReason;
            doPost("/auth/challenge/status", sendData, _onCancelSuccess,
                _onCancelError)
          } else {
            var returnData = _cancelMessage();
            _callbackFn("onAuthResult", returnData);
          }
        } else {
          data.challengeUrl ? startChallenge(data.challengeUrl) : _onError(
              {"Error": "Invalid Challenge Callback Url"});
        }

      } else {
        _callbackFn("onAuthResult", data);
      }
    } else {
      _onError(data);
    }
}

返されたtransStatusに基づいて異なるフローを実行します。

注目

transStatusはYCNUAとです。

  • Y: 認証 / 口座確認に成功
  • C: チャレンジが必要
  • N: 未認証 / 口座未確認
  • U: 認証 / 口座確認を実行できなかった
  • A: 処理の試行が実施された
  • R: 未認証 / 口座未確認または取引拒否

詳細はAPIドキュメントを参照してください。

フリクションレス認証結果

もしtransStatusCではない場合(つまりフリクションレスの場合)、3ds-web-adapterはフリクションレスフローへ移行し(ステップ 14(F))_callbackFn("onAuthResult", data)を呼び結果を表示します (11行目)。

1
2
3
4
5
6
7
8
9
//process.html
function _callbackFn(type, data) {

  switch (type) {
    case "onAuthResult":
      // "Show results in separate page"を表示する。
      $("#sepButton").removeClass("d-none");
      showResult(data);
      break;

showResult(data)関数はprocess.htmlページ内に結果を表示します:

result shortcut

フリクションレス認証処理はこの時点で終了になります。そして加盟店チェックアウト処理は、認証結果情報を使用して通常のオーソリ処理に移動できます。

情報

認証結果は別のページにも表示されます。これは処理 3で説明されます。

チャレンジ処理の続行

transStatusCの場合、ACSがチャレンジを要求したことを示します。 3DS2認証を継続してチャレンジプロセスに進むか、チャレンジが不要な場合は認証プロセスを終了するかを決定するのは、3DSリクエスター次第です。このデモでは、 options.cancelChallengeパラメーターを使用して、チャレンジフローに関する3DSリクエスターの意思決定を示します。この機能は、BRWテストページで概説されています。

備考

デモの目的で、キャンセルチャレンジプロセスはフロントエンドのjavascript3ds-web-adapterで実装されています。セキュリティ上の理由から、このプロセスは本番環境ではバックエンドで実装する必要がある場合があります。

3ds-web-adapterは、options.cancelChallengeパラメーターをチェックします。 options.cancelChallenge = trueの場合、3ds-web-adapterはチャレンジをキャンセルします。オプションで、指定されたキャンセルの理由に応じて、 options.cancelReasonを設定してActiveServerに送信することもできます。指定された理由は、3ds-web-adapterによってバックエンドから/auth/challenge/statusに送信されます。 バックエンドがこのリクエストを処理する方法を確認するには、こちらを参照してください。

チャレンジ結果のキャンセル画面は、次のスクリーンショットのようになります。

cancel challenge screen

options.cancelChallenge = trueが存在しない場合(または値がfalseに設定されている場合)、3ds-web-adapterstartChallenge()を呼び出し、チャレンジウィンドウにsrcchallengeUrlに設定したiframeを挿入します(ステップ. 14(C))

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//3ds-web-adapter.js
function startChallenge(url) {

  _callbackFn("onChallengeStart");
  // チャレンジ用iframeを生成する
  $('<iframe src="' + url
      + '" width="100%" height="100%" style="border:0" id="' + "cha_" + iframeId
      + '"></iframe>')
  .appendTo(iframeContainer);

}

注目

チャレンジシナリオを試したい場合は、こちらのガイドに従ってください。

iframeclass属性がwidth="100%"height="100%"に設定される事が分かります。これはiframeがACSに提供される内容に従ってサイズ変更をする為に必要です。チャレンジスクリーンは以下のスクリーンショットに似た表示になるはずです。

result screen

そしてカード会員はワンタイムパスワードのような認証データを入力してチャレンジフォームを送信する事が出来ます。ACSは取引とカード会員を認証してチャレンジ結果を返します。

チャレンジフローが終了した後、 ActiveServer3DSリクエスターバックエンド内の/3ds-notifyエントリーポイントをiframeを通して呼び出し、_onAuthResultが呼ばれます。3DSリクエスターは以前 (ステップ 19(C))と同様にnotify_3ds_events.htmlページをレンダリングします。3ds-web-adapterは認証結果を取得しそれをprocess.htmlページに表示する為に_onAuthResult()関数を呼び出します。

情報

本番環境ではACSはカード会員から得られた情報から複雑なリスクベースの認証を実行します。同様に、認証メソッド(例えばワンタイムパスワード、生体認証など)がカード会員のイシュアーにより決定され、実行されます。

処理3: 認証結果の取得

チャレンジ処理が終了した後 (もしくはフリクションレス処理結果を別ページに表示する必要がある場合)、オーソリ処理に必要な最終認証結果を取得するために、認証結果を要求する必要があります。

なぜ別の認証結果要求が必要なのか?

処理 2の後で利用可能な認証の結果を既に持っているのに、なぜ結果をもう一度要求する必要があるのか不思議に思うかもしれません。

これは、ステップ. 13で認証結果がページに転送されているためです。認証結果ページは、チェックアウトページとは別のページとして表示されるのが一般的です。この場合、3ds-web-adapterは結果を3DSリクエスターまたは結果ページに転送できますが、認証結果はクライアント側コードによって再転送されるため、推奨されるデータフローではありません。一般的に安全ではないと見なされます。

3DSリクエスターのサーバー側には、常に元のソース(ActiveServer)から結果を取得する独自のメカニズムが必要です。3DSリクエスターのクライアント側が認証結果を提供するのではなく、3DSリクエスターのサーバー側が認証結果を取得し結果ページを提供することでデータがより安全であることが保証できます。したがって、このステップで認証結果を再要求する必要があります。

このデモでは、process.html内の処理 2の最後のShow results in separate pageを選択して別のページに結果を表示する事も出来ます。

認証結果を取得し別ページに表示する為に、フロントエンドが必要な事は:

まず、process.htmlページ内でserverTransIdsessionStorage内に保存し、result.htmlページに移動します。

1
2
3
4
5
6
7
8
9
//process.html
function openResultInNewWindow() {
if (serverTransId) {
  var url = '/result';

  sessionStorage.setItem("serverTransId", serverTransId);
  window.open(url, 'newwindow', 'height=800,width=1000');
}
}

result.htmlページ内で、認証結果を取得する為に 3ds-web-adapter内のresult()メソッドを呼び出します。result()メソッドは認証結果の取得メッセージをバックエンドへ送信します。バックエンドはこのリクエストを受信し、/api/v1/auth/brw/resultメッセージをActiveServerに送信します。バックエンドがどのようにこのリクエストを処理するか確認するには、こちらを 参照してください。コールバック関数showData()はページに結果を表示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//result.html
var serverTransId = sessionStorage.getItem("serverTransId");

// 認証結果を要求する。(Step 15(F)またはStep 20(C))
result(serverTransId, showData);

// 認証結果を別ページに表示する (Step 17(F)またはStep 22(C))
function showData(type, data) {
    var toShow = "<dl class='row'>";
    Object.keys(data).forEach(function (k) {
      toShow = toShow + "<dt class='col-sm-4'>" + k + "</dt>" + "<dd class='col-sm-8'>" + data[k]
          + "</dd>";
    });
    toShow += "</dl>";
    $('#showResult').empty().append(toShow);
    $("#resultCard").removeClass("d-none");
}

新しい結果画面は以下のスクリーンショットのようになります:

result screen

成功

この文書ではフロントエンド統合について説明しました。認証が完了した後、チェックアウト処理は取引ステータス、ECIとCAVVを使用してオーソリ処理を続け、取引を完了出来ます。

次のチャプター

次へボタンを選択し3DSリクエスターの バックエンド実装について確認できます。