フロントエンド実装

この章では、サンプル・コードを使用して、加盟店アプリのフロントエンドの実装方法について説明します。

Note

サンプル・コードに目を通し、実装してから、次に進んでください。

次の図に、フロントエンド用の主なファイルを示します。これらは/finalプロジェクトの/resourceディレクトリにあります。必要に応じディレクトリ構想を確認してください。3ds-web-adapter.js3DS-web-adapterです。

front-end files

処理 1: 認証の初期化

認証を初期化するためには、フロントエンドが以下のように動作する必要があります。

ユーザーが決済ページの"Continue to checkout"ボタンをクリックすると、ブラウザーは、認証処理を開始するための情報を3DS-web-adapterに送信します(ステップ.1)。この処理はcheckout.htmlcheckout()関数で実行されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//checkout.html
function checkout() {
    ...
    $("#cardholderInfoCard").remove();
    $("#checkoutCard").removeClass("d-none");

    //デモ用に結果画面を表示するため2秒後に移動する
    setTimeout(function () { window.location.href = "/auth/result"}, 2000);
    threeDSAuth(transId, cardHolderInfo, purchaseInfo);
}

3ds-web-adapter.jsthreeDSAuth()関数が実行されます(ステップ.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
30
31
32
    //3ds-web-adapter.js
    function threeDSAuth(transId, cardHolderInfo, purchaseInfo) {

        console.log('init authentication');
        $.ajax({
            url: '/auth/init',
            type: 'POST',
            contentType: "application/json",
            data: JSON.stringify({
                threeDSRequestorTransID: transId,
                cardExpiryDate: purchaseInfo.expiryDate,
                purchaseAmount: purchaseInfo.purchaseAmount,
                purchaseCurrency: purchaseInfo.purchaseCurrency,
                acctNumber: purchaseInfo.acctNumber,
                cardHolderInfo: cardHolderInfo
                // ここで他の情報を3DSリクエスターに送ることもできる。acctInfo等
            }),
            success: function (data) {
                console.log('init auth returns:', data);
                $('<iframe id="threeds-container" width="0" height="0" 
                style="visibility: hidden;" 
                src="' + data.threeDSServerCallbackUrl + '"></iframe>')
                    .appendTo('.challengeContainer');
            },
            error: function () {
                alert('error');
            },
            dataType: 'json'

        });

    }

threeDSAuth()関数は3DSリクエスターのURL /auth/initにPOSTリクエストを行います(行6)。オブジェクトはJSON形式でPOSTされます。3DSリクエスターがこのリクエストを処理しているかの確認については、ここを参照してください。

Note

各自の実装方法によってthreeDSAuth()を変更し、cardholderInfopurchaseInfotransIdでなく独自のオブジェクトを転送できます。データ構造については、API documentを参照してください。

/auth/initからの応答が正常に得られると(ステップ.5)3ds-web-adapter.jsは非表示のiframeを決済ページに埋め込み(ステップ.6)、ACSと3DSサーバーがthreeDSServerCallbackUrlを使用してブラウザー情報を収集できるようになります(ステップ.7)行20〜23)。

Info

シーケンス図のステップ.1ステップ.7がこの処理 1: 認証の初期化で行われます。

処理 2: 認証の実行

認証を実行するためには、フロントエンドが以下のように動作をする必要があります。

notify_3ds_events.htmlは、認証処理を開始するために使用されます(ステップ.8)3DSリクエスターは、このファイルに対し、transIdcallbackType、およびオプションの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
31
32
33
<!--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" data-th-value="${callbackName}"/>
    <input type="hidden" id="transId" name="transId" data-th-value="${transId}"/>
    <input type="hidden" id="param" name="param" data-th-value="${callbackParam}"/>
</form>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"
        integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
<script>

//チェックアウトページのiframe内で実行される。

var callbackFn = parent[$('#notifyCallback').val()];

//callbackFnは3DSリクエスターの3ds-notifyハンドラーメソッドから渡されます
if (typeof callbackFn === 'function') {
    callbackFn($('#transId').val(),$('#param').val());
}

</script>

</body>
</html>

callbackNameにより、3ds-web-adapter.jsで呼び出されるメソッドが異なることが分かります(ステップ.8)callbackNameの値は_onThreeDSMethodFinishedまたは_onThreeDSMethodSkippedのいずれかでなければなりません。 各メソッドについて以下に説明します。

  • _onThreeDSMethodFinished - 3DSメソッドが完了したことを通知し、ブラウザー情報の収集が完了したことを示す_doAuth()を呼び出します。
  • _onThreeDSMethodSkipped - 3DSメソッドをスキップしたことを通知し、ACSによるブラウザー情報の収集をスキップしたことを示す_doAuth()を呼び出します。

3ds-web-adapter_doAuth()は、/authへのPOSTリクエストを行い認証を実行します(ステップ.9)3DSリクエスターがこのリクエストを処理しているかの確認については、ここを参照してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    //3ds-web-adapter.js
    function _doAuth(transId) {
        ...    
        $.post("/auth", {id: transId}).done(function (data) {
            console.log('auth returns:', data);

            if (!data) {
                alert('error');
                return;
            }

            switch (data.transStatus) {
                case "Y":
                    authSuccess(data, transId);
                    break;
                case "C":
                    startChallenge(data.challengeUrl);
                    break;
                ...
            }
    }

_doAuth()/authへリクエストをPOSTし(行4)、結果のdataを取得します。この後に実行されるフローは、返されたtransStatusにより異なります(行14と17)。

transStatusYの場合は認証に成功したことを意味します。3DS-web-adapterはフリクションレス・フローに切り替わり、次の処理であるauthSuccess()関数を実行します(ステップ.14(F))。-> 認証結果の取得

transStatusCの場合、3ds-web-adapter.jsstartChallenge()を呼び出し、チャレンジ画面を表示するためsrc属性がchallengeUrlにセットされたiframeをに挿入します(ステップ.14(C))

1
2
3
4
5
6
7
8
9
    function startChallenge(url) {
    //remove the spinner
    $(".spinner").remove();

    //create the iframe
    $('<iframe src="'+url+'" class="h-100 w-100 border-0" id="challengeWindow" name="challengeWindow"></iframe>')
     .appendTo('.challengeContainer');

    }

Note

チャレンジシナリオをテストしたい場合は、20個のパイナップルをカートに追加するなど、$100以上の商品の購入操作を試みてください。

iframeクラスがh-100およびw-100にセットされたことが分かります。これらはそれぞれ、height: 100%!importantおよびwidth: 100%!importantというcss型を実装するためのブートストラップ・デフォルト・クラスです。これが必要なのは、ACSから与えられた内容に応じてiframeのサイズを調整しなければならないためです。チャレンジ画面は次のスクリーンショットのようになるはずです。

result screen

Info

実際の状況でACSが実行するのは、購入金額の確認だけで済むような簡単な処理ではなく、取得したカード会員情報に応じたリスクに基づく複雑な認証処理になります。同様に、認証メソッドはイシュアーにより決定され実行されます。

処理 3: 認証結果の取得

認証結果を取得するためには、フロントエンドが以下のように動作する必要があります。

認証結果が用意できると、3DSリクエスターnotify_3ds_events.html_onAuthResultを使用してフロントエンドに通知します。次に、3ds-web-adapterは次の図のURL /auth/resultを使用して認証結果の取得リクエストを3DSリクエスターに送信します。3DSリクエスターnotify_3ds_events.htmlメッセージを処理しているかの確認については、ここを参照してください。

1
2
3
    function _onAuthResultReady(transId) {
        //redirect to result page
        window.location.href = "/auth/result?txid=" + transId;
最後に、ブラウザーは認証結果の詳細を表示するresult.htmlページにリダイレクトします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <div class="col-sm-8">
     <div class="card">
         <div class="card-header">Transaction successful</div>
         <div class="card-body">
             <h4 class="card-title text-success">3D secure authentication was successful</h4>
             <p class="card-text">You may move onto authorization using the result below 
                  or you can press "Back To Home" to restart authentication process.</p>

             <dl class="row">
                 <dt class="col-sm-3">Trans Status</dt>
                 <dd class="col-sm-9" data-th-text="${result.transStatus}">Y</dd>
                 <dt class="col-sm-3">ECI</dt>
                 <dd class="col-sm-9" data-th-text="${result.eci}">eci value</dd>
                 <dt class="col-sm-3">CAVV</dt>
                 <dd class="col-sm-9" data-th-text="${result.authenticationValue}">cavv value</dd>
                 <dt class="col-sm-3">Other Info</dt>
                 <dd class="col-sm-9"></dd>
             </dl>

             <a href="/" class="btn btn-primary">Back To Home</a>
         </div>
     </div>
 </div>

新しい結果画面は、次のスクリーンショットのようになります。 result screen

Success

お疲れ様でした。これでフロントエンドの導入は完了です。通常、この処理の後は、取引を完了するための、取引ステータス、ECI、CAVVを使用してオーソリの実行に進みます。

次のチャプター

nextボタンを選択し、3DSリクエスター用のバックエンド導入についてご覧ください。