ChargX Documentation

3DS

3-D Secure reduces your e-commerce payment fraud risk and increases customer confidence.

3DS is a fraud-prevention service that verifies transactions before they are sent to the payment gateway for processing.

It works by securely exchanging information between the merchant, the card issuer, and the customer to confirm that the transaction is being initiated by the legitimate cardholder.

In most cases, this authentication happens seamlessly in the background without any customer interaction—commonly referred to as a frictionless flow. In rare situations, the card issuer may require additional verification. This challenge flow prompts the customer to confirm their identity using extra authentication steps, such as a one-time password.

The resulting authentication data is included in the transaction request, helping reduce fraud, prevent chargebacks, and protect your business from unauthorized or malicious activity.

Quick Start Guide

WooCommerce plugin

If you use WooCommerce plugin - make sure you use 0.18.2+ version.

Once installed, you will see 2 new settings:

  • Enable 3-D Secure (off by default)
  • DOM selector to mount 3DS UI element. The plugin provides default container #threeds-placeholder, but you can have your own and set its selector here:
3-D Secure configuration in WooCommerce plugin

Once 3-D Secure UI is mounted and needs user verification, it will be displayed below the credit card form - this is where #threeds-placeholder element is located:

3-D Secure verificationin WooCommerce plugin

Custom integration

When you do custom integration and build your custom UI, follow this guide:

1. Add the Gateway.js Script tag to your Checkout page

<script 
  src="https://secure.networkmerchants.com/js/v1/Gateway.js">
</script>

2. Initialize the library

Perform Retrieve pretransact keys API request and obtain gatewayPublicKey in response.

Then use it to initialize the library:

const gateway = Gateway.create(pretransactResponse.gatewayPublicKey);

3. Initialize the 3DS client:

const threeDS = gateway.get3DSecure();

4. Create a UI object with the details of the transaction

Obtain token from response of Exchange credit card details for a token API request and use as for paymentToken field:

const options = {
  paymentToken: "...", 
  currency: 'USD',
  amount: '100.00',
  firstName: 'John',
  lastName: 'Samuels',
  email: 'johnsamuels@example.com',
  address1: '11th Street 47 W',
  city: 'New York',
  state: 'NY',
  country: 'US',
  postalCode: '10001',
  phone: '+1 212-555-0123',
};

const threeDSUI = threeDS.createUI(options);

5. Start the authentication process

Mount the 3DS UI into the DOM by specifying a selector. The frame will be rendered inside the target element.

<div id="threeds-placeholder"></div>
  
...

threeDSUI.start('#threeds-placeholder');

Important

JavaScript frameworks such as React, Vue, or Angular often manage the DOM dynamically, which can cause the frame to be unintentionally removed. To ensure the interface remains visible, make sure the framework does not control or re-render the element used as the mount point.

6. Listen callbacks

Callbacks are used to detect when the customer has completed the authentication process:

// Listen for the 'challenge' callback to ask the user for a password
threeDSUI.on('challenge', (e) => {
  console.log('[3DS] Challenged', e);
});

// Listen for the 'complete' callback to provide all the needed 3DS data
threeDSUI.on('complete', (e) => {
  console.log('[3DS] complete', e);
});

// Listen for the 'failure' callback to indicate that the customer has failed to authenticate
threeDSUI.on('failure', (e) => {
  console.error('[3DS] failure', e);

  if (e.code === "TRANSACTION_STATUS_N" || e.code === "TRANSACTION_STATUS_R") {
    // hard stop, show error message

    // R — Rejected
    // Issuer rejected authentication
    // High fraud signal
    // ❌ Do not fall back

    // N — Not authenticated / Failed
    // Authentication attempted but failed
    // ❌ Do not fall back

    alert("Error while doing 3DS: " + e.message)
    return;
  }

  if (e.code === "TRANSACTION_STATUS_U") {
    if (e.cardHolderInfo?.toLowerCase() === "challenge cancelled by user") {
      alert("Verification was cancelled. Your payment wasn't completed. Please try again");
    } else {
       // Authentication unavailable / not enrolled
       // The card does NOT participate in 3DS
       // → ✅ Proceed with payment WITHOUT liability shift
    }
  }
});

// Listen for any errors that might occur
gateway.on('error', (e) => {
  console.error('[3DS] gateway general error', e);
})

7. Submit 3DS data in /transact request

Implement a callback for the complete event that sends the 3DS data as a part of Charge credit card.

// Listen for the 'complete' callback to provide all the needed 3DS data
threeDSUI.on('complete', async (e) => {
  console.log('[3DS] complete', e);

  doTransactRequest({   
    threeDS: { 
      cavv: e.cavv,
      xid: e.xid,
      eci: e.eci,
      cardHolderAuth: e.cardHolderAuth,
      threeDsVersion: e.threeDsVersion,
      directoryServerId: e.directoryServerId,
      // cardHolderInfo should not be passed into the API and should be displayed to the customer if a value is present.
      //
      // cardHolderInfo: e.cardHolderInfo
    }
  });
});

threeDSUI.on('failure', (e) => {
  ...

  if (e.code === "TRANSACTION_STATUS_U") {
    if (e.cardHolderInfo?.toLowerCase() === "challenge cancelled by user") {
      alert("Verification was cancelled. Your payment wasn't completed. Please try again");
    } else {
      // Authentication unavailable / not enrolled
      // The card does NOT participate in 3DS
      // → ✅ Proceed with payment WITHOUT liability shift

      doTransactRequest({});
    }
  };
});

const doTransactRequest = (3dsParams) => {
  const transactParams = {
    amount: "100.00",
    type: "fiat",
    opaqueData: { token },
    customer: {
      name: "John Samuels",
      email: "johnsamuels@example.com",
      phone: "+1 212-555-0123",
    },
    billingAddress: {
      street: "11th Street 47 W",
      unit: "",
      city: "New York",
      state: "NY",
      zipCode: "10001",
      countryCode: "US",
      phone: "+1 212-555-0123",
    },
    orderId: `externalOrderId_112233`,

    // 3DS data
    ...3dsParams,
  };

  const response = await fetch("https://api.chargx.io/transact", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-publishable-api-key": publishableApiKey,
    },
    body: JSON.stringify(transactParams),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Request failed (${response.status}): ${errorText}`);
  }

  const responseData = await response.json();
}

React SDK

Follow custom integration guide above to integrate 3DS into React apps.

Testing guide - Sandbox

The following test cards are available in Sandbox to simulate various authentication scenarios. We recommend testing these cases before going live to verify that your integration works correctly and behaves as expected.

"Not Enrolled" Frictionless Flow

These cards will return Enrolled: "N" and trigger a frictionless flow where the card is not enrolled in 3DS:

  • Visa: 4111111111111111
  • Mastercard: 5431111111111111
  • Amex: 341111111111111
  • Discover: 6799990100000000019

Expected Result:

  • cardholder_auth = 'attempted'
  • CAVV = empty
  • ECIFlag = "07" (Not enrolled)

"Frictionless Authentication" Flow

These cards will return Enrolled: "Y" with a CAVV present, simulating silent authentication:

  • Visa: 4100000000000100
  • Mastercard: 5100000000000107
  • Amex: 340000000000108
  • Discover: 6440000000000104

Expected Result:

  • cardholder_auth = 'verified'
  • CAVV = mock value (e.g., "mock_cavv_0002_1234567890")
  • ECIFlag = "05"

Challenge Flow Test Cards

These cards will trigger a 3DS challenge flow requiring user interaction:

  • Visa: 4100000000005000
  • Mastercard: 5100000000005007
  • Amex: 340000000005008
  • Discover: 6440000000005004

Expected Result:

  • Enrolled: "Y"
  • PAResStatus: "C" (Challenge required)
  • ECIFlag: "02" (Challenge required)
  • User will be presented with a mock challenge interface. Use the following actions to trigger each result:
ResultDescriptionAction
PassReceive a successful step-up authenticationEnter code 12345
FailReceive a failed step-up authentication responseEnter any code other than 12345
CancelIncomplete step-up authentication responseSelect Cancel