Integrate 3D Secure 2.0 with CSE



For integration with 3D Secure 2.0 with CSE (Client side encryption), You require below information.

  1. CSE- Client Side Encryption with Apexx(CSE)

  2. 3DS 2.0 Account Credentials

CSE - Client Side Encryption

  • Client Side Encryption (CSE) is a card payment integration method that gives merchants full flexibility with how the present card collection fields to their customers whilst allowing merchants to minimise their exposure when it comes to PCI Compliance.

  • CSE integration merchants to be PCI DSS compliant to a level of Self Assessment Questionnaire A-EP or above, in accordance with the latest PCI DSS standards (v3.2.1).

  • Your particular level of compliance may differ depending on your own unique setup, so we recommend consulting a QSA to make sure you're set up in the right way to meet your desired PCI DSS level.

3DS 2.0 Account Credentials

  • For 3DS 2.0 we are using Cardinal Cruise. You need to register with cardinal cruise for it.

  • As part of registering for Cardinal Cruise, need below 3 values that are used to authenticate yourself with Cardinal. 

  • JWT creation requires that values, these values have been generated and handed off to the Cardinal. We will provide you with these details during the onboarding process based on your preference.



API Identifier

A non-secure value that should be passed within the JWT under the iss claim.

Org Unit Id

a non-secure value that should be passed within the JWT under the OrgUnitId claim.


A secure value that should only ever be known between you and Cardinal. This value should never be rendered or displayed anywhere your users could find it. The API Key should only be used to sign the JWT and to verify a JWT signature from Cardinal. It should never be included within the JWT itself.

Note: A symmetric encryption key used to decrypt JWE's. This value should never be exposed to the public. It is used in Apple Pay only.

How It Works

Cardinal Cruise uses Songbird.js to perform most of the heavy lifting on behalf of our customers.  It will handle all device data collection and communication with our Cardinal Centinel platform, as well as the user experience to the consumer for both CCA and Alternative Payment brands.

  • Because Songbird is a client side JavaScript library it can only interact with payment brands client side. Any interactions that require server side implementations - such as our Cardinal Cruise Hybrid integration - are out of scope for Songbird.js, and the merchant may need to integrate to the Cardinal Centinel platform directly.


  • The diagram below shows the high-level event execution order of a transaction from the merchant's perspective. The enumerated events do not map to the integration steps found within this document. The enumerated events are simply to help illustrate the order events occur during a transaction.

3DS 2.0 Integration Steps

  1. Create JWT(JSON Web Token)

  • JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

  • Cardinal Cruise utilizes a JWT to handle authentication and to assist in passing secure data between you and Cardinal.

  • The JWT is a JWS with the signature using a SHA-256 HMAC hash algorithm. 

  • The JWT must be created server-side and sent to the front end to be injected into the JavaScript initialization code. 

  • Creating a JWT client-side is not a valid activation option. Each order should have a uniquely generated JWT associated with it.

  • You can create JWT with your preferable server side programming language like php,java,.net. Etc

  • Security Warning : For security reasons, all JWT creation must be done on the server side. When creating the JWT, use your company API Key as the JWT secret. 

    JWT Fields:

  1. Mandatory Fields: Below fields are mandatory for creating JWT.




JWT Id - A unique identifier for this JWT. This field should change each time a JWT is generated.


Issued At - The epoch time in seconds of when the JWT was generated. This allows us to determine how long a JWT has been around and whether we consider it expired or not.


Issuer - An identifier of who is issuing the JWT. We use this value to contain the Api Key identifier or name.


The merchant SSO OrgUnitId


The JSON data object being sent to Cardinal. This object is usually an Order object

  1. Optional Fields: Below fields are mandatory for creating JWT.




This is a merchant supplied identifier that can be used to match up data collected from Cardinal Cruise and Centinel. Centinel can then use data collected to enable rules or enhance the authentication request.


A boolean flag that indicates how Centinel Api should consume the Payload claim. When set to true, this tells Centinel Api the Payload claim is an object. When set to false, the Payload claim is a stringified object. Some Jwt libraries do not support passing objects as claims, this allows those who only allow strings to use their libraries without customization


Expiration - The numeric epoch time that the JWT should be consider expired. This value is ignored if its larger than 2 hrs. By default we will not consider any JWT older than 2 hrs.

JWT creation example:  JWT_Creation 

The below code samples are to provide you with a basic idea on how to generate JWT.

Example payload : 


  "jti": "19ec6910-5048-11e6-8c35-8789b865ff15",  //UUID

  "iat": 1577091197, //JWT initialisation time

  "iss": "<API Identifier >",

  "OrgUnitId": "<Org Unit Id>",

  "Payload": {

    "OrderDetails": {

      "OrderNumber": "19ec6910-5048-11e6-8c35-8789b865ff15" //your transactionId



  "ObjectifyPayload": true,

  "exp": 1577096197 //JWT expiration time


Please find the field details for the Payload object from the below link:

JWT library: 

You can find JWT libraries as per your requirement from here

Note:  A JWT is composed of 3 sections of URL base64 encoded strings of data separated by a period. The first 2 sections are JSON objects while the last section is a digital signature that has been URL base64 encoded.

JWT Sample: 


Header : A small JSON object that specifies what type of algorithm was used to generate the digital signature. This section is denoted in the above example in red.

Payload : A JSON object that contains the data being sent from one party to another. This is where all request data the merchant sends to Cardinal lives and vise Versa. This section is denoted in the above example in green.

Signature : A URL base64 encoded the hash value of the header and payload. This is used to verify the contents of the JWT have not been tampered with. Signatures are generated using a shared secret value so only the creator and consumer have the ability to create the signature. As long as the shared secret stays a secret between the 2 parties no one should be able to spoof the signature. Signatures are verified by the consumer recreating the hash value and checking that it matches the one attached to the JWT. This section is denoted above in blue.

Decrypt JWT: You can use this link

Conclusion: So based on the above information you can create JWT from payload data.

  1. Include the Script in Hosted payment page

  • In this step, You need to import songbird.js in your Hosted payment page.

  • Add Songbird.js to your site just like any other client-side script - through a script tag. 

Test environment :

<script src=""></script>

Production environment :

<script src=""></script>

  1. Configure It
  • In this step, You have to configure Cardinal.configure()  in your JavaScript file.

  • Using the Cardinal.configure() function allows you to pass a configuration object into Songbird.

  • There are different configuration options that can be used to initialize Songbird through Cardinal.configure function. You can call this function once per page load. you can configure your root level configuration like logging, button, payment, etc.



logging: {

 level: "on"  // on - Enable logging




  1. Listen for Events
  • In this step, You have to trigger below two events.

  • This function sets up an event subscription with Songbird to trigger a callback function when the event is triggered by Songbird.

  • You can subscribe to an event using the Cardinal.on() function structured like:



Cardinal.on('payments.setupComplete', function(){

    // Do something



  • This optional event triggers when Songbird has successfully initialized, after calling the Cardinal.setup() function.

  • This event will not trigger if an error occurred during your Cardinal.setup() call (this includes a failed JWT authentication) and this event will only trigger once per page load. If your callback gets executed, you know that Songbird is available to run transactions. 

  • This function will receive 2 arguments that describe the loaded state of Songbird and the current session identifier.

  • You will get sessionId from this event. It will be used in further steps.


Cardinal.on('payments.setupComplete', function(){

var  sessionId = setupCompleteData.sessionId;



  • This event is triggered when the transaction has been finished. This is how Songbird hands back control to your webpage.

  •  This event is triggered with the alternative payment brand results that require a payment authorization. The ActionCode (SUCCESS, NOACTION, FAILURE, ERROR) field should be used to determine the overall state of the transaction. 

  • Additional information can be found in the fields ErrorNumber and ErrorDescription if the ActionCode indicates an issue was encountered.


Cardinal.on("payments.validated", function (data, jwt) {


    switch(data.ActionCode) {

        case "SUCCESS":

        // Handle successful transaction, send JWT to backend to verify


        case "NOACTION":

        // Handle no actionable outcome


        case "FAILURE":

        // Handle failed transaction attempt


        case "ERROR":

        // Handle service level error




  1. Initialize It
  • In this step, You need to pass a jwt token(that you have created in 1st step) as a hidden field in your Hosted payment page and set up cardinal in your JavaScript file.

  • You need to authenticate your system and set up CCA or all the Alternative Payment brands your account is configured for. 

  • Cardinal.setup() will initiate the communication process with Cardinal.

  • You should only call this function once per page load.

  • A common way to pass your JWT into this JavaScript function is to place the JWT value into a hidden input on page load. Within Cardinal.setup(), you can then look for that element and select its value.

JWT hidden field in the HTML : 

 <input type="hidden" id="JWTContainer" value="[Insert your JWT here]" />

Cardinal Setup : 
  Cardinal.setup("init", {

                jwt: document.getElementById("JWTContainer").value


  • Note: Cardinal.setup does not catch errors.  Use the payments.validated event mentioned in Step 4 instead.

  1. Include BIN Detection 

  • In this step, You need to trigger a bin.process event with a card number.

  • The merchant will need to provide a minimum of the first 6 (e.g. BIN) up to the full card number of the consumer (e.g. max of 19 digits).  

  • This event will wait for a static amount of time before resolving to allow for device data collection to complete. The implementor should wait for this event to complete before moving forward with a transaction.

  • This event will need to occur before the Lookup Request (Enrollment).

  • You can check the bin details whether it is correct or not and a way to specifically run the EMV 3DS Method URL.

  • If no Method URL was provided, Cardinal will not perform additional device data collection at this time.

  • The bin.process event will return a promise that will be fulfilled when the EMV 3DS Method URL 'attempt' has been completed. The promised result will include a single object containing the result of the 3DS Method URL attempt.


 Cardinal.trigger("bin.process", 520000)

 .then(function(results) {


    if(results.Status) {

        // Bin profiling was successful. 

           console.log("Bin profiling successful...");

    } else {

        // Bin profiling failed

        console.log("Bin profiling failed...");



    // An error occurred during profiling

       console.log("Exception in Bin profiling ");


For more details bin.process :

  • You can verify the result.Status by using console.log("verify message"). It’s optional.

Steps to implement Client-Side Encryption

  1. Encryption key
  • The encryption key must be supplied to the Client-Side Encryption library and is used to encrypt the sensitive card details. 

  • The decryption key exists within a secure environment at APEXX.

  1. Include the APEXX CSE JavaScript Library
  • For CSE to work, reference the APEXX CSE JavaScript library. The way to do this. Automatically reference the latest version of the current major release.

Test environment :

<script src=""></script>

  1. Set the Public Key (Encryption Key)
  • To set your Public Key (to identify you on our server)


  1. Implement the form
  • For the form to work, use the “data-apexx” attribute for each item of cardholder data.



      <form action="[YOUR FORM ACTION]" method="post" data-apexx="payment-form"> 

        <input data-apexx="card_holder_name"> <!-- This is the cardholder name field --> 

        <input data-apexx="card_number"> <!-- This is the card number field --> 

        <input data-apexx="exp_month"> <!-- This is the expiry month field --> 

        <input data-apexx="exp_year"> <!-- This is the expiry year field --> 

        <input data-apexx="cvv"> <!-- This is the CVV field--> 

        <input type="hidden" name="[YOUR DATA PARAMETER]"     data-apexx="encrypted_data">  <!-- This is the hidden field for encrypted_data --> 

        <input type="hidden" name="maskedCardNumber" data-apexx="masked_card_number">  <!-- This is the hidden field for masked_card_number --> 

        <input type="submit"> 



  • In this example the data-apexx="encrypted_data" attribute sends the encrypted data to your server using [YOUR DATA PARAMETER].

  1. How it all fits together
  • Include the APEXX CSE Javascript file & Set the Public Key in the head of the HTML page.

        <script type="text/javascript" src=""></script> 
       <script type="text/javascript"> 
         window.onload = function() { 
           APEXX.setPublicKey([YOUR PUBLIC KEY]); 

  • Include the apexx payment form html in the card details collection elements of your payment page.

<form action="[YOUR FORM ACTION]" method="post" data-apexx="payment-form">    

    <input data-apexx="card_holder_name"> 

    <input data-apexx="card_number"> 

    <input data-apexx="exp_month"> 

    <input data-apexx="exp_year"> 

    <input data-apexx="cvv"> 

    <input type="hidden" name="[YOUR DATA PARAMETER]" data-apexx="encrypted_data"> 

    <input type="hidden" name="maskedCardNumber" data-apexx="masked_card_number"> 

    <input type="submit">


Below steps will be the flow to process a CSE payment with APEXX.

  1. Include APEXX.js(apexx-cse-1.0.js) into your page.

  2. Implement the HTML form as mentioned above.

  3. On pressing of Submit/Pay button, our JS(apexx-cse-1.0.js) will take the data i.e card_number, exp_month, exp_year, cvv, etc. and do the encryption and JS file will set the encrypted data into hidden parameter and call to form action (merchant server-side)

  4. Call APEXX /payment/direct API with encrypted value along with other information. (Server(Merchant) to server(APEXX) API call).i.e:


    "account":"your _account_id",




    "capture_now": false,

    "card": {

        "encrypted_data" : "{encrypted_value}"


    "dynamic_descriptor": "Your Order",

    "customer": {

        "customer_id" : "",

        "last_name" : "",

        "postal_code" : "",

        "account_number" : "",

        "date_of_birth" : "1993-07-01"


    "customer_ip": "",

    "billing_address": {

        "first_name": "testfirstname",

        "last_name": "testlastname",

        "email": "",

        "address": "5a Underwood Street",

        "city": "London",

        "state": "London",

        "postal_code": "N17LY",

        "country": "GB",

        "phone": "07437836162"


    "merchant_reference": "your_reference",

    "recurring_type": "first",

    "user_agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB;rv: Gecko/20101203 Firefox/3.6.13 (.NET CLR 3.5.30729)",

    "webhook_transaction_update": "",

    "shopper_interaction": "ecommerce",

    "three_ds": {

        ”three_ds_required” : ”true”,

    “three_ds_version”: ”2.0”,

    “reference_id”: “Cardinal Session Id”




CSE Fields with validation function

Filed Name

Possible values


Validation function

Card Holder Name

Only alphabetic characters, Special characters allowed are (hyphen(-), underscore(_), dot(.), comma(,) and apostrophe(‘))

It is representing the card holder name and it's optional field.

isValidCardHolderName([CARD HOLDER NAME);

Success: {"isError":false}

Failed: {

    "errorMessage":"Please enter valid card holder name",



Card Number

12-19 Digit

Card number used for the transaction, also known as PAN.

Characters Validation :

isValidCardNumber([CARD NUMBER]);

Card Number Validation with Luhn check:

luhnCheck([CARD NUMBER]);

Success: {"isError":false}

Failed: {

    "errorMessage":"Please enter valid card number",



Expiry Month

2 Digit (01-12)

Card expiry month. A string representing the month, valid values are 01 to 12.

isValidMonth([EXPIRY MONTH]);

Success: {"isError":false}

Failed: {

    "errorMessage":"Please enter valid Expiry Month",



Expiry Year

2 Digit

Card expiry year. A string representing the last two digits of the year, e.g. 19 for 2019.

isValidYear([EXPIRY YEAR]);

Success: {"isError":false}

Failed: {

    "errorMessage":"Please enter valid Expiry Year",




3-4 Digit

The Card Verification Value. Note: CVV is not required when a transaction is a recurring or one click transaction.


Success: {"isError":false}

Failed: {

    "errorMessage":"Please enter valid CVV",




  • Conclusion: So based on the above information you can integrate with CSE.

  • Now you have CSE encrypted data, You need to pass it for Lookup Request(Enrollment).

Lookup Request (Enrollment)

  • In this step, You need to make a first transaction of the Lookup/Authenticate pair that is used to process the Consumer Authentication transaction request. 

  • All the fields of the transaction request have different roles like the TransactionType (Credit/Debit) value will dictate how the transaction will be processed. 

  • You must ensure that Cardinal has the proper Acquiring Merchant Identification Number and corresponding Acquiring Bank Identification Numbers (BINs) configured in your account. 

  • For the lookup request, you need to call our 3DSEnrol API  with some extra fields like below.

  • You need to pass your encrypted data in the card block for the Enrollment process.

  • Here reference_id will be sessionId that we created in step - 4(Listen for Events).




// 3DS Card data in encrypted form in card block




"encrypted_data": "string"



  // 3DS 2.0 fields in three_ds block


    ”three_ds_required” : ”true”,

    “three_ds_version”: ”2.0”,

    “reference_id”: “string” // it should be sessionId of cardinal



Handling the Lookup Response (Enrollment Response)

  • The Lookup Response will return an ACSUrl, paReq, reference_id, authentication_transaction_id, three_ds_enrollment, three_ds_version and specification_version field.

  • The lookup response verifies that the transaction is eligible for Authentication or not by the three_ds_enrollment element.

  • If the three_ds_enrollment element contains a true value, then Authentication is available and you need to redirect to the ACSUrl to complete the transaction.

  • If the three_ds_enrollment element value is false, then you are NOT eligible for Authentication.

  • After calling our enrollment API, you will receive a lookup response like below:



              "_id": "string"

"created_at": "string",



“three_ds_required": true,

"three_ds_version": "2.0",

"acsURL": "string",

"paReq": "string",

"three_ds_enrollment" : true,

“reference_id”: ”string”,

"authentication_transaction_id": "string",


"specification_version": "string"



Continue with CCA 
  • After the Lookup Response is returned, take the acsURL, Payload, and TransactionId and include them in the Cardinal.continue function in order to proceed with the authentication session.

  • The Cardinal.continue will display a modal window and automatically post the consumer's session over to the acs url for authentication.  


Request fields

Enrollment response fields











AcsUrl:"",             Payload:"eyJtZXNzYWdlVHlwZSI6IkNSZXEiLCJtZXNzYWdlVmVyc2lvbiI6IjIuMi4wIiwidGhyZWVEU1NlcnZlclRyYW5zSUQiOiI1YmJjZjRkYS1iZWU5LTQwNWUtYjE2NS04ZjEzMWQ0NjYxZTkiLCJhY3NUcmFuc0lEIjoiNWVlY2ZlY2YtODUwYy00MGEyLTgyOGYtZjA5MGE4NWM1ZWRkIiwiY2hhbGxlbmdlV2luZG93U2l6ZSI6IjAyIn0"



OrderDetails: {

TransactionId: "J10RCDJbUOLW7St1j9O0"



Handling the CCA Response 

  • Songbird.js will handle all the user interactions until CCA has returned back the authentication result. The next step in the integration is to add logic to your payments.validated event to handle specific return values for CCA.

  • You will receive two elements in the response: JSON data, JWT

  • The field ActionCode should be used as the primary transaction status indicator.

  • Below are the possible values for ActionCode and what they indicate:

    • NOACTION - Authentication was not applicable.

    • SUCCESS - Authentication was completed successfully.

    • FAILURE - Authentication resulted in a failure.

    • ERROR - An error was encountered. 

For more details :

JSON Response Example:


 "iss": "<API Identifier>",

 "iat": 1577091255,

 "exp": 1577098455,

 "jti": "61729408-0c2a-4be6-8ee2-892b4d05ce00",

 "ConsumerSessionId": "1_7076814d-5117-4940-9e4e-aa92a8b03c01",

 "ReferenceId": "1_7076814d-5117-4940-9e4e-aa92a8b03c01",

 "aud": "19ec6910-5048-11e6-8c35-8789b865ff15",

 "Payload": {

  "Validated": true,

  "Payment": {

     "Type": "CCA",

     "ProcessorTransactionId": "J10RCDJbUOLW7St1j9O0",

     "ExtendedData": {

     "CAVV": "MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=",

     "ECIFlag": "02",

     "ThreeDSVersion": "2.2.0",

     "PAResStatus": "Y",

     "SignatureVerification": "Y"



  "ActionCode": "SUCCESS",

  "ErrorNumber": 0,

  "ErrorDescription": "Success"



JWT Response Example:


JWT Validation

  • Once the response JWT is received in the payments.validated event, you will need to send the response JWT to your backend to verify and extract the results.

  • Note: JWT validation should only ever be done on the server side for security reasons.

  • You can validate JWT by JAVA, .NET, or PHP programs in the backend.

For more information and code samples:


  • After the successful validation of JWT, you need to call 3DS Verify Authentication API with the external authentication data.

  • You will receive a final response SUCCESS or FAILED after authorisation.

For API request/response:

Request Example:


"_id": "string",

“paRes”: “string”,

"three_ds_authentication": {

"xid": "string",

"cavv": "string",

"cavv_algorithm": "string",

"3ds_status": "string",

"eci": "string",

"3ds_enrolled": "string",

"directory_server_transaction_id": "string",

"3ds_transaction_status_reason": "string",

"3ds_score": "string",

"3ds_preference": "string",

"3ds_mode": "string",

"3ds_version": "string",

"3ds_result": "string",

"3ds_server_transaction_id": "string"



  • Conclusion: Based on above all steps you can integrate 3DS 2.0 with CSE.