Skip to content

バックエンド実装(v2)

この章では、サンプルコードを使用して、加盟店サイトのバックエンドへの実装方法について説明します。

バックエンドには3DSリクエスターを実装する必要があります。3DSリクエスター3ds-web-adapterから情報を受信しActiveServerへリクエストを送信します。またActiveServerからの認証結果も受信し、結果を3ds-web-adapterへ転送します。

3DSリクエスターのデモのコードは、バックエンドの実装を以下のサーバーサイドの言語付きで提供します:

  • Java - JavaのバージョンはSpringbootフレームワークを使用して実装されています。Springbootの詳細はhttps://spring.io/projects/spring-bootを参照してください。

  • C# - C#のバージョンはASP.netを使用して実装されています。

  • PHP - PHPのバージョンはcURL(Client URL Library)とPHP7.2を使用して実装されています。

  • Go - GoのバージョンはGo 1.12とGoモジュールサポートを使用して実装されます。すべての依存性はgo.modファイルに記載されています。

なぜバックエンドの実装が必要なのか?

EMVCo's 3Dセキュア2.0の仕様で定義されているように、3DSサーバーと3DSリクエスターの環境が分かれている時、これらの2つのコンポーネント間の通信は相互認証されていなければなりません。

[Req 300] もし3DSリクエスターと3DSサーバーが別のコンポーネントである場合は、コンポーネント間で転送されるデータが、支払いシステムのセキュリティ要件を満たすレベルで保護され両方のサーバーで相互認証されている事を確保してください。

ActiveServerと認証プロセスを行う前に、3DSリクエスターはActiveServer と相互TLS接続を確立する必要があります。クライアント証明書を所持し3DSリクエスターでそれが設定されている事を確認してください。そうでない場合は、導入ページにある クライアント証明書の取得と3DSリクエスター詳細の設定 の説明に従ってください。

HTTPクライアントの為のTLS設定の導入は下記にあります:

次に、認証プロセスと認証シーケンスに基づいたバックエンド実装の詳細について記述します。

  • Java - TLS構成とクライアント証明書のロード方法は、クラスRestClientConfigにあります。

  • C# - TLS構成とクライアント証明書のロード方法は、クラスRestClientHelper.csにあります。

  • PHP - TLS構成とクライアント証明書のロード方法は、ファイルRestClientConfig.phpにあります。

  • Go - TLS構成とクライアント証明書のロード方法は、ファイルhttps.goにあります。

次に、認証プロセスおよび認証シーケンスに基づいたバックエンド実装の詳細を説明します。

処理 1: 認証の初期化

認証を初期化するためには、3DSリクエスターが以下のように動作する必要があります。

  • 認証の初期化リクエストを3DS-web-adapter(Step. 2)から受信する。

  • ActiveServerにリクエストを送信する (Step. 3)。

  • ActiveServer(Step. 4)から応答を受信する。

  • 受信したデータを3DS-web-adapter (Step. 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
25
26
27
28
29
30
31
32
33
34
35
36
37
//AuthControllerV2.java
/**
 * 認証の初期化要求を3DS-web-adapterから受信する。 (ステップ 2)
 * ActiveServerに認証を初期化するためのデータを送信する。
 */
@PostMapping("/v2/auth/init")
public Message initAuth(@RequestBody Message request,
    @PathVariable(value = "messageCategory") String messageCategory) {

  // リクエスターのトランザクションIDを生成する。
  String transId = UUID.randomUUID().toString();
  request.put("threeDSRequestorTransID", transId);
  // イベントコールバックURLを「リクエスターURL + /3ds-notify」に設定する。
  String callBackUrl = config.getBaseUrl() + "/3ds-notify";
  request.put("eventCallbackUrl", callBackUrl);

  // 認証を初期化するためのActiveServerのURL
  String initAuthUrl = config.getAsAuthUrl() + "/api/v2/auth/brw/init/" + messageCategory;
  logger.info("initAuthRequest on url: {}, body: \n{}", initAuthUrl, request);

  // ActiveServerに認証を初期化するためのデータを送信する。(ステップ 3)
  // ActiveServerから応答を受け取る (ステップ 4)
  Message response =
        sendRequest(initAuthUrl, request, HttpMethod.POST);
  logger.info("initAuthResponseBRW: \n{}", response);

  if (response != null) {
      //save initAuth response into session storage
      session.setAttribute((String) response.get("threeDSServerTransID"),
          response);
  } else {
      logger.error("Error in initAuth response");
  }

  // 3ds-web-adapterデータを返答する。 (ステップ 5)
  return response;
}
 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
//AuthV1Controller.cs
/// <summary>
/// 認証の初期化要求を3DS-web-adapterから受信する。 (ステップ 2)
/// ActiveServerに認証を初期化するためのデータを送信する。
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost, Route("v2/auth/init")]
public Message initAuth([FromBody]Message request, string messageCategory)
{
  // リクエスターのトランザクションIDを生成する。
    string transId = Guid.NewGuid().ToString();
    request["threeDSRequestorTransID"] = transId;
  // イベントコールバックURLを「リクエスターURL + /3ds-notify」に設定する。
    string callBackUrl = Config.BaseUrl + "/3ds-notify";
    request["eventCallbackUrl"] = callBackUrl;

  // 認証を初期化するためのActiveServerのURL
    string initAuthUrl = Config.AsAuthUrl + "/api/v2/auth/brw/init";
    logger.Info(string.Format("initAuthRequest on url: {0}, body: \n{1}", initAuthUrl, request));

  // ActiveServerに認証を初期化するためのデータを送信する。(ステップ 3)
  // ActiveServerから応答を受け取る (ステップ 4)
    Message response = (Message)RestClientHelper.PostForObject(initAuthUrl, request, typeof(Message));
    logger.Info(string.Format("initAuthResponseBRW: \n{0}", response));

  if (response != null)
      //save initAuth response into session storage
      HttpContext.Current.Session[(String)response["threeDSServerTransID"]] = response;
  else
      logger.Error("Error in initAuth response");

  // 3ds-web-adapterデータを返答する。 (ステップ 5)
    return response;
}
 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
<?php
//AuthControllerV2.php
/**
 * 認証の初期化要求を3DS-web-adapterから受信する。 (ステップ 2) 
 * ActiveServerに認証を初期化するためのデータを送信する。
 */
public function initAuth()
{
    $requestData = Utils::_getJsonData();
  // リクエスターのトランザクションIDを生成する。
    $requestData->threeDSRequestorTransID = Utils::_getUUId();
    //Fill the event call back url with requestor url + /3ds-notify
    $requestData->eventCallbackUrl = $this->config->getBaseUrl() . "/3ds-notify";

  // 認証を初期化するためのActiveServerのURL
    $initAuthUrl = "/api/v2/auth/brw/init";

  // ActiveServerに認証を初期化するためのデータを送信する。(ステップ 3)
  // ActiveServerから応答を受け取る (ステップ 4)
  $response = $this->restTemplate->post($initAuthUrl, $requestData);

  $responseBody = json_decode($response->getBody());
  $_SESSION[$responseBody->threeDSServerTransID] = $responseBody;

  // 3ds-web-adapterデータを返答する。 (ステップ 5)
    Utils::_returnJson($response->getBody());
}
 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
//main.go
//AuthControllerV2 routers v2
func authControllerV1(r *gin.Engine, config *Config, httpClient *http.Client) {
    r.POST("/v2/auth/init/:messageCategory", func(c *gin.Context) {


        var message map[string]interface{}

        err := c.ShouldBindJSON(&message)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        //adding requestorTransId
        message["threeDSRequestorTransID"] = uuid.New()
        //add callback event url
        message["eventCallbackUrl"] = config.GPayments.BaseUrl + "/3ds-notify"

        callASAPI(message, "/api/v2/auth/brw/init", c, httpClient, config, func(resp []byte) error {
            //store the response in current session.

            authUrl, err := getAuthUrl(resp)

            if err != nil {
                return err
            }
            session := sessions.Default(c)
            session.Set("authUrl", authUrl)
            _ = session.Save()
            return nil
        })

    })
...
}

注目

eventCallbackUrl{baseUrl}/3ds-notifyに設定しています。このURLを設定することでActiveServerからブラウザー情報の収集が完了した時通知を受ける事ができます。(ステップ. 7)baseUrlis set here.

initAuthリクエストが{ActiveServer auth url}/api/v2/auth/brw/init/{messageCategory}のURLに送信されます。initAuthリクエストのデータ構造をご覧になりたい場合は認証APIドキュメントを参照してください。

認証APIマスタークライアント証明書用のHTTPヘッダー

認証APIマスタークライアント証明書を使用してビジネス管理者ユーザーとして加盟店に代わって認証を行う場合、バックエンドは、HTTPヘッダーにAS-Merchant-Tokenフィールドを追加する必要があります。このフィールドは加盟店プロファイルから取得できるマーチャントトークンを設定します。

 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
//注目: sendRequest()がActiveServerにリクエストを送信します。
//これがgroupAuthの場合、リクエストにはAS-Merchant-Tokenのフィールドを持つHTTPヘッダーが含まれている必要があります。
private Message sendRequest(String url, Message request, HttpMethod method) {

    HttpEntity<Message> req;
    HttpHeaders headers = null;

    if (config.isGroupAuth()) {
      //the certificate is for groupAuth, work out the header.
      headers = new HttpHeaders();
      headers.add("AS-Merchant-Token", config.getMerchantToken());
    }

    switch (method) {
      case POST:
        req = new HttpEntity<>(request, headers);
        return restTemplate.postForObject(url, req, Message.class);
      case GET:
        if (headers == null) {
          return restTemplate.getForObject(url, Message.class);
        } else {
          req = new HttpEntity<>(headers);
          return restTemplate.exchange(url, HttpMethod.GET, req, Message.class).getBody();
        }
      default:
        return null;
    }
  }
}
 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
//注目: PostForObject()がActiveServerにリクエストを送信します。
//これがgroupAuthの場合、リクエストにはAS-Merchant-Tokenのフィールドを持つHTTPヘッダーが含まれている必要があります。
//RestClientHelper.cs
public static object PostForObject(string url, object request, Type responseType)
{
    try
    {
        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
        req.Method = "POST";
        req.ClientCertificates.Add(GetClientCertificate());
        //the certificate is for groupAuth, work out the header
        if (Config.GroupAuth)
            req.Headers.Add("AS-Merchant-Token", Config.MerchantToken);
        req.ContentType = "application/json;charset=utf-8";
        string strRequest = JsonConvert.SerializeObject(request, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
        byte[] postData = System.Text.Encoding.UTF8.GetBytes(strRequest);
        req.ContentLength = postData.Length;
        using (Stream streamOut = req.GetRequestStream())
            streamOut.Write(postData, 0, postData.Length);
        string result = null;
        using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
            result = streamIn.ReadToEnd();
        return JsonConvert.DeserializeObject(result, responseType);
    }
    catch (Exception exp)
    {
        logger.Error(exp.Message, exp);
        throw exp;
    }
}
 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
<?php
//注目: post()がActiveServerにリクエストを送信します。
//これがgroupAuthの場合、リクエストにはAS-Merchant-Tokenのフィールドを持つHTTPヘッダーが含まれている必要があります。
//RestClientConfig.php
public function __construct(Config $config)
{
    ...
    if ($config->isGroupAuth()) {
        $this->headers = array('AS-Merchant-Token' => $config->getMerchantToken());
    }
}

function post($request_uri, $post_data)
{
    $response = $this->client->request(
        "POST",
        $request_uri,
        ['json' => $post_data, 'headers' => $this->headers]);
    return $response;
}

function get($request_uri)
{
    $response = $this->client->request("GET", $request_uri, ['headers' => $this->headers]);
    return $response;
}
 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
74
75
76
//注目: callAsAPI()がActiveServerにリクエストを送信します。
//これがgroupAuthの場合、リクエストにはAS-Merchant-Tokenのフィールドを持つHTTPヘッダーが含まれている必要があります。
func callASAPI(message map[string]interface{},
    url string,
    c *gin.Context,
    httpClient *http.Client,
    config *Config,
    rHandler respHandler) {
    //add ActiveServer base URL
    callASAPIWithUrl(message, config.GPayments.AsAuthUrl+url, c, httpClient, config, rHandler)
}
func callASAPIWithUrl(
    message map[string]interface{},
    url string,
    c *gin.Context,
    httpClient *http.Client,
    config *Config,
    rHandler respHandler) {

    var r *http.Request
    var err error

    if message == nil {
        r, err = http.NewRequest("GET", url, nil)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
    } else {
        var data []byte
        data, err = json.Marshal(message)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }

        r, err = http.NewRequest("POST", url, bytes.NewBuffer(data))
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
        r.Header.Set("Content-Type", "application/json;charset=utf-8")

    }

    //if this is groupAuth
    if config.GPayments.GroupAuth {
        r.Header.Set("AS-Merchant-Token", config.GPayments.MerchantToken)
    }

    response, err := httpClient.Do(r)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    defer response.Body.Close()

    contentType := response.Header.Get("Content-Type")
    responseBody, err := ioutil.ReadAll(response.Body)

    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    if rHandler != nil {

        if err = rHandler(responseBody); err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
    }

    c.Data(http.StatusOK, contentType, responseBody)

}

処理2: 認証の実行

認証を実行する為に3DSリクエスターが必要な事は:

ブラウザ情報の収集 (ステップ7)が完了した時、ActiveServer はあなたがeventCallBackUrlに設定したhttp://localhost:8082/3ds-notifyに通知を行います。3DSリクエスターはこの通知を処理し必要なパラメータをnotify-3ds-events.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;
  if ("3DSMethodFinished".equals(callbackType)) {

    callbackName = "_on3DSMethodFinished";

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

    callbackName = "_on3DSMethodSkipped";

  } else if ("AuthResultReady".equals(callbackType)) {
    callbackName = "_onAuthResult";
  } else {
    throw new IllegalArgumentException("invalid callback type");
  }

  model.addAttribute("transId", transId);
  model.addAttribute("callbackName", callbackName);
  model.addAttribute("callbackParam", param);

  return "notify_3ds_events";
}
 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
//MainController.cs
[HttpPost, ActionName("3ds-notify")]
public ActionResult NotifyResult()
{
    string transId = Request.Params["requestorTransId"];
    string callbackType = Request.Params["event"];
    string param = Request.Params["param"];

    String callbackName;
    if ("3DSMethodFinished".Equals(callbackType))
        callbackName = "_on3DSMethodFinished";
    else if ("3DSMethodSkipped".Equals(callbackType))
        callbackName = "_on3DSMethodSkipped";
    else if ("AuthResultReady".Equals(callbackType))
        callbackName = "_onAuthResult";
    else
        throw new ArgumentException("invalid callback type");

    dynamic model = new ExpandoObject();
    model.transId = transId;
    model.callbackName = callbackName;
    model.callbackParam = param;

    return View("notify_3ds_events", model);
}
 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
<?php
//MainController.php
public function notifyResult()
{
    $requestorTransId = $_POST["requestorTransId"];
    $callbackType = $_POST["event"];
    $param = (isset($_POST["param"]) && !empty($_POST["param"])) ? $_POST["param"] : "";

    if ($callbackType === "3DSMethodFinished") {

        $callbackName = "_on3DSMethodFinished";

    } else if ($callbackType === "3DSMethodSkipped") {

        $callbackName = "_on3DSMethodSkipped";

    } else if ($callbackType === "AuthResultReady") {
        $callbackName = "_onAuthResult";

    } else if ($callbackType === "InitAuthTimedOut") {

        $callbackName = "_onInitAuthTimedOut";
    } else {
        echo "invalid callback type";
        exit;
    }

    $this->model["transId"] = $requestorTransId;
    $this->model["callbackName"] = $callbackName;
    $this->model["callbackParam"] = $param;

    $this->_render("notify_3ds_events");
}
 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
//main.go
//Main controller
func mainController(r *gin.Engine, config *Config) {
...
    r.POST("/3ds-notify", func(c *gin.Context) {

        transId := c.PostForm("requestorTransId")
        if transId == "" {
            c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parameter"})
        }

        event := c.PostForm("event")
        if event == "" {
            c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parameter"})
        }

        param := c.Param("param")

        var callbackName string

        switch event {
        case "3DSMethodFinished":

            callbackName = "_on3DSMethodFinished"

        case "3DSMethodSkipped":

            callbackName = "_on3DSMethodSkipped"

        case "AuthResultReady":

            callbackName = "_onAuthResult"

        case "InitAuthTimedOut":

            callbackName = "_onInitAuthTimedOut"

        default:

            c.JSON(http.StatusBadRequest, gin.H{"error": "invalid callback type"})
            return

        }

        renderPage(gin.H{
            "transId":       transId,
            "callbackName":  callbackName,
            "callbackParam": param,
        }, notify3DSEventsTpl, c)
    })

}

注目

このハンドラーメソッドはActiveServerにより呼び出され、ActiveServerによりrequestorTransIdevent、オプションのparamが提供されます。event3DSMethodFinished3DSMethodSkipped、もしくはAuthResultReadyになります。そしてハンドラーメソッドはページコンテキスト内の適切な属性値を設定しnotify_3ds_events.htmlページへ返します。 そしてMustache テンプレートエンジンを使用してページにレンダリングします。

フロントエンドのnotify_3ds_events.htmlの実装を確認したい場合はこちらから確認できます。

そして3DSクライアントがブラウザ情報の収集を終えた時、authエンドポイントを呼び出し認証を開始します。 3DSリクエスター認証の実行リクエストを処理します。(ステップ 9ステップ 10ステップ 12ステップ 13) 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//AuthControllerV2.java
/**
 * 認証の実行要求を3DS-web-adapterから受信する。 (ステップ 9)
 * 受信したデータをActiveServerに送信し認証を実行する。
 */
@PostMapping("/v2/auth")
public Message auth(@RequestBody Message request) {

  // 認証を実行するためのActiveServerのURL
  String authUrl = config.getAsAuthUrl() + "/api/v2/auth/brw";
  logger.info("requesting BRW Auth API {}, body: \n{}", authUrl, request);

  // データをActiveServerに送信し認証を実行する。(ステップ 10)
  // 応答データをActiveServerから受信する。(ステップ 12)
  Message response =
        sendRequest(authUrl, request, HttpMethod.POST);
  logger.info("authResponseBRW: \n{}", response);

  //3ds-web-adapterデータを返答する。 (ステップ 13)
  return response;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//AuthController.cs
/// <summary>
/// 認証の実行要求を3DS-web-adapterから受信する。 (ステップ 9)
/// 受信したデータをActiveServerに送信し認証を実行する。
/// </summary>
/// <param name="trans"></param>
/// <returns></returns>
[HttpPost, Route("auth")]
public Message auth([FromBody]Message request)
{
    //ActiveServer url for Execute Authentication
    String authUrl = Config.AsAuthUrl + "/api/v2/auth/brw";
    logger.Info(string.Format("requesting BRW Auth API {0}, body: \n{1}", authUrl, request));

    //データをActiveServerに送信し認証を実行する。 (ステップ 10)
    //応答データをActiveServerから受信する。 (ステップ 12)
    Message response = (Message)RestClientHelper.PostForObject(authUrl, request, typeof(Message));
    logger.Info(string.Format("authResponseBRW: \n{0}", response));

    //3ds-web-adapterデータを返答する。 (ステップ 13)
    return response;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
//AuthControllerV2.php
/**
 * 認証の実行要求を3DS-web-adapterから受信する。 (ステップ 9)
 * 受信したデータをActiveServerに送信し認証を実行する。
 */
public function auth()
{
    $requestData = Utils::_getJsonData();

    //ActiveServer url for Execute Authentication
    $authUrl = "/api/v2/auth/brw";

    //データをActiveServerに送信し認証を実行する。 (ステップ 10)
    //応答データをActiveServerから受信する。 (ステップ 12)
    $response = $this->restTemplate->post($authUrl, $requestData);

    //3ds-web-adapterデータを返答する。 (ステップ 13)
    Utils::_returnJson($response->getBody()->getContents());}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//AuthController routers
func authController(r *gin.Engine, config *Config, httpClient *http.Client) {
...
    r.POST("/v2/auth", func(c *gin.Context) {

        var message map[string]interface{}

        err := c.ShouldBindJSON(&message)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        callASAPI(message, "/api/v2/auth/brw", c, httpClient, config)

    })
...
}

注目

3DSリクエスターは、フロントエンドへ返答します。返されるメッセージには、フリクションレスフローまたはチャレンジフローのいずれかによってYまたはCtransStatus値を含みます。フロントエンドがどのように transStatusを処理するかを確認するには、こちらを参照してください。応答データの構造を確認するには、APIドキュメントを参照してください。

チャレンジフローをキャンセル

transStatus = Cの場合、3DSクライアントはチャレンジを開始するか否かを選択できます。3DSクライアントがチャレンジをキャンセルすることを選択した場合、 /auth/challenge/statusエンドポイントを呼び出してキャンセルした理由を指定できます。 3DSリクエスターActiveServerに送信します。 フロントエンドがこのリクエストを処理する方法を確認するには、こちらを参照してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//AuthControllerV2.java
@PostMapping("/auth/challenge/status")
public Message challengeStatus(@RequestBody Message request) {

    String challengeStatusUrl = config.getAsAuthUrl() + "/api/v2/auth/challenge/status";
    logger.info("request challenge status API {}, body: \n{}", challengeStatusUrl, request);

    Message response =
        sendRequest(challengeStatusUrl, request, HttpMethod.POST);
    logger.info("challengeStatus response: \n{}", response);

    return response;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//AuthV1Controller.cs
[HttpPost, Route("v2/auth/challenge/status")]
public Message challengeStatus([FromBody]Message request)
{
    String challengeStatusUrl = Config.AsAuthUrl + "/api/v2/auth/challenge/status";
    logger.Info(string.Format("request challenge status API {0}, body: \n{1}", challengeStatusUrl, request));

    Message response = (Message)RestClientHelper.PostForObject(challengeStatusUrl, request, typeof(Message));
    logger.Info(string.Format("challengeStatus response: \n{0}", response));

    return response;
}
1
2
3
4
5
6
7
8
9
<?php
//AuthControllerV2.php
public function challengeStatus()
{
    $requestData = Utils::_getJsonData();
    $challengeStatusUrl = "/api/v2/auth/challenge/status";
    $response = $this->restTemplate->post($challengeStatusUrl, $requestData);
    Utils::_returnJson($response->getBody()->getContents());
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//AuthControllerV2 routers
r.POST("/v2/auth/challenge/status", func(c *gin.Context) {
    var message map[string]interface{}
    err := c.ShouldBindJSON(&message)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    callASAPI(message, "/api/v2/auth/challenge/status", c, httpClient, config, nil)
})

処理 3: 認証結果の取得

認証結果を取得する為に3DSリクエスターが必要な事は:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//AuthControllerV2.java
/**
 * 認証結果の取得要求を受信する。(ステップ15(F)とステップ20(C))
 * ActiveServerから認証結果を取得する。
 */
@GetMapping("/v2/auth/result")
public Message result(@RequestParam("txid") String serverTransId) {

// ActiveServerからURLを取得する
String resultUrl = config.getAsAuthUrl() +
    "/api/v2/auth/brw/result?threeDSServerTransID=" +
    serverTransId;

// ActiveServerから認証結果を取得する。 (ステップ16(F)とステップ21(C))
Message response = sendRequest(resultUrl, null, HttpMethod.GET);
logger.info("authResponse: \n{}", response);

//認証結果をresult.htmlに表示する。(ステップ17(F)とステップ22(C))
return response;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/// <summary>
/// 認証結果の取得要求を受信する。 (ステップ15(F)とステップ20(C))
/// ActiveServerから認証結果を取得する。
/// </summary>
/// <param name="txid"></param>
/// <returns></returns>
[HttpGet, Route("v2/auth/result")]
public Message authResult(String txid)
{

    string serverTransId = txid;
    //ActiveServer url for Retrieve Results
    string resultUrl = Config.AsAuthUrl + "/api/v2/auth/brw/result?threeDSServerTransID=" + serverTransId;

    //ActiveServerから認証結果を取得する。 (ステップ16(F) と ステップ21(C))
    Message result = (Message)RestClientHelper.GetForObject(resultUrl, typeof(Message));

    //認証結果をresult.htmlに表示する。(ステップ17(F) と ステップ22(C))
    return result;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php
//AuthControllerV2.php
/**
 * 認証結果の取得要求を受信する。 (ステップ15(F) と ステップ20(C))
 * ActiveServerから認証結果を取得する。
 */
public function result()
{
    $serverTransId = $_GET["txid"];
    //ActiveServer url for Retrieve Results
    $resultUrl = "/api/v2/auth/brw/result?threeDSServerTransID=" . $serverTransId;

    //ActiveServerから認証結果を取得する。 (ステップ16(F) と ステップ21(C))
    $response = $this->restTemplate->get($resultUrl);

    //認証結果をresult.htmlに表示する。(ステップ17(F) と ステップ22(C))
    Utils::_returnJson($response->getBody()->getContents());
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//AuthController routers
func authController(r *gin.Engine, config *Config, httpClient *http.Client) {
...
    r.GET("/auth/result", func(c *gin.Context) {

        transId := c.Query("txid")
        if transId == "" {
            c.JSON(http.StatusBadRequest, gin.H{"error": "invalid transId"})
            return

        }

        callASAPI(nil, "/api/v2/auth/brw/result?threeDSServerTransID="+transId, c, httpClient, config, nil)
    })
...
}

成功

この文書ではバックエンド実装についての説明は以上で終了です。認証が完了した後、チェックアウト処理は取引ステータス、ECIとCAVVを使用して承認の実行を続け、取引を完了出来ます。

次のチャプター

次へボタンを選択しGPayments 3DSリクエスターの為のサンプルコードのすべての機能の説明をご覧ください。