Skip to content

3DS Requestor

To integrate ActiveSDK, the merchant site needs to implement a 3DS Requestor in the backend. The following diagram shows the relationship between the ActiveSDK, the 3DS Requestor and other 3DS components. As defined by EMVCo's 3DSecure 2.0 specification, the communication between the merchant applications and the 3DS Server must be mutual authenticated. So a backend 3DS Requestor is needed to establish a mutual TLS connection with 3DS Server.

sdk-integration-architecture.png

This set of documents will take you through the process of integrating the ActiveSDK with our 3DS Requestor demo code. In this demo, the 3DS Server which the 3DS Requestor connected to is our ActiveServer. You can modify the 3DS Requestor demo code to connect to any 3DS Servers.

The following is the prerequisite to using this guide:

  • Knowledge of one of the following: Java, PHP, C#, or Go

Checkout and run the sample code

The 3DS Requestor demo code can be downloaded from the download page . Please follow the Get client certificate and Configure 3DS Requestor details instructions in the Requestor download page , and run the Requestor before moving onto next step.

Note

An activated and running ActiveServer instance (or a Saas ActiveServer instance) is required to run the Requestor demo code. Please contact GPayments if you need any help.

Implementation between Requestor and 3DS Server

The 3DS Requestor receives AuthRequest from the mobile APP and sends the request to 3DS Server. It also receives AuthResponse from 3DS Server and forwards the result to the mobile APP.

The demo 3DS Requestor code provides the backend implementation with the following server side languages:

  • Java: The java version is implemented based on the Springboot framework. For details of Springboot, check out https://spring.io/projects/spring-boot
  • C#: The C# version is implemented based on ASP.net.
  • PHP: The PHP version is implemented based on PHP 7.2 with cURL (Client URL Library).
  • Go: The Go version is implemented based on Go 1.12 with Go Module support. All dependencies are listed in go.mod file.

Before starting the authentication process with 3DS Server, the 3DS Requestor needs to establish a mutual TLS connection with 3DS Server. Make sure you have followed the Get client certificate and Configure 3DS Requestor details instructions in the Requestor download page.

Tip

The implementation of TLS configuration for the HTTP Client can be found as follows:

  • Java: The TLS configuration and client certificate loading can be found in class RestClientConfig.java.
  • C#: The TLS configuration and client certificate loading can be found in class RestClientHelper.cs.
  • PHP: The TLS configuration and client certificate loading can be found in file RestClientConfig.php.
  • Go: The TLS configuration and client certificate loading can be found in file https.go.

Execute authentication

To execute authentication, the 3DS Requestor needs to implement the /v2/auth/app endpoint to:

  • Receive the AuthRequest from the mobile APP.
  • Forward the request to 3DS Server.
  • Receive the AuthResponse from 3DS Server.
  • Return the response data to the mobile APP.
//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})

})

Tip

This demo code is to send AuthRequest to ActivesServer. To check the data structure of AuthRequest for ActiveServer, refer to the API document. To send AuthRequest to other 3DS Servers, please refer to the corresponding API documents.

ActiveServer users only

By default, the URL above will send the AuthRequest to GPayments TestLabs for testing purposes. When moving into production, to send the API request to the card scheme directory server, the trans-type query parameter must be appended to this API URL. See API description for further information on usage.

If you are using a Master Auth API client certificate to authenticate a Business Admin user on behalf of a merchant, the back-end needs to add a HTTP Header in the HTTP Request with a field of AS-Merchant-Token, which should be set to the merchantToken from the merchants profile. Detail implementation, refer to here.

Get authentication result

For frictionless flow, the authentication result will return to the mobile APP at the end of Execute authentication process.

For challenge flow, a transStatus=C will returned to the mobile APP at the end of Execute authentication process and a challenge process will be executed between ActiveSDK and ACS. After the challenge process finished, the mobile APP should call /v2/auth/result endpoint to get the authentication result. The 3DS Requestor needs to send a request to ActiveServer to get the result and return the result to the mobile APP.

//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;
}
[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})

    })
...
}

Implementation between mobile App and Requestor

The mobile App needs to send the AuthRequest to Requestor. Following is a demo code of Android App to send the request. The iOS implementation is similar.

Warning

It is up to the merchant to implement the communication between the mobile App and the Requestor. It is required to use suitable authentication process to secure the communication.

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);
            ...
          }
        }
    }
}

Requestor troubleshooting

In the requestor, a front-end App test page is implemented for troubleshooting. Go to App test page by selecting Test pages -> App.

Mock AuthRequest data is pre-filled in this page, only for test/demo purposed. Press the Test App button, the mock data will be sent to the /v2/auth/app endpoint in the requestor back-end. The AuthResponse will be shown on the Response area. Note, for a production authentication, AuthRequest must be initiated by an integrated 3DS SDK.

auth-app-1.png