Important

We have released a new version. Documentation for our latest version can be found here.

Last Updated: 17 January 2024 | Change Log

Challenge Display & Verification

If the authentication response outcome is challenged and the authentication.version is 2.x.x (3DS2) you can use the SDK to provide the improved 3DS2 Challenge display for mobile devices.

If the authentication.version is 1.x.x (3DS1) you must follow the 3DS1 Challenge display. The integration more closely follows the steps for web.

3DS2 Challenge display

If the authentication.version is 2.x.x you will need the following values from the authentication response to use in the SDK.

Access NameValue fromCardinal SDK Name
challenge.referenceauthentication responsetransactionId
challenge.payloadauthentication responsepayload

The Access 3DS API will be periodically tested against the latest version of the Cardinal SDK. Current tested Cardinal SDK version: v2.2.5

SDK challenge display:

Customize Challenge Interface

As part of SDK setup you can customise the challenge user interface

3DS1 Challenge display

If the authentication.version is 1.x.x use the following steps to display the challenge screen.

Note

3DS version 1 was implemented by issuers long before smartphones existed so offers a degraded experience compared to version 2. We expect version 1 traffic from issuers to drop during 2021.

Challenge form (webView)

POST the request to the challenge.url with the challenge.jwt and optional MD.

The MD field allows you to pass url parameters (max 1024 characters) in the challenge form that is included/echoed in the response url (challenge.returnUrl).

iOS

Add a WKWebView to your Storyboard and UIViewController and enable JavaScript

@IBOutlet var webView: WKWebView!
override func viewDidLoad() {
    super.viewDidLoad()
    webView.configuration.preferences.javaScriptEnabled = true
}

Implement a method similar to the code snippet below in your UIViewController

let iframeSrcDoc = """
<html>
  <body onload='document.frmLaunch.submit();'>
    <form name='frmLaunch' method='POST' action='\(challengeUrl)'>
      <input type='hidden' name='JWT' value='\(jwt)'>
      <input type='hidden' name='MD' value='\(md)'>
    </form>
  </body>
</html>
"""
// A viewport meta tag is used to scale the content nicely to the device's screen size
let html = """
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <iframe srcdoc="\(iframeSrcDoc)" width="100%" height="400" frameborder="0">
    </iframe>
  </body>
</html>
"""
webView.loadHTMLString(html, baseURL: URL(string: "about:srcdoc")!)
Android

Add a WebView to your fragment and enable JavaScript

  1. Kotlin
  2. Java
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val webView = view.findViewById<WebView>(...)
    webView.settings.javaScriptEnabled = true
}

Implement a method similar to the code snippet below in your Fragment

  1. Kotlin
  2. Java
val iframeSrcDoc = """
<html>
  <body onload='document.frmLaunch.submit();'>
    <form name='frmLaunch' method='POST' action='$challengeUrl'>
      <input type='hidden' name='JWT' value='$jwt'>
      <input type='hidden' name='MD' value='$md'>
    </form>
  </body>
</html>
"""
// A viewport meta tag is used to scale the content nicely to the device's screen size
val html = """
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <iframe srcdoc="$iframeSrcDoc" width="100%" height="400" frameborder="0">
    </iframe>
  </body>
</html>
"""
webView.loadData(html, "text/html; charset=utf-8", "UTF-8")

Intercept challenge return

Intercept the POST request to your challenge.returnUrl

iOS

Have your UIViewController adopt the WKNavigationDelegate protocol

Point the navigationDelegate property of your WKWebView to your UIViewController, just before the call to webView.loadHTMLString

webView.navigationDelegate = self

Implement the following method in your UIViewController to be notified when the challenge has been completed

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
     if navigationAction.request.url!.absoluteString == "<value of your challenge return url>" {
        // TODO: add your logic
     }
     decisionHandler(.allow)
}
Android

Intercept the POST request to your challenge return url

Create a class that extends WebViewClient and override the onLoadResource method to be notified when the challenge has been completed

  1. Kotlin
  2. Java
private class CustomWebViewClient: WebViewClient() {
    override fun onLoadResource(view: WebView?, url: String?) {
        if (url.toString() == "<value of your challenge return url>") {
            // TODO: add your logic
        }
        super.onLoadResource(view, url)
    }
}

Set the webViewClient property of your WebView to an instance of this class, just before the call to webView.loadData()

  1. Kotlin
  2. Java
webView.webViewClient = CustomWebViewClient()

Verification

Once the challenge has been completed, a verification request needs to be made to verify the result of the challenge.

Important

You should only request the verification API from your backend system, not call it directly from the mobile application using the Access credentials.

POST your verification request to our 3ds:verify action link received in your authentication response.

Verification example request

Note

You must use v2 and later of the API for the Android/iOS SDK

POST https://try.access.worldpay.com/verifications/customers/3ds/verification

Verification request body:

{
    "transactionReference": "unique-transactionReference",
    "merchant": {
        "entity": "default"
    },
    "challenge": {
        "reference": "123456789"
    }
}

Verification responses

Here are examples of the verification responses you would receive.

  1. Authenticated
  2. Authentication Failed
  3. Signature Failed
  4. Unavailable
  5. Bypassed
{
    "outcome": "authenticated",
    "transactionReference": "unique-transactionReference",
    "authentication": {
        "version": "2.1.0",
        "authenticationValue": "MAAAAAAAAAAAAAAAAAAAAAAAAAA=",
        "eci": "05",
        "transactionId": "c5b808e7-1de1-4069"
    }
}

Use the values: version, authenticationValue, eci, transactionId from the request when authorizing a payment. The values prove that the verification was successful, and that the fraud liability has shifted to the issuer.

ParameterDescription
authentication.versionThe version of 3DS used to process the transaction.
Note
Required for Mastercard's Identity Check transactions in Authorization.
authentication.authenticationValueA cryptographic value that provides evidence of the outcome of a 3DS verification.
  • Visa - Cardholder Authentication Verification Value (CAVV)
  • Mastercard - Universal Cardholder Authentication Field (UCAF)

Used when authorizing a payment.
authentication.eciElectronic Commerce Indicator (ECI).
Indicates the outcome of the 3DS authentication.
  • 02 or 05 - Fully Authenticated Transaction
  • 01 or 06 - Attempted Authentication Transaction
  • 00 or 07 - Non 3-D Secure Transaction
  • Mastercard - 02, 01, 00
  • Visa - 05, 06, 07
  • Amex - 05, 06, 07
  • JCB - 05, 06, 07
  • Diners - 05, 06, 07

You will need to use this when you are authorizing a payment.
authentication.transactionIdA transaction identifier.
If provided, you should use it as part of your payment authorization.
If the authentication.version has a major version of:
  • 1 - value returned known as xid
  • 2 - value returned known as dsTransactionId

Next steps


Take a payment