3DS transactions

Integrate 3D Secure (3DS) into your checkout.

Overview

By implementing 3DS authentication into your payment flow, you benefit from:

  • Additional security: 3DS adds multiple layers of authentication and risk assessment.
  • Liability shift: Successful 3DS authentication typically shifts fraud liability from merchant to card issuer.
  • Higher success rate: Banks are more likely to approve 3DS-authenticated transactions.

However, the 3DS payment flow is longer than the non-3DS one due to the additional authentication steps. It may also require active customer participation if a challenge is presented.

Payment flow

The 3D Secure flow is made up of nine key steps.

Step 1: Submission

The customer clicks the submit button or the payment is triggered programmatically. The submitAsync() method in CardSubmitComponent is invoked and validation occurs inside itand validation occurs inside it. If validation passes, the method continues with the payment processing. If it fails, the method exits early and doesn't proceed with the transaction.

Step 2: Card tokenisation

If it's a new card, the SDK sends the card details to the tokenisation service. If it's a saved card, the SDK retrieves the existing token.

Step 3: Evaluation

The SDK evaluates whether 3DS authentication is required. In this flow, 3DS is required based on factors like transaction amount, risk assessment, or regulatory requirements.

Step 4: Pre-initiation

This is the initial step in the 3DS authentication flow. It establishes the authentication session by sending transaction and card details to the payment processor.

This step has two associated callbacks:

  • onPreInitiateAuthentication: Returns configuration for the authentication setup.
  • onPostInitiateAuthentication: Receives the result of the pre-initiation call.

Step 5: Fingerprinting

During this step, device information and browser characteristics are collected by the fingerprinting component. It creates a hidden iframe that submits transaction details to the issuer's fingerprint URL, enabling risk assessment based on the user's device profile.

Step 6: Authentication

The 3DS server evaluates the transaction risk and determines the authentication path:

  • Frictionless flow: If the transaction is low-risk, authentication completes automatically without customer interaction.
  • Challenge flow: If additional verification is needed, the customer completes the 3DS authentication challenge (PIN entry, SMS code, biometric verification, etc.)

The authentication step has two associated callbacks:

  • onPreAuthentication: Configures the main authentication parameters.
  • onPostAuthentication: Receives authentication results and challenge data.

Step 7: Authentication result

The SDK receives the 3DS authentication result indicating whether authentication was successful, failed, or requires additional action.

Step 8: Authorisation

This is the final step of the 3DS authentication flow. You receive the transaction data along with the 3DS authentication results and decide whether to proceed. At this point, you can still add additional data or cancel the transaction entirely. The SDK then sends the authorisation request to the payment gateway, including the 3DS authentication data.

The authorisation step has two associated callbacks:

  • onPreAuthorisation: Provides final transaction data, including 3DS authentication results. This is your last chance to modify the transaction before authorisation.
  • onPostAuthorisation: Receives the final transaction result from the payment gateway. The transaction is either approved or declined and final transaction details are available, along with 3DS authentication confirmation.

Step 9: Authorisation result

You receive the final authorisation response from the payment gateway. The transaction is either approved or declined and final transaction details are available, along with 3DS authentication confirmation.

Implementation

Before you start

To use 3D Secure in your application, you first need to enable it in the Unity Portal:

  1. In the Unity Portal, go to Merchant setup > Merchant groups.
  2. Select a merchant group.
  3. Click the Services tab.
  4. Click Edit in the Card service row.
  5. Click Configure modules in the top right.
  6. Click the toggle next to ThreeD secure service.

You'll also need to get the following from your payment processor:

  • acquirerProfileId: Your acquirer profile identifier.
  • providerId: Your 3DS provider identifier.
  • Test credentials for the sandbox environment.

Step 1: Configure your SDK

To start, set up your sdkConfig to include the shopper object.

const sdkConfig = {
  transactionData: {
    amount: 99.99,
    currency: 'USD',
    entryType: 'Ecom',
    intent: 'Authorisation',
    merchantTransactionId: 'order-123',
    merchantTransactionDate: () => new Date().toISOString(),
    // Include 3DS-friendly data
    shopper: {
      email: '[email protected]',
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  // Your other SDK configuration
};

Step 2: Implement callbacks

Next, implement your chosen callbacks. Note that some are required.

const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
  // REQUIRED: Provide 3DS configuration
  onPreInitiateAuthentication: () => {
    return {
      acquirerProfileId: "your_acquirer_profile_id",
      providerId: "your_3ds_provider_id",
      requestorAuthenticationIndicator: '01',
      timeout: 120
    };
  },

  // OPTIONAL: Handle the pre-initiation result
  onPostInitiateAuthentication: (result) => {
    console.log('3DS pre-initiation completed:', result);
    if (result.status === 200) {
      console.log('3DS setup successful');
    } else {
      console.log('3DS setup failed:', result);
    }
  },
  
  // REQUIRED: Configure main authentication
  onPreAuthentication: async (preInitResult) => {
    console.log('Configuring 3DS authentication:', preInitResult);
    
    return {
      merchantCountryNumericCode: "840",
      merchantLegalName: "Your company name",
      challengeWindowSize: 4,
      requestorChallengeIndicator: "01",
      timeout: 300
    };
  },

  // OPTIONAL: Handle the authentication result
  onPostAuthentication: (authResult, threeDSData) => {
    console.log('3DS authentication completed:', authResult);
    console.log('3DS data:', threeDSData);
    
    if (authResult.status === 200) {
      console.log('Authentication successful');
    } else {
      console.log('Authentication failed or challenged');
    }
  },
  
  // REQUIRED: Final transaction approval
  onPreAuthorisation: async (preAuthData, threeDSData) => {
    console.log('Final 3DS data:', threeDSData);
    console.log('Transaction data:', preAuthData);
    
    // You can add additional transaction data here
    return {
      ...preAuthData.transactionInitiationData,
      // Add any additional data if needed
    };
  },

  // OPTIONAL: Handle the final result
  onPostAuthorisation: (result) => {
    if (result.success) {
      console.log('Payment successful with 3DS!');
      console.log('Transaction ID:', result.transactionId);
      // Redirect to a success page
    } else {
      console.log('Payment failed:', result.errorMessage);
      // Handle failure
    }
  }
});

Step 3: Handle common scenarios

Conditional 3DS

Use the following snipper to only trigger 3DS transactions above a certain amount.

const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
  onPreInitiateAuthentication: () => {
    if (sdkConfig.transactionData.amount > 100) {
      return {
        acquirerProfileId: "your_profile",
        providerId: "your_provider",
        requestorAuthenticationIndicator: '01',
        timeout: 120
      };
    }
    
    return null;
  }
});

Different transaction types

Use the following snippet to handle different types of transactions.

const get3DSConfig = (transactionType) => {
  const baseConfig = {
    acquirerProfileId: "your_profile",
    providerId: "your_provider",
    timeout: 120
  };

  switch (transactionType) {
    case 'payment':
      return { ...baseConfig, requestorAuthenticationIndicator: '01' };
    case 'recurring':
      return { ...baseConfig, requestorAuthenticationIndicator: '02' };
    case 'add-card':
      return { ...baseConfig, requestorAuthenticationIndicator: '04' };
    default:
      return { ...baseConfig, requestorAuthenticationIndicator: '01' };
  }
};

onPreInitiateAuthentication: () => get3DSConfig('payment')

Step 4: Handle errors

Lastly, make sure to implement proper error handling.

const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
  onSubmitError: (error: BaseSdkException) => {
    console.error('Payment error:', error);

    // Handle specific 3DS errors
    if (error instanceof TokenVaultException) {
        showError("Token vault exception.");
    } else if (error instanceof ValidationException) {
        showError("Validation failed.");
    } else if (error instanceof TransactionAuthenticationRejectedException) {
        showError('Payment was rejected by your bank.');
    } else if (error instanceof PreInitiateAuthenticationFailedException) {
        showError("Pre-initiate authentication failed.");
    } else if (error instanceof AuthenticationFailedException) {
        showError('Payment authentication failed. Please try again.');
    } else if (error instanceof TransactionAuthenticationRequireScaExemptionException) {
        showError("Transaction authentication requires SCA exemption.");
    } else if (error instanceof TransactionAuthenticationInvalidException) {
        showError("Transaction authentication is invalid.");
    } else if (error instanceof NetworkSdkException) {
        showError("Network error occurred. Please try again.");
    } else if (error instanceof UnexpectedSdkException) {
        showError('Payment failed. Please try again.');
    }
  },

  onPostAuthentication: (authResult: AuthenticationResult, threeDSData: ThreeDSAuthenticationData) => {
    // Handle authentication failures
    if (authResult instanceof FailedAuthenticationResult) {
        console.log('Authentication failed:', authResult.errorReason);
        // Don't proceed to authorisation
        return;
    }

    // Check 3DS status
    if (threeDSData?.state === 'AuthenticationFailed') {
        showError('Card authentication failed');
        return;
    }

    console.log('Authentication successful, proceeding to payment');
},onSubmitError: (error) => {
    console.error('Payment error:', error);
    
    // Handle specific 3DS errors
    if (error.code === 'AUTHENTICATION_FAILED') {
      showError('Payment authentication failed. Please try again.');
    } else if (error.code === 'CHALLENGE_TIMEOUT') {
      showError('Authentication timed out. Please try again.');
    } else if (error.code === 'AUTHENTICATION_REJECTED') {
      showError('Payment was rejected by your bank.');
    } else {
      showError('Payment failed. Please try again.');
    }
  },

  onPostAuthentication: (authResult, threeDSData) => {
    // Handle authentication failures
    if (authResult.status !== 200) {
      console.log('Authentication failed:', authResult.errorMessage);
      // Don't proceed to authorisation
      return;
    }
    
    // Check 3DS status
    if (threeDSData?.state === 'AuthenticationFailed') {
      showError('Card authentication failed');
      return;
    }
    
    console.log('Authentication successful, proceeding to payment');
  }
});

Example

The following example shows a simple 3DS implementation using the new card component.

// Create and customise components
const newCardComponent = pxpCheckoutSdk.create('new-card', {
  // Step 1: Set up 3DS
  onPreInitiateAuthentication: () => ({
    acquirerProfileId: process.env.ACQUIRER_PROFILE_ID,
    providerId: process.env.PROVIDER_ID,
    requestorAuthenticationIndicator: '01',
    timeout: 120
  }),

  // Step 2: Configure authentication
  onPreAuthentication: async (preInitResult: PreInitiateSuccessAuthenticationResult) => ({
    merchantCountryNumericCode: "840",
    merchantLegalName: "Your Company Ltd",
    challengeWindowSize: 4,
    requestorChallengeIndicator: "01",
    timeout: 300
  }),

  // Step 3: Handle final authorisation
  onPreAuthorisation: async (preAuthData: PreAuthorizationData, threeDSData: ThreeDSAuthenticationData) => {
    return {
      psd2Data: {
        // scaExemption: "AnonymousCard" as ScaExemptionType,
      },
      threeDSecureData: {
        cardHolderAuthenticationVerificationValue: "1",
        directoryServerTransactionId: "2",
        electronicCommerceIndicator: "06",
        threeDSecureTransactionStatus: "7",
        threeDSecureVersion: "8",
      }
      };
    },

    // Step 4: Handle success/failure
    onPostAuthorisation: (result: BaseSubmitResult) => {
      if (result instanceof AuthorisedSubmitResult || result instanceof CapturedSubmitResult) {
        window.location.href = '/payment-success';
      } else {
        const errorResult = result as BaseStatusCode200SubmitResult;
        showError('Payment failed: ' + errorResult.state);
      }
    },

    // Step 5: Error handling
    onSubmitError: (error: BaseSdkException) => {
      console.error('3DS Error:', error);
      showError('Payment authentication failed');
    }
}) as NewCardComponent;

Callback data

This section describes the data received by the different callbacks as part of the 3DS flow.

📘

Note that the onPreInitiateAuthentication callback doesn't receive anything so isn't included. Instead, it returns your 3DS configuration in the PreInitiateIntegratedAuthenticationData object.

onPostInitiateAuthentication

The onPostInitiateAuthentication callback receives an authentication result (AuthenticationResult) that can be either a success (PreInitiateSuccessAuthenticationResult) or a failure (FailedAuthenticationResult).

Success

When successful, onPostInitiateAuthentication receives the following PreInitiateSuccessAuthenticationResult.

{
  authenticationId: "auth_12345",
  state: "PendingClientData",
  scaMandated: true,
  applicableExemptions: "LVP"
}

Parameter

Description

authenticationId
string or null

The unique identifier for this 3DS session.

state
string (enum) or null

The state of the authentication.

Possible values:

  • AuthenticationSuccessful
  • AuthenticationFailed
  • AuthenticationRejected
  • AuthenticationError
  • PendingCustomerChallenge
  • PendingClientData

scaMandated
boolean

Whether Strong Customer Authentication (SCA) is required.

applicableExemptions
string or null

The list of exemptions that apply to this transaction.

Possible values:

  • LVP: Low-value payment
  • TRA: Transaction risk analysis

Here's an example of what to do with this data:

onPostInitiateAuthentication: (result) => {
  if ('status' in result) {
    // This is a failure - ignore or log briefly
    console.log('3DS pre-initiation failed - skipping processing');
    return;
  }
  
  // This is a PreInitiateSuccessAuthenticationResult - process it
  console.log(`3DS Authentication ID: ${result.authenticationId}`);
  
  // Check if SCA (Strong Customer Authentication) is mandated
  if (result.scaMandated) {
    console.log('SCA is required - 3DS must complete successfully');
    // Show user message: "Additional verification required"
  }
  
  // Check available exemptions
  if (result.applicableExemptions === 'LVP') {
    console.log('Low value exemption available');
    // You might skip 3DS for small amounts
  } else if (result.applicableExemptions === 'TRA') {
    console.log('Transaction risk analysis exemption available');
  }
  
  // Check the current state
  switch (result.state) {
    case 'PendingClientData':
      console.log('Waiting for client data collection');
      break;
    case 'AuthenticationSuccessful':
      console.log('Authentication already completed successfully');
      break;
    case 'AuthenticationFailed':
      console.log('Authentication failed');
      break;
    case 'PendingCustomerChallenge':
      console.log('Challenge required from customer');
      break;
    default:
      console.log(`Authentication state: ${result.state}`);
  }
}

Failure

When unsuccessful, onPostInitiateAuthentication receives the following FailedAuthenticationResult.

{
  errorCode: "3DS_001",
  errorReason: "Invalid acquirer profile",
  correlationId: "corr_98765",
  details: [
    "The specified acquirer profile is not valid",
    "Please check your configuration"
  ],
  status: 400
}
ParameterDescription
errorCode
string or null
The error code.
errorReason
string or null
The reason for the error.
correlationId
string or null
The correlation ID.
details
array of strings or null
Additional details about the error.
status
HttpStatusCode
The HTTP status code.

Here's an example of what to do with this data:

onPostInitiateAuthentication: (result) => {
  // Check if this is a failure result
  if ('status' in result) {
    // This is a FailedAuthenticationResult
    console.error('3DS Authentication Failed:', {
      status: result.status,
      errorCode: result.errorCode,
      errorReason: result.errorReason,
      correlationId: result.correlationId
    });
    
    // Handle specific error cases
    if (result.status === 400) {
      console.log('Bad request - check your authentication data');
    } else if (result.status === 401) {
      console.log('Authentication failed - invalid credentials');
    } else if (result.status === 500) {
      console.log('Server error - try again later');
    }
    
    // Show user-friendly error message
    const userMessage = result.errorReason || 'Payment verification failed. Please try again.';
    showError(userMessage);
    
    // Log additional details if available
    if (result.details && result.details.length > 0) {
      console.log('Error details:', result.details);
    }
    
    // Use the correlation ID for support/debugging
    if (result.correlationId) {
      console.log(`Reference ID for support: ${result.correlationId}`);
    }
    
    return; // Exit early on failure
  }
  
  // Handle success case...
  console.log('Authentication succeeded');
}

onPreAuthentication

The onPreAuthentication callback receives the successful result of the pre-initiation (PreInitiateSuccessAuthenticationResult).

{
  authenticationId: "auth_12345",
  state: "PendingClientData",
  scaMandated: true,
  applicableExemptions: "LVP"
}

Parameter

Description

authenticationId
string or null

The unique identifier for this 3DS session.

state
string (enum) or null

The state of the authentication.

Possible values:

  • AuthenticationSuccessful
  • AuthenticationFailed
  • AuthenticationRejected
  • AuthenticationError
  • PendingCustomerChallenge
  • PendingClientData

scaMandated
boolean

Whether Strong Customer Authentication (SCA) is required.

applicableExemptions
string or null

The list of exemptions that apply to this transaction.

Possible values:

  • LVP: Low-value payment
  • TRA: Transaction risk analysis

Here's an example of what to do with this data:

onPreAuthentication: async (preInitResult) => {
  console.log('Received pre-initiation data:', preInitResult);
  
  // Use the authentication ID to track this transaction
  const authId = preInitResult.authenticationId;
  console.log(`Proceeding with authentication ID: ${authId}`);
  
  // Check if SCA is mandated to adjust challenge preference
  let challengeIndicator: RequestorChallengeIndicatorType = "01"; // No preference
  
  if (preInitResult.scaMandated) {
    challengeIndicator = "04";
    console.log('SCA mandated - requesting challenge');
  } else if (preInitResult.applicableExemptions === 'LVP') {
    challengeIndicator = "10"; 
    console.log('Low value exemption - requesting no challenge');
  } else if (preInitResult.applicableExemptions === 'TRA') {
    challengeIndicator = "05";
    console.log('TRA exemption - requesting no challenge');
  }
  
  return {
    merchantCountryNumericCode: "840",
    merchantLegalName: "Your company name",
    challengeWindowSize: 4,
    requestorChallengeIndicator: challengeIndicator,
    timeout: preInitResult.scaMandated ? 600 : 300
  };
}

onPostAuthentication

The onPostAuthentication callbacks receives:

  • An authentication result (AuthenticationResult ). This can be either a success (InitiateIntegratedSuccessAuthenticationResult or a failure (FailedAuthenticationResult).
  • The 3DS authentication data (ThreeDSAuthenticationData) if a challenge occurred.

3DS authentication details

{
  authenticationId: "auth_12345",
  state: "AuthenticationSuccessful",
  threeDSecureVersion: "2.2.0",
  directoryServerTransactionId: "ds_trans_id",
  cardHolderAuthenticationVerificationValue: "cavv_value_here",
  electronicCommerceIndicator: "05"
}

Parameter

Description

authenticationId
string

The unique identifier for this 3DS session.

state
string

The state of the authentication.

Possible values:

  • AuthenticationSuccessful
  • AuthenticationFailed
  • AuthenticationRejected
  • AuthenticationError

threeDSecureVersion
string

The 3DS secure version.

directoryServerTransactionId
string

The transaction ID assigned by the Directory Server.

cardHolderAuthenticationVerificationValue
string

The cardholder authentication verification value (CAVV).

electronicCommerceIndicator
string

The Electronic Commerce Indicator (ECI).

Possible values:

  • 01
  • 02
  • 05
  • 06
  • 07

Authentication result

Success

{
  uniqueId: "unique_12345",
  state: "completed",
  transactionStatus: "Y", // AuthenticationVerificationSuccessful
  electronicCommerceIndicator: "05", // 3DS authenticated
  exemptionGranted: false,
  exemptionGrantedByIssuer: "79", // NoExemptionApplied
  acsUrl: undefined, // No challenge needed
  challengeData: undefined, // No challenge needed
  stateData: { 
    code: "success", 
    reason: "Authentication completed successfully" 
  },
  cardholderInfo: undefined
}
{
  uniqueId: "unique_67890",
  state: "challenge_required",
  transactionStatus: "C",
  electronicCommerceIndicator: "07",
  exemptionGranted: false,
  exemptionGrantedByIssuer: "79",
  acsUrl: "https://acs.bank.com/challenge",
  challengeData: "eyJhbGciOiJIUzI1NiJ9...",
  stateData: { 
    code: "pending", 
    reason: "Challenge required from cardholder" 
  },
  cardholderInfo: "Additional details about the cardholder"
}

Parameter

Description

uniqueId
string

The unique identifier for this 3DS session.

state
string

The state of the authentication.

transactionStatus
string

The status of the transaction.

Possible values:

  • Y: Authentication verification successful
  • N: Not authenticated / not verified
  • U: Authentication couldn't be performed
  • A: Attempts processing performed
  • C: Challenge required
  • R: Authentication rejected
  • I: Informational only

electronicCommerceIndicator
string

The Electronic Commerce Indicator (ECI).

Possible values:

  • 01: 3DS not available (non-3DS transaction)
  • 02: 3DS available but not used (you attempted authentication but it wasn't possible)
  • 05: 3DS authentication successful (fully authenticated)
  • 06: 3DS authentication attempted (issuer or cardholder not enrolled)
  • 07: 3DS authentication failed but transaction allowed (soft decline)

exemptionGranted
boolean

Whether an exemption was granted.

exemptionGrantedByIssuer
string

The type of exemption granted by the issuer.

Possible values:

  • 05: Transaction risk analysis exemption
  • 08: Trust list exemption
  • 10: Low value exemption
  • 11: Secure corporate payments exemption
  • 79: No exemption applied

acsUrl
string

The ACS URL.

challengeData
string

Base64 encoded challenge data.

stateData
object

Details about the state.

stateData.code
string

The state code.

stateData.reason
string

The state reason.

cardholderInfo
string

Additional details about the cardholder.

Failure

{
  errorCode: "CHALLENGE_FAILED",
  errorReason: "Cardholder failed 3D Secure challenge",
  correlationId: "corr_challenge_failed_456",
  details: [
    "Incorrect SMS verification code entered",
    "Maximum authentication attempts exceeded"
  ],
  status: 401
}
ParameterDescription
errorCode
string
The error code.
errorReason
string
The reason for the error.
correlationId
string
The correlation ID.
details
array of strings
Additional details about the error.
status
HttpStatusCode
The HTTP status code.

Here's an example of what to do with this data:

onPostAuthentication: (authResult, threeDSData) => {
  console.log('Authentication result:', authResult);
  console.log('3DS data:', threeDSData);
  
  if (authResult.status !== 200) {
    console.error('Authentication failed:', authResult.errorMessage);
    showError('Card verification failed. Please try a different payment method.');
    return; // Stop the payment flow
  }
  
  // Check transaction status
  switch (authResult.transactionStatus) {
    case 'Y':
      console.log('Authentication successful - no challenge needed');
      showMessage('Card verified successfully');
      break;
      
    case 'C':
      console.log('Challenge completed - checking result...');
      if (threeDSData?.state === 'AuthenticationSuccessful') {
        console.log('Challenge completed successfully');
        showMessage('Verification completed');
      } else {
        console.log('Challenge failed');
        showError('Verification failed. Please try again.');
        return; // Stop payment
      }
      break;
      
    case 'N':
      console.log('Authentication failed');
      showError('Card verification failed');
      return; // Stop payment
      
    case 'R':
      console.log('Authentication rejected');
      showError('Payment was rejected by your bank');
      return; // Stop payment
  }
  
  // Log important 3DS data for transaction
  if (threeDSData) {
    console.log(`ECI: ${threeDSData.electronicCommerceIndicator}`);
    console.log(`CAVV: ${threeDSData.cardHolderAuthenticationVerificationValue}`);
    // These values prove successful 3DS authentication
  }
  
  console.log('Proceeding to final authorisation...');
}

onPreAuthorisation

The onPreAuthorisation callback returns:

  • Pre-authorisation data (preAuthData): Transaction data ready for authorisation.
  • 3DS data (threeDSData): Final 3DS authentication results.

Pre-authorisation data

The pre-authorisation data includes transaction initiation data (for new cards) or card token data (for saved cards).

{
  initiateIntegratedSuccessAuthenticationResult: {
    status: 200,
    authenticationId: "auth_12345678-abcd-1234-efgh-123456789012",
    uniqueId: "unique_98765",
    state: "Completed",
    transactionStatus: "Y",
    electronicCommerceIndicator: "05",
    exemptionGranted: false,
    exemptionGrantedByIssuer: "79",
    acsUrl: null, 
    challengeData: null,
    stateData: {
     code: "SUCCESS",
     reason: "Authentication completed successfully"
    },
    cardholderInfo: "Additional cardholder verification completed"
  },
  transactionInitiationData: {
    psd2Data: {
      scaExemption: null,
    },
    threeDSecureData: {
      threeDSecureVersion: "2.2.0",
      electronicCommerceIndicator: "05",
      cardHolderAuthenticationVerificationValue: "jGvQIvG/5UhjAREALGYYemQLXPI=",
      directoryServerTransactionId: "ds_trans_12345",
      threeDSecureTransactionStatus: "Y"
    },
    identityVerification: {
      nameVerification: boolean,
    },
    addressVerification: AddressVerification;
  }
}
{
  initiateIntegratedSuccessAuthenticationResult: null,
  transactionInitiationData: null,
  cardTokenData: {
    gatewayTokenId: "token_abc123def456789",
    schemeTokenId: "scheme_xyz987654321",
    maskedPrimaryAccountNumber: "****-****-****-4242",
    cardExpiryMonth: "12",
    cardExpiryYear: "2025",
    scheme: "VISA",
    fundingSource: "CREDIT",
    ownerType: "PERSONAL",
    issuerName: "Chase Bank",
    issuerCountryCode: "US",
    lastSuccessfulPurchaseDate: "2024-01-15T10:30:00Z",
    lastSuccessfulPayoutDate: null
  }
}

Parameter

Description

initiateIntegratedSuccessAuthenticationResult
object

Details about the successful authentication.

initiateIntegratedSuccessAuthenticationResult.status
string

The HTTP status.

initiateIntegratedSuccessAuthenticationResult.authenticationId
HttpStatusCode

The unique identifier for this 3DS session.

initiateIntegratedSuccessAuthenticationResult.uniqueId
string

The unique identifier for this 3DS session.

initiateIntegratedSuccessAuthenticationResult.state
string

The state of the authentication.

initiateIntegratedSuccessAuthenticationResult.transactionStatus
string

The status of the transaction.

Possible values:

  • Y: Authentication verification successful
  • N: Not authenticated / not verified
  • U: Authentication couldn't be performed
  • A: Attempts processing performed
  • C: Challenge required
  • R: Authentication rejected
  • I: Informational only

initiateIntegratedSuccessAuthenticationResult.electronicCommerceIndicator
string

The Electronic Commerce Indicator (ECI).

Possible values:

  • 01: 3DS not available (non-3DS transaction)
  • 02: 3DS available but not used (you attempted authentication but it wasn't possible)
  • 05: 3DS authentication successful (fully authenticated)
  • 06: 3DS authentication attempted (issuer or cardholder not enrolled)
  • 07: 3DS authentication failed but transaction allowed (soft decline)

initiateIntegratedSuccessAuthenticationResult.exemptionGranted
boolean

Whether an exemption was granted.

initiateIntegratedSuccessAuthenticationResult.exemptionGrantedByIssuer
string

The type of exemption granted by the issuer.

Possible values:

  • 05: Transaction risk analysis exemption
  • 08: Trust list exemption
  • 10: Low value exemption
  • 11: Secure corporate payments exemption
  • 79: No exemption applied

initiateIntegratedSuccessAuthenticationResult.acsUrl
string

The ACS URL.

initiateIntegratedSuccessAuthenticationResult.challengeData
string

Base64 encoded challenge data.

initiateIntegratedSuccessAuthenticationResult.stateData
object

Details about the state.

initiateIntegratedSuccessAuthenticationResult.stateData.code
string

The state code.

initiateIntegratedSuccessAuthenticationResult.stateData.reason
string

The state reason.

initiateIntegratedSuccessAuthenticationResult.cardholderInfo
string

Additional details about the cardholder.

transactionInitiationData
object

Details about the transaction, if associated with a new card, null otherwise.

transactionInitiationData.psd2Data
object or null

Details about PSD2. This is required for non-3DS transactions.

transactionInitiationData.psd2Data.scaExemption
string or null

The type of SCA exemption that applies to this transaction.

Possible values:

  • AnonymousCard
  • LowValue
  • SecureCorporate
  • TransactionRiskAnalysis
  • TrustedBeneficiary

transactionInitiationData.threeDSecureData
object or null

Details about 3DS.

transactionInitiationData.threeDSecureData.threeDSecureVersion
string

The 3DS secure version.

transactionInitiationData.threeDSecureData.electronicCommerceIndicator
string

The Electronic Commerce Indicator (ECI).

Possible values:

  • 01
  • 02
  • 05
  • 06
  • 07

transactionInitiationData.threeDSecureData.directoryServerTransactionId
string

The transaction ID assigned by the Directory Server.

transactionInitiationData.threeDSecureData.cardHolderAuthenticationVerificationValue
string

The cardholder authentication verification value (CAVV).

transactionInitiationData.threeDSecureData.threeDSecureTransactionStatus
string

The status of the 3DS transaction.

Possible values:

  • Y: Authentication Verification Successful
  • N: Not Authenticated / Not Verified
  • U: Authentication Couldn't Be Performed
  • A: Attempts Processing
  • C: Challenge Required
  • R: Authentication Rejected
    • Y: Authentication verification successful

    • N: Not authenticated / not verified

    • U: Authentication couldn't be performed

    • A: Attempts processing performed

    • C: Challenge required

    • R: Authentication rejected

    • I: Informational only

transactionInitiationData.identityVerification.nameVerification
boolean

Whether the cardholder's name matches the name associated with the registered address on file.

transactionInitiationData.addressVerification
object

Details about the address verification.

transactionInitiationData.identityVerification.countryCode
string

The country code associated with the cardholder's address, in ISO-3166-1 alpha-2 format.

transactionInitiationData.identityVerification.houseNumberOrName
string

The cardholder's street address.

transactionInitiationData.identityVerification.postalCode
string

The postal or ZIP code associated with the cardholder's address.

cardTokenData
object

Details about the card token if associated with a saved card, null otherwise.

cardToken.gatewayTokenId
string or null

The gateway token ID.

cardToken.schemeTokenId
string or null

The scheme token ID.

cardToken.maskedPrimaryAccountNumber
string or null

The masked Primary Account Number (PAN).

cardToken.cardExpiryMonth
string or null

The expiry month (MM) of the card.

cardToken.cardExpiryYear
string or null

The expiry year (YYYY) of the card.

cardToken.scheme
string or null

The card scheme.

cardToken.fundingSource
string or null

The funding source.

cardToken.ownerType
string or null

The owner type.

cardToken.issuerName
string or null

The issuer name.

cardToken.issuerCountryCode
string or null

The country code of the issuer.

cardToken.lastSuccessfulPurchaseDate
string or null

The date of the last successful purchase.

cardToken.lastSuccessfulPayoutDate
string or null

The date of the last successful payout.

Here's an example of what to do with this data:

onPreAuthorisation: async (preAuthData, threeDSData) => {
  console.log('Final authorisation data:', preAuthData);
  console.log('Final 3DS data:', threeDSData);
  
  // Verify 3DS authentication was successful
  if (threeDSData) {
    if (threeDSData.state !== 'AuthenticationSuccessful') {
      console.error('3DS authentication not successful:', threeDSData.state);
      showError('Payment authentication failed');
      return null; // Cancel the transaction
    }
    
    console.log('3DS authentication confirmed successful');
    console.log(`Authentication method: ${threeDSData.electronicCommerceIndicator}`);
  }
  
  // Get the base transaction data
  const transactionData = preAuthData.transactionInitiationData;
  
  // Add any additional data you need
  const finalTransactionData = {
    ...transactionData,
    
    // Add custom metadata
    metadata: {
      threeDSVersion: threeDSData?.threeDSecureVersion,
      authenticationId: preAuthData.initiateIntegratedSuccessAuthenticationResult?.authenticationId,
      timestamp: new Date().toISOString()
    },
    
    // Add shipping info if needed
    shippingAddress: {
      addressLine1: "123 Main St",
      city: "New York",
      postalCode: "10001",
      countryCode: "US"
    },
    
    // Add additional PSD2 data if in Europe
    psd2Data: {
      scaExemption: threeDSData?.state === 'AuthenticationSuccessful' ? null : 'LowValue'
    }
  };
  
  console.log('Sending final transaction for authorisation');
  return finalTransactionData;
}

3DS data

The 3DS data includes details about the final result of the authentication.

{
  authenticationId: "auth_12345678-abcd-1234-efgh-123456789012",
  state: "AuthenticationSuccessful",
  threeDSecureVersion: "2.2.0",
  directoryServerTransactionId: "ds_trans_12345678-1234-1234-1234-123456789012",
  cardholderAuthenticationVerificationValue: "jGvQIvG/5UhjAREALGYYemQLXPI=",
  electronicCommerceIndicator: "05"
}

Parameter

Description

authenticationId
string

The unique identifier for this 3DS session.

state
string

The state of the authentication.

Possible values:

  • AuthenticationSuccessful
  • AuthenticationFailed
  • AuthenticationRejected
  • AuthenticationError

threeDSecureVersion
string

The 3DS secure version.

directoryServerTransactionId
string

The transaction ID assigned by the Directory Server.

cardHolderAuthenticationVerificationValue
string

The cardholder authentication verification value (CAVV).

electronicCommerceIndicator
string

The Electronic Commerce Indicator (ECI).

Possible values:

  • 01
  • 02
  • 05
  • 06
  • 07

Here's an example of how to handle this data:

onPreAuthorisation: (preAuthData, threeDSData) => {
  console.log('Final authorisation check:', { preAuthData, threeDSData });
  
  // Check if 3DS authentication occurred
  if (threeDSData) {
    // 3DS was used - check if it was successful
    if (threeDSData.state === 'AuthenticationSuccessful') {
      console.log('3DS authentication successful');
      console.log(`Protection level: ${threeDSData.electronicCommerceIndicator}`);
    } else {
      console.log('3DS authentication failed');
      showError('Card verification failed. Please try again.');
      return null; // Cancel the payment
    }
  } else {
    // No 3DS authentication (frictionless or skipped)
    console.log('Payment proceeding without 3DS');
  }
  
  // Proceed with the transaction
  return preAuthData.transactionInitiationData;
}

onPostAuthorisation

The onPostAuthorisation callback receives the final transaction result with 3DS confirmation (BaseSubmitResult).

Success

If the transaction was successful, you'll receive either an AuthorisedSubmitResult or a CapturedSubmitResult.

{
  state: "Authorised",
  providerResponse: {
    code: "00",
    message: "Approved",
    cardVerificationCodeResult: "M",
    addressVerificationServiceResult: "Y"
  },
  fundingData: {
    cardVerificationCodeResult: "Matched",
    addressVerificationServiceResult: "Y"
  }
}

Parameter

Description

state
string

The final state of the transaction.

Possible values:

  • Authorised
  • Captured
  • Refused

providerResponse
object

Details about the provider's response.

providerResponse.code
string

The raw result code returned by the provider that processed the transaction.

providerResponse.message
string

The raw message associated with the result code from the provider that processed the transaction.

providerResponse.cardVerificationCodeResult
string

The Card Verification Code (CVC) result returned by the provider. This is a raw data indicating the outcome of the CVC check performed during the transaction processing.

providerResponse.addressVerificationServiceResult
string

The Address Verification Service (AVS) result returned by the provider. This is a raw data indicating the outcome of the AVS check performed during the transaction processing.

fundingData
object

Details about the payment method.

fundingData.cardVerificationCodeResult
string

The Card Verification Code (CVC) result in human-readable format.

fundingData.addressVerificationServiceResult
string

The Address Verification Service (AVS) result in human-readable format.

Here's an example of what to do with this data:

onPostAuthorisation: (result) => {
  console.log('Final payment result:', result);
  
  if (result.success) {
    console.log('Payment successful with 3DS authentication!');
    console.log(`Transaction ID: ${result.transactionId}`);
    
    // Check if 3DS was used
    if (result.threeDSAuthenticationData) {
      console.log('3DS authentication confirmed');
      console.log(`ECI: ${result.threeDSAuthenticationData.electronicCommerceIndicator}`);
      
      // Log liability shift information
      const eci = result.threeDSAuthenticationData.electronicCommerceIndicator;
      if (eci === '05' || eci === '06') {
        console.log('Liability shift achieved - protected from chargebacks');
      }
      
      // Store 3DS data for compliance/auditing
      storeTransactionAudit({
        transactionId: result.transactionId,
        threeDSVersion: result.threeDSAuthenticationData.threeDSecureVersion,
        authenticationId: result.threeDSAuthenticationData.authenticationId,
        eci: result.threeDSAuthenticationData.electronicCommerceIndicator
      });
    }
    
    // Redirect to success page
    window.location.href = `/payment-success?tx=${result.transactionId}`;
    
  } else {
    console.error('Payment failed:', result.errorMessage);
    console.log(`Error code: ${result.errorCode}`);
    
    // Handle different failure types
    if (result.errorCode === 'INSUFFICIENT_FUNDS') {
      showError('Insufficient funds. Please try a different payment method.');
    } else if (result.errorCode === '3DS_AUTHENTICATION_FAILED') {
      showError('Card verification failed. Please try again.');
    } else {
      showError('Payment failed. Please try again or contact support.');
    }
  }
}

Failure (declined)

If the bank or issuer declines the transaction, you'll receive a RefusedSubmitResult.

{
  state: "Refused",
  stateData: {
    code: "05",
    message: "Do not honor"
  },
  providerResponse: {
    code: "05",
    message: "Do not honor",
    merchantAdvice: {
      code: "01",
      message: "Try another payment method"
    },
    cardVerificationCodeResult: "M",
    addressVerificationServiceResult: "Y"
  },
  fundingData: {
    cardVerificationCodeResult: "Matched",
    addressVerificationServiceResult: "Y"
  }
}

Parameter

Description

state
string

The final state of the transaction.

Possible values:

  • Authorised
  • Captured
  • Refused

stateData
object

Additional details about the state.

stateData.code
string

The state code.

stateData.message
string

The state message.

providerResponse
object

Details about the provider's response.

providerResponse.code
string

The raw result code returned by the provider that processed the transaction.

providerResponse.message
string

The raw message associated with the result code from the provider that processed the transaction.

providerResponse
object

Details about the provider's response.

providerResponse.code
string

The raw result code returned by the provider that processed the transaction.

providerResponse.message
string

The raw message associated with the result code from the provider that processed the transaction.

providerResponse.cardVerificationCodeResult
string

The Card Verification Code (CVC) result returned by the provider. This is a raw data indicating the outcome of the CVC check performed during the transaction processing.

providerResponse.addressVerificationServiceResult
string

The Address Verification Service (AVS) result returned by the provider. This is a raw data indicating the outcome of the AVS check performed during the transaction processing.

fundingData
object

Details about the payment method.

fundingData.cardVerificationCodeResult
string

The Card Verification Code (CVC) result in human-readable format.

fundingData.addressVerificationServiceResult
string

The Address Verification Service (AVS) result in human-readable format.

Here's an example of how to handle it.

function handlePaymentResult(result: BaseSubmitResult) {
  console.log('Payment result received:', result);
  
  if (result instanceof AuthorisedSubmitResult) {
    // SUCCESS
    handlePaymentSuccess(result);
    
  } else {
    // FAILURE - Handle different failure types
    handlePaymentFailure(result);
  }
}

function handlePaymentSuccess(result: AuthorisedSubmitResult) {
  console.log('Payment successful!');
  console.log(`Transaction ID: ${result.transactionId}`);
  
  // Check 3DS authentication data
  if (result.threeDSAuthenticationData) {
    console.log('3DS authentication confirmed');
    console.log(`ECI: ${result.threeDSAuthenticationData.electronicCommerceIndicator}`);
  }
  
  // Redirect to success page
  window.location.href = `/payment-success?tx=${result.transactionId}`;
}

function handlePaymentFailure(result: BaseSubmitResult) {
  console.error('Payment failed:', result);
  
  if (result instanceof FailedSubmitResult) {
    // System/network failure (non-200 HTTP)
    handleSystemFailure(result);
    
  } else if (result instanceof RefusedSubmitResult) {
    // Bank/issuer declined (200 HTTP but refused)
    handleBankDecline(result);
    
  } else if (result instanceof ErrorSubmitResult) {
    // Processing error (200 HTTP but error state)
    handleProcessingFailure(result);
    
  } else {
    // Unknown failure type
    console.error('Unknown failure type:', result);
    showError('Payment failed. Please try again.');
    logError('Unknown payment failure type', result);
  }
  
  // Hide loading states
  hideLoadingSpinner();
  enablePaymentForm();
}

onSubmitError

If an error occurs at any point in the 3DS flow (e.g., due to a network outage), you'll receive the following data.

{
  errorCode: "GATEWAY_TIMEOUT",
  errorReason: "Payment gateway timeout",
  correlationId: "corr_12345678",
  httpStatusCode: 504,
  details: ["Connection timeout after 30 seconds"]
}

Here's an example of how to handle this data:

function handleProcessingError(error: BaseSdkException) {
  console.error('Processing error occurred:', error);
  
  // Common processing errors
  switch (error.code) {
    // 3DS Authentication Errors
    case 'AUTHENTICATION_FAILED':
      showError('Card verification failed. Please try again.');
      logError('3DS authentication failed', error);
      break;
      
    case 'AUTHENTICATION_TIMEOUT':
      showError('Verification timed out. Please try again.');
      logError('3DS authentication timeout', error);
      break;
      
    case 'AUTHENTICATION_REJECTED':
      showError('Payment was rejected during verification.');
      logError('3DS authentication rejected', error);
      break;
      
    // Validation Errors
    case 'VALIDATION_FAILED':
      showError('Please check your payment details and try again.');
      logError('Form validation failed', error);
      break;
      
    case 'INVALID_CARD_NUMBER':
      showError('Invalid card number. Please check and try again.');
      break;
      
    case 'INVALID_EXPIRY_DATE':
      showError('Invalid expiry date. Please check and try again.');
      break;
      
    case 'INVALID_CVC':
      showError('Invalid security code. Please check and try again.');
      break;
      
    // Network/System Errors
    case 'NETWORK_ERROR':
      showError('Connection failed. Please check your internet and try again.');
      logError('Network error during payment', error);
      break;
      
    case 'GATEWAY_TIMEOUT':
      showError('Payment system is busy. Please try again in a moment.');
      logError('Gateway timeout', error);
      break;
      
    case 'SERVICE_UNAVAILABLE':
      showError('Payment service temporarily unavailable. Please try again later.');
      logError('Payment service unavailable', error);
      break;
      
    // Token/Configuration Errors
    case 'TOKEN_INVALID':
      showError('Payment method is no longer valid. Please try a different card.');
      logError('Invalid payment token', error);
      break;
      
    case 'CONFIGURATION_ERROR':
      showError('Payment setup error. Please contact support.');
      logError('SDK configuration error', error);
      break;
      
    default:
      showError('An unexpected error occurred. Please try again.');
      logError('Unknown processing error', error);
  }
  
  // Hide loading states
  hideLoadingSpinner();
  enablePaymentForm();
}