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

# Card-On-File

# Overview

For External Cards-On-File, Synctera supports **Instant Payments**. An instant payment can be either:

* a `PULL` from an external card to fund an account on the Synctera platform or
* a `PUSH` or a payout from an account on the Synctera platform to an external card

For more details on supported scenarios, see External Cards.

## Prerequisites

To create an external card-on-file payment, you must first:

* Create a [Customer](https://docs.synctera.com/v2/docs/create-a-personal-customer)
* Create an [Account](https://docs.synctera.com/v2/docs/create-accounts-guide) for the customer
* Have External Cards enabled with the help of your Synctera implementation representative
* Tokenize card
* Add card-on-file using card token
* Enable 3-D Secure (3DS) authentication (`PULL` payments only)

# Enable 3-D Secure (3DS) Authentication

For additional security, Synctera External Cards supports 3-D Secure (3DS) authentication, a globally accepted authentication solution designed to increase security, reduce fraud and reduce chargebacks 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/).

3DS is only required for `PULL` transactions.

## 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:

* Production:     [https://songbird.cardinalcommerce.com/edge/v1/songbird.js](https://songbird.cardinalcommerce.com/edge/v1/songbird.js)
* Staging:        [https://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js](https://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js)

Code snippet:

```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
```

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

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

## 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)](#2-device-data-collection-ddc)
3. (**backend**) Call [Lookup 3DS](/v2/reference/lookup3ds)
4. (**frontend**) Depending on Lookup 3DS results, present [3DS Challenge](#4-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.

## 1. 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:**

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

**Example response:**

```json theme={"system"}
{
  "device_data_collection_jwt": "{DDC_JWT}",
  "device_data_collection_url": "{DDC_URL}",
  "id": "{3DS_ID}"
}
```

## 2. 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:

```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
```

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

## 3. 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.

> 📘
>
> To detect the `transaction_mode`, you can use [UAParser.js](https://www.npmjs.com/package/ua-parser-js):
>
> ```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";
> ```

`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:**

```shell theme={"system"}
curl \
  $baseurl/v0/external_cards/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"
  }'
```

**Example response (success):**

```json theme={"system"}
{
  "id": "{3DS_ID}",
  "status": "SUCCESS"
}
```

**Example response (challenge):**

```json theme={"system"}
{
  "challenge_payload": "{CHALLENGE_PAYLOAD}",
  "challenge_url": "{CHALLENGE_URL}",
  "id": "{3DS_ID}",
  "processor_transaction_id": "{PROCESSOR_TRANSACTION_ID}",
  "status": "CHALLENGE_REQUIRED"
}
```

## 4. 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:

```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}" },
  },
);
```

## 5. 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:**

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

**Example respons**e:

```json theme={"system"}
  {
    "id": "{3DS_ID}",
    "status": "SUCCESS"
  }'
```

## 6. 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:**

```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}",
  }'
```

## Sandbox Testing - 3DS

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) for information about how to create an External Card.

# Create External Card-On-File Payment

Once an External Card has been tokenized and added on file, it may be used to perform transactions. An `account_id` must be provided, which refers to the Synctera Account that the funds will flow into or out from, depending on the type of transaction.

The `merchant` object contains merchant descriptor information that will be shown on financial statements and transaction details. If not provided, default information, defined during onboarding, is used.

**Example request:**

```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": 2500,
    "merchant": {
        "name": "Jane Taylor’s Pub",
        "address": {
            "address_line_1": "4455 Vine Street",
            "city": "Lake Forest",
            "state": "IL",
            "postal_code": "60045",
            "country_code": "US"
        },
        "email": "janetaylorspub@email.com"
    }
  }'
```

**Example response:**

```json theme={"system"}
{
  "account_id": "{ACCOUNT_ID}",
  "amount": 2500,
  "country_code": "US",
  "created_time": "2023-01-18T17:37:59.449877-05:00",
  "currency": "USD",
  "customer_id": "{CUSTOMER_ID}",
  "external_card_id": "{EXTERNAL_CARD_ID}",
  "id": "{EXTERNAL_CARD_TRANSFER_ID}",
  "last_modified_time": "2023-01-18T17:37:59.449877-05:00",
  "merchant": {
      "name": "Jane Taylor’s Pub",
      "address": {
          "address_line_1": "4455 Vine Street",
          "city": "Lake Forest",
          "state": "IL",
          "postal_code": "60045",
          "country_code": "US"
      },
      "email": "janetaylorspub@email.com"
  },
  "status": "SUCCEEDED",
  "transaction_id": "{TRANSACTION_ID}",
  "type": "PULL"
}
```

The `status` field of the response indicates the outcome of the transaction:

* `SUCCEEDED`: The transaction was successful and funds are available - *terminal* status
* `DECLINED`: The transaction could not be completed due to a specific rule, e.g. low balance or velocity control - *terminal* status
* `CANCELED`: The transaction could not be completed due to error, e.g. upstream processing error - *terminal* status
* `UNKNOWN`: The transaction status is indeterminate - *non-terminal* status
* `PENDING`: The transaction has been initialized - *non-terminal* status

For the *non-terminal* status, you can subscribe to the `EXTERNAL_CARD_TRANSFER.UPDATED` webhook to be notified of status change, or simply [Get External Card Transfer](/v2/reference/getexternalcardtransfer) at a later time. Once a transaction is in a *terminal* status, it will not change.

`transaction_id` can be used to look up the transaction using the [Transactions API](/v2/reference/listpostedtransactions).

**3DS Response:**

If you receive the following response, it means 3-D Secure is required for this transaction and `three_ds_id` must be provided. See the previous section for more information. Note that this is only applicable to `PULL` transactions.

```json theme={"system"}
{
  "code": "EXTERNAL_CARD_TRANSFER_3DS_REQUIRED",
  "detail": "3-D Secure authorization required for this external card transfer",
  "status": 422,
  "title": "Rule Violation",
  "type": "https://dev.synctera.com/errors/rule-violation"
}
```

## Me-to-You PUSH Transactions

In the typical case, the originating Account and the External Card are assumed to be owned by the same Person (**me-to-me** transaction). However, it's also possible to initiate a (`PUSH` only) transaction to an External Card owned by Person who is not the originating Account owner (**me-to-you** transaction). In this scenario, `originating_customer_id` is supplied with the Person who owns the originating Account.
Note that an approval is required for this use case as it requires an MSB licence - see here.

**Example request:**

```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_customer_id": "{CUSTOMER_ID}": 
    "originating_account_id": "{ACCOUNT_ID}",
    "currency": "USD",
    "type": "PULL",
    "amount": 1000,
  }'
```

# Reverse External Card Transaction

Reversals can only be applied to `PULL` transactions. The full or partial amount may be reversed. However, only one partial reversal may be applied to a single transaction.

**Example request:**

```shell theme={"system"}
curl \
  $baseurl/v0/external_cards/transfers/{EXTERNAL_CARD_TRANSFER_ID}/reversals \
  -H "Authorization: Bearer $apikey" \
  -H 'Content-Type: application/json' \
  -d '
  {
    "currency": "USD",
    "amount": 2500
  }'
```

**Example response:**

```json theme={"system"}
{
  "account_id": "{ACCOUNT_ID}",
  "amount": 2500,
  "country_code": "US",
  "created_time": "2023-01-18T17:53:12.52114-05:00",
  "currency": "USD",
  "customer_id": "{CUSTOMER_ID}",
  "external_card_id": "{EXTERNAL_CARD_ID}",
  "id": "{EXTERNAL_CARD_TRANSFER_REVERSAL_ID}",
  "last_modified_time": "2023-01-18T17:53:12.52114-05:00",
  "merchant": {
      "name": "Jane Taylor’s Pub",
      "address": {
          "address_line_1": "4455 Vine Street",
          "city": "Lake Forest",
          "state": "IL",
          "postal_code": "60045",
          "country_code": "US"
      },
      "email": "janetaylorspub@email.com"
  },
  "status": "SUCCEEDED",
  "transaction_id": "{TRANSACTION_ID}",
  "type": "PULL_REVERSAL"
}
```

# Sandbox Testing

The following card PANs can be used in sandbox environment for testing various scenarios:

| Network    | PAN              | Push Enabled | Pull Enabled | Type    |
| ---------- | ---------------- | ------------ | ------------ | ------- |
| Visa       | 4005519200000004 | Y            | Y            | Debit   |
| Visa       | 4217651111111119 | Y            | N            | Debit   |
| Visa       | 4111111111111111 | Y            | Y            | Credit  |
| Mastercard | 2223000048400011 | N            | Y            | Debit   |
| Mastercard | 5105105105105100 | N            | N            | PrePaid |

> ⚠️
>
> Be aware that while testing in the sandbox environment, `amount` values `1`, `1100`, `2`, `1200`, `3`, `1300`, `4` and `1400` are special values reserved for generating upstream processor errors. They can be used in any [Create External Card Transfer](/v2/reference/createexternalcardtransfer) request to force an error to occur.
