Skip to content

3D Secure (3DS)

Introduction

3D Secure (3DS) is an authentication protocol designed to reduce fraud in online card payments. It adds an extra verification step where the cardholder's bank (issuer) authenticates the customer before the payment is authorized. The Payment Gateway supports 3D Secure 2.x, which provides a better user experience through risk-based authentication — many transactions are approved without customer interaction (frictionless flow), while higher-risk transactions require the cardholder to complete a challenge (e.g., entering an OTP or confirming in a banking app).

3DS authentication is a regulatory requirement under PSD2 Strong Customer Authentication (SCA) in the European Economic Area and is widely adopted by card networks globally.

When is 3DS Triggered?

Payment Method 3DS Behavior
Card (card) 3DS is always initiated
Saved Card (saved_card) 3DS is always initiated
Google Pay (google-pay) 3DS is initiated for PAN_ONLY tokens; skipped for CRYPTOGRAM_3DS tokens
Network Token (network-token) 3DS is skipped when a valid cryptogram and ECI are provided
OCT No 3DS — Original Credit Transactions do not require authentication
Payout No 3DS — Payouts do not require authentication

3DS Flow Overview

When a card transaction requires 3DS authentication, the response from the /api/transactions/authorize endpoint will include additional fields that guide the merchant through the authentication process. The flow involves up to two steps: device fingerprinting and challenge authentication.

sequenceDiagram
    participant Customer
    participant Merchant
    participant Gateway as Payment Gateway
    participant ACS as Issuer ACS

    Merchant ->> Gateway: POST /api/transactions/authorize
    Gateway -->> Merchant: Response with action (THREE_DS_2_FINGERPRINT)

    Note over Merchant: Step 1 — Device Fingerprint
    Merchant ->> Merchant: Decode token from action response
    Merchant ->> ACS: POST threeDSMethodData (hidden iframe)
    ACS -->> Gateway: Notification (via three_ds_method_notification_url)

    Merchant ->> Gateway: POST /api/transactions/three-ds/{id}/request-authentication
    Gateway -->> Merchant: Response (approved, or challenge required)

    alt Frictionless Flow
        Note over Merchant: Transaction approved without customer interaction
    else Challenge Required
        Note over Merchant: Step 2 — Challenge
        Merchant ->> Merchant: Decode token, build CReq
        Merchant ->> ACS: POST creq (full-page redirect to ACS)
        Customer ->> ACS: Complete authentication (OTP, biometric, etc.)
        ACS -->> Gateway: Authentication result (CRes)
        Gateway ->> Gateway: Process authentication & authorize transaction
        Gateway -->> Customer: Redirect to merchant return_url
        Note over Merchant: Customer returned — verify status via API or webhook
    end

Response Handling

After calling /api/transactions/authorize, the response contains four top-level fields. Which fields are populated determines the next step:

Response Scenario result action redirect form_submit Next Step
Approved (no 3DS needed) Transaction data null null null Done — transaction is approved
Declined Transaction data null null null Done — transaction is declined
Fingerprint required null Action details null null Perform device fingerprinting
Redirect required null null Redirect URL null Redirect customer to URL
Form submit required null null null Form details Auto-submit form to URL

Step 1 — Device Fingerprint

When the response contains an action object with type: "THREE_DS_2_FINGERPRINT", you must collect the customer's device fingerprint by submitting a hidden form to the issuer's Access Control Server (ACS).

Action Response Structure

{
  "result": null,
  "action": {
    "transaction_id": "vvIVFmPuwYosePLsoDsW",
    "session_id": "abc123-session-id",
    "type": "THREE_DS_2_FINGERPRINT",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
    "payment_data": "encrypted_payment_data_string"
  },
  "redirect": null,
  "form_submit": null
}

Action Fields

Field Type Description
transaction_id string The transaction identifier
session_id string The session identifier for this 3DS flow
type string The action type — see Action Type Values below
token string Base64-encoded JSON containing ACS URLs and 3DS server transaction data
payment_data string Encrypted payment data — must be sent back when completing the transaction

Action Type Values

The type field uses the following values:

Value Meaning Next Step
THREE_DS_2_FINGERPRINT Device fingerprint collection required Perform device fingerprinting
THREE_DS_2_CHALLENGE 3DS challenge authentication required Perform challenge authentication

Match on action type correctly

Use the exact values above when checking the type field. For example, use action.type === "THREE_DS_2_FINGERPRINT" and action.type === "THREE_DS_2_CHALLENGE" in your implementation. Do not check for shortened values like "fingerprint" or "challenge".

Decoded Token Structure

The token field is a Base64-encoded JSON object with the following structure:

{
  "acs_url": "https://acs.issuer.com/challenge",
  "message_version": "2.2.0",
  "three_ds_server_trans_id": "abc123-server-trans-id",
  "three_ds_method_notification_url": "https://gateway.example.com/three-ds/notification",
  "three_ds_method_url": "https://acs.issuer.com/fingerprint",
  "acs_trans_id": "xyz789-acs-trans-id",
  "challenge_window_size": "05"
}
Field Description
three_ds_method_url URL to submit the fingerprint form to
three_ds_server_trans_id 3DS server transaction ID — used in subsequent API calls
three_ds_method_notification_url URL where the ACS will notify the gateway of fingerprint completion
acs_url ACS URL for challenge step (used in Step 2 if needed)
acs_trans_id ACS transaction ID (used in Step 2 if needed)
message_version 3DS protocol version
challenge_window_size Challenge window size code (used in Step 2 if needed)

Implementation

1. Decode the token:

const decodedToken = JSON.parse(atob(action.token));

2. Build the threeDSMethodData:

Construct a JSON object containing the server transaction ID and notification URL, then Base64 URL-encode it:

const fingerprintData = {
  threeDSServerTransID: decodedToken.three_ds_server_trans_id,
  threeDSMethodNotificationURL: decodedToken.three_ds_method_notification_url
};
const threeDSMethodData = btoa(JSON.stringify(fingerprintData));

3. Create and auto-submit the hidden form:

<iframe id="fingerprint-iframe" name="fingerprint-iframe"
        style="display:none;" width="0" height="0"></iframe>

<form id="fingerprint-form" method="POST"
      action="<decodedToken.three_ds_method_url>"
      target="fingerprint-iframe">
  <input type="hidden" name="threeDSMethodData"
         value="<threeDSMethodData>" />
</form>

<script>
  document.getElementById('fingerprint-form').submit();
</script>

4. Wait for the fingerprint to complete (timeout: ~5 seconds), then proceed to request authentication.

Requesting Authentication

After the fingerprint step, request authentication by calling the 3DS authentication endpoint:

POST /api/transactions/three-ds/{threeDsServerTransId}/request-authentication
Authorization: Bearer <access_token>
Content-Type: application/json
{
  "fingerprint_result": "<base64-encoded completion indicator>",
  "payment_data": "<payment_data from the action response>"
}

The response uses the same structure as the initial /api/transactions/authorize response. Check the top-level fields to determine the next step:

  • If result is present — the transaction is finalized (frictionless flow: approved or declined)
  • If action is present with type: "THREE_DS_2_CHALLENGE" — proceed to challenge authentication
  • If redirect is present — redirect the customer
  • If form_submit is present — auto-submit the form

Step 2 — Challenge

If the issuer requires additional customer authentication after the fingerprint step, the response will indicate a challenge. This can be returned as an action with type: "THREE_DS_2_CHALLENGE", a redirect, or a form_submit.

Action with Challenge Type

When the response contains an action with type: "THREE_DS_2_CHALLENGE":

{
  "result": null,
  "action": {
    "transaction_id": "vvIVFmPuwYosePLsoDsW",
    "session_id": "abc123-session-id",
    "type": "THREE_DS_2_CHALLENGE",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
    "payment_data": "encrypted_payment_data_string"
  },
  "redirect": null,
  "form_submit": null
}

1. Decode the token (same structure as the fingerprint token):

const decodedToken = JSON.parse(atob(action.token));

2. Build the Challenge Request (CReq):

const creq = {
  threeDSServerTransID: decodedToken.three_ds_server_trans_id,
  acsTransID: decodedToken.acs_trans_id,
  messageVersion: decodedToken.message_version,
  messageType: "CReq",
  challengeWindowSize: decodedToken.challenge_window_size
};
const encodedCReq = btoa(JSON.stringify(creq));

3. Submit the CReq to the ACS:

You can submit the CReq using either a full-page redirect or an iframe. Choose the approach that fits your integration:

Redirect the customer's browser to the ACS for authentication. This is the simplest approach — the gateway handles the authentication result and redirects the customer back to your return_url with the processed transaction result.

<form id="challenge-form" method="POST"
      action="<decodedToken.acs_url>">
  <input type="hidden" name="creq" value="<encodedCReq>" />
</form>

<script>
  document.getElementById('challenge-form').submit();
</script>

After the customer completes the challenge, the gateway processes the authentication, authorizes the transaction, and redirects the customer to your return_url. See After Authentication for details on verifying the transaction status.

No server-side callback needed

With the full-page redirect approach, the gateway handles the /authentication-completed step internally. You do not need to call this endpoint yourself — the transaction result is delivered directly to your return_url.

Display the challenge UI within your page using an iframe. This approach keeps the customer on your page and gives you more control over the UX, but requires handling the challenge response client-side.

<iframe id="challenge-iframe" name="challenge-iframe"
        width="100%" height="400"></iframe>

<form id="challenge-form" method="POST"
      action="<decodedToken.acs_url>"
      target="challenge-iframe">
  <input type="hidden" name="creq" value="<encodedCReq>" />
</form>

<script>
  document.getElementById('challenge-form').submit();
</script>

The customer will see the issuer's authentication UI inside the iframe (e.g., OTP input, biometric prompt).

4. Listen for the Challenge Response (CRes):

The ACS sends the challenge result back via a window.message event:

const acsOrigin = new URL(decodedToken.acs_url).origin;
window.addEventListener('message', function(event) {
  if (event.origin !== acsOrigin) return; // Ignore messages from unexpected origins
  const data = JSON.parse(event.data);
  if (data.type === 'CHALLENGE_RESPONSE') {
    const transStatus = data.trans_status;
    // Proceed to submit the result to the gateway
  }
});

5. Submit the authentication result to the gateway:

POST /api/transactions/three-ds/authentication-completed
Authorization: Bearer <access_token>
Content-Type: application/json
{
  "payment_data": "<payment_data from the action response>",
  "session_id": "<session_id from the action response>",
  "trans_status": "<trans_status from CRes>"
}

The response follows the same structure as the initial /api/transactions/authorize response — check the result, action, redirect, and form_submit fields to determine the next step.

Additional authentication may be required

The /authentication-completed endpoint does not always return a final transaction result. If the issuer determines that further authentication is needed, the response will contain a new action, redirect, or form_submit — just like the initial authorization response.

When this happens, handle the response exactly as you would after the initial /api/transactions/authorize call:

  • If action is present with type: "THREE_DS_2_FINGERPRINT" — perform device fingerprinting again
  • If action is present with type: "THREE_DS_2_CHALLENGE" — perform challenge authentication again
  • If redirect is present — redirect the customer
  • If form_submit is present — auto-submit the form
  • If result is present — the transaction is finalized (approved or declined)

This additional authentication round can occur at most once per authentication attempt. If the issuer requests further authentication after the second round, the transaction will be automatically declined. However, if the transaction is declined and the gateway auto-retries with a different provider, a new 3DS flow may be initiated — see Limits for details.

Transaction Status Values (trans_status)

Value Meaning
Y Authentication successful
N Not authenticated (denied)
U Authentication could not be performed
A Authentication attempted
C Challenge required (additional interaction needed)
R Authentication rejected

Redirect Flow

When the response contains a redirect object instead of an action:

{
  "result": null,
  "action": null,
  "redirect": {
    "transaction_id": "vvIVFmPuwYosePLsoDsW",
    "session_id": "abc123-session-id",
    "url": "https://acs.issuer.com/challenge?id=..."
  },
  "form_submit": null
}

Redirect the customer's browser to the provided url. After authentication, the gateway processes the transaction and redirects the customer back to your return_url. See After Authentication for details on verifying the transaction status.

Form Submit Flow

When the response contains a form_submit object:

{
  "result": null,
  "action": null,
  "redirect": null,
  "form_submit": {
    "transaction_id": "vvIVFmPuwYosePLsoDsW",
    "session_id": "abc123-session-id",
    "url": "https://acs.issuer.com/challenge",
    "data": {
      "PaReq": "eJxVUt1ugjAUvl...",
      "MD": "vvIVFmPuwYosePLsoDsW",
      "TermUrl": "https://gateway.example.com/callback"
    }
  }
}

Create a form with the provided fields and auto-submit it:

<form id="challenge-form" method="POST"
      action="https://acs.issuer.com/challenge">
  <input type="hidden" name="PaReq" value="eJxVUt1ugjAUvl..." />
  <input type="hidden" name="MD" value="vvIVFmPuwYosePLsoDsW" />
  <input type="hidden" name="TermUrl" value="https://gateway.example.com/callback" />
</form>

<script>
  document.getElementById('challenge-form').submit();
</script>

After authentication, the gateway processes the transaction and redirects the customer back to your return_url. See After Authentication for details on verifying the transaction status.


Challenge Notification URL

After the customer completes the 3DS challenge, the issuer's ACS sends the result (CRes) back by POSTing an application/x-www-form-urlencoded request to a notification URL. How this URL is resolved depends on your integration type:

Integration Default notification URL Who handles the ACS callback
Hosted Payment Page (HPP) https://api.finrelay.io/three-ds/{id}/hosted-payment-page/challenge-notification-url Gateway — no action required
Embedded Fields (EF) https://api.finrelay.io/three-ds/{id}/embedded-fields/challenge-notification-url Gateway — no action required
Direct API https://api.finrelay.io/three-ds/{id}/challenge-notification-url (overridable) Gateway by default; you if you supply options.three_ds.notification_url

Overriding the Notification URL (API Only)

For Direct API integrations, you can terminate the ACS callback on your own backend by supplying options.three_ds.notification_url in the authorize request. This is useful when you want to run server-side code immediately after the challenge completes — for example, to log the result, update an order, or apply custom routing before finalizing the transaction.

{
  "terminal_id": "{TERMINAL_ID}",
  "reference": "ORDER-123456",
  "currency": "EUR",
  "amount": 10000,
  "transaction_type": "AUTHORIZE",
  "payment_method": { ... },
  "customer": { ... },
  "browser_info": { ... },
  "options": {
    "three_ds": {
      "notification_url": "https://your-domain.com/3ds/challenge-complete"
    }
  }
}

API integration only

The override is honored only when the transaction's integration type is DIRECT_API. For Hosted Payment Page and Embedded Fields the field is ignored and the gateway-managed URL is always used — do not send it for those flows.

Validation Rules

The supplied URL is validated at the HTTP boundary. Invalid values cause /api/transactions/authorize to return 400 Bad Request before any transaction is created:

  • Must be https://
  • Must be a well-formed URL with a host
  • Must not exceed 256 characters (EMV 3DS 2.x notificationURL limit)

Required Follow-up: authentication-completed

When you override the notification URL, the gateway no longer learns that the challenge has finished — the ACS is posting to your server, not ours. You are responsible for calling the authentication-completed endpoint to let the gateway finalize the authorization.

1. Receive the ACS callback. The ACS sends an application/x-www-form-urlencoded POST to the URL you supplied, with a cres parameter containing the Base64URL-encoded challenge response:

POST https://your-domain.com/3ds/challenge-complete
Content-Type: application/x-www-form-urlencoded

cres=eyJ0aHJlZURTU2VydmVyVHJhbnNJRCI6Ii4uLiIsInRyYW5zU3RhdHVzIjoiWSJ9

2. Decode the CRes to extract transStatus and threeDSServerTransID:

// Node.js 16+ — 'base64url' encoding is built-in
const cresBase64Url = req.body.cres;
const cresJson = Buffer.from(cresBase64Url, 'base64url').toString('utf8');
const cres = JSON.parse(cresJson);
// cres = { threeDSServerTransID: "...", transStatus: "Y", ... }

On runtimes without native Base64URL support, convert to standard Base64 first (-+, _/, re-pad with =) and then decode — for example with atob in the browser or the Web Crypto-compatible APIs in Deno/Bun.

3. Call authentication-completed from your server with the session_id (the 3DS server transaction ID) and trans_status:

POST /api/transactions/three-ds/authentication-completed
Authorization: Bearer <access_token>
Content-Type: application/json
{
  "session_id": "<cres.threeDSServerTransID>",
  "trans_status": "<cres.transStatus>",
  "payment_data": "<payment_data from the challenge action response>"
}

All three fields are strings. payment_data is the opaque blob returned on the original challenge action response (the same value you decoded and forwarded to the ACS when building the CReq) — pass it through unchanged.

The response follows the same shape as /api/transactions/authorize — inspect result, action, redirect, form_submit to determine the next step. See Handling Repeated Authentication.

4. Navigate the customer. Because the ACS callback lands on your server (not on a page the customer sees), your endpoint must also return an HTML response that moves the browser on — typically an auto-submitting form, a meta-refresh, or a 302 redirect to the next step in your checkout.

<!DOCTYPE html>
<html>
  <body onload="document.forms[0].submit()">
    <form method="POST" action="https://your-domain.com/checkout/3ds-complete">
      <input type="hidden" name="order_id" value="ORDER-123456" />
      <noscript>
        <button type="submit">Continue</button>
      </noscript>
    </form>
  </body>
</html>

When you do not override the notification URL

With the default gateway-managed URL you do not need to call authentication-completed yourself — the gateway receives the CRes, finalizes the authorization, and the browser is redirected to your return_url (HPP) or delivered a window.postMessage (EF).


Handling Repeated Authentication

In certain scenarios, the Payment Gateway may return an action, redirect, or form_submit response after you have already completed a 3DS authentication step. This means the issuer or the gateway requires additional authentication before the transaction can be finalized.

When Does This Happen?

After completing a challenge (/authentication-completed): The issuer may request an additional authentication round. For example, after submitting the challenge result with trans_status: "C", the gateway may return a new action with type: "THREE_DS_2_FINGERPRINT" or type: "THREE_DS_2_CHALLENGE".

After a transaction is declined and automatically retried: When a transaction is declined, the Payment Gateway may automatically retry the transaction with a different payment provider. This retry goes through a full authorization flow, which includes 3DS authentication. As a result, you may receive a new action, redirect, or form_submit response that requires the customer to complete 3DS authentication again — this time with the new provider.

How to Handle It

Your integration should always check the response structure after every API call in the 3DS flow, not just after the initial /api/transactions/authorize:

1. Check if `result` is present → Transaction is finalized (approved/declined)
2. Check if `action` is present → Handle fingerprint or challenge
3. Check if `redirect` is present → Redirect the customer
4. Check if `form_submit` is present → Auto-submit the form

Build a response handler, not a step counter

Rather than coding a linear sequence of steps, implement a single response handler that inspects each response and routes to the appropriate action. This naturally handles repeated authentication without special-case logic.

Limits

The gateway enforces limits at two levels:

  • Per-authentication attempt: A maximum of one additional authentication round is allowed. If the issuer requests further authentication after this, the attempt will be automatically declined.
  • Fallback routing: When a transaction is declined, the gateway may automatically retry with a different payment provider — up to N times (configurable, default is 5). Each retry goes through a full authorization and 3DS flow, so the customer may be asked to authenticate multiple times across retries.

After Authentication

Once the customer completes the 3DS challenge (via full-page redirect, redirect, or form submit flows), the gateway processes the authentication result and authorizes the transaction. The customer is then redirected back to your return_url regardless of the outcome (approved, declined, canceled, or error).

Auto-retries

If the gateway performed an auto-retry with a different provider, the customer may need to complete 3DS again before reaching a final status.

Verifying Transaction Status

The redirect to your return_url indicates that the transaction has been processed, but does not include the transaction result in the URL. To determine the outcome (approved or declined), verify the transaction status server-side:

GET /api/transactions/{transaction_id}/status
Authorization: Bearer <access_token>

The transaction will be in one of the following statuses:

Status Description
APPROVED Authentication successful, transaction authorized
DECLINED Authentication failed or transaction was declined by the issuer
CANCELED Customer canceled or abandoned the challenge
SESSION_EXPIRED Authentication session timed out before the customer completed the challenge

Use Webhooks

For the most reliable status updates, configure a webhook to receive real-time notifications when the transaction reaches a final status. This is especially important for handling edge cases where the customer closes the browser before being redirected back to your site.


Browser Info

The browser_info object is required for all card transactions and is used by the issuer to assess risk during 3DS authentication. You must collect this information from the customer's browser.

Required Fields

Field Type Description Example
user_agent string The browser's User-Agent header "Mozilla/5.0..."
accept_header string The browser's Accept header "text/html,application/xhtml+xml..."
java_enabled boolean Whether Java is enabled false
color_depth integer Screen color depth in bits 24
screen_height integer Screen height in pixels 1080
screen_width integer Screen width in pixels 1920
time_zone_offset integer Timezone offset from UTC in minutes -120
language string Browser language "en-US"

JavaScript Collection Example

const browserInfo = {
  user_agent: navigator.userAgent,
  accept_header: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  java_enabled: navigator.javaEnabled(),
  color_depth: screen.colorDepth,
  screen_height: screen.height,
  screen_width: screen.width,
  time_zone_offset: new Date().getTimezoneOffset(),
  language: navigator.language
};

Accept Header

The accept_header value cannot be read directly from JavaScript. Use a standard value as shown above, or capture it server-side from the customer's HTTP request headers.


Timeouts

Step Timeout Description
Device fingerprint 5 seconds If the ACS does not respond within this time, proceed to request authentication regardless
Challenge 10 minutes Maximum time allowed for the customer to complete the challenge

Complete Integration Example

Below is a simplified example of the full 3DS flow for a card payment:

sequenceDiagram
    participant Browser as Customer Browser
    participant Server as Merchant Server
    participant Gateway as Payment Gateway
    participant ACS as Issuer ACS

    Browser ->> Server: Checkout (card details + browser info)
    Server ->> Gateway: POST /api/transactions/authorize
    Gateway -->> Server: action (type: THREE_DS_2_FINGERPRINT, token, payment_data)
    Server -->> Browser: Decoded token + payment_data

    Note over Browser: Step 1 — Device Fingerprint
    Browser ->> Browser: Build threeDSMethodData from token
    Browser ->> ACS: POST threeDSMethodData (hidden iframe to threeDsMethodUrl)
    Note over Browser: Wait up to 5 seconds

    Browser ->> Server: Fingerprint complete
    Server ->> Gateway: POST /api/transactions/three-ds/{id}/request-authentication
    Gateway -->> Server: Frictionless approval or challenge action

    alt Approved (Frictionless)
        Server -->> Browser: Payment successful
    else Challenge Required (action)
        Server -->> Browser: Challenge token + payment_data
        Note over Browser: Step 2 — Challenge
        Browser ->> Browser: Build CReq from token
        Browser ->> ACS: POST creq (full-page redirect to acsUrl)
        Browser ->> ACS: Customer completes authentication
        ACS -->> Gateway: Authentication result (CRes)
        Gateway ->> Gateway: Process authentication & authorize transaction
        Gateway -->> Browser: Redirect to merchant return_url
        Note over Browser: Customer returned — verify status via API or webhook
    else Redirect Required
        Server -->> Browser: Redirect URL
        Browser ->> ACS: Browser redirect
        ACS -->> Gateway: Authentication result
        Gateway -->> Browser: Redirect to merchant return_url
        Note over Browser: Customer returned — verify status via API or webhook
    end

Simulator

The 3DS authentication simulator allows you to test different authentication flows without connecting to a real 3DS Directory Server or Access Control Server. When your merchant account is configured with the authentication-simulator service, you can control the 3DS flow by passing the simulated_flow field in the transaction metadata.

Controlling the 3DS Flow

Include the simulated_flow field in your authorization request metadata to select which authentication scenario to simulate:

{
  "terminal_id": "your-terminal-id",
  "payment_method": {
    "type": "card",
    "card": {
      "encrypted_data": "..."
    }
  },
  "metadata": {
    "simulated_flow": "VERSIONING_FINGERPRINT_Y_CHALLENGE"
  }
}

If simulated_flow is not provided, the simulator defaults to VERSIONING_FINGERPRINT_Y_CHALLENGE.

Available Simulated Flows

Flow Fingerprint Authentication Result Description
VERSIONING_NOT_ENROLLED N/A Card is not enrolled in 3DS. The gateway proceeds directly to authorization without authentication.
VERSIONING_FINGERPRINT_Y_CHALLENGE Yes (with method URL) Challenge → Y Full 3DS flow: fingerprint collection via method URL, then challenge authentication. The challenge completes with trans_status: Y (authenticated). This is the default flow.
VERSIONING_FINGERPRINT_TIMEOUT_CHALLENGE Yes (simulates timeout) Challenge → Y Same as above, but the fingerprint step simulates a timeout. The flow continues to challenge authentication after the timeout.
VERSIONING_FINGERPRINT_TIMEOUT_FRICTIONLESS Yes (simulates timeout) Frictionless A Fingerprint step simulates a timeout, but the issuer approves frictionlessly with trans_status: A (attempted).
VERSIONING_FINGERPRINT_N_CHALLENGE No (no method URL) Challenge → Y No fingerprint collection (no method URL returned). Proceeds directly to challenge authentication.
VERSIONING_FINGERPRINT_N_FRICTIONLESS No (no method URL) Frictionless N No fingerprint collection. The issuer returns trans_status: N (not authenticated) — the transaction will be declined.
VERSIONING_FINGERPRINT_A_FRICTIONLESS No (no method URL) Frictionless A No fingerprint collection. The issuer returns trans_status: A (attempted) — the transaction is approved frictionlessly.

Flow Details

Flows with Fingerprint (Method URL)

The flows VERSIONING_FINGERPRINT_Y_CHALLENGE and VERSIONING_FINGERPRINT_TIMEOUT_CHALLENGE return a ThreeDsSupportedResponse that includes a threeDsMethodUrl. Your integration should perform the device fingerprinting step before requesting authentication.

  • VERSIONING_FINGERPRINT_Y_CHALLENGE: The method URL responds normally, allowing the fingerprint to complete within the timeout.
  • VERSIONING_FINGERPRINT_TIMEOUT_CHALLENGE: The method URL deliberately delays its response, simulating a timeout scenario. Your integration should proceed after the 5-second fingerprint timeout.

Both flows result in a challenge being returned after the authentication request.

Flows without Fingerprint (No Method URL)

The flows VERSIONING_FINGERPRINT_N_CHALLENGE, VERSIONING_FINGERPRINT_N_FRICTIONLESS, and VERSIONING_FINGERPRINT_A_FRICTIONLESS return a versioning response without a threeDsMethodUrl. Your integration should skip the fingerprint step and proceed directly to requesting authentication.

  • VERSIONING_FINGERPRINT_N_CHALLENGE: Authentication request returns a challenge.
  • VERSIONING_FINGERPRINT_N_FRICTIONLESS: Authentication request returns a frictionless result with trans_status: N — the transaction is declined without customer interaction.
  • VERSIONING_FINGERPRINT_A_FRICTIONLESS: Authentication request returns a frictionless result with trans_status: A — the transaction is approved without customer interaction.

Not Enrolled Flow

VERSIONING_NOT_ENROLLED returns a ThreeDsNotSupportedResponse, indicating the card is not enrolled in 3DS. The gateway skips authentication entirely and proceeds to authorization.

Customizing Challenge Responses

You can override fields in the challenge response by including an authentication_simulator object in your transaction metadata. This allows you to test specific challenge outcomes:

{
  "metadata": {
    "simulated_flow": "VERSIONING_FINGERPRINT_Y_CHALLENGE",
    "authentication_simulator": {
      "transStatus": "N",
      "eci": "07",
      "authenticationValue": "custom-auth-value"
    }
  }
}

Available override fields in authentication_simulator:

Field Type Default Description
transStatus string Y The challenge result status (Y, N, U, A, C, R)
eci string "05" Electronic Commerce Indicator
authenticationValue string (simulator default) The authentication value (CAVV)
messageVersion string "2.2.0" 3DS message version
dsTransID string (simulator default) Directory Server transaction ID

Testing Scenarios

Here are common integration test scenarios and the metadata to use:

Happy Path — Full 3DS with Challenge

{
  "metadata": {
    "simulated_flow": "VERSIONING_FINGERPRINT_Y_CHALLENGE"
  }
}

Tests the complete flow: fingerprint → challenge → approved.

Frictionless Approval

{
  "metadata": {
    "simulated_flow": "VERSIONING_FINGERPRINT_A_FRICTIONLESS"
  }
}

Tests a frictionless approval without customer interaction.

Frictionless Decline (Not Authenticated)

{
  "metadata": {
    "simulated_flow": "VERSIONING_FINGERPRINT_N_FRICTIONLESS"
  }
}

Tests a frictionless decline where the issuer rejects the authentication.

Card Not Enrolled in 3DS

{
  "metadata": {
    "simulated_flow": "VERSIONING_NOT_ENROLLED"
  }
}

Tests behavior when the card does not support 3DS authentication.

Fingerprint Timeout

{
  "metadata": {
    "simulated_flow": "VERSIONING_FINGERPRINT_TIMEOUT_FRICTIONLESS"
  }
}

Tests that your integration handles a fingerprint timeout correctly and proceeds to authentication.