Skip to content

3DSリクエスターセットアップガイド

3DSリクエスター は加盟店側の3DS2コンポーネントであり、購入者のデバイスから3DSサーバーに3Dセキュアデータを渡すために使用されます。 決済認証における3DSリクエスターは通常、既存の加盟店のオンラインショッピング用ウェブサーバーです。

3DSリクエスターは、3DSリクエスターアプリまたは購入者のデバイス/ブラウザに3DSメソッドを介してカード会員につながります。ブラウザべース のトランザクションを3Dセキュア処理するには、3DSメソッドを使用してブラウザ情報/デバイス詳細情報を収集し、チャレンジが必要な場合、ACS(アクセス・コントロール・サーバー)はカード会員にUIを表示するためのHTMLをブラウザに提供します。

この項では、ActiveServerに接続するためのGPaymentsのサンプルコードを使用し、3DSリクエスターコードを加盟店ウェブサーバーに組み込む概要を説明します。加盟店アプリの組み込みに関する詳細は、ActiveSDKの文書を参照してください。

前提条件

本書の利用に際して想定されている前提条件を、以下に示します。

  • Javaの基本事項を理解していること
  • ウェブテクノロジーの基本事項を理解していること(HTML、CSS、Javascript)
  • JDKがインストールされていること。
  • 任意のIDE。
  • Apache Mavenがインストールされていること(https://maven.apache.org/install.html)。
  • GPaymentsのサンプルコードパッケージをここからダウンロードしてあること

はじめに

3DS2フローの組み込みに使用するサンプルの加盟店決済サイトは、Springboot プロジェクトを使用して作成されています。参考までに、HTMLページの作成に使用したものを以下に示します。

サンプルの加盟店決済ページには以下の 3つ の主要ページが含まれています。

  • index.html - 購入する商品を選択するショッピングサイト。
  • checkout.html - カード会員情報と処理画面が含まれた決済ページ。
  • result.html -トランザクションが成功したか失敗したかを表示するトランザクション結果ページ。

サンプルコード実行方法

上記のGPaymentsサンプルコードパッケージには次の2つのフォルダーが含まれています

  • sampleCode - サンプルの加盟店決済ページ (3DS2認証フローは含まれていません)
  • finalCode - 3DS認証フローが組み込まれた 最終的な加盟店決済ページ。本書の順を追った説明に従って操作を進め、最後に得られるのがこの最終コードです。

サンプルの加盟店サイトを実行する手順を、以下に示します。

  1. 3DSリクエスターと3DSサーバーが相互認証を行えるよう、GPaymentsからユーザーに提供されるクライアント証明書(.p12ファイル)を/finalCode/src/main/resources/certsというファイルパスにコピーします。証明書ファイルを受け取っていない場合は、GPaymentsにお問い合わせください。

  2. ターミナル(Linux)または コマンドプロンプト(Windows)を開き、実行するパッケージに応じてプロジェクトのルートディレクトリ/sampleCodeまたは/finalCodeに移動します。

  3. ルートディレクトリで次のコマンド行を実行します。初めて実行する際にはMavenが必要な依存関係をダウンロードするため、2、3分かかることがあります。

1
mvn spring-boot:run

ポイント

上記のコマンドを実行する前に必ず、Mavenがインストールされ使用可能な状態になっていなければなりません。Mavenのインストールについては、https://maven.apache.org/install.htmlを参照してください

注意

Windowsの場合は、WindowsファイアウォールがJavaのネットワークアクセスに関するセキュリティアラートを表示することがありますが、アクセスを許可して先に進んでください。

http://localhost:8082にアクセスするとサンプルのカートページを表示できます。カートにいくつかの商品を追加し、デフォルトのカード会員情報を使用して決済してみてください。

サンプルの加盟店サイト

ポイント

ポート8082が使用中の場合はエラーが表示される可能性があります。その場合は、/resources/application.propertiesファイルに次の行を追加してポートを設定してください。

1
server.port = # 使用したいポート番号

注意

デモ専用のサンプルのリクエスターコードはHTTPで実行されています。

3DS2の処理フロー

3DS2には次の2つの主要な認証フローがあります。

  • フリクションレスフロー - フリクションレスフローでは、認証 (AReq/ARes) メッセージを含む3Dセキュア認証フローが開始されます。このフローでは、リスクベースの認証を使用して、認証を成功させるためにカード会員による追加の操作が必要か否かを判断します。
  • チャレンジフロー - ACSが「認証を完了するためにはカード会員による追加の情報が必要である」と判断した場合は、フリクションレスフローからチャレンジフローに切り換わります(リスクが特定の限界値より大きい場合や法的に要求されている場合など)。チャレンジフローでは、フリクションレスフローに含まれる AReq、ARes メッセージの他に、チャレンジ (CReq/CRes) メッセージと結果 (RReq/RRes) メッセージが含まれます。

チャレンジフローを次の図に示します challenge flow

点線は、3DS2の範囲外であるオーソリのプロセスを示します。

認証ステップ

これらの認証フローに基づき、ActiveServerによる ブラウザベース 認証を実行するには、次のような3つのステップがあります。

  1. 認証の開始

    • /api/v1/auth/brw/init/{messageCategory}を呼び出します。
    • /brw/init/{messageCategory} には、3DSサーバーが認証要求(AReq)を生成するのに必要な(3DSリクエスターからの)すべての情報が含まれています。
  2. 認証の実行

    • /api/v1/auth/brwを呼び出します。
    • /brw は、3DSメソッドとデバイスの情報収集が完了したときに3DSリクエスターが呼び出しを行うために使用されます。
  3. 認証結果の取得

    • /api/v1/auth/brw/resultを呼び出します。
    • /brw/result は、認証結果(AResまたはRResチャレンジのステータスに応じ)に含まれる情報を3DSリクエスターに応答します。

シーケンス図

次のシーケンス図は、GPayments APIとサンプルコードを使用した3DS2フローにおける 3DSリクエスター の機能に焦点を当て、3DS2認証処理をステップごとに分解して示したものです。

3ds2diagram

  1. 認証を開始するための情報を送信

    • 3DS2認証を行えるよう、カード会員から収集した情報を使用して、3ds-web-adapter.js内で定義されているthreeDSAuth()関数を呼び出します。
  2. 認証の開始

    • threeDSAuth()は、決済ページから収集した情報を使用して3DSリクエスターにPOST要求を行い、認証を開始するように3DSリクエスターに要求します。
  3. 認証の開始 (/brw/init/{messageCategory})

  4. threeDSServerCallbackUrlを応答

  5. threeDSServerCallbackUrlを応答

    • 3DSリクエスターは3ds-web-adapterにこの情報を返します。
  6. コールバックiframeを設置

    • srcthreeDSServerCallbackUrlに設定された非表示のiframeを挿入します。これにより、3DSサーバーが3DSリクエスターに接続可能になります。3DSサーバーは、このiframeへのコールバックを行います。
  7. ブラウザ情報の収集とACS 3DSメソッドの実行

    • ACSは3DSメソッドURLを使用してブラウザに接続し、ブラウザ情報を収集します(3DSサーバーにより実現)。
  8. コールバック関数の呼び出し

    • 3DSメソッドが完了した場合またはスキップされた場合にACSが3DSリクエスターに通知できるよう、認証開始時に3DSリクエスターはeventCallBackUrlを送ります。
    • 3DSリクエスターは、通知を受け取るとブラウザに通知し、notify-3ds-events.htmlを開きcallbackFnを呼び出します。
  9. 認証の実行

    • callbackFn_on3DSMethodSkipped()または_on3DSMethodFinished()のいずれかであり、最終的にはいずれもdoAuth()を呼び出し、doAuth()は3DSリクエスターに認証を実行するように指示します。
  10. 認証の実行 (/brw)

    • 3DSリクエスターは/brwへの呼び出しを行い、/brwは認証処理を開始します。
  11. AReq/ARes

    • 認証要求は、ディレクトリサーバーを介して3DSサーバーからACSに送られます。認証応答と結果は、ACSから3DSサーバーに送られます。
  12. 認証結果を応答

    • /brwは、3DSリクエスターへの戻り値としてtranStatusを返します。
  13. 認証結果を応答

    • 認証結果をウェブアダプターに返します。

解説

返されたtransStatusが「Y」の場合はステップ14(F)に、「C」の場合はステップ14(C)に進んでください。

フリクションレスフローの場合

14(F). 結果の表示(フリクションレスの場合)

  • 認証結果として得られたtransStatusが「Y」の場合はauthSuccess()が呼び出され、authSuccess()はページを/auth/result?transIdにリダイレクトします。

15(F). 認証結果を要求 (認証結果を表示するページが必要な場合)

  • ブラウザは3DSリクエスターにtransIDを通知し、トランザクションの結果を要求可能な状態になります。

16(F). 認証結果を要求(/brw/result)

  • 3DSリクエスターは/brw/resultを呼び出し、3DSサーバーから認証結果の要求します。

17(F). ブラウザに結果を表示

  • 結果ページに認証結果が表示されます。

チャレンジフローの場合

14(C). iframe の設置(チャレンジの場合)。

  • 認証結果として得られたtransStatusが「C」にセットされている場合はstartChallenge()が呼び出され、startChallenge()はチャレンジ用のiframeを挿入します。

15(C). HTMLの交換

  • ACSはチャレンジ画面をiframeに埋め込み、カード会員は認証チャレンジを行います。

16(C). チャレンジ結果の判定

  • ACSは、実行されたチャレンジの合否を判定します。

17(C). RReq/Res

  • ACSはディレクトリサーバーを介して3DSサーバーに、認証結果を含む結果要求を送ります。要求を受け取った3DSサーバーは結果応答を返します。

18(C). チャレンジ応答(最終的なCRes)

  • ACSは3DSリクエスターに、最終的なチャレンジ応答を送ります。

19(C). 3DSチャレンジを終了して結果画面を表示

  • チャレンジが終了したため、3DSリクエスターはページを/auth/result?transIdにリダイレクトします。

20(C). 認証結果を要求(認証結果を表示するページが必要な場合)

  • ブラウザは3DSリクエスターにtransIdを通知し、トランザクションの結果を要求に使用可能な状態になります。

21(C). 認証結果を要求(/brw/result)

  • ステップ16aと同様に、3DSリクエスターは/brw/resultからの結果の受け取りを要求します。

22(C). ブラウザに結果を表示

  • ブラウザの結果画面に認証結果が表示されます。

3DSリクエスター実装ガイド

この項では、前提条件の項でダウンロードした簡単なショッピングサイトプロジェクトに3DS2フローを組み込めるように、上記のステップを実装する方法を説明します。この項の説明は、既存の決済処理に同じ機能を追加する場合にも適用できます。

このチュートリアルではIntellij IDEAを使用しています。

完成したコードについてさらに詳しく知りたい場合は、3DS2が組み込まれた最終的な加盟店決済ページを確認してください(GPaymentsサンプルコードパッケージ/finalCodeフォルダーにあります)。3DSリクエスターのデモコードの概要については、最終的なデモコードの項を参照してください。

解説

本書の目的は、Javaを使用して3DSリクエスターを実装するための一般的なフローを理解していただくことにあります。一般的なフローを理解して頂くことにより、異なるプログラミング言語で開発されている可能性のある加盟店の決済ページに3DSリクエスターを組み込めるようにいたします。

ステップ1:認証の開始

まず、認証を開始する(すなわち/brw/init/{messageCategory}を呼び出す)手順について説明します。

messageCategorypaまたはnpaのいずれかであり、それぞれ、決済認証または非決済認証を意味します。

ヒント

各APIのエンドポイントは認証APIの文書の参照先にリンクされており、APIの使用方法に関する詳細情報にアクセスできます。

GPaymentsサンプルコードパッケージには/sampleCodeというフォルダーがあります。読者のIDEで [ファイル]>[開く]>[参照] とクリックし、sampleCodeフォルダーを指定して [OK] をクリックし、このディレクトリを開きます。

open ide browse ide

また、認証機能の実装に役立つ3ds-web-adapter.jsも用意されています。

解凍したfinalCodeプロジェクトから3ds-web-adapter.jsをコピーし、sampleCodeプロジェクトの/resources/static/jsディレクトリに貼り付けます。必要に応じjsディレクトリを作成してください。このファイルの場所が分からない場合は、最終的なコードのディレクトリツリーを確認してください。

後述の強調表示になっている行を 追加 して3ds-web-adapter.jsファイルをcheckout.htmlページにインポートします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
....
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
        integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
        integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
        crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
        integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
        crossorigin="anonymous"></script>
<script src="/js/3ds-web-adapter.js"></script>
.....

アダプターを使用するようにcheckout.htmlを編集します。checkout.htmlcheckout()関数を見てください。次に示すコードのようになっているはずです。この関数は、JQueryを使用してHTML文書からカード会員入力フィールドの値を取得し、新しい変数transIdcardHolderInfo、およびpurchaseInfoを初期化します。

 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
34
35
36
37
38
39
40
41
42
//checkout.html 
function checkout() {
    var transId = $('#transId').val();

    // NOTE: Some attributes are set to default values for demo purpose
    var cardHolderInfo = {
        billAddrLine1: $('#billAddrLine1').val(),
        billAddrLine2: $('#billAddrLine2').val(),
        billAddrLine3: 'abc Line 3',
        billAddrCity: $('#billAddrCity').val(),
        billAddrState: $('#billAddrState').val(),
        billAddrPostCode: $('#billAddrPostCode').val(),
        billAddrCountry: '036',
        shipAddrLine1: $('#shipAddrLine1').val(),
        shipAddrLine2: $('#shipAddrLine2').val(),
        shipAddrLine3: 'abc Line 3',
        shipAddrCity: $('#shipAddrCity').val(),
        shipAddrState: $('#shipAddrState').val(),
        shipAddrPostCode: $('#shipAddrPostCode').val(),
        shipAddrCountry: '036',
        mobilePhone: {cc: '61', subscriber: $('#phoneNumber').val()},
        homePhone: {cc: '61', subscriber: '90123917131'},
        workPhone: {cc: '61', subscriber: '89102183192'},
        email: $('#email').val(),
        cardholderName: $('#cardholderName').val()
    };

    var purchaseInfo = {
        acctNumber: $('#cardNumber').val(),
        purchaseAmount: '100',
        purchaseCurrency: '840', //USD
        expiryDate: $('#cardExpiry').val()
    };

    //remove cardholder information class, checkout button and show spinner effect
    $("#checkoutButton").remove();

    $("#cardholderInfoCard").remove();
    $("#checkoutCard").removeClass("d-none");

    setTimeout(function () { window.location.href = "/auth/result"}, 2000); 
}

checkout()の中の強調表示になっている行をcheckout.htmlに追加し、threeDSAuth()関数を呼び出します(ステップ1)。これで認証処理が開始されます。

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

    //move to result screen 2 seconds after
    setTimeout(function () { window.location.href = "/auth/result"}, 2000);
    threeDSAuth(transId, cardHolderInfo, purchaseInfo);
}
次に示す強調表示になっている行を コメントアウトするか削除 します。この行は、デモのために2秒の遅延後結果画面に移動する目的でのみ記述されたものです。
1
2
3
4
5
6
7
8
function checkout() {
    ...
    $("#cardholderInfoCard").remove();
    $("#checkoutCard").removeClass("d-none");

    //setTimeout(function () { window.location.href = "/auth/result"}, 2000);
    threeDSAuth(transId, cardHolderInfo, purchaseInfo);
}

ポイント

次に示すコードは、3ds-web-adapter.jsで定義されているthreeDSAuth()関数です。threeDSAuth()はページからオブジェクトをJSONフォーマットで/auth/init(6行目)に送信しています。たとえば、threeDSRequestorTransIDがページからの値transIdに初期化されています。

 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
            // you can pass on more information if you need to e.g. 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()は必要に応じ変更でき、cardholderInfopurchaseInfotransIdの代わりに既存の加盟店決済ページから必要とするオブジェクトを送ることができます。転送可能なデータエレメントの型は、最終的なコードの/dto/activeserverディレクトリにあるInitAuthRequestBRWに示されています。また、APIの文書でも確認できます。

3ds-web-adapter.js/auth/initにPOST要求を行います(ステップ2)。したがって、この要求を処理するための新しいコントローラークラスが必要です。

/java/sample_requestorディレクトリに新しいコントローラークラスAuthController.javaを作成します。

次に示すコードをAuthController.java内に 追加 します。このコードには/auth/init要求に対応するハンドラ関数initAuth()が含まれています。

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.gpayments.requestor.sample_requestor;

import com.gpayments.requestor.sample_requestor.dto.activeserver.*;
import com.gpayments.requestor.sample_requestor.transaction.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@RestController
@RequestMapping("/")
public class AuthController {

    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
    // RestTemplate class for making API request
    private final RestTemplate restTemplate;
    private final TransactionManager transMgr;

    // ActiveServer entry points for authentication
    public static final String THREE_DS_SERVER_URL = "THREE_DS_SERVER_URL";
    private final String THREE_DS_REQUESTOR_URL = "THREE_DS_REQUESTOR_URL";

     @Autowired
     public AuthController(RestTemplate restTemplate, TransactionManager transMgr) {
        this.restTemplate = restTemplate;
        this.transMgr = transMgr;
     }

   /**
    * Handler method for /auth/init, it initialises authentication through /brw/init/{}
    *
    * @param request
    * @return
    */
   @PostMapping("/auth/init")
   public InitAuthResponseBRW initAuth(@RequestBody InitAuthRequestBRW request) {
       // find transaction with transaction id
       MerchantTransaction transactionInfo = transMgr.findTransaction(request.getThreeDSRequestorTransID());
       // fill the request with default data
       fillInitAuthRequestBRW(request, THREE_DS_REQUESTOR_URL + "/3ds-notify");

       logger.info("initAuthRequest {}", request);
       String initBrwUrl = THREE_DS_SERVER_URL + "/brw/init/{messageCategory}";
       // Initialise authentication by making  POST request to /brw/init/{messageCategory} (Step. 3)
       InitAuthResponseBRW initAuthResponseBRW = restTemplate.postForObject(initBrwUrl,
               request, InitAuthResponseBRW.class);

       logger.info("initAuthResponseBRW {}", initAuthResponseBRW);

       // set InitAuthResponseBRW for future use
       transactionInfo.setInitAuthResponseBRW(initAuthResponseBRW);
       return initAuthResponseBRW;
   }
}
決済認証を開始する場合は{messageCategory}paに、非決済認証を開始する場合は{messageCategory}npa置換 します。下の例ではpaを使用しています。
1
2
3
4
5
6
7
//AuthController.java
String initBrwUrl = THREE_DS_SERVER_URL + "/brw/init/pa";
// Initialise authentication by making  POST request to /brw/init/{messageCategory} (Step. 3)
InitAuthResponseBRW initAuthResponseBRW = restTemplate.postForObject(initBrwUrl, 
    request, InitAuthResponseBRW.class);

....
文字列THREE_DS_SERVER_URLを、デフォルト設定で提供されているURLに 置換 します。本番環境の場合はユーザー自身の3DSサーバーのURLになります。
1
2
3
//AuthController.java
private final String THREE_DS_SERVER_URL = "https://uat.testlab.activeserver.cloud/api/v1/auth";
....
文字列THREE_DS_REQUESTOR_URLを、デフォルト設定で提供されているURLに置換するか、ユーザー自身がホストする3DSリクエスターのURLに変更します。
1
2
3
//AuthController.java
private final String THREE_DS_REQUESTOR_URL = "http://localhost:8082";
....
activeserverフォルダーを コピー して、/java/sample_requestor/dtoディレクトリに貼り付けます。このフォルダーには、API呼び出しを行うのに必要なすべてのクラスが含まれています。認証を開始する上で最も重要なのが、以下のクラスです。

AuthController.javaに以下の関数を 追加 します。この関数は、InitAuthRequestBRWにデモ用のデフォルトデータをセットします。ただし本番環境の場合は、加盟店のデータベースからのデータ、またはcheckout.htmlから送られたカード会員情報からのデータをロードするように、この関数を書き直してください。

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//AuthController.java
 /**
 * This method is to fill in the InitAuthRequestBRW with demo data, you need to fill the information from your database
 * @param initAuthRequestBRW
 * @param eventCallBackUrl
 */
private void fillInitAuthRequestBRW(InitAuthRequestBRW initAuthRequestBRW, String eventCallBackUrl) {

    initAuthRequestBRW.setAcctID("personal account");

    // Fill AcctInfo with default data.
    AcctInfo acctInfo = new AcctInfo();
    acctInfo.setChAccAgeInd("03");
    acctInfo.setChAccChange("20160712");
    acctInfo.setChAccChangeInd("04");
    acctInfo.setChAccDate("20140328");
    acctInfo.setChAccPwChange("20170328");
    acctInfo.setChAccPwChangeInd("02");
    acctInfo.setNbPurchaseAccount("11");
    acctInfo.setPaymentAccAge("20160917");
    acctInfo.setPaymentAccInd("02");
    acctInfo.setProvisionAttemptsDay("3");
    acctInfo.setShipAddressUsage("20160714");
    acctInfo.setShipAddressUsageInd("02");
    acctInfo.setShipNameIndicator("02");
    acctInfo.setSuspiciousAccActivity("02");
    acctInfo.setTxnActivityDay("1");
    acctInfo.setTxnActivityYear("21");
    initAuthRequestBRW.setAcctInfo(acctInfo);

    initAuthRequestBRW.setAcctType("03");
    initAuthRequestBRW.setAuthenticationInd("01");//01 = Payment transaction

    // fills ThreeDSRequestorAuthenticationInfo
    ThreeDSRequestorAuthenticationInfo threeDSRequestorAuthenticationInfo = new ThreeDSRequestorAuthenticationInfo();
    threeDSRequestorAuthenticationInfo.setThreeDSReqAuthData("login GP");
    threeDSRequestorAuthenticationInfo.setThreeDSReqAuthMethod("02");
    threeDSRequestorAuthenticationInfo.setThreeDSReqAuthTimestamp("201711071307");
    initAuthRequestBRW.setAuthenticationInfo(threeDSRequestorAuthenticationInfo);

    // fills MerchantRiskIndicator, optional but strongly recommended for the accuracy of risk based authentication
    MerchantRiskIndicator merchantRiskIndicator = new MerchantRiskIndicator();
    merchantRiskIndicator.setDeliveryEmailAddress("test@123.com");
    merchantRiskIndicator.setDeliveryTimeframe("02");
    merchantRiskIndicator.setGiftCardAmount("123");
    merchantRiskIndicator.setGiftCardCount("02");
    merchantRiskIndicator.setGiftCardCurr("840");
    merchantRiskIndicator.setPreOrderDate("20170519");
    merchantRiskIndicator.setPreOrderPurchaseInd("02");
    merchantRiskIndicator.setReorderItemsInd("01");
    merchantRiskIndicator.setShipIndicator("01");

    initAuthRequestBRW.setMerchantRiskIndicator(merchantRiskIndicator);

    /**
     * Options for threeDSRequestorChallengeInd - Indicates whether a challenge is requested for this transaction.
     * Values accepted:
     *  01 = No preference
     *  02 = No challenge requested
     *  03 = Challenge requested: 3DS Requestor Preference
     *  04 = Challenge requested: Mandate
     *  05–79 = Reserved for EMVCo future use (values invalid until defined by EMVCo)
     *  80-99 = Reserved for DS use
     */
    initAuthRequestBRW.setChallengeInd("01");
    initAuthRequestBRW.setEventCallbackUrl(eventCallBackUrl); //Set this to your url
    initAuthRequestBRW.setMerchantID("123456789012345");
    initAuthRequestBRW.setPurchaseDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
    initAuthRequestBRW.setPurchaseInstalData("24");
    initAuthRequestBRW.setRecurringExpiry("20180131");
    initAuthRequestBRW.setRecurringFrequency("6");
    initAuthRequestBRW.setTransType("03");
}

MerchantTransaction.javaおよび対応するGetメソッドとSetメソッドに、新しい変数initAuthResponseBRW追加 します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//MerchantTransaction.java
import com.gpayments.requestor.sample_requestor.dto.activeserver.InitAuthResponseBRW;

public class MerchantTransaction {

    private InitAuthResponseBRW initAuthResponseBRW;

    ...

    //getter 
    public InitAuthResponseBRW getInitAuthResponseBRW() {
        return initAuthResponseBRW;
    }

    //setter
    public void setInitAuthResponseBRW(InitAuthResponseBRW initAuthResponseBRW) {
        this.initAuthResponseBRW = initAuthResponseBRW;
    }
ActiveServerへのAPI呼び出しを行うには、クライアント証明書利用したをRESTTemplateが必要があります。

GPaymentsからユーザーに提供されるクライアント証明書(.p12ファイル)を コピー し、/resources/certsディレクトリに貼り付けます。/certsディレクトリがない場合は作成してください。証明書ファイルを受け取っていない場合は、GPaymentsにお問い合わせください。

最終的なコードの中にある証明書ファイルcacerts.jksコピー し、/resources/certsディレクトリに貼り付けます。証明書が必要な理由は、相互SSL認証を行うためにユーザーの3DSリクエスターと3DSサーバーの間の相互認証が必要だからです。

/java/sample_requestor/configディレクトリ内に構成クラスRestClientConfig.javaを作成します。configディレクトリがない場合は作成してください。

次に示すコードを コピー し、RestClientConfig.javaに貼り付けます。

 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
34
35
36
37
//RestClientConfig.java
package com.gpayments.requestor.sample_requestor.config;

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;

@Configuration
public class RestClientConfig {
    private final String CERTIFICATE_PASSWORD = "CERTIFICATE_PASSWORD";

    @Bean
    public RestTemplate restTemplate() throws Exception {
        SSLContext sslContext = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getURL("classpath:certs/client_certificate.p12"), CERTIFICATE_PASSWORD.toCharArray(), CERTIFICATE_PASSWORD.toCharArray())
                .loadTrustMaterial(ResourceUtils.getURL("classpath:certs/cacerts.jks"), CERTIFICATE_PASSWORD.toCharArray())
                .build();

        CloseableHttpClient client = HttpClients.custom()
                .setSSLContext(sslContext)
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .build();
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);

        return new RestTemplate(httpRequestFactory);
    }
}
CERTIFICATE_PASSWORDを、デフォルト設定で提供されるパスワードに 置換 します。本番環境では、ユーザーの証明書が含まれているキーストア用のパスワードになります。

1
2
//RestClientConfig.java
private String CERTIFICATE_PASSWORD = "123456";

解説

上記の方法はJava言語でのクライアント証明書を使用してユーザーの3DSリクエスターと3DSサーバーが相互認証する方法ですが、サーバ側のコードを別の言語で実装される場合には、類似の手順が必要です。

SSLContextBuilderクラスに必要なApache HttpClientを取得するため、以下の依存関係をpom.xml追加 します。

1
2
3
4
5
6
7
8
<dependencies>
    ...
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.6</version>
    </dependency>   
</dependencies>
必要なすべてのクラスがインポートされており、エラーがないことを確認してください。

もう一度アプリを 実行 し、http://localhost:8082からアクセスして決済処理を行います。 /api/v1/auth/brw/init/{messageCategory}に対し成功応答が得られ、Intellijのログをチェックすると下記のような有効なthreeDSServerCallbackUrlを取得出来ているはずです。

1
2
3
4
5
6
7
8
9
{
    "errorCode":"null",
    "errorComponent":"null",
    "errorDescription":"null",
    "errorDetail":"null",
    "errorMessageType":"null",
    "threeDSServerCallbackUrl":"https://uat-callback.testlab.activeserver.cloud/api/v1/auth/brw/callback?transId=ade87add-b50c-42da-bbea-cdcbd85dcbf3",
    "threeDSServerTransID":"ade87add-b50c-42da-bbea-cdcbd85dcbf3"
}

決済処理は3ds-notifyの段階で停止しますが、これは3ds-notifyがまだ実装されていないためであり、この実装方法については次の項で説明します。

ヒント

コード変更を確定するにはプロジェクトを再実行する必要があります。CTRL+Cを押して現在実行中のアプリを終了し、次に示すコマンド行を入力してプロジェクトを再実行します。

1
mvn spring-boot:run

3DSサーバーはコールバックURLをACSは3DSメソッドを使用して、リスクベース認証に必要なブラウザ情報を収集します。

ポイント

/auth/initからの成功応答が得られた後(ステップ5)3ds-web-adapter.jsは非表示のiframeを決済ページに挿入しますので(ステップ6) 、ACSと3DSサーバーはthreeDSServerCallbackUrlを使用してブラウザ情報を収集できます(ステップ7))。

1
2
3
4
5
//3ds-web-adapter.js
success: function(data) {
    $('<iframe id="threeds-container" width="0" height="0" style="visibility: hidden;" src="'+ data.threeDSServerCallbackUrl+'"></iframe>')
    .appendTo('.challengeContainer');
},

解説

シーケンス図のステップ17は、このステップが終了した際には実装されています。

ステップ2: 認証の実行

前のステップで、3ds-web-adapter.jsthreeDSServerCallbackUrlにセットされたコールバックsrcを使用してiframeを挿入しています。これにより、3DSサーバーはブラウザへのコールバックを行って(ステップ7)必要なブラウザ情報を収集できます。

また、eventCallBackUrlTHREE_DS_REQUESTOR_URL/3ds-notifyにセットして /brw/init/{messageCategory} を呼び出しています(第2行)。このURLは3DSサーバーが3DSメソッドが完了またはスキップされた場合に3DSリクエスターに通知するエンドポイントになります。

1
2
3
//AuthController.java
fillInitAuthRequestBRW(request, THREE_DS_REQUESTOR_URL + "/3ds-notify");
.....

ですので、3DSサーバーからのAPIコールを受け取るハンドラ関数が必要になります。

次に示すコードをMainController.java追加 します。このハンドラ関数はパラメーターtransIdcallbackTypeを受け付けます。オプションパラメーターcallbackType3DSMethodFinishedまたは3DSMethodSkippedのいずれかです。さらに、ハンドラ関数は、String型notify_3ds_eventsを返します。Springbootに詳しくない方のために参考としてですが、これは/resources/templatesディレクトリから名前が一致するHTMLページを返すことを意味します。

 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
// MainController.java
@PostMapping("/3ds-notify")
public String notifyResult(
        @RequestParam("requestorTransId") String transId,
        @RequestParam("event") String callbackType,
        @RequestParam(name = "param", required = false) String param,
        Model model) {

    String callbackName;
    // check the callbackType and initialise callbackName
    if ("3DSMethodFinished".equals(callbackType)) {

        callbackName = "_on3DSMethodFinished";

    } else if ("3DSMethodSkipped".equals(callbackType)) {

        callbackName = "_on3DSMethodSkipped";

    } else {
        throw new IllegalArgumentException("invalid callback type");
    }

    //Pass on the object to the page
    model.addAttribute("transId", transId);
    model.addAttribute("callbackName", callbackName);
    model.addAttribute("callbackParam", param);

    return "notify_3ds_events";
}

/3ds-notifyへの呼び出しにより返されるページnotify_3ds_events.htmlという新しいHTMLファイルを、/resources/templatesディレクトリ内に 作成 します。このページのjavascriptによりcallbackFn()が呼び出すたされます。

 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>

//notify parent checkout page to proceed with rest procedures.

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

//callbackFn is defined in 3ds-notify handler method
if (typeof callbackFn === 'function') {
    callbackFn($('#transId').val(),$('#param').val());
}

</script>

</body>
</html>

3DSサーバーから与えられるcallbackNameによって、3ds-web-adapter.js内で呼び出すメソッドが異なることが分かります(ステップ8)。各メソッドについて、以下に説明します。

  • _onThreeDSMethodFinished - 3DSメソッドが完了したことを通知し、_doAuth()を呼び出します。
  • _onThreeDSMethodSkipped - 3DSメソッドがスキップされたことを通知し、_doAuth()を呼び出します。

3ds-web-adapter内の_doAuth()は、/authへのPOST要求を行い、認証を実行します(ステップ9)

この要求を処理する新しいハンドラ関数を、AuthController.java追加 します。このメソッドは、threeDSRequestorTransIDthreeDSServerTransIDを使用して/brw へのPOST API要求を行わなければなりません(ステップ10)。3DSサーバーはAReqを作成して送信し3DS処理を開始しAResを受信し処理します(ステップ. 11)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//AuthController.java
@PostMapping("/auth")
public AuthResponseBRW auth(@RequestParam("id") String transId) {

    MerchantTransaction transaction = transMgr.findTransaction(transId);

    //create authentication request.
    AuthRequestBRW authRequest = new AuthRequestBRW();
    authRequest.setThreeDSRequestorTransID(transaction.getId());
    authRequest.setThreeDSServerTransID(transaction.getInitAuthResponseBRW().getThreeDSServerTransID());

    String brwUrl = THREE_DS_SERVER_URL + "/brw";
    AuthResponseBRW response = restTemplate.postForObject(brwUrl, authRequest, AuthResponseBRW.class);

    logger.info("authResponseBRW {}", response);

    return response;
}
アプリを 再実行 し、デフォルト値を使用して決済処理を行います。/api/v1/auth/brw に対する成功応答が得られ、transStatusが「Y」にセットされていなければなりません(ステップ12)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
    "acsChallengeMandated":"null",
    "authenticationType":"null",
    "authenticationValue":"YXV0aCB2YWx1ZSB4eHh4eHh4eHg=",
    "cardholderInfo":"null",
    "challengeUrl":"null",
    "eci":"06",
    "errorCode":"null",
    "errorComponent":"null",
    "errorDescription":"null",
    "errorDetail":"null",
    "errorMessageType":"null",
    "threeDSServerTransID":"52056514-7504-40c7-886f-2a0452d8edbd",
    "transStatus":"Y",
    "transStatusReason":"null"
}
チャレンジシナリオをテストしたい場合は、/resources/test_scenario.txtの中のチャレンジシナリオカード範囲を使用し、次に示すスクリーンショットで赤枠になで囲まれているフィールドのカード番号を変更します。

challenge card number

有効なchallengeUrlがセットされており、transStatusは「C」にセットされてます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
    "acsChallengeMandated":"Y",
    "authenticationType":"01",
    "authenticationValue":"null",
    "cardholderInfo":"null",
    "challengeUrl":"https://uat-callback.testlab.activeserver.cloud/api/v1/auth/brw/challenge/init?txid=c8a32fb7-9556-4242-896b-562ee8ca25df",
    "eci":"null",
    "errorCode":"null",
    "errorComponent":"null",
    "errorDescription":"null",
    "errorDetail":"null",
    "errorMessageType":"null",
    "threeDSServerTransID":"c8a32fb7-9556-4242-896b-562ee8ca25df",
    "transStatus":"C",
    "transStatusReason":"null"
}

/resources/test_scenario.txtにはチャレンジウィンドウで要求されるワンタイムパスコードも示されていますが、callbackType AuthResultReadyの処理機能がまだ実装されていないためエラーになります。この機能は、次のステップで実装します。

ポイント

transStatusが「C」である場合、3ds-web-adapter.jsstartChallenge(url)を呼び出し、startChallenge(url)は、srcchallengeUrlにセットされた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');

}
iframeのクラスはh-100w-100にセットされていることが分かります。これは、height: 100%!importantwidth: 100%!importantのcssスタイルを実装するためのブートストラップデフォルトクラスです。この設定が必要なのは、ACSから提供されるコンテンツに応じてiframeのサイズを変更しなければならないためです。チャレンジ画面は次に示すスクリーンショットのようになるはずです。

result screen

解説

/resources/test_scenario.txtはデモのためにのみ示すものであり、本番環境では、ACSは、提供されたカード番号でなく取得したカード会員情報に基づいてリスクベースの認証を実行します。同様に、認証の方法(ワンタイムパスワード、生体認証等)は、カード会員のイシュアーにより決定され実装されます。

ステップ3: 認証結果の取得

前のステップで説明しましたように、認証結果をカード会員に表示するためには、認証結果を要求する必要があります。フリクションレスフローでもこれが必要になります(次の警告欄を参照)。

なぜ認証結果更新が必要なのか?

フリクションレスフローの場合は、ステップ12ですでに認証結果が得られているのになぜもう一度結果を要求する必要があるのか、疑問に思われるかもしれません。これは、ステップ13で認証結果がページに転送されるためです。3ds-web-adapterが結果を3DSリクエスターに送り返すことも可能ですが、データが安全でないと見なされます。サーバー側では常に、元の情報源である3DSサーバーから戻される結果をサーバー自身の機能により取得できなければなりません。このステップで結果の受信を要求する必要があるのは、このためです。

チャレンジフローの場合は、3DSメソッドのステータス通知と同様に3ds-notifyのエンドポイントを使用して3DSサーバーから認証結果が通知されます。

次に強調表示で示されているように、「AuthResultReady」というcallbackTypeを処理するための新しいelse ifステートメントをMainController.javaに追加します。

1
2
3
4
5
6
7
8
//MainController.java
if ("3DSMethodFinished".equals(callbackType)) {
    callbackName = "_on3DSMethodFinished";
} else if ("3DSMethodSkipped".equals(callbackType)) {
    callbackName = "_on3DSMethodSkipped";
} else if ("AuthResultReady".equals(callbackType)) {
    callbackName = "_onAuthResult";
}

_onAuthResultReadynotify-3ds-eventsから呼び出され、ウィンドウを/auth/result?txid=+transIdにリダイレクトします。認証結果が必要な場合は、ハンドラー/auth/result/brw/resultを呼び出して結果の要求を行うことができます。

強調表示になっているコードをMainController.java追加 します。

 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
34
35
36
37
38
39
40
41
42
43
44
45
import org.springframework.web.client.RestTemplate;
import com.gpayments.requestor.sample_requestor.dto.activeserver.AuthResponseBRW;
import com.gpayments.requestor.sample_requestor.transaction.MerchantTransaction;
//MainController.java
public class MainController {
    private static final Logger logger = LoggerFactory.getLogger(MainController.class);
    private final TransactionManager transMgr;
    private final Shop shop;
    private final Cart cart;
    private final RestTemplate restTemplate;

    @Autowired
    public MainController(
            TransactionManager threeDSRequestorTransactionManager,
            Shop shop,
            Cart cart,
            RestTemplate restTemplate) {
        this.transMgr = threeDSRequestorTransactionManager;
        this.shop = shop;
        this.cart = cart;
        this.restTemplate = restTemplate;
    }

    ...

    @GetMapping("/auth/result")
    public String result(
            @RequestParam("txid") String transId,
            Model model) {

        MerchantTransaction transaction = transMgr.findTransaction(transId);
        String resultUrl = AuthController.THREE_DS_SERVER_URL + "/brw/result?threeDSServerTransID=" +
                        transaction.getInitAuthResponseBRW().getThreeDSServerTransID();
        AuthResponseBRW response = restTemplate.getForObject(resultUrl, AuthResponseBRW.class);

        //convey result to the page.
        model.addAttribute("result", response);
        //make a copy of cart just for displaying
        model.addAttribute("cart", new Cart(cart));
        // clear the actual cart content
        cart.clearItem("all");

        return "result";
    }
}
result.htmlで強調表示になっているコードは、削除するかコメントアウト してください。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<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">Thank you for your purchase!</h4>-->
            <!--<p class="card-text">Want to continue shopping? Press "Back to Home".</p>-->

            <a href="/" class="btn btn-primary btn-block">Back to Home</a>

        </div>
    </div>
</div>
削除したコードの代わりに強調表示になっている以下のコードを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>

成功

ご苦労様でした。これで、ActiveServerを組み込んだ加盟店決済ページが動作する状態を実現できました。もう一度アプリを実行してみてください。成功したことを示す結果ページが開き、トランザクションステータス、ECI、CAVVが表示されるはずです。

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

ヒント

この処理の後は通常、トランザクションを完了させるため、トランザクションステータス、ECI、CAVVを使用したオーソリを実行することになります。

最終的なデモコード

最終的なデモコードは、GPaymentsサンプルコードパッケージにある/finalCodeフォルダーからご確認いただけます。このコードは、ActiveServerでデフォルト設定のままで動作しますので、このガイドに従ってアプリを実行するだけです。

コード要約

3DSリクエスターデモコードは、以下のような複数のエンドポイントとコンポーネントを実装します。

  • /auth/init - このエンドポイントは3ds-web-adapter.jsから呼び出され、認証開始要求を受け付け、3DS2認証処理を開始します。3DSリクエスターは、ActiveServerの認証開始APIである/api/v1/auth/brw/init/{messageCategory}を呼び出します。

  • /auth - このエンドポイントは3ds-web-adapter.jsから呼び出され、認証実行要求を受け付け、ブラウザ情報の収集後に3DS2認証を実行し、オプションで3DSメソッドを実行します。3DSリクエスターは、ActiveServerの認証APIである/api/v1/auth/brwを呼び出します。

  • /3ds-notify - ActiveServerから3DSリクエスターに、3DSメソッド実行ステータス、認証結果、チャレンジ結果を通知できるようにします。

  • 3ds-web-adapter.js - 3DSリクエスター処理のスキャフォールディング用に作成された簡潔なJavaScriptのライブラリ。

  • Classes under package com.gpayments.requestor.sample_requestor.dto.activeserver - ActiveServer APIのDTO (データ転送オブジェクト)

加盟店ウェブサイトに組み込まれる場合、3DSリクエスターのデモコードは、加盟店サイトの決済ページに組み込むための3ds-web-adapter.jsを提供します。加盟店ウェブサイトは、3ds-web-adapter.jsthreeDSAuth()を呼び出してカード会員情報をセットし、認証処理を開始します。加盟店ウェブサイトは、3DSリクエスターが関連するコールバックURLと実行すべき3DSメソッドをセットできるようにするためのiframeを提供する必要があります。

認証処理が終わると、3DSリクエスターは、イベントコールバックURLである/3ds-notifyを使用して認証結果を通知します。この後、決済代行会社または加盟店はオーソリ処理に進むことができます。

ディレクトリツリー

次に示すのは最終的な加盟店決済サイトのディレクトリツリーです。チュートリアル終了時のsampleCodeは次のディレクトリのようになります。

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Final demo code directory tree
|- java.com.gpayments.requestor.sample_requestor 
|   |- config
|   |   |- RestClientConfig.java
|   |         
|   |- dto 
|   |   |- activeserver
|   |   |    |- AcctInfo.java
|   |   |    |- AuthRequestBRW.java
|   |   |    |- AuthResponseBRW.java
|   |   |    |- CardholderInformation.java
|   |   |    |- InitAuthRequestBRW.java
|   |   |    |- InitAuthResponseBRW.java
|   |   |    |- MerchantRiskIndicator.java
|   |   |    |- PhoneNumber.java
|   |   |    |- ThreeDSRequestorAuthenticationInfo.java
|   |   |  
|   |   |- CardholderInfo.java
|   |   |- Cart.java
|   |   |- Item.java
|   |   |- Shop.java
|   |   
|   |- transaction 
|   |     |- MerchantTransaction.java 
|   |     |- TransactionManager.java 
|   |   
|   |- AuthController.java   
|   |- MainController.java   
|   |- SampleRequestorApplication.java   
|      
|- resources   
    |-static
    |   |-css
    |   |   |-spinner.css
    |   |   |-style.css
    |   |   
    |   |-images
    |   |   |-apple.jpg
    |   |   |-banana.jpg
    |   |   |-pineapple.jpg
    |   |   |-visa-logo.jpg
    |   |   
    |   |-js 
    |   |   |-3ds-web-adapter.js    
    |       
    | -certs
    |   |- client_certificate.p12    
    |   |- cacerts.jks 
    |        
    |-templates
    |   |- checkout.html       
    |   |- index.html       
    |   |- notify_3ds_events.html       
    |   |- result.html       
    |  
    |-application.properties
    |-test_scenarios.txt

ライトボックスのチャレンジウィンドウ

チュートリアルではチャレンジ画面がインライン要素として横並びに表示されていましたが、ユーザーのウェブサイトのデザインによってはポップアップウィンドウ内にチャレンジウィンドウを表示さ せたい場合もあります。UI自体はACSにより提供されますので、ユーザーがUIの実装について心配する必要はありません。

checkout.html開き 、次に示すようなコード部分を見つけてください。これはチャレンジウィンドウを定義している部分です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 <!--checkout.html-->
<div class="card d-none" id="checkoutCard">
    <div class="challengeContainer border">
        <div class="spinner row h-100 justify-content-center align-items-center">
            <div class="col">
                <div class="sk-fading-circle">
                    <div class="sk-circle1 sk-circle"></div>
                    <div class="sk-circle2 sk-circle"></div>
                    <div class="sk-circle3 sk-circle"></div>
                    <div class="sk-circle4 sk-circle"></div>
                    <div class="sk-circle5 sk-circle"></div>
                    <div class="sk-circle6 sk-circle"></div>
                    <div class="sk-circle7 sk-circle"></div>
                    <div class="sk-circle8 sk-circle"></div>
                    <div class="sk-circle9 sk-circle"></div>
                    <div class="sk-circle10 sk-circle"></div>
                    <div class="sk-circle11 sk-circle"></div>
                    <div class="sk-circle12 sk-circle"></div>
                </div>    
                <div class="text-center"><img class="w-25" src="images/visa-logo.png"/></div>
            </div>
        </div>
    </div>
</div>
第2、3行をコメントアウト し、横並びのインラインチャレンジカードを無効にします。
 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
 <!--checkout.html-->
<!--<div class="card d-none" id="checkoutCard">-->
    <!--<div class="challengeContainer border">-->
        <div class="spinner row h-100 justify-content-center align-items-center">
            <div class="col">
                <div class="sk-fading-circle">
                    <div class="sk-circle1 sk-circle"></div>
                    <div class="sk-circle2 sk-circle"></div>
                    <div class="sk-circle3 sk-circle"></div>
                    <div class="sk-circle4 sk-circle"></div>
                    <div class="sk-circle5 sk-circle"></div>
                    <div class="sk-circle6 sk-circle"></div>
                    <div class="sk-circle7 sk-circle"></div>
                    <div class="sk-circle8 sk-circle"></div>
                    <div class="sk-circle9 sk-circle"></div>
                    <div class="sk-circle10 sk-circle"></div>
                    <div class="sk-circle11 sk-circle"></div>
                    <div class="sk-circle12 sk-circle"></div>
                </div>    
                <div class="text-center"><img class="w-25" src="images/visa-logo.png"/></div>
            </div>
        </div>
    </div>
</div>
<!--Cardholder information -->
<div class="card" id="cardholderInfoCard">
チャレンジウィンドウがポップアップウィンドウ内に表示されるよう、第4、5、6、7、8、30、31行 を追加します。
 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
<!--checkout.html-->
<!--<div class="card d-none" id="checkoutCard">-->
<!--<div class="challengeContainer border">-->
<div class="modal fade" id="authBox" data-backdrop="static" data-keyboard="false" tabindex="-1"
             role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog h-100 d-flex flex-column justify-content-center my-0" role="document">
        <div class="modal-content">
            <div class="modal-body challengeContainer">
                <div class="spinner row h-100 justify-content-center align-items-center">
                    <div class="col">
                        <div class="sk-fading-circle">
                            <div class="sk-circle1 sk-circle"></div>
                            <div class="sk-circle2 sk-circle"></div>
                            <div class="sk-circle3 sk-circle"></div>
                            <div class="sk-circle4 sk-circle"></div>
                            <div class="sk-circle5 sk-circle"></div>
                            <div class="sk-circle6 sk-circle"></div>
                            <div class="sk-circle7 sk-circle"></div>
                            <div class="sk-circle8 sk-circle"></div>
                            <div class="sk-circle9 sk-circle"></div>
                            <div class="sk-circle10 sk-circle"></div>
                            <div class="sk-circle11 sk-circle"></div>
                            <div class="sk-circle12 sk-circle"></div>
                        </div>    
                        <div class="text-center"><img class="w-25" src="images/visa-logo.png"/></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<!--Cardholder information -->
<div class="card" id="cardholderInfoCard">
次に、checkout.htmlの最後まで下にスクロールすると、checkout()関数の中に次のようなコードがあります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//checkout.html
function checkout() {
    ....
     var purchaseInfo = {
        acctNumber: $('#cardNumber').val(),
        purchaseAmount: '100',
        purchaseCurrency: '840', //USD
        expiryDate: $('#cardExpiry').val()
    };

    //remove cardholder information class, checkout button and show spinner effect
    $("#checkoutButton").remove();    

    $("#cardholderInfoCard").remove();
    $("#checkoutCard").removeClass("d-none");

    threeDSAuth(transId, cardHolderInfo, purchaseInfo);
}
カード会員情報クラスを削除してカードにスピナーエフェクトを表示している 第14、15行をコメントアウト します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//checkout.html
function checkout() {
    ....
     var purchaseInfo = {
        acctNumber: $('#cardNumber').val(),
        purchaseAmount: '100',
        purchaseCurrency: '840', //USD
        expiryDate: $('#cardExpiry').val()
    };

    //remove cardholder information class, checkout button and show spinner effect
    $("#checkoutButton").remove();

    //$("#cardholderInfoCard").remove();
    //$("#checkoutCard").removeClass("d-none");

    threeDSAuth(transId, cardHolderInfo, purchaseInfo);
}

コメントアウトした部分の代わりにポップアップチャレンジウィンドウ用のポップアップボックスを表示するよう、第17行を追加 します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//checkout.html
function checkout() {
    ....
     var purchaseInfo = {
        acctNumber: $('#cardNumber').val(),
        purchaseAmount: '100',
        purchaseCurrency: '840', //USD
        expiryDate: $('#cardExpiry').val()
    };

    //remove cardholder information class, checkout button and show spinner effect
    $("#checkoutButton").remove();

    //$("#cardholderInfoCard").remove();
    //$("#checkoutCard").removeClass("d-none");

    $('#authBox').modal();

    threeDSAuth(transId, cardHolderInfo, purchaseInfo);
}

作成したコードを 再実行 すると、チャレンジ画面がポップアップウィンドウになったことが分かります。カード番号は、/resources/test_scenario.txt内で使用可能なチャレンジシナリオカードの範囲に変更してください。

challenge light box

デモコードのデフォルト設定

最終的なデモコードで設定されているデフォルト値を以下に示します。順を追ったチュートリアルの中で、この項を参照することになるかもしれません。

サポート

本書をお読みになった後に質問事項がある場合はサポートさせていただきますので、techsupport@gpayments.comにて弊社あてにEメールをお送りください。