Nova integração do MPI Cielo para autenticação 3DS

Autenticação 3DS - novo MPI Cielo (V3)

A integração MPI V3 implementa o protocolo de autenticação EMV 3D Secure com arquitetura exclusivamente server-to-server. Nesse modelo, o estabelecimento controla todas as chamadas à API, incluindo a exibição do desafio ao portador quando necessário.

⚠️

Versão do MPI vs versão do 3DS

Esta documentação descreve o novo MPI Cielo (V3), que é o merchant plugin responsável pela integração da autenticação 3DS no modelo server-to-server.

É importante destacar que:

  • A mudança de MPI V2 → MPI V3 refere-se apenas à evolução do plugin de integração (MPI).
  • Não há alteração na versão do protocolo 3DS.

Ou seja:

ComponenteVersão
MPI (integração)✅ V3 (novo modelo server-to-server)
3DS (protocolo de autenticação)✅ 2.2 (Visa, Mastercard) / 2.1 (Elo, Amex)

✅ Em resumo: você está adotando um novo modelo de integração (MPI V3), mas a autenticação 3DS continua seguindo as mesmas versões já suportadas anteriormente.


Nesta página

  1. Benefícios
  2. Pré-requisitos
  3. Ambientes, endpoints e token
  4. Fluxo de integração
  5. Etapa 1 – Preparar o front-end
  6. Etapa 2 – AUTH: obter o access_token
  7. Etapa 3 – INIT: inicializar a sessão
  8. Etapa 4 – Carregar e inicializar o script
  9. Etapa 5 – ENROLL: autenticar o cartão
  10. Etapa 6 – Exibir o desafio
  11. Etapa 7 – VALIDATE: validar a autenticação
  12. Códigos de retorno e erros

Benefícios

A integração MPI V3 oferece:

  • Maior segurança: todas as chamadas sensíveis ficam no back-end;
  • Controle total do fluxo de autenticação pelo estabelecimento;
  • Flexibilidade para tratar cada cenário de autenticação de forma independente.

Pré-requisitos

Antes de iniciar a integração, você precisa ter o produto 3DS habilitado.

Ambientes, endpoints e tokens

APIs

OperaçãoURL de sandboxURL de produção
access_token (AUTH)https://mpisandbox.braspag.com.br/v3/auth/tokenhttps://mpi.braspag.com.br/v3/auth/token
Inicialização (INIT)https://mpisandbox.braspag.com.br/v3/3ds/inithttps://mpi.braspag.com.br/v3/3ds/init
Matrícula e autenticação (ENROLL)https://mpisandbox.braspag.com.br/v3/3ds/enrollhttps://mpi.braspag.com.br/v3/3ds/enroll
Validação de autenticação (VALIDATE)https://mpisandbox.braspag.com.br/v3/3ds/validatehttps://mpi.braspag.com.br/v3/3ds/validate

Scripts JavaScript

Ambientempi.jsmpiHelpers.js
Sandboxhttps://mpisandbox.braspag.com.br/Scripts/V3/mpi.jshttps://mpisandbox.braspag.com.br/Scripts/V3/mpiHelpers.js
Produçãohttps://mpi.braspag.com.br/Scripts/V3/mpi.jshttps://mpi.braspag.com.br/Scripts/V3/mpiHelpers.js

Sandbox: use para validar a integração antes de ir para produção. Para simular cenários de autenticação, utilize os cartões de teste 3DS.

Produção: use apenas para transações reais. Transações em produção geram custos e podem resultar em penalizações pelas bandeiras e pelos emissores.

Diferença entre tokens

O MPI utiliza dois tokens distintos:

  • access_token:
    • obtido na operação AUTH;
    • é do tipo bearer;
    • usado apenas no back-end;
    • não deve ser exposto no front-end.
  • token (INIT):
    • retornado pela operação INIT;
    • é um JWT;
    • deve ser enviado ao front-end para inicializar o MPI.

Fluxo de integração

A integração segue uma sequência obrigatória de etapas distribuídas entre back-end e front-end:

[BACK-END]  1. AUTH              → Obter `access_token`
[BACK-END]  2. INIT              → Obter `referenceId` e `token`
[FRONT-END] 3. MPI.load          → Carregar recursos e dependências
[FRONT-END] 4. MPI.init          → Inicializar script com `token` e `referenceId`
[FRONT-END] 5. MPI.updateCard    → Atualizar número do cartão
[FRONT-END] 6. Enviar pedido     → Coletar dados do pedido e BrowserInfo
[BACK-END]  7. ENROLL            → Autenticar cartão
            │
            ├── Status 0 (Não autenticado) → Decisão da loja
            ├── Status 1 (Autenticado)     → Prosseguir para autorização
            └── Status 2 (Desafio)         →
                  [FRONT-END] MPI.challenge → Exibir desafio ao portador
                  [FRONT-END] Enviar dados pós-desafio ao back-end
                  [BACK-END]  8. VALIDATE   → Confirmar autenticação
                               ├── Status 0 → Decisão da loja
                               └── Status 1 → Prosseguir para autorização

Importante: o access_token tem validade de 20 minutos em produção. A API bloqueia chamadas fora de ordem e chamadas repetidas. Respeite sempre a sequência: AUTH → INIT → ENROLL → VALIDATE. Não exponha o access_token no front-end.


Etapa 1 – Preparar o front-end

1.1 Incluir os scripts

Adicione as bibliotecas na página de checkout. Use as URLs correspondentes ao ambiente:

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>

Produção:

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

Código-fonte do mpi.js (sandbox)

O código abaixo é o conteúdo completo do mpi.js disponibilizado no ambiente de sandbox (https://mpisandbox.braspag.com.br/Scripts/V3/mpi.js). Ele é responsável pela, inicialização da sessão, atualização do cartão e exibição do desafio.

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) {
        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(), 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");
        });

        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://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js",
            "SDB": "https://songbirdstag.cardinalcommerce.com/edge/v1/songbird.js",
            "PRD": "https://songbird.cardinalcommerce.com/edge/v1/songbird.js"
        };
        return enviroments[environment];
    }

    function loadScript(url, callback) {
        const head = document.getElementsByTagName('head')[0];
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url;
        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);
        }
    };
})();

Pontos de atenção no mpi.js:

  • MPI.load(config) carrega o Cardinal Commerce de forma assíncrona e aciona onLoadComplete ao concluir.
  • MPI.init(referenceId, token) — a ordem dos parâmetros é (referenceId, token). Internamente, o método inverte a chamada para _initialize(token, referenceId).
  • MPI.updateCard() lê o número do cartão via cardNumberReader e dispara Cardinal.trigger("accountNumber.update", cardNumber). Caso o cartão seja atualizado durante a sessão ativa, é necessário chamar MPI.updateCard() novamente antes de seguir para o ENROLL.
  • MPI.challenge(challengeData, order) chama Cardinal.continue("cca", challengeData, order) para exibir o desafio.
  • O callback onValidationRequired é acionado nos ActionCode: SUCCESS, NOACTION, FAILURE e ERROR (quando ProcessorTransactionId está presente).
  • O callback onError é acionado quando o ActionCode é ERROR sem ProcessorTransactionId, ou em respostas inesperadas.

Código-fonte do mpiHelpers.js (sandbox)

O código abaixo é o conteúdo completo do mpiHelpers.js disponibilizado no ambiente de sandbox (https://mpisandbox.braspag.com.br/Scripts/V3/mpiHelpers.js). Ele fornece os utilitários getBrowserInfo, getOrderBuilder e as constantes de moeda e canal usados nas etapas anteriores.

O mpiHelpers.js será usado na Etapa 4 – Carregar e inicializar o 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;
            }
        }
    };
})();

Pontos de atenção no mpiHelpers.js:

  • getBrowserInfo() coleta userAgent, screenWidth, screenHeight, colorDepth, timeZoneOffset, language e javaEnabled do navegador. O campo javascriptEnabled é sempre 'Y'.
  • getOrderBuilder() retorna o builder de pedido. Todos os métodos with* são encadeáveis e terminam com .build().
  • withShippingAddressAsBillingAddress() copia o endereço de cobrança para o de entrega sem necessidade de repetir os dados.
  • buildCartItem() cria um item avulso para uso com withCartItems(...items).

1.2 Configurar o script mpi.js

Neste passo você irá configurar o script mpi.js.

Chame MPI.load passando um objeto de configuração com os parâmetros e callbacks necessários:

MPI.load({
    Environment: "SDB",   // "SDB" para sandbox | "PRD" para produção
    Debug: true,          // true ativa os logs no console do navegador

    // Acionado quando as dependências externas (Cardinal) são carregadas.
    // Use para habilitar o botão de compra ou indicar que o checkout está pronto.
    onLoadComplete: function () {
        document.getElementById("btnPagar").disabled = false;
    },

    // Acionado quando Cardinal.setup é concluído e o script está pronto para uso.
    // Só chame MPI.updateCard() depois deste evento.
    onReady: function () {
        console.log("MPI pronto.");
    },

    // Acionado em caso de erro durante o processo de autenticação.
    // O objeto error contém: { Xid, Eci, ReturnCode, ReturnMessage, ReferenceId }
    onError: function (error) {
        console.error("Código:", error.ReturnCode);
        console.error("Mensagem:", error.ReturnMessage);
    },

    // Acionado após o portador concluir o desafio.
    // O objeto validationEvent contém: { TransactionId }
    // Use o TransactionId para chamar a operação VALIDATE no back-end.
    onValidationRequired: function (validationEvent) {
        const transactionId = validationEvent.TransactionId;
        enviarParaBackend({ transactionId: transactionId });
    },

    // Chamado imediatamente antes de executar MPI.updateCard().
    // Deve retornar o número do cartão como string (somente dígitos).
    cardNumberReader: function () {
        return document.getElementById("cardNumber").value.replace(/\D/g, "");
    }
});

Parâmetros de configuração:

ParâmetroTipoValores aceitosDescrição
Environmentstring"SDB" ou "PRD"Ambiente de execução
Debugbooleanotrue ou falseAtiva os logs no console (debug)

Callbacks de evento:

CallbackArgumento recebidoDescrição
onLoadCompleteAcionado quando as dependências estão carregadas.
onReadyAcionado quando a inicialização do script é concluída.
onError{ Xid, Eci, ReturnCode, ReturnMessage, ReferenceId }Acionado quando ocorre um erro durante o processo.
onValidationRequired{ TransactionId }Acionado após o desafio. Use TransactionId para disparar o VALIDATE.
cardNumberReaderChamado pelo script para ler o número do cartão. Retorna uma string.

Etapa 2 – AUTH: obter o access_token

Execute esta operação exclusivamente no back-end. Nunca exponha o access_token no front-end.

Requisição

Método: POST
URL de sandbox: https://mpisandbox.braspag.com.br/v3/auth/token
URL de produção: https://mpi.braspag.com.br/v3/auth/token

Headers:

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

Body:

{
    "EstablishmentCode": "1111111111",
    "MerchantName": "NOME DA LOJA",
    "MCC": "0000"
}

Campos da requisição:

CampoTipoObrigatórioTamanhoDescrição
EstablishmentCodenuméricoSim10Código do estabelecimento na adquirente
MerchantNamestringSim1–25Nome do estabelecimento
MCCnuméricoSim4Categoria do estabelecimento (MCC)

Resposta

{
    "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"
    }
]

Campos da resposta:

Campo

Tipo

Descrição

access_token

string

Token de acesso. Use em todas as chamadas seguintes. Não exponha o access_token no front-end.

token_type

string

Tipo do token. Valor fixo: bearer.

expires_in

string

Tempo de validade do access_toke em segundos.
Valor: 1200 (20 min).

Erros comuns (400 Bad Request)

Campo com erroCenárioCódigo de erro
EstablishmentCodeCampo enviado vazio605
MerchantNameCampo enviado vazio606
MCCCampo enviado vazio607
MCCValor não numérico (ex.: "XXXX")607

Etapa 3 – INIT: inicializar a sessão

Execute esta operação no back-end após obter o access_token. O token retornado deve ser enviado ao front-end para inicializar o script MPI.

Atenção: o token retornado pelo INIT é diferente do access_token. Apenas o token pode ser exposto no front-end. Não exponha o access_token no front-end.

Requisição

Método: POST
URL de sandbox: https://mpisandbox.braspag.com.br/v3/3ds/init
URL de produção: 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"
}

Campos da requisição:

Campo

Tipo

Obrigatório

Tamanho

Intervalo

Descrição

orderNumber

string

Sim

1–50

Número do pedido

currency

string

Sim

3

986 (BRL)

Código ISO da moeda

amount

numérico

Não

0–99.999.999.999

Valor total do pedido em centavos.
Valor padrão: 0.

Resposta

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

Campos da resposta:

CampoTipoDescrição
referenceIdstringIdentificador de referência. Use no corpo da requisição do ENROLL.
tokenstringToken para inicialização do script no front-end via MPI.init.

Erros comuns (400 Bad Request)

Campo com erroCenário
orderNumberCampo enviado vazio

Etapa 4 – Carregar e inicializar o script

Após o back-end executar o INIT e retornar referenceId e token, envie esses valores ao front-end.

Atenção: o token retornado pelo INIT é diferente do access_token. Apenas o token pode ser exposto no front-end. Não exponha o access_token no front-end.

4.1 Inicializar o script

O trecho abaixo é um exemplo de como fazer a inicialização do script, adapte com os dados necessários:

// Chame o endpoint do seu back-end que executa o INIT e retorna referenceId e token
fetch("/api/ENDPOINTDALOJA", { method: "POST" })
    .then(response => response.json())
    .then(data => {
        // data.referenceId e data.token são retornados pela operação INIT
        MPI.init(data.referenceId, data.token);
        // Aguarde o callback onReady antes de prosseguir.
    });

A assinatura do método é MPI.init(referenceId, token). A ordem dos parâmetros é obrigatória.

4.2 Atualizar o cartão e enviar o pedido

Chame MPI.updateCard() antes de enviar o pedido ao back-end. O método invoca o cardNumberReader definido na configuração e não retorna valor.

ℹ️

Sempre chame MPI.updateCard() novamente quando o número do cartão puder ter mudado entre tentativas.

O método envia o cardNumber atual para o Cardinal e o associa à sessão ativa — sem essa atualização, a autenticação pode usar um número desatualizado. Isso se aplica principalmente quando o comprador troca de cartão durante a mesma sessão.

Nesses casos, chame MPI.updateCard() antes de um novo ENROLL para manter a sessão sincronizada.

document.getElementById("btnPagar").addEventListener("click", function () {
    // 1. Atualiza os dados do cartão no script
    MPI.updateCard();

    // 2. Coleta os dados do navegador
    const browserInfo = MPIHelpers.getBrowserInfo();

    // 3. Envia ao endpoint do seu back-end para executar o ENROLL
    fetch("/api/ENDPOINTDALOJA", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            orderNumber: "ORD-0000123",
            // ... demais campos do pedido
            browserInfo: browserInfo
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 2) {
            // Desafio necessário
            const order = construirObjetoPedido();
            MPI.challenge(
                {
                    acsUrl:        data.challenge.acsUrl,
                    payload:       data.challenge.pareq,
                    transactionId: data.challenge.transactionId
                },
                order
            );
        }
        // Status 0 ou 1: tratar no back-end
    });
});

Use MPIHelpers.getBrowserInfo() para coletar automaticamente os dados obrigatórios do objeto BrowserInfo:

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

Você também pode consultar moedas e canais aceitos via MPIHelpers.Constants:

// Retorna o mapa de moedas: { BRL: "986", USD: "840", EUR: "978", ... }
const moedas = MPIHelpers.Constants.getCurrencyISO();

// Retorna os canais aceitos: { ECOMMERCE: "S", MOTO: "M", RETAIL: "R", MOBILE_DEVICE: "P", TABLET: "T" }
const canais = MPIHelpers.Constants.getOrderChannels();

Etapa 5 – ENROLL: autenticar o cartão

Execute esta operação no back-end. Ela realiza a matrícula do portador e a autenticação do cartão no protocolo 3DS.

Veja os principais cenários de autenticação suportados pela API, com exemplos de comportamento esperados para cada tipo de requisição:

CenárioTipo de autenticaçãoDescriçãoResultado esperado
Cenário 1Sem desafio (campos obrigatórios)Simula uma autenticação enviando apenas os campos obrigatórios.Status = 1
Cenário 2Sem desafio (campos obrigatórios + opcionais)Simula uma autenticação com dados adicionais para enriquecer a análise de risco.Status = 1
Cenário 3Sem desafio (companhias aéreas)Simula uma autenticação sem desafio específica para companhias aéreas.Status = 1
Cenário 4Sem desafio (transações recorrentes)Simula uma autenticação sem desafio específica para transações recorrentes.Status = 1
Cenário 5Com desafioSimula uma autenticação que exige desafio (ex.: step-up).Status = 2 com objeto challenge
Cenário 6Falha na autenticaçãoSimula uma falha no processo de autenticação.Status = 0
Cenário 7Data OnlyColeta apenas dados do portador sem gerar desafio.Status = 1 sem objeto challenge

Cartões de teste sem desafio

Para simular autenticações sem desafio, envie as requisições com um dos seguintes cartões de teste:

Cartão

Bandeira

Resultado

Cenário

Status

4000000000002701
5200000000002235
6505290000002000

VISA
MASTER
ELO

Success

Autenticação sem desafio e portador autenticado com sucesso.

1

4000000000002925
5200000000002276
6505290000002018

VISA
MASTER
ELO

Failure

Autenticação sem desafio e portador autenticado com falha.

0

Cartões de teste com desafio

Para simular autenticações com desafio, envie as requisições com um dos seguintes cartões de teste:

Cartão

Bandeira

Resultado

Cenário

Status

4000000000002503 5200000000002151
6505290000002190

Visa Master Elo

Success

Autenticação com desafio e portador autenticado com sucesso.

2

4000000000002370
5200000000002490
6505290000002208

Visa Master
Elo

Failure

Autenticação com desafio e portador autenticado com falha.

0

4000000000002420
5200000000002664
6505290000002257

Visa Master
Elo

Unenrolled

Autenticação com desafio indisponível no momento.

0

4000000000002644
5200000000002656
6505290000002265

Visa
Master
Elo

Error

Erro de sistema durante a etapa de autenticação.

0

Cartões de teste para DataOnly

Para simular autenticações via DataOnly, envie as requisições com um dos seguintes cartões de teste:

Cartão

Bandeira

Resultado

Cenário

Status

5200000000002805
4000000000002024

Mastercard
Visa

Success

Autenticação via DataOnly

0

Requisição

Método: POST
URL de sandbox: https://mpisandbox.braspag.com.br/v3/3ds/enroll
URL de produção: https://mpi.braspag.com.br/v3/3ds/enroll

Headers:

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

Cenário 1 – Sem desafio (campos obrigatórios)

Veja um exemplo de requisição que simula uma autenticação sem desafio enviando apenas os campos obrigatórios.

Para ter o Status = 1, use o número de cartão de teste 4000000000002701.

{
    "TransactionMode": "S",
    "MerchantUrl": "https://www.sualojanome.com.br",
    "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": "Produto A",
            "Description": "Descrição do produto A",
            "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"
    }
}

Cenário 2 – Sem desafio (campos obrigatórios e opcionais)

Veja um exemplo de requisição que simula uma autenticação sem desafio enviando também os campos opcionais UserAccount, ShipTo e campos avançados de Order para enriquecer a análise de risco.

Para ter o Status = 1, use o número de cartão de teste 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": "Produto A",
            "Description": "Descrição do produto A",
            "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": "valor_customizado_1",
        "2": "valor_customizado_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"
    }
}

Cenário 3 – Sem desafio (companhias aéreas)

Veja um exemplo de requisição para companhias aéreas que simula uma autenticação sem desafio enviando apenas os campos obrigatórios. Para ter o Status = 1, use o número de cartão de teste 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"
    }
}

Cenário 4 - Sem desafio (transações recorrentes)

Para enviar simular uma autenticação sem desafio para transações recorrentes é necessário enviar os campos necessários no objeto Recurring e usar o cartão de teste 4000000000002701.

ℹ️

No fluxo de pagamentos recorrentes, apenas a primeira transação da série é submetida ao processo de autenticação.

As transações subsequentes da mesma recorrência não são autenticadas novamente, sendo enviadas diretamente para autorização.

Veja um exemplo de requisição que simula uma autenticação sem desafio para transações recorrentes:

{
    "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"
    }
}

Cenário 5 – Com desafio

Veja um exemplo de requisição que simula uma autenticação com desafio. Para ter o Status = 2 com o objeto challenge, use o número de cartão de teste 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": "Produto A",
            "Description": "Descrição do produto A",
            "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"
    }
}

Cenário 6 – Falha na autenticação

Veja um exemplo de requisição que simula uma falha na autenticação. Para ter o Status = 0, use o número de cartão de teste 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": "Produto A",
            "Description": "Descrição do produto A",
            "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"
    }
}

Cenário 7 – Data Only

Veja um exemplo de requisição que simula uma autenticação via Data Only sem desafio enviando apenas os campos obrigatórios. Para ter o Status = 1, use o número de cartão de teste 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"
    }
}

Parâmetros da requisição ENROLL

A tabela a seguir apresenta todos os parâmetros disponíveis para envio na requisição da etapa ENROLL. Envie obrigatoriamente os parâmetros marcados como obrigatórios. Recomendamos o envio de todos os parâmetros para aumentar a chance de ter uma autenticação sem desafio (frictionless).

CampoTipoObrigatórioDescrição
orderNumberstringSimNúmero do pedido
currencystringSimMoeda. Valor: BRL
card.cardNumberstringSimNúmero do cartão
card.expirationMonthnuméricoNãoMês de validade
card.expirationYearnuméricoNãoAno de validade
card.paymentMethodstringCondicionalTipo do cartão. Obrigatório para cartões múltiplos (crédito e débito no mesmo cartão).
card.cardAliasstringNãoApelido do cartão
card.defaultCardbooleanoNãotrue se é o cartão padrão do comprador na loja
card.cardAddedDatedataNãoData em que o cartão foi cadastrado na loja (formato YYYY-MM-DD)
billTo.namestringSimNome do portador
billTo.phoneNumberstringSimNúmero de celular
billTo.emailstringSimEndereço de e-mail
billTo.street1stringSimPrimeira linha do endereço
billTo.street2stringNãoSegunda linha do endereço
billTo.citystringSimCidade
billTo.statestringSimEstado (sigla)
billTo.zipCodestringSimCEP
billTo.countrystringSimPaís (código ISO, 2 caracteres)
billTo.customerIdstringNãoCPF ou documento do comprador
browserInfo.userAgentstringSimUser agent do navegador
browserInfo.screenWidthstringSimLargura da tela em pixels
browserInfo.screenHeightstringSimAltura da tela em pixels
browserInfo.colorDepthstringSimProfundidade de cor (bits)
browserInfo.timeZoneOffsetstringSimDeslocamento de fuso horário em minutos
browserInfo.languagestringSimIdioma do navegador (ex.: pt-BR)
browserInfo.javaEnabledstringSimJava habilitado: "Y" ou "N"
browserInfo.JavaScriptEnabledstringSimJavaScript habilitado: "Y" ou "N"
transactionModestringNãoCanal da transação: M (MOTO), R (varejo), S (e-commerce), P (mobile), T (tablet)
merchantUrlstringNãoURL do site do estabelecimento
merchantNewCustomerstringNãoIndica se é novo comprador: "true" ou "false"
totalAmountnuméricoNãoValor total do pedido em centavos
installmentsnuméricoNãoNúmero de parcelas
shipToSameAsBillTobooleanoNãotrue se o endereço de entrega é igual ao de cobrança; false para inserir outro endereço de entrega em ShipTo
shipTo.namestringNãoNome do destinatário
shipTo.phoneNumberstringNãoNúmero de celular
shipTo.emailstringNãoEndereço de e-mail
shipTo.street1stringNãoPrimeira linha do endereço
shipTo.street2stringNãoSegunda linha do endereço
shipTo.citystringNãoCidade
shipTo.statestringNãoEstado (sigla)
shipTo.zipCodestringNãoCEP do endereço de entrega
shipTo.countrystringNãoPaís (código ISO, 2 caracteres)
shipTo.shippingMethodstringNãoMétodo de envio: lowcost (econômico), sameday (mesmo dia), oneday (próximo dia), twoday (dois dias), threeday (três dias), pickup (retirada na loja), other (outros), none (sem envio)
shipTo.firstUsageDatedataNãoData da primeira utilização do endereço de entrega (formato YYYY-MM-DD)
cartItems.namestringNãoNome do item
cartItems.descriptionstringNãoDescrição do item
cartItems.skustringNãoSKU do item
cartItems.quantitynuméricoNãoQuantidade
cartItems.unitPricenuméricoNãoValor unitário do item em centavos
deviceInfo.ipAddressstringNãoEndereço IP do dispositivo
deviceInfo.channelstringNãoCanal da transação. Valores aceitos: Browser, SDK, 3RI
deviceInfo.devices.fingerPrintstringNãoIdentificador retornado pelo provedor de Device Fingerprint
deviceInfo.devices.providerstringNãoNome do provedor. Valores aceitos: cardinal, inauth, threatmetrix
order.productCodestringNãoCódigo do produto no estabelecimento
order.countLast24HoursnuméricoNãoPedidos do comprador nas últimas 24 horas
order.countLast6MonthsnuméricoNãoPedidos do comprador nos últimos 6 meses
order.countLast1YearnuméricoNãoPedidos do comprador no último ano
order.cardAttemptsLast24HoursnuméricoNãoTransações com o mesmo cartão nas últimas 24 horas
order.marketingOptinstringNão"true" se o comprador aceitou receber ofertas de marketing
order.marketingSourcestringNãoOrigem da campanha de marketing
userAccount.guestbooleanoNãotrue se o comprador não possui cadastro na loja
userAccount.createdDatestringNãoData de criação da conta (formato YYYY-MM-DD)
userAccount.changedDatestringNãoData da última alteração na conta (formato YYYY-MM-DD)
userAccount.passwordChangedDatestringNãoData da última alteração de senha (formato YYYY-MM-DD)
userAccount.authenticationMethodstringNãoMétodo de autenticação na loja: 01 (sem autenticação), 02 (login da loja), 03 (ID federado), 04 (autenticador FIDO)
userAccount.authenticationProtocolstringNãoProtocolo de login utilizado na loja
userAccount.authenticationTimestampdata e horaNãoData e hora do login na loja
airline.travelLegs.carrierstringNãoCódigo IATA da companhia aérea
airline.travelLegs.departureDatestringNãoData de partida (formato YYYY-MM-DD)
airline.travelLegs.originstringNãoCódigo IATA do aeroporto de origem
airline.travelLegs.destinationstringNãoCódigo IATA do aeroporto de destino
airline.passengers.namestringNãoNome do passageiro
airline.passengers.ticketPricenuméricoNãoValor da passagem em centavos
airline.numberOfPassengersnuméricoNãoTotal de passageiros
airline.passportCountrystringNãoPaís emissor do passaporte (código ISO)
airline.passportNumberstringNãoNúmero do passaporte
recurring.endDatedataNãoData de término da recorrência (formato YYYY-MM-DD)
recurring.frequencynuméricoNãoFrequência: 1 (mensal), 2 (bimestral), 3 (trimestral), 4 (quadrimestral), 6 (semestral), 12 (anual)
recurring.originalPurchaseDatedataNãoData da primeira transação que originou a recorrência
recurring.recurringInfos.typestringNãoTipo do pagamento recorrente: 1 (primeira transação), 2 (subsequente), 3 (modificação), 4 (cancelamento)
recurring.recurringInfos.validationIndicatorstringNãoIndica se foi validado: 0 (não validado), 1 (validado)
recurring.recurringInfos.maximumAmountstringNãoValor máximo acordado com o titular
recurring.recurringInfos.referenceNumberstringNãoNúmero de referência exclusivo da recorrência
recurring.recurringInfos.occurrencestringNãoFrequência: 01 (diário), 02 (2x por semana), 03 (semanal), 04 (a cada 10 dias), 05 (quinzenal), 06 (mensal), 07 (bimestral), 08 (trimestral), 09 (quadrimestral), 10 (semestral), 11 (anual), 12 (não programado)
recurring.recurringInfos.numberOfPaymentsstringNãoTotal de pagamentos durante a assinatura
recurring.recurringInfos.amountTypestringNãoTipo de valor: 1 (fixo), 2 (máximo)
merchantDefinedData.1stringNãoDados adicionais definidos pelo lojista
merchantDefinedData.2stringNãoDados adicionais definidos pelo lojista
merchantDefinedData.3stringNãoDados adicionais definidos pelo lojista
authNotifyOnlybooleanoNãotrue para autenticação no modo somente notificação (sem interação com o portador)
brandEstablishmentCodestringNãoCódigo de estabelecimento da bandeira
challengeWindowSizestringNãoTamanho da janela de desafio: "250x400", "390x400", "600x400", "500x600" ou "FullScreen"

Resposta ENROLL (200 OK)

Veja os campos retornados na resposta da operação ENROLL:

CampoTipoDescrição
statusnuméricoResultado da autenticação. Consulte a tabela de status.
Reason.CodestringCódigo do motivo. Veja Códigos de motivo.
Reason.MessagestringMensagem do motivo.
Challenge.AcsUrlstringURL do Access Control Server (ACS) para exibição do desafio
Challenge.PareqstringRequisição de autenticação do portador (Payer Auth Request)
Challenge.TransactionIdstringIdentificador da transação
Authentication.XidstringXID da autenticação
Authentication.EcistringIndicador ECI
Authentication.CavvstringCAVV da autenticação

Status da autenticação

Veja os possíveis status da autenticação:

StatusSignificadoAção recomendada
0Não autenticadoAvalie o ECI retornado para decidir se prossegue com a transação. Consulte a tabela de ECI.
1AutenticadoProssiga para a autorização da transação.
2Desafio necessárioExiba o desafio ao portador. Consulte a Etapa 6.

Erros comuns (400 Bad Request)

Veja

Campo com erroCenário
ExpirationMonthValor fora do intervalo permitido (1–12)

Etapa 6 – Exibir o desafio

Esta etapa se aplica apenas quando o ENROLL retornar status = 2.

6.1 Construir o objeto de pedido com MPIHelpers

Use MPIHelpers.getOrderBuilder() para montar o objeto necessário para MPI.challenge. Os métodos são encadeáveis:

const order = MPIHelpers.getOrderBuilder()

    // Dados do pedido: número, moeda (código ISO) e canal
    .withOrderDetails("ORD-0000123", "986", "S")

    // ID da transação retornado pelo ENROLL (campo challenge.transactionId)
    .withTransaction("<transactionId retornado pelo ENROLL>")

    // Dados do cartão: número, mês e ano de validade
    .withCard("4000000000002503", 3, 2029)

    // E-mail do comprador
    .withEmail1("[email protected]")
    .withEmail2("[email protected]")   // opcional

    // Endereço de cobrança
    .withBillingAddress(
        "João da Silva",
        "Av. Paulista, 1000",
        "Apto 42",
        "São Paulo",
        "SP",
        "01310-100",
        "BR",
        "11999990001"
    )

    // Endereço de entrega (se diferente do de cobrança)
    .withShippingAddress(
        "Maria da Silva",
        "Rua das Flores, 500",
        "Casa 3",
        "Campinas",
        "SP",
        "13010-050",
        "BR",
        "11999990002"
    )

    // Ou use o endereço de cobrança como entrega:
    // .withShippingAddressAsBillingAddress()

    // Itens do carrinho: nome, descrição, SKU, quantidade, preço unitário (em centavos)
    .withCartItem("Produto A", "Descrição do produto A", "SKU-001", 1, 1000)

    .build();

6.2 Chamar o método challenge

// Dados retornados pela operação ENROLL quando status = 2
const challengeData = {
    acsUrl:        enrollResponse.challenge.acsUrl,
    payload:       enrollResponse.challenge.pareq,
    transactionId: enrollResponse.challenge.transactionId
};

MPI.challenge(challengeData, order);

Após o portador concluir o desafio, o callback onValidationRequired é acionado com { TransactionId }. Envie esse valor ao back-end para executar o VALIDATE.


Etapa 7 – VALIDATE: validar a autenticação

Execute esta operação no back-end após o desafio. O resultado é definitivo — não existe novo desafio nessa etapa.

Requisição

Método: POST
URL de sandbox: https://mpisandbox.braspag.com.br/v3/3ds/validate
URL de produção: 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"
    }
}

Atenção: os campos do VALIDATE são enviados em minúsculas (ordernumber, totalamount, cardnumber, expirationmonth, expirationyear), diferente do padrão PascalCase usado no ENROLL. Respeite o casing exato conforme os exemplos acima.

Campos da requisição:

CampoTipoObrigatórioTamanhoDescrição
transactionIdstringSim1–26Identificador da transação retornado pelo ENROLL
ordernumberstringSim1–50Número do pedido
currencystringSim3Código ISO da moeda (ex.: 986 para BRL)
totalamountnuméricoSimValor total do pedido em centavos
cardCardSimDados do cartão. Consulte Card.
billToCustomerIdstringNãoDocumento de identificação do comprador

Resposta VALIDATE (200 OK)

Possíveis campos retornados na resposta da operação VALIDATE:

CampoTipoDescrição
statusnuméricoResultado da autenticação. Consulte a tabela de status.
Reason.CodestringCódigo do motivo. Veja Códigos de motivo.
Reason.MessagestringMensagem do motivo.
Authentication.VersionstringVersão do protocolo 3DS
Authentication.DirectoryServerTransactionIdstringIdentificador da transação no Directory Server
Authentication.XidstringXID da autenticação
Authentication.EcistringIndicador ECI
Authentication.CavvstringCAVV da autenticação

Status retornados em VALIDATE

Status retornados na operação VALIDATE:

Os Status retornados são os mesmos da operação ENROLL. Apenas o status 2 não é retornado na operação VALIDATE pois indica desafio.

StatusSignificadoAção recomendada
0Não autenticadoAvalie o ECI retornado para decidir se prossegue com a transação. Consulte a tabela de ECI.
1AutenticadoProssiga para a autorização da transação.

Erros comuns (400 Bad Request)

Possíveis erros no retorno da operação VALIDATE:

Campo com erroCenário
currencyValor com formato inválido ou moeda não suportada
transactionIdCampo ausente ou vazio
cardObjeto ausente ou cardnumber vazio

Códigos de retorno e erros

Status HTTP

Veja os status HTTP, o significado e a ação recomendada no retorno de cada um:

CódigoSignificadoAção recomendada
200Operação bem-sucedida
400Falha de validaçãoVerifique os campos no objeto ErrorResult retornado na resposta.
401Não autorizadoRegere o access_token e reinicie o processo.
409Operação repetida ou fora de ordemRespeite a sequência: AUTH → INIT → ENROLL → VALIDATE.
500Erro interno do servidorTente novamente. Se o problema persistir, acione o suporte.

Códigos de erro da operação AUTH

Veja os possíveis códigos de erro na operação AUTH:

CódigoDescrição
605Código do estabelecimento não informado
606Nome da loja não informado
607MCC não informado ou inválido

Códigos de motivo (Reason Codes)

Veja os possíveis códigos de motivos (Reason Codes):

CódigoDescrição
100Sucesso
101Campo obrigatório ausente
102Valor de campo inválido
234Erro de configuração
475Portador matriculado
476Portador não pode ser autenticado

Para códigos não listados, a API retorna a mensagem Unexpected error occurred. Acione o suporte Cielo para análise.

Status da autenticação

Veja os possíveis status da autenticação:

ValorDescrição
0Não autenticado
1Autenticado
2Desafio

Cartões de teste (sandbox)

Cartões de teste sem desafio

Para simular autenticações sem desafio, envie as requisições com um dos seguintes cartões de teste:

Cartão

Bandeira

Resultado

Cenário

Status

4000000000002701
5200000000002235
6505290000002000

VISA
MASTER
ELO

Success

Autenticação sem desafio e portador autenticado com sucesso.

1

4000000000002925
5200000000002276
6505290000002018

VISA
MASTER
ELO

Failure

Autenticação sem desafio e portador autenticado com falha.

0

Cartões de teste com desafio

Para simular autenticações com desafio, envie as requisições com um dos seguintes cartões de teste:

Cartão

Bandeira

Resultado

Cenário

Status

4000000000002503 5200000000002151
6505290000002190

Visa Master Elo

Success

Autenticação com desafio e portador autenticado com sucesso.

2

4000000000002370
5200000000002490
6505290000002208

Visa Master
Elo

Failure

Autenticação com desafio e portador autenticado com falha.

0

4000000000002420
5200000000002664
6505290000002257

Visa Master
Elo

Unenrolled

Autenticação com desafio indisponível no momento.

0

4000000000002644
5200000000002656
6505290000002265

Visa
Master
Elo

Error

Erro de sistema durante a etapa de autenticação.

0

Cartões de teste para DataOnly

Para simular autenticações via DataOnly, envie as requisições com um dos seguintes cartões de teste:

Cartão

Bandeira

Resultado

Cenário

Status

5200000000002805
4000000000002024

Mastercard
Visa

Success

Autenticação via DataOnly

0