3DS authentication

3DS is a security protocol used to authenticate users. This provides an extra layer of protection for payment card transactions in card-not-present scenarios. It was designed to allow a cardholder to establish his identity to prevent payment fraud, stymie unauthorized transactions, and reduce chargebacks.

Here at Paybyrd, we offer this security flow in two ways:

  1. Using our HostedForm payment flow;
  2. Using our javascript plugin (npm).

Overview

We have three ways to execute the 3DS flow, the first two, Paybyrd deal with it.

Paybyrd deals with everything

The first one, Paybyrd takes care of everything, you just have to create a payment of type "card" and then redirect to the given "actionUrl" returned on response. We will execute all the payment flow and if successful, execute the payment with the auth data.

Callback url flow

For the second flow, you can start a payment at Paybyrd, let us deal with 3DS authentication and then we send a callback with the result so you decide what to do. To use this flow, we need you to pass to us a callback url on "threedsecure.callbackUrl" property.

curl --location --request POST 'https://gatewaysandbox.paybyrd.com/api/v2/payment' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: 7c051c9e-4506-4545-834f-af0d88863374' \
--data-raw '{
    "type":"Card",
    "amount": "0.01",
    "currency": "EUR",
    "card":{
        "number":  "5431885746329897",
        "expiration":"08/30",
        "cvv":"735",
        "holder":"Paybyrd"
    },
    "threeDSecure": {
        "callbackUrl": "https://your-callback-site.com"
    }
}'

The callback is called with a query parameter called "threeDSecureResult" encoded in base 64, and it payload is:

{
  "transactionId":"61b5784e-fae5-43c0-9062-d65eb18b370d",
  "status":"NotEnrolled",
  "authenticationData":{
    "threeDsVersion":"V2",
    "verificationMethod":"ThreeDSecure"
  },
  "code":"305",
  "message":"Unknown BIN",
  "details":"No CRD found, card with BIN 272029 is not enrolled with any known DS",
  "transStatus":"N",
  "transStatusReason":"01"
}
{
  "transactionId":"351eb16c-e985-4160-9f1f-8284a68c9d74",
  "status":"Unauthorized",
  "authenticationData":{
    "threeDsVersion":"V2",
    "dsTransactionId":"5e4381d2-c4f3-4283-8388-c83af8f3df18",
    "verificationMethod":"ThreeDSecure"
  },
  "code":"802",
  "message":"Challenge declined",
  "details":"",
  "transStatus":"N",
  "transStatusReason":"26"
}
{
  "transactionId":"7831c311-5753-4f4f-a61a-41d40d9030ea",
  "status":"Expired",
  "authenticationData":{
    "threeDsVersion":"V2",
    "dsTransactionId":"4a62c676-e8b5-4e6f-9dcd-88819c1e3eb0",
    "verificationMethod":"ThreeDSecure"
  },
  "code":"803",
  "message":"Challenge Expired",
  "details":"Challenge execution must happen in 3 minutes",
  "transStatus":"N",
  "transStatusReason":"14"
}
{
  "transactionId":"5443477d-80ab-44c4-b673-0036e48a7bc2",
  "status":"Authorized",
  "authenticationData":{
    "threeDsVersion":"V2",
    "aav":"ad84cce6b3ea4db98777f02f2528f929",
    "dsTransactionId":"1d67b1e0-d624-4e60-9735-ae2dded98f1e",
    "eci":"05",
    "verificationMethod":"ThreeDSecure"
  },
  "transStatus":"Y",
  "transStatusReason":"17"
}

To execute the payment, the following request should be made:

curl --location -g --request POST 'https://gateway.paybyrd.com/api/v2/payment/5443477d-80ab-44c4-b673-0036e48a7bc2/pay' \
--header 'Content-Type: application/json' \
--data-raw '{
    "card": {
        "number":  "5431885746329897",
        "expiration":"08/30",
        "cvv":"123",
        "holder":"Paybyrd holder",
        "threeDsVersion": "V2",
        "aav": "ad84cce6b3ea4db98777f02f2528f929",
        "dsTransactionId": "1d67b1e0-d624-4e60-9735-ae2dded98f1e",
        "eci": "05",
        "verificationMethod": "ThreeDSecure"
    }
}'

Self-deal

To self-deal the 3DS authentication we've created a library to simplify the implementation using Paybyrd API.

Overview

Setup 3DS flow

In order to setup 3DS flow, the following request should be made:

curl --location --request POST 'https://threedsecuresandbox.paybyrd.com/api/v2' \
--header 'Content-Type: application/json' \
--data-raw '{
    "cardNumber": "5431885746329897",
    "cardExpiration": "04/30",
    "cardHolder": "Paybyrd",
    "merchantId": 10,
    "purchaseAmount": 100,
    "purchaseCurrency": "EUR",
    "isSimulated": false
}'
Parameters
  • cardNumber:: The card number
  • cardExpiration: The card expiration
  • cardHolder: The card holder
  • merchantId: The ID of the merchant on Paybyrd
  • purchaseCurrency: The currency of the purchase
  • purchaseAmount: The amount in cents
  • isSimulated: To use simulation mode on 3DS

The response to that request would be:

{
    "data": {
        "id": "20e80e87-a71e-4b87-931a-e0d725aa9f74",
        "actionUrl": "https://link-s.paybyrd.com/3ds_3n4cNhGq8"
    }
}
{
    "error": {
        "code": "BYRD900",
        "message": "One or more validation errors were found",
        "details": [
            "The card number is invalid"
        ]
    }
}

Now that we have the ID of the process let's use the 3DS library.

How to install

npm i @paybyrd/threedsecure-service

How to configure ThreeDSecureService

Parameters
  • container: HTML Element where the iframe needed to execute the challenge will be created. The iframe will be resizable as the container. DEFAULT: document.body
  • threeDSecureUrl: Paybyrd ThreeDSecure API base URL.DEFAULT: https://threedsecure.paybyrd.com
  • maxAttempts: The max attempts when the API returns transient errors, like 409, 404 or 504 status codes.DEFAULT: 50
  • attemptDelay: The amount of time in milliseconds to wait for the next attempt when some transient error happens.DEFAULT: 2000
  • culture: The culture that should be used for API responses.DEFAULT: en-US
  • onProgressFn: A notification function to identify what is happening inside the service.DEFAULT: NULL
const container = document.getElementById('container-3ds');
const threeDSecureUrl = 'https://threedsecure.paybyrd.com';
const maxAttempts = 50;
const attemptDelay = 2000;
const culture = 'en-US';
const onProgressFn = ({
    type,
    error
}) => console.log(`${type}:${error}`);

const threeDSService = new ThreeDSecureService({
    container,
    threeDSecureUrl,
    maxAttempts,
    attemptDelay,
    culture,
    onProgressFn
});

Executing the 3DS flow

Parameters
  • id: The ID of the 3DS verification created on backend
const request = {
    id: "20e80e87-a71e-4b87-931a-e0d725aa9f74"
}

const threeDSResponse = await threeDSecureService.execute(request);

The threeDsResponse would be something like this:

{
    "data": {
        "transactionId": "61b5784e-fae5-43c0-9062-d65eb18b370d",
        "status": "NotEnrolled",
        "paymentStatus": null,
        "authenticationData": {
            "threeDsVersion": "V2",
            "aav": null,
            "dsTransactionId": null,
            "eci": null,
            "verificationMethod": "ThreeDSecure"
        },
        "code": "305",
        "message": "Unknown BIN",
        "details": "No CRD found, card with BIN 272029 is not enrolled with any known DS",
        "executePayment": false,
        "transStatus": "N",
        "transStatusReason": "01"
    }
}
{
    "data": {
        "transactionId": "351eb16c-e985-4160-9f1f-8284a68c9d74",
        "status": "Unauthorized",
        "paymentStatus": null,
        "authenticationData": {
            "threeDsVersion": "V2",
            "aav": null,
            "dsTransactionId": "5e4381d2-c4f3-4283-8388-c83af8f3df18",
            "eci": null,
            "verificationMethod": "ThreeDSecure"
        },
        "code": "802",
        "message": "Challenge declined",
        "details": "",
        "executePayment": false,
        "transStatus": "N",
        "transStatusReason": "26"
    }
}
{
    "data": {
        "transactionId": "7831c311-5753-4f4f-a61a-41d40d9030ea",
        "status": "Expired",
        "paymentStatus": null,
        "authenticationData": {
            "threeDsVersion": "V2",
            "aav": null,
            "dsTransactionId": "4a62c676-e8b5-4e6f-9dcd-88819c1e3eb0",
            "eci": null,
            "verificationMethod": "ThreeDSecure"
        },
        "code": "803",
        "message": "Challenge Expired",
        "details": "Challenge execution must happen in 3 minutes",
        "executePayment": false,
        "transStatus": "N",
        "transStatusReason": "14"
    }
}
{
    "data": {
        "transactionId": "5443477d-80ab-44c4-b673-0036e48a7bc2",
        "status": "Authorized",
        "paymentStatus": null,
        "authenticationData": {
            "threeDsVersion": "V2",
            "aav": "ad84cce6b3ea4db98777f02f2528f929",
            "dsTransactionId": "1d67b1e0-d624-4e60-9735-ae2dded98f1e",
            "eci": "05",
            "verificationMethod": "ThreeDSecure"
        },
        "code": null,
        "message": null,
        "details": null,
        "executePayment": false,
        "transStatus": "Y",
        "transStatusReason": "17"
    }
}