> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synctera.com/llms.txt
> Use this file to discover all available pages before exploring further.

# External Cards 3-D Secure (3DS)

> For additional security, Synctera External Cards supports 3-D Secure (3DS), a globally accepted authentication solution designed to increase security and reduce fraud for e-commerce payments.

3DS operates at the per transaction level in real time at time of payment. Additional information about 3DS can be found [here](https://www.emvco.com/emv-technologies/3-d-secure/). Synctera's 3DS provider is [Cardinal Commerce](https://www.cardinalcommerce.com/), facilitated through [TabaPay](https://tabapay.com/).

In the context of External Cards, 3DS is only applicable to `PULL` transactions. Depending on your External Cards program, 3DS may be required for some or all `PULL` transaction. For more information, please talk to your Synctera implementation and onboarding contact.

For testing purposes, refer to this [list of test PANs](https://developers.tabapay.com/reference/3ds-test-cases-and-cards) from our vendor that can be used to test various 3DS scenarios in the sandbox environment. See [External Cards guide](/v2/docs/external-cards-guide) for information about how to create an External Card.

## Implementation

An implemention of External Cards 3DS authentication consists of **frontend** and **backend** components.

The **backend** is responsible for interacting with Synctera's three 3DS endpoints: [Initialize 3DS](/v2/reference/initialize3ds), [Lookup 3DS](/v2/reference/lookup3ds) and [Authenticate 3DS](/v2/reference/authenticate3ds).

The **frontend** is responsible for performing **Device Data Collection (DDC)** and (if necessary) presenting the cardholder with the **3DS Challenge**.

**Device Data Collection (DDC)**: A process which collects and sends details about the cardholder's physical device being used to complete the payment. It is sometimes the case that DDC is all that is required for a successful authentication. This is called a *frictionless* authentication.

**3DS Challenge**: If DDC is not enough to authenticate the cardholder, the card issuer may require a *challenge*, also known as a *step up*. This is a dynamic prompt which the cardholder must interact with to complete successfully in order to help prove they are the true card owner. The content and structure of the challenge varies and is determined by the issuer. For example, the cardholder may be required to provide a one time password (OTP) obtained through a separate channel.

### Frontend Implementation

This guide contains information about how to implement and execute the necessary 3DS frontend components, including code snippets for a *browser* implementation: `BROWSER` passed for `device_channel` of [Lookup 3DS](/v2/reference/lookup3ds) ([step 3](#3-lookup-3ds)).

For a *mobile app* (`SDK`) implementation, please refer to our vendor's [documentation](https://developers.tabapay.com/reference/how-to-use-the-3ds-sdk-starter-guide) for how to obtain and implement the 3DS SDK, provided by [JFrog](https://jfrog.com/). Your Synctera implementation and onboarding will provide you with JFrog credentials.

In order to perform the frontend processes, the Cardinal script must be loaded into the browser document according to environment:

<CardGroup cols={1}>
  <Card title="Production" icon="square-1" href="https://songbird.cardinalcommerce.com/edge/v1/songbird.js" iconType="solid" horizontal>
    [https://songbird.cardinalcommerce.com/edge/v1/songbird.js](https://songbird.cardinalcommerce.com/edge/v1/songbird.js)
  </Card>

  <Card title="Staging" icon="square-2" href="https://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js" iconType="solid" horizontal>
    [https://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js](https://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js)
  </Card>
</CardGroup>

Code snippet:

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  // Ensure any previous Cardinal instance is removed from the document.
  // After being used once, the Cardinal library cannot be reused.
  delete window.Cardinal;
  ​
  const headElement = document.getElementsByTagName('head')[0];
  const songbirdScriptElement = document.createElement('script');
  songbirdScriptElement.async = true;
  songbirdScriptElement.type = 'text/javascript';
  songbirdScriptElement.onload = () => {
    if (window.Cardinal) {
      // SUCCESS
    } else {
      // FAILURE
    }
  };
  songbirdScriptElement.onerror = () => undefined; // FAILURE
  songbirdScriptElement.src = SONGBIRD_LIBRARY_URL;
  ​
  setTimeout(() => undefined, 5000); // FAILURE
  ```
</CodeGroup>

If you would like debug information output by the Cardinal library, you can add this:

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  window.Cardinal.configure({
    logging: { level: 'on' },
  });
  ```
</CodeGroup>

## Flow

The entire flow of a 3DS authentication looks like this:

1. (**backend**) Initialize the 3DS authentication with [Initialize 3DS](/v2/reference/initialize3ds)
2. (**frontend**) Perform [Device Data Collection (DDC)](#device-data-collection-ddc)
3. (**backend**) Call [Lookup 3DS](/v2/reference/lookup3ds)
4. (**frontend**) Depending on Lookup 3DS results, present [3DS Challenge](#3ds-challenge) to cardholder
5. (**backend**) Validate 3DS Challenge results with [Authenticate 3DS](/v2/reference/authenticate3ds)
6. (**backend**) Attach successful 3DS authentication to External Card transaction

Note that results of [step 3](#3-lookup-3ds) dictate whether [step 4](#4-3ds-challenge) and [step 5](#5-authenticate-3ds) are necessary, or if you may proceed directly to [step 6](#6-attach-to-external-card-transaction).

Continue reading for complete details about each step.

<Steps>
  <Step title="Initialize 3DS">
    To begin a 3DS authentication, first call [Initialize 3DS](/v2/reference/initialize3ds), specifying the `external_card_id`, `amount`, and `currency`.

    If successful, `device_data_collection_jwt` and `device_data_collection_url` will be returned (used in [step 2](#2-device-data-collection-ddc)), and `id`, used in all subsequent 3DS calls. The JWT will expire in 2 hours, so DDC must be used before then.

    Example request:

    <CodeGroup>
      ```shell Shell theme={"system"}
      curl \
        $baseurl/v0/external_cards/transfers/initialize_3ds \
        -H "Authorization: Bearer $apikey" \
        -H 'Content-Type: application/json' \
        -d '
        {
          "external_card_id": "{EXTERNAL_CARD_ID}",
          "amount": 1500,
          "currency": "USD"
        }'
      ```
    </CodeGroup>

    Example response:

    <CodeGroup>
      ```json JSON theme={"system"}
      {
        "device_data_collection_jwt": "{DDC_JWT}",
        "device_data_collection_url": "{DDC_URL}",
        "id": "{3DS_ID}"
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Device Data Collection (DDC)">
    Using `{DDC_JWT}`, obtained from the previous step, perform DDC with the Cardinal library. This is a background process that does not require any user interaction. Only proceed to the next step once the process completes successfully.

    Code snippet:

    <CodeGroup>
      ```javascript JavaScript theme={"system"}
      // listen for DDC Process completion
      window.Cardinal.on('payments.setupComplete', (setupCompleteData) => {
        // finished - ready to proceed
      });
      ​
      // initialize Cardinal DDC process
      window.Cardinal.setup('init', { jwt: '{DDC_JWT}' });
      ​
      setTimeout(() => undefined, 5000); // FAILURE
      ```
    </CodeGroup>

    For additional information, please refer to our vendor's [documentation](https://developers.tabapay.com/reference/device-data-collection).
  </Step>

  <Step title="Lookup 3DS">
    Upon completion of DDC, the next step is to call [Lookup 3DS](/v2/reference/lookup3ds). `device_channel` must be set according to your 3DS [frontend implementation](#frontend-implementation): `SDK` for *mobile app*, or `BROWSER` for mobile or desktop internet *browser*. If the device channel is `BROWSER`, you can optionally provide `device_details`. This is a set of device data (collected separately by you) to be used as a fallback in case there is an issue with DDC.

    For `authentication_indicator`, select the options that best reflect the type of transaction being performed. For `transaction_mode`, select the correct device type.

    <Info>
      To detect the `transaction_mode`, you can use [UAParser.js](https://www.npmjs.com/package/ua-parser-js):

      <CodeGroup>
        ```javascript JavaScript theme={"system"}
        import UAParser from 'ua-parser-js';
        const ua = new UAParser();
        const { type: deviceType } = ua.getDevice();
        const transactionMode = deviceType === 'mobile'
        ? "MOBILE_DEVICE"
        : deviceType === 'tablet'
          ? "TABLET_DEVICE"
          : "COMPUTER_DEVICE";
        ```
      </CodeGroup>
    </Info>

    `status` from the response indicates the outcome:

    * `SUCCESS`: The cardholder was successfully authenticated - proceed to [step 6](#6-attach-to-external-card-transaction)
    * `FAILED`: The cardholder failed authentication
    * `CHALLENGE_REQUIRED`: A challenge is required to complete authentication - proceed to [step 4](#4-3ds-challenge)
    * `NOT_ENROLLED`: The card provider does not support 3DS, so authentication cannot be completed
    * `UNKNOWN`: An indiscriminate error occured with the 3DS authentication and it cannot be completed

    If `CHALLENGE_REQUIRED` status is returned, `processor_transaction_id`, `challenge_url` and `challenge_payload` will also be returned, which are needed to perform the 3DS challenge in the next step.

    Example request:

    <CodeGroup>
      ```shell Shell theme={"system"}
      curl \
        $baseurl/v0/external_cards/transfers/lookup_3ds \
        -H "Authorization: Bearer $apikey" \
        -H 'Content-Type: application/json' \
        -d '
        {
          "id": "{3DS_ID}",
          "authentication_indicator": "PAYMENT",
          "transaction_mode": "COMPUTER_DEVICE",
          "device_channel": "BROWSER"
        }'
      ```
    </CodeGroup>

    Example response (success):

    <CodeGroup>
      ```json JSON theme={"system"}
      {
        "id": "{3DS_ID}",
        "status": "SUCCESS"
      }
      ```
    </CodeGroup>

    Example response (challenge):

    <CodeGroup>
      ```json JSON theme={"system"}
      {
        "challenge_payload": "{CHALLENGE_PAYLOAD}",
        "challenge_url": "{CHALLENGE_URL}",
        "id": "{3DS_ID}",
        "processor_transaction_id": "{PROCESSOR_TRANSACTION_ID}",
        "status": "CHALLENGE_REQUIRED"
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="3DS Challenge">
    To trigger the challenge, `{CHALLENGE_URL}`, `{CHALLENGE_PAYLOAD}` and `{PROCESSOR_TRANSACTION_ID}` are required, obtained from the previous step. Once triggered, a modal window will be displayed containing the challenge for the user to complete. They have 10 minutes to complete the challenge before timing out.

    Use an event listener to handle the various challenge results upon completion:

    | Result     | Description                                                                                                                                                                                               |
    | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `SUCCESS`  | The challenge was completed successfully.                                                                                                                                                                 |
    | `NOACTION` | There was no service level error, but authentication was not applicable. If `challengeJwt` is returned, you may proceed to [step 5](#5-authenticate-3ds), but note that it may still result in a failure. |
    | `FAILURE`  | The user failed the challenge.                                                                                                                                                                            |
    | `CANCEL`   | The challenge was canceled by the user.                                                                                                                                                                   |
    | `ERROR`    | An error was encountered while completing the challenge.                                                                                                                                                  |
    | `TIMEOUT`  | The challenged timed out.                                                                                                                                                                                 |

    Any result except `SUCCESS` or `NOACTION` should be treated as a failure.

    Assuming a successful outcome, retain `challengeJwt` for performing [Authenticate 3DS](/v2/reference/authenticate3ds) ([step 5](#5-authenticate-3ds)). This must be performed within 2 hours or else the JWT will expire.

    Code snippet:

    <CodeGroup>
      ```javascript JavaScript theme={"system"}
      // event listener, triggered once the challenged is complete/cancelled (or error)
      window.Cardinal.on('payments.validated', (data, challengeJwt) => {
        switch (data.ActionCode) {
          case 'SUCCESS':
            return challengeJwt // SUCCESS
          case 'NOACTION':
            return challengeJwt || undefined // SUCCESS OR FAILURE
          case 'FAILURE':
            return // FAILURE
          case 'CANCEL':
            return // FAILURE
          case 'ERROR':
            return // FAILURE
          case 'TIMEOUT':
            return // FAILURE
          default:
            return // FAILURE
        }
        return undefined;
      });
      ​
      // trigger the challenge
      window.Cardinal.continue(
        'cca',
        { AcsUrl: "{CHALLENGE_URL}", Payload: "{CHALLENGE_PAYLOAD}" },
        {
          OrderDetails: { TransactionId: "{PROCESSOR_TRANSACTION_ID}" },
        },
      );
      ```
    </CodeGroup>
  </Step>

  <Step title="Authenticate 3DS">
    Assuming a positive result from the challenge, the last thing to do before the 3DS authentication is complete is to call [Authenticate 3DS](/v2/reference/authenticate3ds). You must provide `challenge_jwt`, obtained from the challenge.

    Example request:

    <CodeGroup>
      ```shell Shell theme={"system"}
      curl \
        $baseurl/v0/external_cards/transfers/authenticate_3ds \
        -H "Authorization: Bearer $apikey" \
        -H 'Content-Type: application/json' \
        -d '
        {
          "id": "{3DS_ID}",
          "challenge_jwt": "{CHALLENGE_JWT}"
        }'
      ```
    </CodeGroup>

    Example response:

    <CodeGroup>
      ```json JSON theme={"system"}
        {
          "id": "{3DS_ID}",
          "status": "SUCCESS"
        }'
      ```
    </CodeGroup>
  </Step>

  <Step title="Attach to External Card Transaction">
    Finally, once you have a successful 3DS authentication, you must provide the `id_3ds` in the transaction request. A successful authentication must be used within 90 days before expiring.

    Example request:

    <CodeGroup>
      ```bash Bash theme={"system"}
      curl \
        $baseurl/v0/external_cards/transfers \
        -H "Authorization: Bearer $apikey" \
        -H 'Content-Type: application/json' \
        -d '
        {
          "external_card_id": "{EXTERNAL_CARD_ID}",
          "originating_account_id": "{ACCOUNT_ID}",
          "currency": "USD",
          "type": "PULL",
          "amount": 1500,
          "3ds_id": "{3DS_ID}",
        }'
      ```
    </CodeGroup>
  </Step>
</Steps>
