New Integration of MPI Cielo for 3DS Authentication

3DS Authentication – New MPI Cielo (V3)

ℹ️

The new MPI Cielo (V3) is exclusive to clients with PCI certification.

The MPI V3 integration implements the EMV 3D Secure authentication protocol with an exclusively server-to-server architecture. In this model, the merchant controls all API calls, including displaying the challenge to the cardholder when required.

⚠️

MPI version vs. 3DS version

This documentation describes the new Cielo MPI (V3), which is the merchant plugin responsible for integrating 3DS authentication in the server-to-server model.

Key points to note:

  • The change from MPI V2 → MPI V3 refers only to the evolution of the integration plugin (MPI).
  • There is no change in the 3DS protocol version.

That is:

ComponentVersion
MPI (integration)✅ V3 (new server-to-server model)
3DS (authentication protocol)✅ 2.2 (Visa, Mastercard) / 2.1 (Elo, Amex)

✅ In summary: you are adopting a new integration model (MPI V3), but 3DS authentication continues to follow the same versions previously supported.


On this page

  1. Benefits
  2. Prerequisites
  3. Environments, endpoints, and tokens
  4. Integration flow
  5. Step 1 – Prepare the front-end
  6. Step 2 – AUTH: obtain the access_token
  7. Step 3 – INIT: initialize the session
  8. Step 4 – Load and initialize the script
  9. Step 5 – ENROLL: authenticate the card
  10. Step 6 – Display the challenge
  11. Step 7 – VALIDATE: validate the authentication
  12. Step 8 – Authorization
  13. Return codes and errors

Benefits

The MPI V3 integration offers:

  • Greater security: all sensitive calls remain in the back-end;
  • Full control of the authentication flow by the merchant;
  • Flexibility to handle each authentication scenario independently.

Prerequisites

Before starting the integration, you need to:

  1. Have a PCI DSS certificate to transmit card data;
  2. Have the 3DS product enabled.

Environments, endpoints, and tokens

APIs

OperationSandbox URLProduction URL
access_token (AUTH)https://mpisandbox.braspag.com.br/v3/auth/tokenhttps://mpi.braspag.com.br/v3/auth/token
Initialization (INIT)https://mpisandbox.braspag.com.br/v3/3ds/inithttps://mpi.braspag.com.br/v3/3ds/init
Enrollment and authentication (ENROLL)https://mpisandbox.braspag.com.br/v3/3ds/enrollhttps://mpi.braspag.com.br/v3/3ds/enroll
Authentication validation (VALIDATE)https://mpisandbox.braspag.com.br/v3/3ds/validatehttps://mpi.braspag.com.br/v3/3ds/validate

JavaScript scripts

Environmentmpi.jsmpiHelpers.js
Sandboxhttps://mpisandbox.braspag.com.br/Scripts/V3/mpi.jshttps://mpisandbox.braspag.com.br/Scripts/V3/mpiHelpers.js
Productionhttps://mpi.braspag.com.br/Scripts/V3/mpi.jshttps://mpi.braspag.com.br/Scripts/V3/mpiHelpers.js

Sandbox: use to validate the integration before going to production. To simulate authentication scenarios, use the 3DS test cards.

Production: use only for real transactions. Production transactions generate costs and may result in penalties by the card brands and issuers.

Difference between tokens

The MPI uses two distinct tokens:

  • access_token:
    • obtained in the AUTH operation;
    • is of bearer type;
    • used only in the back-end;
    • must not be exposed in the front-end.
  • token (INIT):
    • returned by the INIT operation;
    • is a JWT;
    • must be sent to the front-end to initialize the MPI.

Integration flow

The integration follows a mandatory sequence of steps distributed between back-end and front-end:

[BACK-END]  1. AUTH              → Obtain `access_token`
[BACK-END]  2. INIT              → Obtain `referenceId` and `token`
[FRONT-END] 3. MPI.load          → Load resources and dependencies
[FRONT-END] 4. MPI.init          → Initialize script with `token` and `referenceId`
[FRONT-END] 5. MPI.updateCard    → Update card number
[FRONT-END] 6. Submit order      → Collect order data and BrowserInfo
[BACK-END]  7. ENROLL            → Authenticate card
            │
            ├── Status 0 (Not authenticated) → Merchant decision
            ├── Status 1 (Authenticated)     → Proceed to authorization
            └── Status 2 (Challenge)         →
                  [FRONT-END] MPI.challenge → Display challenge to cardholder
                  [FRONT-END] Send post-challenge data to back-end
                  [BACK-END]  8. VALIDATE   → Confirm authentication
                               ├── Status 0 → Merchant decision
                               └── Status 1 → Proceed to authorization

Important: the access_token is valid for 20 minutes in production. The API blocks out-of-order calls and repeated calls. Always follow the sequence: AUTH → INIT → ENROLL → VALIDATE. Do not expose the access_token in the front-end.


Step 1 – Prepare the front-end

1.1 Include the scripts

Add the libraries to the checkout page. Use the URLs corresponding to the environment:

Sandbox:

<script src="https://mpisandbox.braspag.com.br/Scripts/V3/mpi.js"></script>
<script src="https://mpisandbox.braspag.com.br/Scripts/V3/mpiHelpers.js"></script>

Production:

<script src="https://mpi.braspag.com.br/Scripts/V3/mpi.js"></script>
<script src="https://mpi.braspag.com.br/Scripts/V3/mpiHelpers.js"></script>

Source code of mpi.js (sandbox)

The code below is the complete content of mpi.js available in the sandbox environment (https://mpisandbox.braspag.com.br/Scripts/V3/mpi.js). It is responsible for session initialization, card update, and challenge display.

const MPI = (function () {
    "use strict";

    const DEFAULT_CONFIG = {
        "Debug": true,
        "Environment": "PRD"
    };
    const _logPrefixes = ["[MPI.V3]"];
    const _lastError = {
        "Number": null,
        "Description": null,
        "HasError": function () {
            return this.Number !== null;
        }
    };
    let _configuration = null;
    let _referenceId = null;
    let _loaded = false;

    function loadConfiguration(config) {
        // TODO: Validar configuração.
        if (config) {
            _configuration = config;
            return;
        }
        
        _configuration = DEFAULT_CONFIG;

        log("Default configuration loaded", DEFAULT_CONFIG);
    }

    function _loadResources(config) {
        loadConfiguration(config);

        log("Debug =", getIsDebug());
        log("Enviroment =", getEnvironment());
        log("Config =", _configuration);

        if (_loaded) {
            log("Resources already loaded...");
            return;
        }

        log("Loading resources...");

        loadScript(getCardinalScriptUri(), getCardinalScriptIntegrity(), function () {
            _loaded = true;
            log("Cardinal script loaded.");
            notify("onLoadComplete");
        });
    }

    function _initialize(token, referenceId) {
        if (!_loaded) {
            log("Resources not loaded...");
            return;
        }

        _referenceId = referenceId;

        log("Initializing...");
        log("Telemetry Token =", token);
        log("ReferenceId =", _referenceId);

        Cardinal.configure({
            timeout: "8000",
            maxRequestRetries: "10",
            logging: {
                level: getCardinalLogLevel()
            }
        });

        Cardinal.setup('init', {
            jwt: token
        });

        Cardinal.on('payments.setupComplete', function (data) {
            log("Setup complete.");
            log("SetupCompleteData =", data);
            notify("onReady");
        });

        // Eventos da Cardinal
        Cardinal.on('payments.validated', function (data) {
            log("[MPI]", "Payment validated.");
            log("[MPI]", "ActionCode =", data.ActionCode);
            log("[MPI]", "Data =", data);

            switch (data.ActionCode) {
                case 'SUCCESS':
                case 'NOACTION':
                case 'FAILURE':
                    notify("onValidationRequired", {
                        "TransactionId": data.Payment.ProcessorTransactionId
                    });
                    break;
                case 'ERROR':
                    _lastError.Number = data.ErrorNumber;
                    _lastError.Description = data.ErrorDescription;

                    if (data.Payment && data.Payment.ProcessorTransactionId) {
                        notify("onValidationRequired", {
                            "TransactionId": data.Payment.ProcessorTransactionId
                        }); 
                    }
                    else {
                        notify("onError", {
                            "Xid": null,
                            "Eci": null,
                            "ReturnCode": _lastError.HasError() ? _lastError.Number : "MPI901",
                            "ReturnMessage": _lastError.HasError() ? _lastError.Description : "Unexpected error",
                            "ReferenceId": null
                        });
                    }
                    break;
                default:
                    _lastError.Number = data.ErrorNumber;
                    _lastError.Description = data.ErrorDescription;

                    if (data.ErrorDescription === "Success" &&
                        data.Payment &&
                        data.Payment.ProcessorTransactionId) {
                        notify("onValidationRequired", {
                            "TransactionId": data.Payment.ProcessorTransactionId
                        }); 
                    }
                    else {
                        notify("onError", {
                            "Xid": null,
                            "Eci": null,
                            "ReturnCode": _lastError.HasError() ? _lastError.Number : "MPI902",
                            "ReturnMessage": _lastError.HasError() ? _lastError.Description : "Unexpected authentication response",
                            "ReferenceId": null
                        });
                    }
            }
        });
    }

    function canReadCardNumber() {
        return typeof _configuration["cardNumberReader"] === "function";
    }

    function readCardNumber() {
        if (canReadCardNumber()) {
            return _configuration.cardNumberReader();
        }

        return "";
    }

    function _updateCard() {
        let cardNumber = readCardNumber();

        if (typeof cardNumber !== "string" || cardNumber.trim().length === 0) {
            log("Card number is empty. Skipping update.");
            return;
        }

        log("Updating card... ");

        Cardinal.trigger("accountNumber.update", cardNumber);
    }

    function _challenge(challengeData, order) {
        log("Showing challenge...");
        log("Challenge object =", challengeData);
        log("Order object =", order);

        Cardinal.continue("cca",
            challengeData,
            order);
    }

    function getEnvironment() {
        return _configuration.Environment || "PRD";
    }

    function getCardinalScriptUri() {
        const environment = getEnvironment();
        const enviroments = {
            "TST": "https://cas.static.client.cardinaltrusted.com/songbird/v2.0.0/songbird.js",
            "SDB": "https://cas.static.client.cardinaltrusted.com/songbird/v2.0.0/songbird.js",
            "PRD": "https://static.client.cardinaltrusted.com/songbird/v2.0.0/songbird.js"
        };

        return enviroments[environment];
    }

    function getCardinalScriptIntegrity() {
        const environment = getEnvironment();
        const integrities = {
            "TST": "sha384-6nfn2JGFT+s1LF9R3jP+Nn6HFGdlBXNlv4PaMKXNCSec9+hsazo8euFU8te9H5cZ",
            "SDB": "sha384-6nfn2JGFT+s1LF9R3jP+Nn6HFGdlBXNlv4PaMKXNCSec9+hsazo8euFU8te9H5cZ",
            "PRD": "sha384-LF1l2lmxu53ZO8yd0LMbIzyAosonuf0vOOz/SVegG9xfS0QtUxW53vB2U0JoWJem"
        };

        return integrities[environment];
    }

    function loadScript(url, integrity, callback) {
        const head = document.getElementsByTagName('head')[0];
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url;
        script.integrity = integrity;
        script.crossOrigin = "anonymous";
        script.onload = callback;
        head.appendChild(script);
    }

    function getIsDebug() {
        if (_configuration.Debug !== "undefined") {
            return _configuration.Debug;
        }

        return false;
    }

    function log(...args) {
        if (getIsDebug())
            console.log(..._logPrefixes, ...args);
    }

    function getCardinalLogLevel() {
        return getIsDebug() ? "verbose" : "off";
    }

    function isSubscribed(eventType) {
        return typeof _configuration[eventType] === "function";
    }

    function notify(eventType, eventData) {
        log("Notifying...");
        log("Event type =", eventType);
        log("Event data =", eventData || "None");
        if (isSubscribed(eventType)) {
            _configuration[eventType](eventData);
        }
        else {
            log("Not subscribed")
        }
    }

    return {
        load: function (config) {
            _loadResources(config);
        },
        init: function (referenceId, initToken) {
            _initialize(initToken, referenceId);
        },
        updateCard: function () {
            _updateCard();
        },
        challenge: function (challengeData, order) {
            _challenge(challengeData, order);
        }
    };
})();

Key points about mpi.js:

  • MPI.load(config) loads Cardinal Commerce asynchronously and triggers onLoadComplete when done.
  • MPI.init(referenceId, token) — the parameter order is (referenceId, token). Internally, the method reverses the call to _initialize(token, referenceId).
  • MPI.updateCard() reads the card number via cardNumberReader and fires Cardinal.trigger("accountNumber.update", cardNumber). If the card is updated during an active session, MPI.updateCard() must be called again before proceeding to ENROLL.
  • MPI.challenge(challengeData, order) calls Cardinal.continue("cca", challengeData, order) to display the challenge.
  • The onValidationRequired callback is triggered for ActionCode: SUCCESS, NOACTION, FAILURE, and ERROR (when ProcessorTransactionId is present).
  • The onError callback is triggered when ActionCode is ERROR without ProcessorTransactionId, or on unexpected responses.

Source code of mpiHelpers.js (sandbox)

The code below is the complete content of mpiHelpers.js available in the sandbox environment (https://mpisandbox.braspag.com.br/Scripts/V3/mpiHelpers.js). It provides the getBrowserInfo, getOrderBuilder utilities, and the currency and channel constants used in the previous steps.

mpiHelpers.js will be used in Step 4 – Load and initialize the script

const MPIHelpers = (function () {
    "use strict";

    const _currencyIsoCodes = {
        BRL: "986",
        USD: "840",
        MXN: "484",
        COP: "170",
        CLP: "152",
        ARS: "032",
        PEN: "604",
        EUR: "978",
        PYN: "600",
        UYU: "858",
        VEB: "862",
        VEF: "937",
        GBP: "826"
    };

    const _orderChannels = {
        ECOMMERCE: "S",
        MOTO: "M",
        RETAIL: "R",
        MOBILE_DEVICE: "P",
        TABLET: "T"
    };

    const _orderBuilder = {
        _order: {
            "OrderDetails": {
                "OrderChannel": "S"
            },
            "Consumer": {
                "Account": {},
                "ShippingAddress": {},
                "BillingAddress": {}
            },
            "Cart": []
        },

        withTransaction: function (transactionId) {
            this._order.OrderDetails.TransactionId = transactionId;
            return this;
        },
        withOrderNumber: function (orderNumber) {
            this._order.OrderDetails.OrderNumber = orderNumber;
            return this;
        },
        withCurrency: function (currencyCode) {
            this._order.OrderDetails.CurrencyCode = currencyCode;
            return this;
        },
        withChannel: function (orderChannel) {
            this._order.OrderDetails.OrderChannel = orderChannel;
            return this;
        },
        withOrderDetails: function (orderNumber, currencyCode, orderChannel) {
            this.withOrderNumber(orderNumber);
            this.withCurrency(currencyCode);
            this.withChannel(orderChannel);
            return this;
        },
        withCard: function (cardNumber, expirationMonth, expirationYear) {
            this._order.Consumer.Account.AccountNumber = cardNumber;
            this._order.Consumer.Account.ExpirationMonth = expirationMonth;
            this._order.Consumer.Account.ExpirationYear = expirationYear;
            return this;
        },
        withEmail1: function (email1) {
            this._order.Consumer.Email1 = email1;
            return this;
        },
        withEmail2: function (email2) {
            this._order.Consumer.Email2 = email2;
            return this;
        },
        withBillingAddress: function (fullname, addressLine1, addressLine2, city, state, postalCode, countryCode, phone) {
            this._order.Consumer.BillingAddress.FullName = fullname;
            this._order.Consumer.BillingAddress.Address1 = addressLine1;
            this._order.Consumer.BillingAddress.Address2 = addressLine2;
            this._order.Consumer.BillingAddress.City = city;
            this._order.Consumer.BillingAddress.State = state;
            this._order.Consumer.BillingAddress.PostalCode = postalCode;
            this._order.Consumer.BillingAddress.CountryCode = countryCode;
            this._order.Consumer.BillingAddress.Phone1 = phone;
            return this;
        },
        withShippingAddress: function (fullname, addressLine1, addressLine2, city, state, postalCode, countryCode, phone) {
            this._order.Consumer.ShippingAddress.FullName = fullname;
            this._order.Consumer.ShippingAddress.Address1 = addressLine1;
            this._order.Consumer.ShippingAddress.Address2 = addressLine2;
            this._order.Consumer.ShippingAddress.City = city;
            this._order.Consumer.ShippingAddress.State = state;
            this._order.Consumer.ShippingAddress.PostalCode = postalCode;
            this._order.Consumer.ShippingAddress.CountryCode = countryCode;
            this._order.Consumer.ShippingAddress.Phone1 = phone;
            return this;
        },
        withShippingAddressAsBillingAddress() {
            this.withShippingAddress(
                this._order.Consumer.BillingAddress.FullName,
                this._order.Consumer.BillingAddress.Address1,
                this._order.Consumer.BillingAddress.Address2,
                this._order.Consumer.BillingAddress.City,
                this._order.Consumer.BillingAddress.State,
                this._order.Consumer.BillingAddress.PostalCode,
                this._order.Consumer.BillingAddress.CountryCode,
                this._order.Consumer.BillingAddress.Phone1
            );
            return this;
        },
        withCartItem: function (name, description, sku, quantity, price) {
            const cartItem = this.buildCartItem(name, description, sku, quantity, price);
            this._order.Cart.push(cartItem);
            return this;
        },
        withCartItems: function (...cartItens) {
            this._order.Cart.push(...cartItens);
            return this;
        },
        buildCartItem: function (name, description, sku, quantity, price) {
            return {
                "Name": name,
                "Description": description,
                "SKU": sku,
                "Quantity": quantity,
                "Price": price
            };
        },
        build: function () {
            return this._order;
        }
    };

    function _getBrowserInfo() {
        const screenWidth = window && window.screen ? window.screen.width : '';
        const screenHeight = window && window.screen ? window.screen.height : '';
        const colorDepth = window && window.screen ? window.screen.colorDepth : '';
        const userAgent = window && window.navigator ? window.navigator.userAgent : '';
        const javaEnabled = window && window.navigator && window.navigator.javaEnabled() ? 'Y' : 'N';

        let language = '';
        if (window && window.navigator) {
            language = window.navigator.language ? window.navigator.language : window.navigator.browserLanguage;
        }

        const timeZoneOffset = new Date().getTimezoneOffset();

        return {
            userAgent: userAgent,
            screenWidth: screenWidth,
            screenHeight: screenHeight,
            colorDepth: colorDepth,
            timeZoneOffset: timeZoneOffset,
            language: language,
            javaEnabled: javaEnabled,
            javascriptEnabled: 'Y'
        };
    }

    return {
        getBrowserInfo: function () {
            return _getBrowserInfo();
        },
        getOrderBuilder: function () {
            return _orderBuilder;
        },
        Constants: {
            getCurrencyISO: function () {
                return _currencyIsoCodes;
            },
            getOrderChannels: function () {
                return _orderChannels;
            }
        }
    };
})();

Key points about mpiHelpers.js:

  • getBrowserInfo() collects userAgent, screenWidth, screenHeight, colorDepth, timeZoneOffset, language, and javaEnabled from the browser. The javascriptEnabled field is always 'Y'.
  • getOrderBuilder() returns the order builder. All with* methods are chainable and end with .build().
  • withShippingAddressAsBillingAddress() copies the billing address to the shipping address without needing to repeat the data.
  • buildCartItem() creates a standalone item for use with withCartItems(...items).

1.2 Configure the mpi.js script

In this step you will configure the mpi.js script.

Call MPI.load passing a configuration object with the required parameters and callbacks:

MPI.load({
    Environment: "SDB",   // "SDB" for sandbox | "PRD" for production
    Debug: true,          // true enables logs in the browser console

    // Triggered when external dependencies (Cardinal) are loaded.
    // Use to enable the buy button or indicate that checkout is ready.
    onLoadComplete: function () {
        document.getElementById("btnPagar").disabled = false;
    },

    // Triggered when Cardinal.setup is complete and the script is ready for use.
    // Only call MPI.updateCard() after this event.
    onReady: function () {
        console.log("MPI ready.");
    },

    // Triggered when an error occurs during the authentication process.
    // The error object contains: { Xid, Eci, ReturnCode, ReturnMessage, ReferenceId }
    onError: function (error) {
        console.error("Code:", error.ReturnCode);
        console.error("Message:", error.ReturnMessage);
    },

    // Triggered after the cardholder completes the challenge.
    // The validationEvent object contains: { TransactionId }
    // Use TransactionId to call the VALIDATE operation in the back-end.
    onValidationRequired: function (validationEvent) {
        const transactionId = validationEvent.TransactionId;
        sendToBackend({ transactionId: transactionId });
    },

    // Called immediately before executing MPI.updateCard().
    // Must return the card number as a string (digits only).
    cardNumberReader: function () {
        return document.getElementById("cardNumber").value.replace(/\D/g, "");
    }
});

Configuration parameters:

ParameterTypeAccepted valuesDescription
Environmentstring"SDB" or "PRD"Execution environment
Debugbooleantrue or falseEnables logs in the console (debug)

Event callbacks:

CallbackReceived argumentDescription
onLoadCompleteTriggered when dependencies are loaded.
onReadyTriggered when script initialization is complete.
onError{ Xid, Eci, ReturnCode, ReturnMessage, ReferenceId }Triggered when an error occurs during the process.
onValidationRequired{ TransactionId }Triggered after the challenge. Use TransactionId to trigger the VALIDATE.
cardNumberReaderCalled by the script to read the card number. Returns a string.

Step 2 – AUTH: obtain the access_token

Access credentials

To authenticate calls to the MPI API, you need a ClientId and a ClientSecret corresponding to the target environment.

EnvironmentCredentials
SandboxClientId: dba3a8db-fa54-40e0-8bab-7bfb9b6f2e2e
ClientSecret: D/ilRsfoqHlSUChwAMnlyKdDNd7FMsM7cU/vo02REag=
ProductionAfter completing sandbox testing, obtain the credentials on the Portal E-commerce.

To generate the access_token used in MPI API calls, follow the steps below:

  1. Concatenate the ClientId and the ClientSecret in the format ClientId:ClientSecret;
  2. Encode the result in Base64;
  3. Send the AUTH operation request using the POST method.

Concatenation and encoding example

FieldValue
ClientIddba3a8db-fa54-40e0-8bab-7bfb9b6f2e2e
ClientSecretD/ilRsfoqHlSUChwAMnlyKdDNd7FMsM7cU/vo02REag=
ClientId:ClientSecretdba3a8db-fa54-40e0-8bab-7bfb9b6f2e2e:D/ilRsfoqHlSUChwAMnlyKdDNd7FMsM7cU/vo02REag=
Base64ZGJhM2E4ZGItZmE1NC00MGUwLThiYWItN2JmYjliNmYyZTJlOkQvaWxSc2ZvcUhsU1VDaHdBTW5seUtkRE5kN0ZNc003Y1Uvdm8wMlJFYWc9

The Authorization header must be sent using Basic Authentication

Authorization: Basic ZGJhM2E4ZGItZmE1NC00MGUwLThiYWItN2JmYjliNmYyZTJlOkQvaWxSc2ZvcUhsU1VDaHdBTW5seUtkRE5kN0ZNc003Y1Uvdm8wMlJFYWc9

Run this operation exclusively in the back-end. Never expose the access_token in the front-end.

Request

Method: POST
Sandbox URL: https://mpisandbox.braspag.com.br/v3/auth/token
Production URL: https://mpi.braspag.com.br/v3/auth/token

Headers:

Authorization: Basic <base64_credential>
Content-Type: application/json

Body:

{
    "EstablishmentCode": "1111111111",
    "MerchantName": "MERCHANT NAME",
    "MCC": "0000"
}

Request fields:

FieldTypeRequiredSizeDescription
EstablishmentCodenumericYes10Merchant establishment code at the acquirer
MerchantNamestringYes1–25Merchant name
MCCnumericYes4Merchant Category Code (MCC)

Response

{
    "access_token": "<access_token>",
    "token_type": "bearer",
    "expires_in": "1200"
}
[
    {
        "Code": 605,
        "Message": "Establishment code is a mandatory parameter"
    }
]
[
    {
        "Code": 606,
        "Message": "Merchant name is a mandatory parameter"
    }
]
[
    {
        "Code": 607,
        "Message": "MCC is a mandatory parameter"
    }
]
[
    {
        "Code": 608,
        "Message": "MCC must have 4 digits"
    }
]
[
    {
        "Code": 609,
        "Message": "All merchant data must be informed"
    }
]

Response fields:

FieldTypeDescription
access_tokenstringAccess token. Use in all subsequent calls. Do not expose the access_token in the front-end.
token_typestringToken type. Fixed value: bearer.
expires_instringaccess_token validity period in seconds.
Value: 1200 (20 min).

Common errors (400 Bad Request)

Field with errorScenarioError code
EstablishmentCodeField submitted empty605
MerchantNameField submitted empty606
MCCField submitted empty607
MCCNon-numeric value (e.g., "XXXX")607

Step 3 – INIT: initialize the session

Run this operation in the back-end after obtaining the access_token. The returned token must be sent to the front-end to initialize the MPI script.

Warning: the token returned by INIT is different from the access_token. Only the token can be exposed in the front-end. Do not expose the access_token in the front-end.

Request

Method: POST
Sandbox URL: https://mpisandbox.braspag.com.br/v3/3ds/init
Production URL: https://mpi.braspag.com.br/v3/3ds/init

Headers:

Authorization: Bearer <access_token>
Content-Type: application/json

Body:

{
    "orderNumber": "ORD-0000123",
    "currency": "986",
    "amount": "1000"
}

Request fields:

FieldTypeRequiredSizeRangeDescription
orderNumberstringYes1–50Order number
currencystringYes3986 (BRL)ISO currency code
amountnumericNo0–99,999,999,999Total order amount in cents.
Default value: 0.

Response

{
    "referenceId": "f49f78fc-4427-439b-888c-dfa78a207bca",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwZWJlYjA1My1mZTYxLTRhYWQtYWIyYi0yOGE3MzQxOTQyNzMiLCJpYXQiOjE3ODIxNTg2ODcsImlzcyI6IjYwOTk1OGJiZjc0MzQ0NmUwMWNkYzAzMiIsIk9yZ1VuaXRJZCI6IjYwOTk1OGJiMGQ2MTkzN2NlMjM4ODlmYiIsIlBheWxvYWQiOiJ7XCJPcmRlckRldGFpbHNcIjp7XCJPcmRlck51bWJlclwiOlwiMTIzNDU2XCIsXCJBbW91bnRcIjpcIjEwMDBcIixcIkN1cnJlbmN5Q29kZVwiOlwiOTg2XCJ9fSIsIk9iamVjdGlmeVBheWxvYWQiOmZhbHNlLCJleHAiOjE3ODIxNTk1ODcsIlJlZmVyZW5jZUlkIjoiZjQ5Zjc4ZmMtNDQyNy00MzliLTg4OGMtZGZhNzhhMjA3YmNhIn0.rVvMS5r3rkOKNiDTOOLuz23amwkDbQ84Af3Agw1zCd4"
}
[
    {
        "Code": "OrderNumber",
        "Message": "'Order Number' must not be empty."
    }
]
[
    {
        "Code": "609",
        "Message": "Invalid Access Token"
    }
]

Response fields:

FieldTypeDescription
referenceIdstringReference identifier. Use in the ENROLL request body.
tokenstringToken for script initialization in the front-end via MPI.init.

Common errors (400 Bad Request)

Field with errorScenario
orderNumberField submitted empty

Step 4 – Load and initialize the script

After the back-end runs INIT and returns referenceId and token, send these values to the front-end.

Warning: the token returned by INIT is different from the access_token. Only the token can be exposed in the front-end. Do not expose the access_token in the front-end.

4.1 Initialize the script

The snippet below is an example of how to initialize the script; adapt it with the required data:

// Call your back-end endpoint that runs INIT and returns referenceId and token
fetch("/api/YOUR_ENDPOINT", { method: "POST" })
    .then(response => response.json())
    .then(data => {
        // data.referenceId and data.token are returned by the INIT operation
        MPI.init(data.referenceId, data.token);
        // Wait for the onReady callback before proceeding.
    });

The method signature is MPI.init(referenceId, token). The parameter order is mandatory.

4.2 Update the card and submit the order

Call MPI.updateCard() before submitting the order to the back-end. The method invokes the cardNumberReader defined in the configuration and does not return a value.

ℹ️

Always call MPI.updateCard() again when the card number may have changed between attempts.

The method sends the current cardNumber to Cardinal and associates it with the active session — without this update, authentication may use an outdated number. This applies mainly when the shopper changes cards during the same session.

In these cases, call MPI.updateCard() before a new ENROLL to keep the session synchronized.

document.getElementById("btnPagar").addEventListener("click", function () {
    // 1. Update card data in the script
    MPI.updateCard();

    // 2. Collect browser data
    const browserInfo = MPIHelpers.getBrowserInfo();

    // 3. Send to your back-end endpoint to run ENROLL
    fetch("/api/YOUR_ENDPOINT", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            orderNumber: "ORD-0000123",
            // ... other order fields
            browserInfo: browserInfo
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 2) {
            // Challenge required
            const order = buildOrderObject();
            MPI.challenge(
                {
                    acsUrl:        data.challenge.acsUrl,
                    payload:       data.challenge.pareq,
                    transactionId: data.challenge.transactionId
                },
                order
            );
        }
        // Status 0 or 1: handle in the back-end
    });
});

Use MPIHelpers.getBrowserInfo() to automatically collect the mandatory data for the BrowserInfo object:

const browserInfo = MPIHelpers.getBrowserInfo();
// Returns:
// {
//     userAgent:         "Mozilla/5.0 ...",
//     screenWidth:       1280,
//     screenHeight:      720,
//     colorDepth:        24,
//     timeZoneOffset:    180,
//     language:          "pt-BR",
//     javaEnabled:       "N",
//     javascriptEnabled: "Y"
// }

You can also query accepted currencies and channels via MPIHelpers.Constants:

// Returns the currency map: { BRL: "986", USD: "840", EUR: "978", ... }
const currencies = MPIHelpers.Constants.getCurrencyISO();

// Returns accepted channels: { ECOMMERCE: "S", MOTO: "M", RETAIL: "R", MOBILE_DEVICE: "P", TABLET: "T" }
const channels = MPIHelpers.Constants.getOrderChannels();

Step 5 – ENROLL: authenticate the card

Run this operation in the back-end. It performs cardholder enrollment and card authentication in the 3DS protocol.

See the main authentication scenarios supported by the API, with examples of expected behavior for each request type:

ScenarioAuthentication typeDescriptionExpected result
Scenario 1No challenge (required fields)Simulates authentication sending only the required fields.Status = 1
Scenario 2No challenge (required + optional fields)Simulates authentication with additional data to enrich risk analysis.Status = 1
Scenario 3No challenge (airlines)Simulates a no-challenge authentication specific to airlines.Status = 1
Scenario 4No challenge (recurring transactions)Simulates a no-challenge authentication specific to recurring transactions.Status = 1
Scenario 5With challengeSimulates authentication that requires a challenge (e.g., step-up).Status = 2 with challenge object
Scenario 6Authentication failureSimulates a failure in the authentication process.Status = 0
Scenario 7Data OnlyCollects cardholder data only without generating a challenge.Status = 1 without challenge object

Test cards without challenge

To simulate no-challenge authentications, submit requests with one of the following test cards:

CardCard brandResultScenarioStatus
4000000000002701
5200000000002235
6505290000002000
VISA
MASTER
ELO
SuccessNo-challenge authentication with cardholder successfully authenticated.1
4000000000002925
5200000000002276
6505290000002018
VISA
MASTER
ELO
FailureNo-challenge authentication with cardholder authentication failure.0

Test cards with challenge

To simulate challenge authentications, submit requests with one of the following test cards:

CardCard brandResultScenarioStatus
4000000000002503 5200000000002151
6505290000002190
Visa Master EloSuccessChallenge authentication with cardholder successfully authenticated.2
4000000000002370
5200000000002490
6505290000002208
Visa Master
Elo
FailureChallenge authentication with cardholder authentication failure.0
4000000000002420
5200000000002664
6505290000002257
Visa Master
Elo
UnenrolledChallenge authentication currently unavailable.0
4000000000002644
5200000000002656
6505290000002265
Visa
Master
Elo
ErrorSystem error during the authentication step.0

Test cards for DataOnly

To simulate DataOnly authentications, submit requests with one of the following test cards:

CardCard brandResultScenarioStatus
5200000000002805
4000000000002024
Mastercard
Visa
SuccessDataOnly authentication0

Request

Method: POST
Sandbox URL: https://mpisandbox.braspag.com.br/v3/3ds/enroll
Production URL: https://mpi.braspag.com.br/v3/3ds/enroll

Headers:

Authorization: Bearer <access_token>
Content-Type: application/json

Scenario 1 – No challenge (required fields)

See a request example that simulates no-challenge authentication sending only the required fields.

To get Status = 1, use the test card number 4000000000002701.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://www.yourmerchantname.com",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 1000,
    "Installments": 1,
    "ShipToSameAsBillTo": true,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002701",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "123.456.789-09",
        "Name": "João da Silva",
        "PhoneNumber": "11999990001",
        "Email": "[email protected]",
        "Street1": "Av. Paulista, 1000",
        "Street2": "Apto 42",
        "City": "São Paulo",
        "State": "SP",
        "ZipCode": "01310-100",
        "Country": "BR"
    },
    "CartItems": [
        {
            "Name": "Product A",
            "Description": "Product A description",
            "Sku": "SKU-001",
            "Quantity": 1,
            "UnitPrice": 1000
        }
    ],
    "DeviceInfo": {
        "IpAddress": "200.200.200.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "<fingerprint_id>",
                "Provider": "cardinal"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY"
    },
    "AuthNotifyOnly": false,
    "ChallengeWindowSize": "390x400",
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": null,
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "c00a2117-0da5-454b-8bcb-63774b46cd4f",
        "Xid": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
        "Eci": "05",
        "Cavv": "AJkBBkhgQQAAAE4gSEJydQAAAAA="
    },
    "Status": 1,
    "Reason": {
        "Code": "100",
        "Message": "Success"
    }
}

Scenario 2 – No challenge (required and optional fields)

See a request example that simulates no-challenge authentication also sending the optional fields UserAccount, ShipTo, and advanced Order fields to enrich risk analysis.

To get Status = 1, use the test card number 4000000000002701.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://samplemerchant.org",
    "MerchantNewCustomer": "true",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 1000,
    "Installments": 1,
    "ShipToSameAsBillTo": false,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002701",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "999888777-94",
        "Name": "John Doe",
        "PhoneNumber": "555-0001",
        "Email": "[email protected]",
        "Street1": "Av. Batatinha Quando Nasce",
        "Street2": "Batata",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222222-22",
        "Country": "BR"
    },
    "ShipTo": {
        "Name": "John Doe Jr.",
        "PhoneNumber": "555-0002",
        "Email": "[email protected]",
        "Street1": "Rua Bem Longe",
        "Street2": "Casa 3",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222223-23",
        "Country": "BR"
    },
    "CartItems": [
        {
            "Name": "Product A",
            "Description": "Product A description",
            "Sku": "SKU-001",
            "Quantity": 1,
            "UnitPrice": 1000
        }
    ],
    "DeviceInfo": {
        "IpAddress": "127.0.0.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "<fingerprint_id>",
                "Provider": "cardinal"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY",
        "CountLast24Hours": 2,
        "CountLast6Months": 15,
        "CountLast1Year": 38,
        "CardAttemptsLast24Hours": 1
    },
    "UserAccount": {
        "Guest": false,
        "CreatedDate": "2020-01-10",
        "ChangedDate": "2025-06-01",
        "PasswordChangedDate": "2025-06-01",
        "AuthenticationMethod": "02"
    },
    "MerchantDefinedData": {
        "1": "custom_value_1",
        "2": "custom_value_2"
    },
    "AuthNotifyOnly": false,
    "ChallengeWindowSize": "390x400",
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": null,
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "e4198bcd-5024-4909-8885-5c924149237f",
        "Xid": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
        "Eci": "05",
        "Cavv": "AJkBBkhgQQAAAE4gSEJydQAAAAA="
    },
    "Status": 1,
    "Reason": {
        "Code": "100",
        "Message": "Success"
    }
}

Scenario 3 – No challenge (airlines)

See a request example for airlines that simulates no-challenge authentication sending only the required fields. To get Status = 1, use the test card number 4000000000002701.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://samplemerchant.org",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 700000,
    "Installments": 1,
    "ShipToSameAsBillTo": false,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002701",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "999888777-94",
        "Name": "John Doe",
        "PhoneNumber": "555-0001",
        "Email": "[email protected]",
        "Street1": "Av. Batatinha Quando Nasce",
        "Street2": "Batata",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222222-22",
        "Country": "BR"
    },
    "DeviceInfo": {
        "IpAddress": "127.0.0.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "<fingerprint_id>",
                "Provider": "cardinal"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY"
    },
    "Airline": {
        "NumberOfPassengers": 2,
        "PassportCountry": "BR",
        "PassportNumber": "AA123456",
        "TravelLegs": [
            {
                "Carrier": "LA",
                "DepartureDate": "2026-09-01",
                "Origin": "GRU",
                "Destination": "JFK"
            }
        ],
        "Passengers": [
            {
                "Name": "João da Silva",
                "TicketPrice": 350000
            },
            {
                "Name": "Maria da Silva",
                "TicketPrice": 350000
            }
        ]
    },
    "AuthNotifyOnly": false,
    "ChallengeWindowSize": "390x400",
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": null,
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "b1d0c4ff-0720-437d-8125-0c8b50c067a1",
        "Xid": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
        "Eci": "05",
        "Cavv": "AJkBBkhgQQAAAE4gSEJydQAAAAA="
    },
    "Status": 1,
    "Reason": {
        "Code": "100",
        "Message": "Success"
    }
}

Scenario 4 – No challenge (recurring transactions)

To simulate no-challenge authentication for recurring transactions, you must send the required fields in the Recurring object and use the test card 4000000000002701.

ℹ️

In the recurring payment flow, only the first transaction in the series is submitted to the authentication process.

Subsequent transactions in the same recurrence are not re-authenticated, and are sent directly for authorization.

See a request example that simulates no-challenge authentication for recurring transactions:

{
    "TransactionMode": "S",
    "MerchantUrl": "https://samplemerchant.org",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 1000,
    "Installments": 1,
    "ShipToSameAsBillTo": true,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002701",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "999888777-94",
        "Name": "John Doe",
        "PhoneNumber": "555-0001",
        "Email": "[email protected]",
        "Street1": "Av. Batatinha Quando Nasce",
        "Street2": "Batata",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222222-22",
        "Country": "BR"
    },
    "CartItems": [
        {
            "Name": "Name412f7da6-a1d5-45f8-ab8a-7011b9522c4e",
            "Description": "Descriptionbde9d2f3-2e05-4183-a06b-44d4ece3ae08",
            "Sku": "Sku54685c5a-2c7e-44a4-9a0e-ef1bdfe324b1",
            "Quantity": 241,
            "UnitPrice": 46
        },
        {
            "Name": "Nameef810b54-de83-4f5a-9390-6402715d877d",
            "Description": "Descriptiond1b4947f-14f6-4a32-a076-a2d1c8f46aa9",
            "Sku": "Sku8cee605b-b222-4936-9ad7-cd5188925015",
            "Quantity": 160,
            "UnitPrice": 24
        },
        {
            "Name": "Name40b447e9-2a22-4ce8-b90b-f803b07bc6b5",
            "Description": "Description584b5011-e969-41ff-94d4-5b68f073b3a5",
            "Sku": "Sku8ad6c9e3-1676-4476-9208-ed61f650aadb",
            "Quantity": 155,
            "UnitPrice": 42
        }
    ],
    "DeviceInfo": {
        "IpAddress": "127.0.0.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "FingerPrinte659bbd0-f2d6-491d-8fd0-f2d179ab42e6",
                "Provider": "Provider01c89ed4-4956-468c-9eea-2e7dd8a0409a"
            },
            {
                "FingerPrint": "FingerPrint89d86f03-a064-4bae-b1fb-cb46984088d1",
                "Provider": "Provider76640b49-b702-43f2-b78d-90796002d2f9"
            },
            {
                "FingerPrint": "FingerPrintd7bde495-6a28-4feb-a515-afacaf608b31",
                "Provider": "Providerdcbaa001-980b-4bcd-b84e-86ced8a71067"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY"
    },
    "MerchantDefinedData": {
        "1": "valuec0b65672-17c3-4e9b-a1fa-9b725f9c95dc",
        "2": "valuee7e1b70f-01ea-4742-9738-99e99eac1863",
        "3": "value9c78f1f9-1728-426c-9ac7-52e330461511"
    },
    "AuthNotifyOnly": false,
    "ChallengeWindowSize": "250x400",
    "Recurring": {
        "EndDate": "2026-12-31",
        "Frequency": 1,
        "OriginalPurchaseDate": "2026-01-01",
        "RecurringInfos": {
            "Type": 1,
            "ValidationIndicator": 1,
            "MaximumAmount": "10.00",
            "ReferenceNumber": "ORD-0000123-001",
            "Occurrence": "06",
            "NumberOfPayments": "12",
            "AmountType": "1"
        }
    },
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": null,
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "2b86c48d-b0a4-43a1-a1ca-86539ecdd394",
        "Xid": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
        "Eci": "05",
        "Cavv": "AJkBBkhgQQAAAE4gSEJydQAAAAA="
    },
    "Status": 1,
    "Reason": {
        "Code": "100",
        "Message": "Success"
    }
}

Scenario 5 – With challenge

See a request example that simulates challenge authentication. To get Status = 2 with the challenge object, use the test card number 4000000000002503.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://samplemerchant.org",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 1000,
    "Installments": 1,
    "ShipToSameAsBillTo": true,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002503",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "999888777-94",
        "Name": "John Doe",
        "PhoneNumber": "555-0001",
        "Email": "[email protected]",
        "Street1": "Av. Batatinha Quando Nasce",
        "Street2": "Batata",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222222-22",
        "Country": "BR"
    },
    "CartItems": [
        {
            "Name": "Product A",
            "Description": "Product A description",
            "Sku": "SKU-001",
            "Quantity": 1,
            "UnitPrice": 1000
        }
    ],
    "DeviceInfo": {
        "IpAddress": "127.0.0.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "<fingerprint_id>",
                "Provider": "cardinal"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY"
    },
    "AuthNotifyOnly": false,
    "ChallengeWindowSize": "390x400",
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": {
        "AcsUrl": "https://1merchantacsstag.cardinalcommerce.com/MerchantACSWeb/creq.jsp",
        "Pareq": "eyJtZXNzYWdlVHlwZSI6IkNSZXEiLCJtZXNzYWdlVmVyc2lvbiI6IjIuMi4wIiwidGhyZWVEU1NlcnZlclRyYW5zSUQiOiJlOTljM2IwNS1mZGZiLTRkNGMtOTdjNS1lMTY5Mjg1OWM1Y2YiLCJhY3NUcmFuc0lEIjoiMjdiYTk3ZmMtMDk4MS00YTZkLWFhYTEtZTIwMjNjNWQzNjk5IiwiY2hhbGxlbmdlV2luZG93U2l6ZSI6IjAyIn0",
        "TransactionId": "2MfUuV2lgIgspJ1gMBV1"
    },
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "1dacc363-f9bb-4dc6-bb49-bc613c7e1d17",
        "Xid": null,
        "Eci": null,
        "Cavv": null
    },
    "Status": 2,
    "Reason": {
        "Code": "475",
        "Message": "Enrolled"
    }
}

Scenario 6 – Authentication failure

See a request example that simulates an authentication failure. To get Status = 0, use the test card number 4000000000002925.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://samplemerchant.org",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 1000,
    "Installments": 1,
    "ShipToSameAsBillTo": true,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002925",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "999888777-94",
        "Name": "John Doe",
        "PhoneNumber": "555-0001",
        "Email": "[email protected]",
        "Street1": "Av. Batatinha Quando Nasce",
        "Street2": "Batata",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222222-22",
        "Country": "BR"
    },
    "CartItems": [
        {
            "Name": "Product A",
            "Description": "Product A description",
            "Sku": "SKU-001",
            "Quantity": 1,
            "UnitPrice": 1000
        }
    ],
    "DeviceInfo": {
        "IpAddress": "127.0.0.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "<fingerprint_id>",
                "Provider": "cardinal"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY"
    },
    "AuthNotifyOnly": false,
    "ChallengeWindowSize": "390x400",
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": null,
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "feb7d684-b8ad-4107-a402-b2816dd51b22",
        "Xid": null,
        "Eci": "07",
        "Cavv": null
    },
    "Status": 0,
    "Reason": {
        "Code": "476",
        "Message": "Customer cannot be authenticated"
    }
}

Scenario 7 – Data Only

See a request example that simulates a Data Only authentication without challenge sending only the required fields. To get Status = 1, use the test card number 4000000000002701.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://samplemerchant.org",
    "OrderNumber": "ORD-0000123",
    "Currency": "BRL",
    "TotalAmount": 1000,
    "Installments": 1,
    "ShipToSameAsBillTo": true,
    "Card": {
        "PaymentMethod": "credit",
        "CardNumber": "4000000000002701",
        "ExpirationMonth": 3,
        "ExpirationYear": 2029
    },
    "BillTo": {
        "CustomerId": "999888777-94",
        "Name": "John Doe",
        "PhoneNumber": "555-0001",
        "Email": "[email protected]",
        "Street1": "Av. Batatinha Quando Nasce",
        "Street2": "Batata",
        "City": "Rio de Janeiro",
        "State": "RJ",
        "ZipCode": "222222-22",
        "Country": "BR"
    },
    "DeviceInfo": {
        "IpAddress": "127.0.0.1",
        "Channel": "Browser",
        "Devices": [
            {
                "FingerPrint": "<fingerprint_id>",
                "Provider": "cardinal"
            }
        ]
    },
    "Order": {
        "ProductCode": "PHY"
    },
    "AuthNotifyOnly": true,
    "ChallengeWindowSize": "390x400",
    "BrowserInfo": {
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "ScreenWidth": "1280",
        "ScreenHeight": "720",
        "ColorDepth": "32",
        "TimeZoneOffset": "180",
        "Language": "pt-BR",
        "JavaEnabled": "N",
        "JavaScriptEnabled": "Y"
    }
}
{
    "Challenge": null,
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "7d6cb131-d09c-4b00-bb69-122225082529",
        "Xid": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
        "Eci": "05",
        "Cavv": "AJkBBkhgQQAAAE4gSEJydQAAAAA="
    },
    "Status": 1,
    "Reason": {
        "Code": "100",
        "Message": "Success"
    }
}

ENROLL request parameters

The table below lists all available parameters for the ENROLL step request. Required parameters must always be sent. We recommend sending all parameters to increase the chance of a frictionless (no-challenge) authentication.

FieldTypeRequiredDescription
orderNumberstringYesOrder number
currencystringYesCurrency. Value: BRL
card.cardNumberstringYesCard number
card.expirationMonthnumericNoExpiration month
card.expirationYearnumericNoExpiration year
card.paymentMethodstringConditionalCard type. Required for multi-function cards (credit and debit on the same card).
card.cardAliasstringNoCard alias
card.defaultCardbooleanNotrue if it is the shopper's default card at the merchant
card.cardAddedDatedateNoDate the card was registered at the merchant (format YYYY-MM-DD)
billTo.namestringYesCardholder name
billTo.phoneNumberstringYesMobile number
billTo.emailstringYesEmail address
billTo.street1stringYesFirst line of the address
billTo.street2stringNoSecond line of the address
billTo.citystringYesCity
billTo.statestringYesState (abbreviation)
billTo.zipCodestringYesZIP code
billTo.countrystringYesCountry (ISO code, 2 characters)
billTo.customerIdstringNoShopper's tax ID or identification document
browserInfo.userAgentstringYesBrowser user agent
browserInfo.screenWidthstringYesScreen width in pixels
browserInfo.screenHeightstringYesScreen height in pixels
browserInfo.colorDepthstringYesColor depth (bits)
browserInfo.timeZoneOffsetstringYesTime zone offset in minutes
browserInfo.languagestringYesBrowser language (e.g., pt-BR)
browserInfo.javaEnabledstringYesJava enabled: "Y" or "N"
browserInfo.JavaScriptEnabledstringYesJavaScript enabled: "Y" or "N"
transactionModestringNoTransaction channel: M (MOTO), R (retail), S (e-commerce), P (mobile), T (tablet)
merchantUrlstringNoMerchant website URL
merchantNewCustomerstringNoIndicates if it is a new shopper: "true" or "false"
totalAmountnumericNoTotal order amount in cents
installmentsnumericNoNumber of installments
shipToSameAsBillTobooleanNotrue if the shipping address is the same as the billing address; false to provide a different shipping address in ShipTo
shipTo.namestringNoRecipient name
shipTo.phoneNumberstringNoMobile number
shipTo.emailstringNoEmail address
shipTo.street1stringNoFirst line of the address
shipTo.street2stringNoSecond line of the address
shipTo.citystringNoCity
shipTo.statestringNoState (abbreviation)
shipTo.zipCodestringNoShipping address ZIP code
shipTo.countrystringNoCountry (ISO code, 2 characters)
shipTo.shippingMethodstringNoShipping method: lowcost (economy), sameday (same day), oneday (next day), twoday (two days), threeday (three days), pickup (in-store pickup), other (other), none (no shipping)
shipTo.firstUsageDatedateNoDate the shipping address was first used (format YYYY-MM-DD)
cartItems.namestringNoItem name
cartItems.descriptionstringNoItem description
cartItems.skustringNoItem SKU
cartItems.quantitynumericNoQuantity
cartItems.unitPricenumericNoItem unit price in cents
deviceInfo.ipAddressstringNoDevice IP address
deviceInfo.channelstringNoTransaction channel. Accepted values: Browser, SDK, 3RI
deviceInfo.devices.fingerPrintstringNoIdentifier returned by the Device Fingerprint provider
deviceInfo.devices.providerstringNoProvider name. Accepted values: cardinal, inauth, threatmetrix
order.productCodestringNoProduct code at the merchant
order.countLast24HoursnumericNoShopper orders in the last 24 hours
order.countLast6MonthsnumericNoShopper orders in the last 6 months
order.countLast1YearnumericNoShopper orders in the last year
order.cardAttemptsLast24HoursnumericNoTransactions with the same card in the last 24 hours
order.marketingOptinstringNo"true" if the shopper opted in to receive marketing offers
order.marketingSourcestringNoMarketing campaign source
userAccount.guestbooleanNotrue if the shopper has no account at the merchant
userAccount.createdDatestringNoAccount creation date (format YYYY-MM-DD)
userAccount.changedDatestringNoDate of last account change (format YYYY-MM-DD)
userAccount.passwordChangedDatestringNoDate of last password change (format YYYY-MM-DD)
userAccount.authenticationMethodstringNoAuthentication method at the merchant: 01 (no authentication), 02 (merchant login), 03 (federated ID), 04 (FIDO authenticator)
userAccount.authenticationProtocolstringNoLogin protocol used at the merchant
userAccount.authenticationTimestampdatetimeNoDate and time of login at the merchant
airline.travelLegs.carrierstringNoIATA airline code
airline.travelLegs.departureDatestringNoDeparture date (format YYYY-MM-DD)
airline.travelLegs.originstringNoIATA code of the origin airport
airline.travelLegs.destinationstringNoIATA code of the destination airport
airline.passengers.namestringNoPassenger name
airline.passengers.ticketPricenumericNoTicket price in cents
airline.numberOfPassengersnumericNoTotal number of passengers
airline.passportCountrystringNoPassport issuing country (ISO code)
airline.passportNumberstringNoPassport number
recurring.endDatedateNoRecurrence end date (format YYYY-MM-DD)
recurring.frequencynumericNoFrequency: 1 (monthly), 2 (bimonthly), 3 (quarterly), 4 (every 4 months), 6 (semiannual), 12 (annual)
recurring.originalPurchaseDatedateNoDate of the first transaction that originated the recurrence
recurring.recurringInfos.typestringNoRecurring payment type: 1 (first transaction), 2 (subsequent), 3 (modification), 4 (void)
recurring.recurringInfos.validationIndicatorstringNoIndicates if validated: 0 (not validated), 1 (validated)
recurring.recurringInfos.maximumAmountstringNoMaximum amount agreed with the cardholder
recurring.recurringInfos.referenceNumberstringNoUnique reference number for the recurrence
recurring.recurringInfos.occurrencestringNoFrequency: 01 (daily), 02 (twice a week), 03 (weekly), 04 (every 10 days), 05 (biweekly), 06 (monthly), 07 (bimonthly), 08 (quarterly), 09 (every 4 months), 10 (semiannual), 11 (annual), 12 (unscheduled)
recurring.recurringInfos.numberOfPaymentsstringNoTotal number of payments during the subscription
recurring.recurringInfos.amountTypestringNoAmount type: 1 (fixed), 2 (maximum)
merchantDefinedData.1stringNoAdditional data defined by the merchant
merchantDefinedData.2stringNoAdditional data defined by the merchant
merchantDefinedData.3stringNoAdditional data defined by the merchant
authNotifyOnlybooleanNotrue for authentication in notify-only mode (no interaction with the cardholder)
brandEstablishmentCodestringNoCard brand establishment code
challengeWindowSizestringNoChallenge window size: "250x400", "390x400", "600x400", "500x600", or "FullScreen"

ENROLL response (200 OK)

See the fields returned in the ENROLL operation response:

FieldTypeDescription
statusnumericAuthentication result. See the status table.
Reason.CodestringReason code. See Reason codes.
Reason.MessagestringReason message.
Challenge.AcsUrlstringAccess Control Server (ACS) URL for challenge display
Challenge.PareqstringPayer Authentication Request (PAReq)
Challenge.TransactionIdstringTransaction identifier
Authentication.XidstringAuthentication XID
Authentication.EcistringECI indicator
Authentication.CavvstringAuthentication CAVV

Authentication status

See the possible authentication statuses:

StatusMeaningRecommended action
0Not authenticatedEvaluate the returned ECI to decide whether to proceed with the transaction. See the ECI table.
1AuthenticatedProceed to transaction authorization.
2Challenge requiredDisplay the challenge to the cardholder. See Step 6.

Common errors (400 Bad Request)

Field with errorScenario
ExpirationMonthValue outside the allowed range (1–12)
Authorization: Bearer <access_token>Invalid Access Token

Step 6 – Display the challenge

This step applies only when ENROLL returns status = 2.

6.1 Build the order object with MPIHelpers

Use MPIHelpers.getOrderBuilder() to assemble the object required for MPI.challenge. The methods are chainable:

const order = MPIHelpers.getOrderBuilder()

    // Order data: number, currency (ISO code), and channel
    .withOrderDetails("ORD-0000123", "986", "S")

    // Transaction ID returned by ENROLL (field challenge.transactionId)
    .withTransaction("<transactionId returned by ENROLL>")

    // Card data: number, expiration month, and year
    .withCard("4000000000002503", 3, 2029)

    // Shopper email
    .withEmail1("[email protected]")
    .withEmail2("[email protected]")   // optional

    // Billing address
    .withBillingAddress(
        "João da Silva",
        "Av. Paulista, 1000",
        "Apto 42",
        "São Paulo",
        "SP",
        "01310-100",
        "BR",
        "11999990001"
    )

    // Shipping address (if different from billing address)
    .withShippingAddress(
        "Maria da Silva",
        "Rua das Flores, 500",
        "Casa 3",
        "Campinas",
        "SP",
        "13010-050",
        "BR",
        "11999990002"
    )

    // Or use the billing address as the shipping address:
    // .withShippingAddressAsBillingAddress()

    // Cart items: name, description, SKU, quantity, unit price (in cents)
    .withCartItem("Product A", "Product A description", "SKU-001", 1, 1000)

    .build();

6.2 Call the challenge method

// Data returned by the ENROLL operation when status = 2
const challengeData = {
    acsUrl:        enrollResponse.challenge.acsUrl,
    payload:       enrollResponse.challenge.pareq,
    transactionId: enrollResponse.challenge.transactionId
};

MPI.challenge(challengeData, order);

After the cardholder completes the challenge, the onValidationRequired callback is triggered with { TransactionId }. Send this value to the back-end to run the VALIDATE operation.


Step 7 – VALIDATE: validate the authentication

Run this operation in the back-end after the challenge. The result is final — there is no new challenge at this step.

Request

Method: POST
Sandbox URL: https://mpisandbox.braspag.com.br/v3/3ds/validate
Production URL: https://mpi.braspag.com.br/v3/3ds/validate

Headers:

Authorization: Bearer <access_token>
Content-Type: application/json

Body:

{
    "ordernumber": "ORD-0000123",
    "currency": "BRL",
    "totalamount": "1000",
    "card": {
        "cardnumber": "4000000000002503",
        "expirationmonth": 3,
        "expirationyear": 2029
    },
    "transactionId": "BR8llUtqlmzu40pzN9m1"
}
{
    "Authentication": {
        "Version": "2.2.0",
        "DirectoryServerTransactionId": "9a7c2937-148a-446a-9da8-3fffbe84f390",
        "Xid": null,
        "Eci": "07",
        "Cavv": null
    },
    "Status": 0,
    "Reason": {
        "Code": "100",
        "Message": "Success"
    }
}

Warning: the VALIDATE fields are sent in lowercase (ordernumber, totalamount, cardnumber, expirationmonth, expirationyear), unlike the PascalCase standard used in ENROLL. Follow the exact casing as shown in the examples above.

Request fields:

FieldTypeRequiredSizeDescription
transactionIdstringYes1–26Transaction identifier returned by ENROLL
ordernumberstringYes1–50Order number
currencystringYes3ISO currency code (e.g., 986 for BRL)
totalamountnumericYesTotal order amount in cents
cardCardYesCard data. See Card.
billToCustomerIdstringNoShopper identification document

VALIDATE response (200 OK)

Possible fields returned in the VALIDATE operation response:

FieldTypeDescription
statusnumericAuthentication result. See the status table.
Reason.CodestringReason code. See Reason codes.
Reason.MessagestringReason message.
Authentication.Versionstring3DS protocol version
Authentication.DirectoryServerTransactionIdstringTransaction identifier at the Directory Server
Authentication.XidstringAuthentication XID
Authentication.EcistringECI indicator
Authentication.CavvstringAuthentication CAVV

Statuses returned in VALIDATE

Statuses returned in the VALIDATE operation:

The statuses returned are the same as in the ENROLL operation. Only status 2 is not returned in the VALIDATE operation, as it indicates a challenge.

StatusMeaningRecommended action
0Not authenticatedEvaluate the returned ECI to decide whether to proceed with the transaction. See the ECI table.
1AuthenticatedProceed to transaction authorization.

Common errors (400 Bad Request)

Possible errors in the VALIDATE operation response:

Field with errorScenario
currencyInvalid format value or unsupported currency
transactionIdField absent or empty
cardObject absent or cardnumber empty

Step 8 – Authorization

After authentication, proceed to authorization by sending the data returned from authentication in the Payment.ExternalAuthentication field.


Return codes and errors

HTTP statuses

See the HTTP statuses, their meaning, and the recommended action for each:

CodeMeaningRecommended action
200Operation successful
400Validation failureCheck the fields in the ErrorResult object returned in the response.
401UnauthorizedRegenerate the access_token and restart the process.
409Repeated or out-of-order operationFollow the sequence: AUTH → INIT → ENROLL → VALIDATE.
500Internal server errorTry again. If the issue persists, contact support.

AUTH operation error codes

See the possible error codes for the AUTH operation:

CodeDescription
605Establishment code not provided
606Merchant name not provided
607MCC not provided or invalid

Reason codes

See the possible reason codes:

CodeDescription
100Success
101Required field missing
102Invalid field value
234Configuration error
475Cardholder enrolled
476Cardholder cannot be authenticated

For codes not listed, the API returns the message Unexpected error occurred. Contact Cielo support for analysis.

Authentication status

See the possible authentication statuses:

ValueDescription
0Not authenticated
1Authenticated
2Challenge

Test cards (sandbox)

Test cards without challenge

To simulate no-challenge authentications, submit requests with one of the following test cards:

CardCard brandResultScenarioStatus
4000000000002701
5200000000002235
6505290000002000
VISA
MASTER
ELO
SuccessNo-challenge authentication with cardholder successfully authenticated.1
4000000000002925
5200000000002276
6505290000002018
VISA
MASTER
ELO
FailureNo-challenge authentication with cardholder authentication failure.0

Test cards with challenge

To simulate challenge authentications, submit requests with one of the following test cards:

CardCard brandResultScenarioStatus
4000000000002503 5200000000002151
6505290000002190
Visa Master EloSuccessChallenge authentication with cardholder successfully authenticated.2
4000000000002370
5200000000002490
6505290000002208
Visa Master
Elo
FailureChallenge authentication with cardholder authentication failure.0
4000000000002420
5200000000002664
6505290000002257
Visa Master
Elo
UnenrolledChallenge authentication currently unavailable.0
4000000000002644
5200000000002656
6505290000002265
Visa
Master
Elo
ErrorSystem error during the authentication step.0

Test cards for DataOnly

To simulate DataOnly authentications, submit requests with one of the following test cards:

CardCard brandResultScenarioStatus
5200000000002805
4000000000002024
Mastercard
Visa
SuccessDataOnly authentication0






Did this page help you?