Skip to content

3DSリクエスター

ActiveSDKを実装するには、加盟店サイトはバックエンドに3DS Requestorを実装する必要があります。以下の図はActiveSDK、3DS Requestor、およびその他のコンポーネントとの関係性を表します。EMVCo 3D Secure 2仕様書に基づき、加盟店アプリと3DS Server間の通信は相互認証される必要があります。そのため、バックエンドには3DS Serverと相互TLSコネクションを確立するための3DS Requestorを実装する必要があります。

sdk-integration-architecture.png

このドキュメントでは、ActiveSDKを弊社の3DS Requestorサンプル・コードと統合するプロセスについて説明します。このデモでは、3DS Requestorが接続する3DS ServerはActiveServerになります。ただし、3DS Requestorのサンプル・コードを修正すればどの3DS Serverにも接続することができます。

以下はこのガイドを使用するための前提条件です:

  • このいずれに関する知識: Java、PHP、C#、もしくはGo

サンプル・コードをチェックアウトして実行する

3DS Requestorサンプル・コードはダウンロード・ページよりダウンロードできます。クライアント証明書の取得および3DSリクエスターデモの設定の手順を参照して、3DS Requestorを実行してから次のステップへ進んでください。

注釈

3DS Requestorサンプル・コードを実行するには、アクティブ化されて稼働中のActiveServerインハウスのインスタンス(もしくはActiveServer SaaSのテナント)が必要になります。詳しくはGPayments社へご連絡ください。

3DS Requestorと3DS Server間の実装

3DS RequestorはモバイルアプリからAuthRequestを受信し、そのリクエストを3DS Serverへ送信します。また、3DS Requestorは3DS ServerからAuthResponseを受信し、その結果をモバイルアプリへ転送します。

3DS Requestorのサンプル・コードはバックエンドの実装方法を以下のサーバー側言語で提示します:

  • Java: Java版はSpringbootのフレームワークを基に実現されています。Springbootに関する詳細については、https://spring.io/projects/spring-bootを参照してください。
  • C#: C#版はASP.netを基に実現されています。
  • PHP: PHP版はPHP 7.2とcURL (Client URL Library)を基に実現されています。
  • Go: Go版はGo 1.12とGo Module対応を基に実現されています。全てのディペンデンシーはgo.modファイルにリストアップされています。

3DS Serverと認証処理を開始する前に、3DS Requestorは3DS Serverと相互TLS認証コネクションを確立する必要があります。クライアント証明書の取得および3DSリクエスターデモの設定の手順を確認してください。

HTTPクライアントのTLS設定

  • Java: TLS設定とクライアント証明書のロードはRestClientConfig.javaクラスから参照できます。
  • C#: TLS設定とクライアント証明書のロードはRestClientHelper.csクラスから参照できます。
  • PHP: TLS設定とクライアント証明書のロードはRestClientConfig.phpクラスから参照できます。
  • Go: TLS設定とクライアント証明書のロードはhttps.goクラスから参照できます。

認証を実行する

認証を実行するには、3DS Requestorは以下のタスクのために/v2/auth/appエンドポイントを実装する必要があります:

  • モバイルアプリからAuthRequestを受信する。
  • そのリクエストを3DS Serverに転送する。
  • 3DS ServerからAuthResponseを受信する。
  • レスポンスのデータをモバイルアプリへ返却する。
//AuthControllerV2.java
@PostMapping("/v2/auth/app")
@ResponseBody
public Message app(@RequestParam(value = "trans-type", required = false) String transType,
    @RequestBody Message request) {
  return authServiceV2.app(transType, request);
}

//AuthServiceV2.java
public Message app(String transType, Message request) {

  //generate requestor trans ID
  String transId = UUID.randomUUID().toString();
  request.put(THREE_DS_REQUESTOR_TRANS_ID, transId);

  String appAuthUrl = config.getAsAuthUrl() + "/api/v2/auth/app";

  //Add parameter trans-type=prod in the appAuthUrl to use prod DS, otherwise use TestLabs DS
  //For more details, refer to: https://docs.activeserver.cloud
  if ("prod".equals(transType)) {
    appAuthUrl = appAuthUrl + "?trans-type=prod";
  }

  logger.info("appAuthRequest on url: {}, body: \n{}", appAuthUrl, request);

  Message response =
      sendRequest(appAuthUrl, request, HttpMethod.POST);
  logger.info("appAuthResponse: \n{}", response);
  return response;
}
//AuthV2Controller.cs
[HttpPost, Route("v2/auth/app")]
public Message app([FromBody] Message request, [FromUri(Name = "trans-type")] string transType = null)
{
    return authServiceV2.app(transType, request);
}

//AuthServiceV2.cs
public Message app(String transType, Message request)
{
    //generate requestor trans ID
    String transId = Guid.NewGuid().ToString();
    request[THREE_DS_REQUESTOR_TRANS_ID] = transId;

    String appAuthUrl = Config.AsAuthUrl + "/api/v2/auth/app";

    //Add parameter trans-type=prod in the appAuthUrl to use prod DS, otherwise use Testlabs DS
    //For more details, refer to: https://docs.activeserver.cloud
    if ("prod".Equals(transType))
        appAuthUrl = appAuthUrl + "?trans-type=prod";

    logger.Info(string.Format("appAuthRequest on url: {0}, body: \n{1}", appAuthUrl, request));

    Message response = (Message)RestClientHelper.PostForObject(appAuthUrl, request, typeof(Message));
    logger.Info(string.Format("appAuthResponse: \n{0}", response));
    return response;
}
<?php
//AuthControllerV2.php
public function app()
{
    $requestData = Utils::_getJsonData();
    $authAppUrl = "/api/v2/auth/app";

    $transType = $_GET["trans-type"];

    if (!empty($transType) && $transType == "prod") {
        $authAppUrl = $authAppUrl . "?trans-type=prod";
    }

    $response = $this->restTemplate->post($authAppUrl, $requestData);
    Utils::_returnJson($response->getBody()->getContents());
}
//api-v2.go
//AuthControllerV2 routers v2
r.POST("/v2/auth/app", func(c *gin.Context) {
  var message Message
  err := c.ShouldBindJSON(&message)
  if err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  //adding requestorTransId
  message[ThreeDSRequestorTransId] = uuid.New()

  callASAPI(asSession{
    message:    message,
    url:        appendTransTypeIfNecessary("/api/v2/auth/app", c),
    context:    c,
    httpClient: httpClient,
    config:     config})

})

注目

このサンプル・コードは、AuthRequestActivesServerへ送信するためのものです。ActiveServerのAuthRequestのデータ構造を確認するには、APIドキュメントを参照してください。他社の3DS ServerへAuthRequestを送信する際は該当製品のAPIドキュメントを参照してください。

ActiveServerを使用するお客様へ

デフォルトでは、上記URLはテスト目的でAuthRequestをGPayments TestLabsへ送信します。本番環境に移行する際にAPIリクエストを各国際ブランドのDirectory Serverへ送信するには、trans-typeクエリー・パラメーターをそのAPI URLに追加する必要があります。使用方法については、APIの説明を参照してください。

Master Auth API クライアント証明書を使用して、加盟店の代わりにBusiness Adminユーザーを認証する場合、バックエンドはHTTPリクエストにAS-Merchant-Tokenフィールドを含んだHTTPヘッダーを追加する必要があります。なお、これを加盟店プロフィールで設定されたmerchantTokenへ設定する必要があります。実装方法の詳細については、こちらを参照してください。

認証結果を取得する

フリクションレス・フローの場合、認証結果は認証実行処理が終了する際にモバイルアプリへ返却されます。

チャレンジ・フローの場合、認証実行処理が終了する際はモバイルアプリへtransStatus=C が返却され、ActiveSDKとACSの間にチャレンジ処理が実行されます。チャレンジ処理が終了する際、モバイルアプリは/v2/auth/result エンドポイントを呼び出し、認証結果を取得します。3DS Requestorはその結果を取得し、モバイルアプリへ返却するためには、ActiveServerへリクエストを送信する必要があります。

//AuthControllerV2.java
@ResponseBody
@GetMapping("/v2/auth/brw/result")
public Message resultBRW(@RequestParam("txid") String serverTransId) {
  return authServiceV2.getBRWResult(serverTransId);
}

//AuthServiceV2.java
public Message getBRWResult(String serverTransId) {
  //ActiveServer url for Retrieve Results
  String resultUrl = config.getAsAuthUrl() +
      "/api/v2/auth/brw/result?threeDSServerTransID=" +
      serverTransId;

  //Get authentication result from ActiveServer
  Message response = sendRequest(resultUrl, null, HttpMethod.GET);
  logger.info("authResponse: \n{}", response);

  return response;
}
/// <summary>
/// Receives the Request for authentication result request (Step 15(F) and Step 20(C))
/// Send data to ActiveServer to Retrieve Authentication Results
/// </summary>
/// <param name="txid"></param>
/// <returns></returns>
[HttpGet, Route("v2/auth/brw/result")]
public Message authResult(String txid)
{
    return authServiceV2.getBRWResult(txid);
}

//AuthServiceV2.cs
public Message getBRWResult(String serverTransId)
{
    //ActiveServer url for Retrieve Results
    string resultUrl = Config.AsAuthUrl + "/api/v2/auth/brw/result?threeDSServerTransID=" + serverTransId;

    //Get authentication result from ActiveServer
    Message result = (Message)RestClientHelper.GetForObject(resultUrl, typeof(Message));

    return result;
}
<?php
//AuthControllerV2.php
public function brwResult()
{
    $serverTransId = $_GET["txid"];

    $response = $this->authService->getBrwResult($serverTransId);

    //Show authentication results on result.html (Step. 17(F), Step. 28(C) or Step. 21(D), 22(D)).
    Utils::_returnJson($response);
}

//AuthServiceV2.php
public function getBrwResult($serverTransId)
{
    //ActiveServer url for Retrieve Results
    $resultUrl = "/api/v2/auth/brw/result?threeDSServerTransID=" . $serverTransId;

    //Get authentication result from ActiveServer (Step 16(F), Step 27(C) or Step 20(D))
    return $this->restTemplate->get($resultUrl)->getBody();
}
//api-v2.go
    r.GET("/v2/auth/brw/result", func(c *gin.Context) {

        transId := getSessionAttribute(c, InitAuthResponse, ThreeDSServerTransId).(string)
        if transId == "" {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid transId"})
            return

        }

        callASAPI(asSession{
            url:        "/api/v2/auth/brw/result?threeDSServerTransID=" + transId,
            context:    c,
            httpClient: httpClient,
            config:     config})

    })
...
}

モバイルアプリと3DS Requestor間の実装

モバイルアプリは3DS RequestorへAuthRequest を送信する必要があります。以下はAndroidアプリの場合にそのリクエストを送信する際のサンプル・コードです。iOSの場合はこれと類似します。

警告

モバイルアプリと3DS Requestorの間の通信を実装するのは加盟店の役割です。安全な通信をするために、適切な認証処理の実施は必要です。

private Button purchaseBtn;
private OkHttpClient client = new OkHttpClient();
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ...    
  purchaseBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        ...
        AuthenticationRequestParameters authParams = transaction
            .getAuthenticationRequestParameters();

        PurchaseRequest appAuthReq = new PurchaseRequest(authParams,
            cardNumberEdit.getText().toString());

        final Request request = new Request.Builder()
            .url(REQUESTOR_URL + "/v2/auth/app")
            .post(RequestBody.create(JSON, appAuthReq.toJson()))
            .build();

        client.newCall(request).enqueue(new Callback() {
          ...
          @Override
          public void onResponse(Call call, Response response) throws IOException {
            progress.dismiss();

            final String responseBody = response.body().string();
            Log.d(TAG, "Success purchase request: " + responseBody);
            ...
          }
        }
    }
}

3DS Requestorのトラブルシューティング

3DS Requestorでは、トラブルシューティングのために、フロントエンドにはアプリ・テストページが実装されています。Test pages -> Appを選択すれば、 アプリ・テストページへ移動することができます。

そのページには、テスト/デモのみの目的でダミーAuthRequestデータが事前に入力されています。Test Appボタンを押すと、ダミー・データは3DS Requestorのバックエンドの/v2/auth/appエンドポイントに送信されます。AuthResponseResponse の部分に表示されます。ちなみに、本番で認証を実施する場合は統合された3DS SDKからAuthRequestを起動する必要があります。

auth-app-1.png