Getting through 3DS2 challenge with Kiwi.com

API Endpoint

Important notice

This website does not contain documentation of the newest features.

If you are a new user of the Kiwi.com APIs, we highly recommend using tequila.kiwi.com.

To access Tequila documentation, create an account.

If you have any questions, use the Tequila support page.

This document helps implementing the new changes with the introduction of 3DS2


1 Background

3D Secure 2 (or 3DS2) is a next generation of well-known 3D Secure 1 (3DS1) customer verification protocol, which offers more control over the payment process, as a result - it significantly increases the payment acceptance rate due to the ability to make 3DS authentication step optional for “good” customers (frictionless flow), and also it reduces the chargeback rate because you can decline fraudsters even before the start of the payment process (*pre-auth check).

IT is also a legal requirement to implement 3DS2 for the payments done in the EU region eversince the PSD2 has come to an effect on 14th of September 2019. The exact applicability rules can be found in the original PSD2 papers on the European Banking Authority’s Portal or download EMVCo’s 3-D Secure Documentation.


2 Introduction

Kiwi.com provides a 3DS2 verification option at the Payment Step of the booking process. The original payment flow was extended to accommodate new changes in 3DS2 which are as follows:

  1. STEP 1: Tokenize payment card against fraud prevention provider (FPP/external MPI)

  2. STEP 2: Collect customer behavior data from the customer device/s

  3. STEP 3: Get verification option for the payment

  4. STEP 4: Complete the verification challenge, if any


3 Requirements

  1. Use our PICI service for tokenization instead of Tactus or PaymentOS (POS). See our sandbox swagger for basic specifications: https://pici-docs.finance-sandbox.skypicker.com

  2. Use Forter script: Forter’s script provides solution for front end part of 3DS2. This prerequisite applies only if you use Kiwi.com's MPI.

  3. You have an additional UI component for displaying the challenge


4 Implementing new changes in short

Assuming that you’ve just completed the save_booking step and waiting for the payment process to start.

Step 1: Tokenization

This is still done via kiwi tokenization endpoint, which now includes a tokenization step with the FPP and returns additional value - risk_assessment.

Step 2: Collect customer behavior data from the customer’s device

  • This is done via FPP SDKs for customers devices - there are versions available for Web, iOS and Android platforms. This document covers the web flow, contact support or your personal manager at kiwi.com if you need mobile versions as well.

  • To enable SDK you need to load the FPP script on the page, which exports the ftr__ global object.

  • To trigger a collection of the customer data, call the init3DS function with therisk_assessment data which was received on the previous step. You need not care about the content of this object, just pass it into the SDK function.

Step 3: Get verification option for the payment

  • Now it is possible to get a fraud decision for the order. It is handled within the confirm_payment call which indicates the outcome of the verification process. Only one change here, you also need to add the risk_assessment data from the tokenization under the threeds_attributes key.

  • There are two more states added into the response: status: 1 and status: 3. The status: 1 is for the legacy 3DS1 challenge, which is never sent to the partners. And the status: 3 is covered in the next step (and also described in the Step 5.3 below)

Step 4: Complete the verification challenge

  • Response from the confirm_payment with status: 3 shows that the customer must complete the 3DS2 challenge.

  • In this case the three_ds_challenge key present with the data is required for triggering it.

  • To enable the challenge step, web frontend must provide some HTML container for it and pass it together with the three_ds_challenge content to the second FPP SDK function ftr__.triggerChallengeIfNeeded, upon receival of the answer retry the confirm_payment call with after_challenge: true and challenge_result filled.

  • This finishes the process with status: 0 in the response.


5 Implementing new changes in details

To understand all the required actions for 3DS2 please review the entire flow with more details.

Diagram Source

5.1 Tokenization

  1. First step in 3DS2 flow is tokenization of payment card by FPP. This is done by submitting card and payer info to the https://fe.payments-kiwi.com/frei/, more details about this API are present here https://pici-docs.finance-sandbox.skypicker.com/#tag/Tokenization.

  2. There is a new required argument order_id which enables the 3DS2 tokenization.

  3. It is used to create a link between the tokenized card and the order.

  4. Omission of this argument disables the 3DS2 tokenization and risk_assessment data is not returned in the answer.

5.1.1 Arguments reference

  • payment.order_id - value of bid/booking_id from the save_booking

  • payment.programId - value of payu_public_key from the save_booking

  • payment.token - value of payu_token from the save_booking

Endpoint URL

https://fe.payments-kiwi.com/frei/
Request Example (click to expand or collapse)
{
    "card":{
    "number":"4111111111111111",
    "expirationMonth":"01",
    "expirationYear":"21",
    "cvv":"123",
    "holder":"TEST TEST"
    },
    "payment":  {
                "programId":"...",
                "order_id":"123",
                "token":"...",
                "gate":"pos",
                "email":"[email protected]",
                "phone":"000"
    }
}

5.1.2 Clean Tokens

3DS2 Response Example (click to expand or collapse)
{
"status": "success",
"token": "1c2ee7ae-b87f-4a30-82c1-78ba1b5d9920",
"encrypted_cvv": "",
"bin_number": "413849",
"last_4_digits": "0000",
"holder_name": "TEST TEST",
"expiration": "2022-12-01",
"vendor": "VISA",
"issuer": null,
"country_code": "FR",
"level": "",
"type": "CREDIT",
"pass_luhn_validation": true,
"risk_assessment": {
    "correlationId": "2020-02-05T141502519-39cee540-v3",
    "threeDSServerTransID": "9c73c258-c516-4d03-be48-a8f0fee4eac7",
    "methodURL": "https://acs-us-east-1.ndsprod.nds-sandbox-issuer.com/api/v1/acs/3ds_method",
    "version": "2.1.0",
    "additionalThreeDsData": {
        "dsIdentifier": "MC00000001",
        "acsStartProtocolVersion": "2.1.0",
        "threeDSMethodURL": "https://acs-us-east-1.ndsprod.nds-sandbox-issuer.com/api/v1/acs/3ds_method",
            "dsEndProtocolVersion": "2.1.0",
            "acsEndProtocolVersion": "2.1.0",
            "dsStartProtocolVersion": "2.0.0",
            "threeDSServerTransID": "9c73c258-c516-4d03-be48-a8f0fee4eac7"
        },
            "status": "success"
    }
}
3DS1 Response Example (click to expand or collapse)
{
"status": "success",
"token": "32317a51-75d9-4b58-a7ae-c2db9ceebbe4",
"encrypted_cvv": "...",
"bin_number": "542288",
"last_4_digits": "0007",
"holder_name": "TEST TEST",
"expiration": "2021-01-01",
"vendor": "MASTERCARD",
"issuer": null,
"country_code": "HK",
"level": "STANDARD",
"type": "CREDIT",
"pass_luhn_validation": true,
"risk_assessment": {
    "correlationId": "2020-02-05T130633345-cf080a3e-v3",
    "threeDSServerTransID": "v1-2020-02-05T130633345-cf080a3e-v3",
    "methodURL": null,
    "version": "1.0.2",
    "additionalThreeDsData": null,
    "status": "success"
    }
}
Non-3DS Response Example (click to expand or collapse)
{
"status": "success",
"token": "4466efae-ec15-4358-a9c2-f89c79023316",
"encrypted_cvv": "...",
"bin_number": "411111",
"last_4_digits": "1111",
"holder_name": "TEST TEST",
"expiration": "2022-12-01",
"vendor": "VISA",
"issuer": null,
"country_code": "US",
"level": "",
"type": "CREDIT",
"pass_luhn_validation": true,
"risk_assessment": {
    "correlationId": "2020-02-05T140908789-5f7ba53d-v3",
    "version": "3DS disabled",
    "status": "success"
    }
}

5.2 The Second step is to collect the data from the customer’s device

  • This requires an external script to be loaded on the page

  • Script source: <script type="text/javascript" id="ftr__script" async="" src="https://078d9f6a6dc1.cdn4.forter.com/sn/078d9f6a6dc1/script.js"></script>

  • Script exports the global ftr__ object. On this step, the entire value of the risk_assessment key from the tokenization is passed as an argument to the init3DS function.

  • There is no need to handle the response from this call but it is required to finish it before continuing with the next step.

Initializing `ftr__.init3DS` script with payload: (click to expand or collapse)
ftr__.init3DS( {
    "correlationId": "2020-02-05T141502519-39cee540-v3",
    "threeDSServerTransID": "9c73c258-c516-4d03-be48-a8f0fee4eac7",
    "methodURL": "https://acs-us-east-1.ndsprod.nds-sandbox-issuer.com/api/v1/acs/3ds_method",
    "version": "2.1.0",
    "additionalThreeDsData": {"dsIdentifier": "MC00000001",
        "acsStartProtocolVersion": "2.1.0",
        "threeDSMethodURL": "https://acs-us-east-1.ndsprod.nds-sandbox-issuer.com/api/v1/acs/3ds_method",
        "dsEndProtocolVersion": "2.1.0",
        "acsEndProtocolVersion": "2.1.0",
        "dsStartProtocolVersion": "2.0.0",
        "threeDSServerTransID": "9c73c258-c516-4d03-be48-a8f0fee4eac7"},
        "status": "success"
}, (err, res) => {})

5.3 Third step is to call confirm_payment

The third step is to call the confirm_payment API with the risk_assessment data by putting it under the threeds_attributes key. Possible responses are:

  • status: 0 - payment was authorized successfully.

  • status: 1 - 3DS1 verification expected. Considered as an error, contact support.

  • status: 2 - Async Payment, check the payment status https://booking-api.skypicker.com/api/v0.1/payu/payment_status?token=xxx&bid=yyy&sandbox=true/false.

    • Payment was accepted and processed by the acquirers and banks but the payment authorization is not confirmed yet.
    • Our frontend calls get_payment_status endpoint. The response contains the field named as payment_status which can have multiple values such as:
      • approved, authorized_pending, or authorization_not_needed: are considered as success (i.e. as if confirm_payment returned status: 0)
      • failed, cancelled, or authorize_failed: means the payment was failed and cannot be recovered.
      • anything else means that we still do not know the result, in such case the Partner must call this endpoint for some time (i.e. for about 1 min) and then declare the error to the user—
  • status: 3 - Customer challenge requested. Challenge request arguments provided under the three_ds_challenge key.

To display a challenge form the same FPP script is used. ftr__.triggerChallengeIfNeeded must be called with the challenge request arguments and the reference to a display container.

Implement following script (click to expand or collapse)
ftr__.triggerChallengeIfNeeded(
{
"isFallback": false,
"cardEnrolled": null,
"acsTransID": "581a3e8c-10d1-4a1f-817f-e8f9c18d83e9",
"exemption": null,
"interactionCounter": null,
"authenticationType": "02",
"ACSUrl": "https://acs-us-east-1.ndsprod.nds-sandbox-issuer.com/api/v1/acs/browser_sdk_challenges",
"transStatusReason": null,
"authenticationValue": null,
"challengeCancel": null,
"outOfScopeForPSD2": null,
"threeDSServerTransID": "d3885994-f13f-4056-9778-01ba55c55d6d",
"version": "2.1.0",
"three_d_secure_mode": null,
"messageCategory": null,
"transStatus": "C",
"three_d_secure_result": null,
"encodedChallengeRequest": "...",
"ECIValue": "EI",
"dsTransID": "fdb41b26-b283-4969-af29-4e846e69ffc1",
"acsChallengeMandated": "N"
},
container, (err, wasChallengePerformed, transStatus, challengeResponse) => {

5.4 Completion of Verification Challenge

  • If a customer was able to finish the challenge, challengeResponse is presented to the customer and it is then possible to continue with payment.

  • Obtained challengeResponse must be submitted as the challenge_result to the confirm_payment call together with the after_challenge: true.

  • Expected responses are status: 0 or status: 2 (along with some errors)


6: Testing the 3DS2 flow locally

  1. Download the index.html and index.js zipped here.

  2. Unzip the files.

  3. In a terminal, navigate to a directory where you unzipped the files. Run the following command:

    npx http-server
  4. Open http://localhost:8080/ in a browser. Click on run 3DS2 flow, open developer’s console (ctrl/command shift i) and see the request and the response.


Generated by aglio on 12 Mar 2021