バックエンド実装
この章では、サンプルコードを使用して、加盟店サイトのバックエンドへの実装方法について説明します。
備考
サンプルコードに目を通し、実行できたら、次に進んでください。
バックエンドには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 | //AuthController.java /** * 認証の初期化要求を3DS-web-adapterから受信する。 (ステップ 2) * ActiveServerに認証を初期化するためのデータを送信する。 */ @PostMapping("/auth/init/{messageCategory}") 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/v1/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); // 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 | //AuthController.cs /// <summary> /// 認証の初期化要求を3DS-web-adapterから受信する。 (ステップ 2) /// ActiveServerに認証を初期化するためのデータを送信する。 /// </summary> /// <param name="request"></param> /// <returns></returns> [HttpPost, Route("auth/init/{messageCategory}")] 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/v1/auth/brw/init/" + messageCategory; 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)); // 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 | <?php //AuthController.php /** * 認証の初期化要求を3DS-web-adapterから受信する。 (ステップ 2) * ActiveServerに認証を初期化するためのデータを送信する。 */ public function initAuth($messageCategory) { $requestData = $this->_getJsonData(); // リクエスターのトランザクションIDを生成する。 $requestData->threeDSRequestorTransID = $this->_getUUId(); //Fill the event call back url with requestor url + /3ds-notify $requestData->eventCallbackUrl = $this->config->getBaseUrl() . "/3ds-notify"; // 認証を初期化するためのActiveServerのURL $initAuthUrl = "/api/v1/auth/brw/init/" . $messageCategory; // ActiveServerに認証を初期化するためのデータを送信する。(ステップ 3) // ActiveServerから応答を受け取る (ステップ 4) $response = $this->restTemplate->post($initAuthUrl, $requestData); // 3ds-web-adapterデータを返答する。 (ステップ 5) $this->_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 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 | //main.go func authController(r *gin.Engine, config *Config, httpClient *http.Client) { r.POST("/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 } //requestorTransIdを追加する message["threeDSRequestorTransID"] = uuid.New() //コールバックイベントURLを追加する。 message["eventCallbackUrl"] = config.GPayments.BaseUrl + "/3ds-notify" callASAPI(message, "/api/v1/auth/brw/init/"+c.Param("messageCategory"), c, httpClient, config) }) ... } //call ActiveServer API, if message == nil, do GET otherwise POST func callASAPI(message map[string]interface{}, url string, c *gin.Context, httpClient *http.Client) { var r *http.Response var err error if message == nil { r, err = httpClient.Get(url) } else { var data []byte data, err = json.Marshal(message) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } r, err = httpClient.Post(url, "application/json;charset=utf-8", bytes.NewBuffer(data)) } defer r.Body.Close() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } response, err := ioutil.ReadAll(r.Body) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } contentType := r.Header.Get("Content-Type") c.Data(http.StatusOK, contentType, response) } |
注目
eventCallbackUrl
をhttp://localhost:8082/3ds-notify
に設定しています。このURLを設定することでActiveServerからブラウザー情報の収集が完了した時通知を受ける事ができます。(ステップ. 7)。
initAuth
リクエストが{ActiveServer auth url}/api/v1/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 | //注目: callAsAPI()がActiveServerにリクエストを送信します。 //これがgroupAuthの場合、リクエストにはAS-Merchant-Tokenのフィールドを持つHTTPヘッダーが含まれている必要があります。 func callASAPI(message map[string]interface{}, url string, c *gin.Context, httpClient *http.Client, config *Config) { var r *http.Request var err error //add ActiveServer base URL url = config.GPayments.AsAuthUrl + url 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) 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 } c.Data(http.StatusOK, contentType, responseBody) } |
処理2: 認証の実行¶
認証を実行する為に3DSリクエスターが必要な事は:
- ステップ 7の後、ActiveServerからの
/3ds-notify
メッセージを処理する。 - 3DS-web-adapter からの
認証の実行
リクエストを処理する (ステップ 9とステップ 10) 。 - 認証結果を受信し、それを3DS-web-adapterへ返答する (ステップ 12とステップ 13) 。
ブラウザ情報の収集 (ステップ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によりrequestorTransId
、event
、オプションのparam
が提供されます。event
は3DSMethodFinished
、3DSMethodSkipped
、もしくは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 | //AuthController.java /** * 認証の実行要求を3DS-web-adapterから受信する。 (ステップ 9) * 受信したデータをActiveServerに送信し認証を実行する。 */ @PostMapping("/auth") public Message auth(@RequestBody Message request) { // 認証を実行するためのActiveServerのURL String authUrl = config.getAsAuthUrl() + "/api/v1/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/v1/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 20 | <?php //AuthController.php /** * 認証の実行要求を3DS-web-adapterから受信する。 (ステップ 9) * 受信したデータをActiveServerに送信し認証を実行する。 */ public function auth() { $requestData = $this->_getJsonData(); //ActiveServer url for Execute Authentication $authUrl = "/api/v1/auth/brw"; //データをActiveServerに送信し認証を実行する。 (ステップ 10) //応答データをActiveServerから受信する。 (ステップ 12) $response = $this->restTemplate->post($authUrl, $requestData); //3ds-web-adapterデータを返答する。 (ステップ 13) $this->_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("/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/v1/auth/brw", c, httpClient, config) }) ... } |
注目
3DSリクエスターは、フロントエンドへ返答します。返されるメッセージには、フリクションレスフローまたはチャレンジフローのいずれかによってY
またはC
のtransStatus
値を含みます。フロントエンドがどのように transStatus
を処理するかを確認するには、こちらを参照してください。応答データの構造を確認するには、APIドキュメントを参照してください。
チャレンジフローをキャンセル¶
transStatus = C
の場合、3DSクライアントはチャレンジを開始するか否かを選択できます。3DSクライアントがチャレンジをキャンセルすることを選択した場合、 /auth/challenge/status
エンドポイントを呼び出してキャンセルした理由を指定できます。 3DSリクエスターがActiveServerに送信します。 フロントエンドがこのリクエストを処理する方法を確認するには、こちらを参照してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 | //AuthController.java @PostMapping("/auth/challenge/status") public Message challengeStatus(@RequestBody Message request) { String challengeStatusUrl = config.getAsAuthUrl() + "/api/v1/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 | //AuthController.cs [HttpPost, Route("auth/challenge/status")] public Message challengeStatus([FromBody]Message request) { String challengeStatusUrl = Config.AsAuthUrl + "/api/v1/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 //AuthController.php public function challengeStatus() { $requestData = $this->_getJsonData(); $challengeStatusUrl = "/api/v1/auth/challenge/status"; $response = $this->restTemplate->post($challengeStatusUrl, $requestData); $this->_returnJson($response->getBody()->getContents()); } |
1 2 3 4 5 6 7 8 9 10 11 | //AuthController routers r.POST("/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/v1/auth/challenge/status", c, httpClient, config) }) |
処理 3: 認証結果の取得¶
認証結果を取得する為に3DSリクエスターが必要な事は:
- 3DS-web-adapterからの
認証結果の取得
リクエストを処理する (ステップ 15(F)もしくはステップ 20(C)) 。 - 結果を取得しフロントエンドへ返す為にActiveServerへリクエストを送信する (ステップ 16(F)、 ステップ 17(F) もしくは ステップ 21(C)、22(C)) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //AuthController.java /** * 認証結果の取得要求を受信する。(ステップ15(F)とステップ20(C)) * ActiveServerから認証結果を取得する。 */ @GetMapping("/auth/result") public Message result(@RequestParam("txid") String serverTransId) { // ActiveServerからURLを取得する String resultUrl = config.getAsAuthUrl() + "/api/v1/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("auth/result")] public Message authResult(String txid) { string serverTransId = txid; //ActiveServer url for Retrieve Results string resultUrl = Config.AsAuthUrl + "/api/v1/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 //AuthController.php /** * 認証結果の取得要求を受信する。 (ステップ15(F) と ステップ20(C)) * ActiveServerから認証結果を取得する。 */ public function result() { $serverTransId = $_GET["txid"]; //ActiveServer url for Retrieve Results $resultUrl = "/api/v1/auth/brw/result?threeDSServerTransID=" . $serverTransId; //ActiveServerから認証結果を取得する。 (ステップ16(F) と ステップ21(C)) $response = $this->restTemplate->get($resultUrl); //認証結果をresult.htmlに表示する。(ステップ17(F) と ステップ22(C)) $this->_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/v1/auth/brw/result?threeDSServerTransID="+transId, c, httpClient, config) }) ... } |
成功
この文書ではバックエンド実装についての説明は以上で終了です。認証が完了した後、チェックアウト処理は取引ステータス、ECIとCAVVを使用して承認の実行を続け、取引を完了出来ます。
次のチャプター
次へボタンを選択しGPayments 3DSリクエスターの為のサンプルコードのすべての機能の説明をご覧ください。