Note: This SDK guide is for preview purposes only and is subject to change.

Challenge Display

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

If the authentication.version is 1.x.x (3DS1) you must follow the3DS1 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

Cardinal Steps:

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

Add a WKWebView to your Storyboard and UIViewController and enable JavaScript

Copied!
@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

Copied!
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")!)

Add a WebView to your fragment and enable JavaScript

Copied!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val webView = view.findViewById<WebView>(...)
    webView.settings.javaScriptEnabled = true
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    WebView webView = view.findViewById(...);
    webView.getSettings().setJavaScriptEnabled(true);
}

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

Copied!
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")
String challengeUrl = "add challengeUrl here";
String 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
String html = "<html>" +
        "<head>" +
        "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" +
        "</head>" +
        "<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

Have your UIViewController adopt the WKNavigationDelegate protocol

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

Copied!
webView.navigationDelegate = self

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

Copied!
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)
}

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

Copied!
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)
    }
}
private static class CustomWebViewClient extends WebViewClient {
    @Override
    public void onLoadResource(WebView view, String url) {
        if (url.equals("<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()

Copied!
webView.webViewClient = CustomWebViewClient()
webView.setWebViewClient(new 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

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

Verification request body:

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

Verification responses

Here are examples of the verification responses you would receive.

Copied!
{
    "outcome": "authenticated",
    "transactionReference": "unique-transactionReference",
    "authentication": {
        "version": "2.1.0",
        "authenticationValue": "MAAAAAAAAAAAAAAAAAAAAAAAAAA=",
        "eci": "05",
        "transactionId": "c5b808e7-1de1-4069"
    }
}
{
    "outcome": "authenticationFailed",
    "transactionReference": "unique-transactionReference",
    "authentication": {
      "version": "1.0.2",
      "eci": "00",
      "transactionId": "N+en2I5+ZK/kQqk69wXdI8XIPg8="
    },
    "_links": {
        "3ds:authenticate": {
            "href": "https://try.access.worldpay.com/verifications/customers/3ds/authentication"
        },
        "curies": [{
            "href": "https://try.access.worldpay.com/rels/verifications/customers/3ds/{rel}",
            "templated": true,
            "name": "3ds"
        }]
    }
}
{
    "outcome": "signatureFailed",
    "transactionReference": "unique-transactionReference",
    "authentication": {
      "version": "1.0.2",
      "eci": "02"
    },
    "_links": {
        "3ds:authenticate": {
            "href": "https://try.access.worldpay.com/verifications/customers/3ds/authentication"
        },
        "curies": [{
            "href": "https://try.access.worldpay.com/rels/verifications/customers/3ds/{rel}",
            "templated": true,
            "name": "3ds"
        }]
    }
}
{
    "outcome": "unavailable",
    "transactionReference": "unique-transactionReference",
    "_links": {
        "3ds:authenticate": {
            "href": "https://try.access.worldpay.com/verifications/customers/3ds/authentication"
        },
        "3ds:verify": {
            "href": "https://try.access.worldpay.com/verifications/customers/3ds/verification"
        },
        "curies": [{
            "href": "https://try.access.worldpay.com/rels/verifications/customers/3ds/{rel}",
            "templated": true,
            "name": "3ds"
        }]
    }
}
{
  "outcome": "bypassed",
  "transactionReference": "6032c024-8d33-4e89-98e9-a944f66c3906",
  "authentication": {
    "version": "2.1.0",
    "eci": "00",
    "transactionId": "c5b808e7-1de1-4069-a17b-f70d3b3b1645"
  }
}

Use the values: version, authenticationValue, eci, transactionId from the request whenauthorizing 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 whenauthorizing 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 areauthorizing a payment.
authentication.transactionIdA transaction identifier.
If provided, you should use it as part of yourpayment authorization.
If the authentication.value has a major version of:
  • 1 - value returned known as xid
  • 2 - value returned known as dsTransactionId

Next steps


Take a payment