React Native

1. Add the threeDS object

Adding the threeDS object to your payment request will enable 3DS.

If 3DS authentication is not available/applicable (e.g. subsequent recurring (MIT), Apple Pay) a validation error message will be returned.

See how much data to provide for guidance on the values to include in the request related to 3DS, and the impact this may have on authentication rates.


2.Device Data Collection (DDC)

The /payments response will return an outcome of 3dsDeviceDatarequired. This contains a JWT, URL and BIN. These values will be used for the device data collection form.

{
  "transactionReference": "Memory265-13/08/1876",
  "outcome": "3dsDeviceDataRequired",
  "deviceDataCollection": {
    "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJPcmdVbml0SWQiOiI2NjAzMDA3YWJlMjMxZTM1ZTNmNTRjODkiLCJpc3MiOiI2NjAzMDA3YTE2ZGQ5YTdlNmEwMzM0MDciLCJleHAiOjE3MTIyNDg3MTIsImlhdCI6MTcxMjI0ODExMiwianRpIjoiZjdjZGVhZWUtMTY4MS00NjlhLTgxZmEtMzBkY2MyOTYzODA3In0.T1a6hOCPVVsQmcCKU3eczwmxlHoWK83tUqIJ_VG4fwc",
    "url": "https://centinelapistag.cardinalcommerce.com/V1/Cruise/Collect",
    "bin": "400000"
  },
  "_actions": {
    "supply3dsDeviceData": {
      "href": "https://try.access.worldpay.com/api/payments/eyJrIjoxLCJkIjoiR0ZSM3R2Z1d4OTI5SEdSVlVaWlk0cllQV3p4TU5raU85Y0ZwSkd2b09FWGo0SnVHYXI0MzJqZlM4RHp5UnRaaiJ9/3dsDeviceData",
      "method": "POST"
    }
  }
}

Device Data Collection form

The device data process for React Native uses:

  • a hidden WebView
  • an HTML page responsible for interfacing the HTML/JavaScript layer with the React Native layer
  • an iframe, embedded in the HTML page
  • a secondary HTML page, loaded via the iframe, and used to kickstart the Device Data Collection
  1. Install and link the React Native WebView component in your application.
# 1. Install dependency
npm install react-native-webview

# 2. Link dependency using your preferred method
react-native link react-native-webview

# 3. Ee-install pods
cd ios
pod install
  1. Add a hidden WebView in your React Native application with the source pointing to the HTML page created in the next step. Also, add an event listener for the message event which will be used to catch messages sent from the HTML layer to the React Native layer.
React Native
<WebView
  source={{ uri: 'replace-this-with-the-url-of-the-html-page-that-wraps-the-iframe' }}
  onMessage={(event) => {
    // deserialising and extracting JSON data from the event
    console.info(JSON.parse(event.nativeEvent.data));
  }}
  containerStyle={{ position: 'absolute', width: 0, height: 0 }}
/>
  1. Create and host the HTML page which will be used to interface the HTML/JavaScript layer with the React Native layer.
HTML
<html lang="en">
<head></head>
<body>
</body>
</html>
  1. Add a script to the HTML page to relay messages, received by postMessage() in the HTML page, to the React Native layer.
JavaScript
<script language="JavaScript">
window.onmessage = (event) => {
  // for Try: https://centinelapistag.cardinalcommerce.com
  // for Production: https://centinelapi.cardinalcommerce.com
  const allowedOrigin = '...';

  // Always verify that the message received is from the expected origin
  if (event.origin !== allowedOrigin) {
    return;
  }

  // the event data must be serialised into a string
  window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
};
</script>
  1. Add an iframe to the HTML page and set the src attribute to the URL of the page that will POST the DDC form. This URL should contain the following query string parameters: deviceDataCollection.jwt, deviceDataCollection.bin and deviceDataCollection.url. Those will be used in the DDC form.
HTML
<iframe src="replace-this-with-the-url-of-your-page-that-posts-the-ddc-form"></iframe>
  1. Create and host the secondary HTML page that POSTs the DDC form.
HTML
<html>
<body>
<!-- Using your preferred programming language, set the 'action' attribute with the value of the query string parameter containing the 'deviceDataCollection.url' from the device data initialization response -->
<form id="collectionForm" name="devicedata" method="POST" action="https://ddcUrl.example.com">

    <!-- Using your preferred programming language, set the 'value' attribute with the value of the query string parameter containing the 'deviceDataCollection.bin' from the device data initialization response -->
    <input type="hidden" name="Bin" value="555555" />

    <!-- Using your preferred programming language, set the 'value' attribute with the value of the query string parameter containing the 'deviceDataCollection.jwt' from the device data initialization response -->
    <input type="hidden" name="JWT" value="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJPcmdVbml0SWQiOiJPcmdVbml0IiwiaXNzIjoiYXBpSWQiLCJleHAiOjE1NjI5MjMzNDYsImlhdCI6MTU2MjkyMzQwNiwianRpIjoiYTAzMWVhOGEtN2E0Zi00YTQwLWI1NjMtOTUzMzYzMzVhZGNmIn0.0IK74OIXBxFsxqeOURJz1TFnz14ZTbFJTdTWo9cHUJQ" />

</form>

<script>
  window.onload = function() {
    document.getElementById('collectionForm').submit();
  }
</script>
</body>
</html>

The Testing page (3DS tab) contains an example form to submit the device data values. This is useful if using tools such as postman/insomnia to test your integration in the early stages.

Device Data Collection postMessage

Once the DDC form is submitted and is successfully sent to the card issuer, you are notified via a postMessage event.

For security, verify the sender's identity using the postMessage origin property as detailed here.

EnvironmentOrigin
Tryhttps://centinelapistag.cardinalcommerce.com
Productionhttps://centinelapi.cardinalcommerce.com

An example postMessage response:

postmessage
{
    "MessageType": "profile.completed",
    "SessionId": "0_3XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX6b5",
    "Status": true
}
KeyValue
messageTypeprofile.completed
SessionIdUUID, not present or undefined
Status
  • true - Use the SessionId value in deviceData.collectionReference as part of the 3dsDeviceData request
  • false - SessionId is empty. Either retry DDC or send the 3dsDeviceData request without the collectionReference.

The DDC call typically takes 1-2 seconds. This depends on the latency between the customer's device, the Cardinal servers and, in part, the type of Device Data Collection performed by the different issuers. The 3DS specification has a maximum response time of 10 seconds.

Note

If no postMessage is provided either retry DDC or send the 3dsDeviceData request without the collectionReference. We highly recommend providing device data (e.g. browserScreenHeight) in the payment request as well. This will maximize authentication rates in the case of DDC failure.

Full integration example code

# 1. Install dependency
npm install react-native-webview

# 2. Link dependency using your preferred method
react-native link react-native-webview

# 3. Ee-install pods
cd ios
pod install

3. Continue with the payment

Once device data has been completed use the supply3dsDeviceData action (from the /payments response) to resume the payment, including the SessionId (Aka: collectionReference) in the request body.

// Action to continue the payment (from /payment response)
"_actions": {
    "supply3dsDeviceData": {
      "href": "https://try.access.worldpay.com/api/payments/eyJrIjoxLCJkIjoiR0ZSM3R2Z1d4OTI5SEdSVlVaWlk0cllQV3p4TU5raU85Y0ZwSkd2b09FWGo0SnVHYXI0MzJqZlM4RHp5UnRaaiJ9/3dsDeviceData",
      "method": "POST"
    }
}
// Include the sessionId (Aka: collectionReference) in the body
{
  "collectionReference": "0_3XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX6b5"
}

If you do not provide a collectionReference, you will see an increased number of challenged and even authenticationFailed outcomes.

The issuer then performs a risk assessment using a combination of data from the Payments API request and the device data collection above. If it passes the payment will proceed to authorization. If not the issuer may request a challenge to verify the identity of the customer.


4.Challenge and verification

If the response outcome from the supply3dsDeviceData action is 3dsChallenged you must display a challenge screen from the issuer to check the customers identity.

{
  "outcome": "3dsChallenged",
  "transactionReference": "Memory265-13/08/1876",
  "authentication": {
    "version": "2.1.0"
  },
  "challenge": {
    "reference": "706hovL8DK1tIGGzQUV1",
    "url": "https://centinelapistag.cardinalcommerce.com/V2/Cruise/StepUp",
    "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJPcmdVbml0SWQiOiI2NjAzMDA3YWJlMjMxZTM1ZTNmNTRjODkiLCJPYmplY3RpZnlQYXlsb2FkIjpmYWxzZSwiaXNzIjoiNjYwMzAwN2ExNmRkOWE3ZTZhMDMzNDA3IiwiUmV0dXJuVXJsIjoiaHR0cDovL3BheW1lbnQuZXhhbXBsZS5jb20iLCJQYXlsb2FkIjoie1wiUGF5bG9hZFwiOlwiZXlKdFpYTnpZV2RsVkhsd1pTSTZJa05TWlhFaUxDSnRaWE56WVdkbFZtVnljMmx2YmlJNklqSXVNUzR3SWl3aWRHaHlaV1ZFVTFObGNuWmxjbFJ5WVc1elNVUWlPaUppTkRKbE5UWmpaaTAyWkRrMkxUUXpNek10T0dJMk5DMWlNbVU0TldZMFpURTFaVGtpTENKaFkzTlVjbUZ1YzBsRUlqb2laV1ZqWldZeE1ETXRNRE13TVMwMFpUbGtMVGsxTmpFdE56ZGlNbVkzTlRFMk5HUmhJaXdpWTJoaGJHeGxibWRsVjJsdVpHOTNVMmw2WlNJNklqQTBJbjBcIixcIkFDU1VybFwiOlwiaHR0cHM6XFwvXFwvMW1lcmNoYW50YWNzc3RhZy5jYXJkaW5hbGNvbW1lcmNlLmNvbVxcL01lcmNoYW50QUNTV2ViXFwvY3JlcS5qc3BcIixcIlRyYW5zYWN0aW9uSWRcIjpcIjcwNmhvdkw4REsxdElHR3pRVVYxXCJ9IiwiZXhwIjoxNzEyMzA2MDk0LCJpYXQiOjE3MTIzMDU0OTQsImp0aSI6IjE4YTIwYzNkLTZhZmMtNDA5My04NGEwLTQ2OGEyYTY5MTE0OCJ9.YEpOuTxnqrXRiHan-givWBd6FfTDJOfNg-h2dF2yA6A",
    "payload": "eyJtZXNzYWdlVHlwZSI6IkNSZXEiLCJtZXNzYWdlVmVyc2lvbiI6IjIuMS4wIiwidGhyZWVEU1NlcnZlclRyYW5zSUQiOiJiNDJlNTZjZi02ZDk2LTQzMzMtOGI2NC1iMmU4NWY0ZTE1ZTkiLCJhY3NUcmFuc0lEIjoiZWVjZWYxMDMtMDMwMS00ZTlkLTk1NjEtNzdiMmY3NTE2NGRhIiwiY2hhbGxlbmdlV2luZG93U2l6ZSI6IjA0In0"
  },
  "_actions": {
    "complete3dsChallenge": {
      "href": "https://try.access.worldpay.com/api/payments/eyJrIjoxLCJkIjoiZXlOaXNtU0lzQnVLTm5BQkt1WjEyMVFxeXg2bUZtb2hEcEpFeDdyYXZ3SDE3NFBpUTBsWUpwekptbW9hR3VVSyJ9/3dsChallenges",
      "method": "POST"
    }
  }
}

Optional MD field

Pass data specific to your checkout session and it will be echoed back in the challenge.returnUrl originally provided. This could, for example, be a checkout sessionId. Any value provided must be URL encoded with a maximum of 1024 characters.

Challenge display

The challenge display for React Native uses:

  • a WebView
  • a mobile-friendly HTML page
  • a fullscreen iframe, embedded in the mobile-friendly HTML page, used to display the challenge page to the end user
  • a secondary HTML page, loaded via the iframe, and used to kickstart the challenge display
  1. Install and link the React Native WebView component in your application.
# 1. Install dependency
npm install react-native-webview

# 2. Link dependency using your preferred method
react-native link react-native-webview

# 3. Ee-install pods
cd ios
pod install
  1. Add a WebView in your React Native application with the source pointing to the HTML page created in the next step.
React Native
<WebView source={{ uri: 'replace-this-with-the-url-of-the-mobile-friendly-page' }} />
  1. Create and host the mobile-friendly HTML page.
HTML
<html lang="en">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="margin: 0">
</body>
</html>
  1. Add a fullscreen iframe to the HTML page and set the src attribute to the URL of the page that will POST the challenge form.
HTML
<iframe src="replace-this-with-the-url-of-your-page-that-posts-the-challenge-form"
        width="100%" height="100%"
        style="height: 100%; width: 100%; border-width: 0">
</iframe>
  1. Create and host the secondary HTML page that POSTs the challenge form.
HTML
<html>
<head>
</head>
<body>

<!-- Using your preferred programming language, set the 'action' attribute with the value of the query string parameter containing the 'challenge.url' from the authentication response -->
<form id="challengeForm" method= "POST" action="https://challengeUrl.example.com">

    <!-- Using your preferred programming language, set the 'value' attribute with the value of the query string parameter containing the 'challenge.jwt' from the authentication response -->
    <input type = "hidden" name= "JWT" value= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1NDQzOGIzYS1iYjUzLTEyY2QtODY0My0xNTM2YmU3M2ZmMzUiLCJpYXQiOiIzODU2NzI5NDgyIiwiaXNzIjoiNWJkOWUwZTQ0NDRkY2UxNTM0MjhjOTQwIiwiT3JnVW5pdElkIjoiNWJkOWI1NWU0NDQ0NzYxYWMwYWYxYzgwIiwiUmV0dXJuVXJsIjoiaHR0cDovL21lcmNoYW50LmV4YW1wbGUuY29tL3RocmVlZHNjaGFsbGVuZ2Vjb21wbGV0ZSIsIlBheWxvYWQiOnsiQUNTVXJsIjoiaHR0cHM6Ly9hY3MuZXhhbXBsZS5jb20vM2RzMi9jaGFsbGVuZ2U_aWQ9MTIzNDU2Nzg5IiwiUGF5bG9hZCI6IlZHaHBjeUJwY3lCaElHSmhjMlVnTmpRZ1pXNWpiMlJsWkNCbGVHRnRjR3hsSUc5bUlHRWdNMFJUSUNKd1lYbHNiMkZrSWc9PSIsIlRyYW5zYWN0aW9uSWQiOiJzUk1QV0NRb1FyRWlWeGVoVG51MCJ9LCJPYmplY3RpZnlQYXlsb2FkIjp0cnVlfQ.3Dqjr5MuEC9AG7uvsJCft94-d70NmgR94zIeru8fAYE" />

    <!-- Optional field (max 1024 characters) for you to pass url parameters in the challenge form that will be included/echoed in the response url (`challenge.returnUrl`) after the challenge is complete -->
    <input type="hidden" name="MD" value="merchantSessionId=1234567890" />

</form>

<script>
  window.onload = function() {
    // Auto submit form on page load
    document.getElementById('challengeForm').submit();
  }
</script>

</body>
</html>

In addition, after the challenge is completed, if you wish to relay a message, you have sent by postMessage() to your mobile-friendly page, to the React Native layer:

  1. Use the following JavaScript in your mobile-friendly page to relay the message to React Native.
Javascript
<script language="JavaScript">
window.onmessage = (event) => {
  // Always verify that the message received is from the expected origin
  if (event.origin !== '...') {
    return;
  }

  // the function below uses a tring, so if you wish to send JSON
  // make sure to serialise it using JSON.stringify()
  window.ReactNativeWebView.postMessage(event.data);
};
</script>
  1. Add an event listener for the message event on your WebView.
React Native
<WebView
  source={{ uri: 'replace-this-with-the-url-of-the-mobile-friendly-page' }}
  onMessage={(event) => {
    // extracting data (string) from the event
    console.info(event.nativeEvent.data);
  }}
/>

The Testing page (3DS tab) contains an example form to submit the challenge values. This is useful if using tools such as postman/insomnia to test your integration in the early stages.

Full integration example code

# 1. Install dependency
npm install react-native-webview

# 2. Link dependency using your preferred method
react-native link react-native-webview

# 3. Ee-install pods
cd ios
pod install

5. Continue with the payment

Once the challenge form has been completed use the action from the 3dsDeviceData response. No body is required.

If you do not complete the challenge display the payment will fail when you post the action below.

// Action to continue the payment (from /3dsDeviceData response)
"_actions": {
    "complete3dsChallenge": {
      "href": "https://try.access.worldpay.com/api/payments/eyJrIjoxLCJkIjoiZXlOaXNtU0lzQnVLTm5BQkt1WjEyMVFxeXg2bUZtb2hEcEpFeDdyYXZ3SDE3NFBpUTBsWUpwekptbW9hR3VVSyJ9/3dsChallenges",
      "method": "POST"
    }
  }

If everything is fine the payment will proceed. You could receive the following two outcomes if the authentication has failed or a downstream error means the authentication details were not returned (unavailable).

{
	"transactionReference": "05651339-d94e-4fdd-82e9-a41d3df47c7d",
	"outcome": "3dsAuthenticationFailed",
	"authentication": {
		"version": "2.1.0",
		"eci": "07",
		"transactionId": "ec89944d-c5b1-4d4b-b39a-a2dc80dd5565"
	}
}

6. Outcome details

In the final payment response a summary of the 3DS authentication is included.

...
"threeDS": {
  "outcome": "authenticated",
  "issuerResponse": "frictionless"
}
...