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 versionThis 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:
Component Version 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
- Benefits
- Prerequisites
- Environments, endpoints, and tokens
- Integration flow
- Step 1 – Prepare the front-end
- Step 2 – AUTH: obtain the
access_token - Step 3 – INIT: initialize the session
- Step 4 – Load and initialize the script
- Step 5 – ENROLL: authenticate the card
- Step 6 – Display the challenge
- Step 7 – VALIDATE: validate the authentication
- Step 8 – Authorization
- 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:
- Have a PCI DSS certificate to transmit card data;
- Have the 3DS product enabled.
Environments, endpoints, and tokens
APIs
| Operation | Sandbox URL | Production URL |
|---|---|---|
access_token (AUTH) | https://mpisandbox.braspag.com.br/v3/auth/token | https://mpi.braspag.com.br/v3/auth/token |
| Initialization (INIT) | https://mpisandbox.braspag.com.br/v3/3ds/init | https://mpi.braspag.com.br/v3/3ds/init |
| Enrollment and authentication (ENROLL) | https://mpisandbox.braspag.com.br/v3/3ds/enroll | https://mpi.braspag.com.br/v3/3ds/enroll |
| Authentication validation (VALIDATE) | https://mpisandbox.braspag.com.br/v3/3ds/validate | https://mpi.braspag.com.br/v3/3ds/validate |
JavaScript scripts
| Environment | mpi.js | mpiHelpers.js |
|---|---|---|
| Sandbox | https://mpisandbox.braspag.com.br/Scripts/V3/mpi.js | https://mpisandbox.braspag.com.br/Scripts/V3/mpiHelpers.js |
| Production | https://mpi.braspag.com.br/Scripts/V3/mpi.js | https://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_tokenis 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 theaccess_tokenin 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 triggersonLoadCompletewhen 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 viacardNumberReaderand firesCardinal.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)callsCardinal.continue("cca", challengeData, order)to display the challenge.- The
onValidationRequiredcallback is triggered forActionCode:SUCCESS,NOACTION,FAILURE, andERROR(whenProcessorTransactionIdis present).- The
onErrorcallback is triggered whenActionCodeisERRORwithoutProcessorTransactionId, 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.jswill 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()collectsuserAgent,screenWidth,screenHeight,colorDepth,timeZoneOffset,language, andjavaEnabledfrom the browser. ThejavascriptEnabledfield is always'Y'.getOrderBuilder()returns the order builder. Allwith*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 withwithCartItems(...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:
| Parameter | Type | Accepted values | Description |
|---|---|---|---|
Environment | string | "SDB" or "PRD" | Execution environment |
Debug | boolean | true or false | Enables logs in the console (debug) |
Event callbacks:
| Callback | Received argument | Description |
|---|---|---|
onLoadComplete | — | Triggered when dependencies are loaded. |
onReady | — | Triggered 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. |
cardNumberReader | — | Called by the script to read the card number. Returns a string. |
Step 2 – AUTH: obtain the access_token
access_tokenAccess credentials
To authenticate calls to the MPI API, you need a ClientId and a ClientSecret corresponding to the target environment.
| Environment | Credentials |
|---|---|
| Sandbox | ClientId: dba3a8db-fa54-40e0-8bab-7bfb9b6f2e2eClientSecret: D/ilRsfoqHlSUChwAMnlyKdDNd7FMsM7cU/vo02REag= |
| Production | After 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:
- Concatenate the
ClientIdand theClientSecretin the formatClientId:ClientSecret; - Encode the result in Base64;
- Send the AUTH operation request using the
POSTmethod.
Concatenation and encoding example
| Field | Value |
|---|---|
ClientId | dba3a8db-fa54-40e0-8bab-7bfb9b6f2e2e |
ClientSecret | D/ilRsfoqHlSUChwAMnlyKdDNd7FMsM7cU/vo02REag= |
ClientId:ClientSecret | dba3a8db-fa54-40e0-8bab-7bfb9b6f2e2e:D/ilRsfoqHlSUChwAMnlyKdDNd7FMsM7cU/vo02REag= |
| Base64 | ZGJhM2E4ZGItZmE1NC00MGUwLThiYWItN2JmYjliNmYyZTJlOkQvaWxSc2ZvcUhsU1VDaHdBTW5seUtkRE5kN0ZNc003Y1Uvdm8wMlJFYWc9 |
The Authorization header must be sent using Basic Authentication
Authorization: Basic ZGJhM2E4ZGItZmE1NC00MGUwLThiYWItN2JmYjliNmYyZTJlOkQvaWxSc2ZvcUhsU1VDaHdBTW5seUtkRE5kN0ZNc003Y1Uvdm8wMlJFYWc9Run this operation exclusively in the back-end. Never expose the
access_tokenin 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:
| Field | Type | Required | Size | Description |
|---|---|---|---|---|
EstablishmentCode | numeric | Yes | 10 | Merchant establishment code at the acquirer |
MerchantName | string | Yes | 1–25 | Merchant name |
MCC | numeric | Yes | 4 | Merchant 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:
| Field | Type | Description |
|---|---|---|
access_token | string | Access token. Use in all subsequent calls. Do not expose the access_token in the front-end. |
token_type | string | Token type. Fixed value: bearer. |
expires_in | string | access_token validity period in seconds.Value: 1200 (20 min). |
Common errors (400 Bad Request)
| Field with error | Scenario | Error code |
|---|---|---|
EstablishmentCode | Field submitted empty | 605 |
MerchantName | Field submitted empty | 606 |
MCC | Field submitted empty | 607 |
MCC | Non-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 returnedtokenmust be sent to the front-end to initialize the MPI script.Warning: the
tokenreturned by INIT is different from theaccess_token. Only thetokencan be exposed in the front-end. Do not expose theaccess_tokenin 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:
| Field | Type | Required | Size | Range | Description |
|---|---|---|---|---|---|
orderNumber | string | Yes | 1–50 | — | Order number |
currency | string | Yes | 3 | 986 (BRL) | ISO currency code |
amount | numeric | No | — | 0–99,999,999,999 | Total 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:
| Field | Type | Description |
|---|---|---|
referenceId | string | Reference identifier. Use in the ENROLL request body. |
token | string | Token for script initialization in the front-end via MPI.init. |
Common errors (400 Bad Request)
| Field with error | Scenario |
|---|---|
orderNumber | Field 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
tokenreturned by INIT is different from theaccess_token. Only thetokencan be exposed in the front-end. Do not expose theaccess_tokenin 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:
| Scenario | Authentication type | Description | Expected result |
|---|---|---|---|
| Scenario 1 | No challenge (required fields) | Simulates authentication sending only the required fields. | Status = 1 |
| Scenario 2 | No challenge (required + optional fields) | Simulates authentication with additional data to enrich risk analysis. | Status = 1 |
| Scenario 3 | No challenge (airlines) | Simulates a no-challenge authentication specific to airlines. | Status = 1 |
| Scenario 4 | No challenge (recurring transactions) | Simulates a no-challenge authentication specific to recurring transactions. | Status = 1 |
| Scenario 5 | With challenge | Simulates authentication that requires a challenge (e.g., step-up). | Status = 2 with challenge object |
| Scenario 6 | Authentication failure | Simulates a failure in the authentication process. | Status = 0 |
| Scenario 7 | Data Only | Collects 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:
| Card | Card brand | Result | Scenario | Status |
|---|---|---|---|---|
| 4000000000002701 5200000000002235 6505290000002000 | VISA MASTER ELO | Success | No-challenge authentication with cardholder successfully authenticated. | 1 |
| 4000000000002925 5200000000002276 6505290000002018 | VISA MASTER ELO | Failure | No-challenge authentication with cardholder authentication failure. | 0 |
Test cards with challenge
To simulate challenge authentications, submit requests with one of the following test cards:
| Card | Card brand | Result | Scenario | Status |
|---|---|---|---|---|
| 4000000000002503 5200000000002151 6505290000002190 | Visa Master Elo | Success | Challenge authentication with cardholder successfully authenticated. | 2 |
| 4000000000002370 5200000000002490 6505290000002208 | Visa Master Elo | Failure | Challenge authentication with cardholder authentication failure. | 0 |
| 4000000000002420 5200000000002664 6505290000002257 | Visa Master Elo | Unenrolled | Challenge authentication currently unavailable. | 0 |
| 4000000000002644 5200000000002656 6505290000002265 | Visa Master Elo | Error | System error during the authentication step. | 0 |
Test cards for DataOnly
To simulate DataOnly authentications, submit requests with one of the following test cards:
| Card | Card brand | Result | Scenario | Status |
|---|---|---|---|---|
| 5200000000002805 4000000000002024 | Mastercard Visa | Success | DataOnly authentication | 0 |
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.
| Field | Type | Required | Description |
|---|---|---|---|
orderNumber | string | Yes | Order number |
currency | string | Yes | Currency. Value: BRL |
card.cardNumber | string | Yes | Card number |
card.expirationMonth | numeric | No | Expiration month |
card.expirationYear | numeric | No | Expiration year |
card.paymentMethod | string | Conditional | Card type. Required for multi-function cards (credit and debit on the same card). |
card.cardAlias | string | No | Card alias |
card.defaultCard | boolean | No | true if it is the shopper's default card at the merchant |
card.cardAddedDate | date | No | Date the card was registered at the merchant (format YYYY-MM-DD) |
billTo.name | string | Yes | Cardholder name |
billTo.phoneNumber | string | Yes | Mobile number |
billTo.email | string | Yes | Email address |
billTo.street1 | string | Yes | First line of the address |
billTo.street2 | string | No | Second line of the address |
billTo.city | string | Yes | City |
billTo.state | string | Yes | State (abbreviation) |
billTo.zipCode | string | Yes | ZIP code |
billTo.country | string | Yes | Country (ISO code, 2 characters) |
billTo.customerId | string | No | Shopper's tax ID or identification document |
browserInfo.userAgent | string | Yes | Browser user agent |
browserInfo.screenWidth | string | Yes | Screen width in pixels |
browserInfo.screenHeight | string | Yes | Screen height in pixels |
browserInfo.colorDepth | string | Yes | Color depth (bits) |
browserInfo.timeZoneOffset | string | Yes | Time zone offset in minutes |
browserInfo.language | string | Yes | Browser language (e.g., pt-BR) |
browserInfo.javaEnabled | string | Yes | Java enabled: "Y" or "N" |
browserInfo.JavaScriptEnabled | string | Yes | JavaScript enabled: "Y" or "N" |
transactionMode | string | No | Transaction channel: M (MOTO), R (retail), S (e-commerce), P (mobile), T (tablet) |
merchantUrl | string | No | Merchant website URL |
merchantNewCustomer | string | No | Indicates if it is a new shopper: "true" or "false" |
totalAmount | numeric | No | Total order amount in cents |
installments | numeric | No | Number of installments |
shipToSameAsBillTo | boolean | No | true if the shipping address is the same as the billing address; false to provide a different shipping address in ShipTo |
shipTo.name | string | No | Recipient name |
shipTo.phoneNumber | string | No | Mobile number |
shipTo.email | string | No | Email address |
shipTo.street1 | string | No | First line of the address |
shipTo.street2 | string | No | Second line of the address |
shipTo.city | string | No | City |
shipTo.state | string | No | State (abbreviation) |
shipTo.zipCode | string | No | Shipping address ZIP code |
shipTo.country | string | No | Country (ISO code, 2 characters) |
shipTo.shippingMethod | string | No | Shipping 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.firstUsageDate | date | No | Date the shipping address was first used (format YYYY-MM-DD) |
cartItems.name | string | No | Item name |
cartItems.description | string | No | Item description |
cartItems.sku | string | No | Item SKU |
cartItems.quantity | numeric | No | Quantity |
cartItems.unitPrice | numeric | No | Item unit price in cents |
deviceInfo.ipAddress | string | No | Device IP address |
deviceInfo.channel | string | No | Transaction channel. Accepted values: Browser, SDK, 3RI |
deviceInfo.devices.fingerPrint | string | No | Identifier returned by the Device Fingerprint provider |
deviceInfo.devices.provider | string | No | Provider name. Accepted values: cardinal, inauth, threatmetrix |
order.productCode | string | No | Product code at the merchant |
order.countLast24Hours | numeric | No | Shopper orders in the last 24 hours |
order.countLast6Months | numeric | No | Shopper orders in the last 6 months |
order.countLast1Year | numeric | No | Shopper orders in the last year |
order.cardAttemptsLast24Hours | numeric | No | Transactions with the same card in the last 24 hours |
order.marketingOptin | string | No | "true" if the shopper opted in to receive marketing offers |
order.marketingSource | string | No | Marketing campaign source |
userAccount.guest | boolean | No | true if the shopper has no account at the merchant |
userAccount.createdDate | string | No | Account creation date (format YYYY-MM-DD) |
userAccount.changedDate | string | No | Date of last account change (format YYYY-MM-DD) |
userAccount.passwordChangedDate | string | No | Date of last password change (format YYYY-MM-DD) |
userAccount.authenticationMethod | string | No | Authentication method at the merchant: 01 (no authentication), 02 (merchant login), 03 (federated ID), 04 (FIDO authenticator) |
userAccount.authenticationProtocol | string | No | Login protocol used at the merchant |
userAccount.authenticationTimestamp | datetime | No | Date and time of login at the merchant |
airline.travelLegs.carrier | string | No | IATA airline code |
airline.travelLegs.departureDate | string | No | Departure date (format YYYY-MM-DD) |
airline.travelLegs.origin | string | No | IATA code of the origin airport |
airline.travelLegs.destination | string | No | IATA code of the destination airport |
airline.passengers.name | string | No | Passenger name |
airline.passengers.ticketPrice | numeric | No | Ticket price in cents |
airline.numberOfPassengers | numeric | No | Total number of passengers |
airline.passportCountry | string | No | Passport issuing country (ISO code) |
airline.passportNumber | string | No | Passport number |
recurring.endDate | date | No | Recurrence end date (format YYYY-MM-DD) |
recurring.frequency | numeric | No | Frequency: 1 (monthly), 2 (bimonthly), 3 (quarterly), 4 (every 4 months), 6 (semiannual), 12 (annual) |
recurring.originalPurchaseDate | date | No | Date of the first transaction that originated the recurrence |
recurring.recurringInfos.type | string | No | Recurring payment type: 1 (first transaction), 2 (subsequent), 3 (modification), 4 (void) |
recurring.recurringInfos.validationIndicator | string | No | Indicates if validated: 0 (not validated), 1 (validated) |
recurring.recurringInfos.maximumAmount | string | No | Maximum amount agreed with the cardholder |
recurring.recurringInfos.referenceNumber | string | No | Unique reference number for the recurrence |
recurring.recurringInfos.occurrence | string | No | Frequency: 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.numberOfPayments | string | No | Total number of payments during the subscription |
recurring.recurringInfos.amountType | string | No | Amount type: 1 (fixed), 2 (maximum) |
merchantDefinedData.1 | string | No | Additional data defined by the merchant |
merchantDefinedData.2 | string | No | Additional data defined by the merchant |
merchantDefinedData.3 | string | No | Additional data defined by the merchant |
authNotifyOnly | boolean | No | true for authentication in notify-only mode (no interaction with the cardholder) |
brandEstablishmentCode | string | No | Card brand establishment code |
challengeWindowSize | string | No | Challenge window size: "250x400", "390x400", "600x400", "500x600", or "FullScreen" |
ENROLL response (200 OK)
See the fields returned in the ENROLL operation response:
| Field | Type | Description |
|---|---|---|
status | numeric | Authentication result. See the status table. |
Reason.Code | string | Reason code. See Reason codes. |
Reason.Message | string | Reason message. |
Challenge.AcsUrl | string | Access Control Server (ACS) URL for challenge display |
Challenge.Pareq | string | Payer Authentication Request (PAReq) |
Challenge.TransactionId | string | Transaction identifier |
Authentication.Xid | string | Authentication XID |
Authentication.Eci | string | ECI indicator |
Authentication.Cavv | string | Authentication CAVV |
Authentication status
See the possible authentication statuses:
Common errors (400 Bad Request)
| Field with error | Scenario |
|---|---|
ExpirationMonth | Value 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:
| Field | Type | Required | Size | Description |
|---|---|---|---|---|
transactionId | string | Yes | 1–26 | Transaction identifier returned by ENROLL |
ordernumber | string | Yes | 1–50 | Order number |
currency | string | Yes | 3 | ISO currency code (e.g., 986 for BRL) |
totalamount | numeric | Yes | — | Total order amount in cents |
card | Card | Yes | — | Card data. See Card. |
billToCustomerId | string | No | — | Shopper identification document |
VALIDATE response (200 OK)
Possible fields returned in the VALIDATE operation response:
| Field | Type | Description |
|---|---|---|
status | numeric | Authentication result. See the status table. |
Reason.Code | string | Reason code. See Reason codes. |
Reason.Message | string | Reason message. |
Authentication.Version | string | 3DS protocol version |
Authentication.DirectoryServerTransactionId | string | Transaction identifier at the Directory Server |
Authentication.Xid | string | Authentication XID |
Authentication.Eci | string | ECI indicator |
Authentication.Cavv | string | Authentication CAVV |
Statuses returned in VALIDATE
Statuses returned in the VALIDATE operation:
The statuses returned are the same as in the ENROLL operation. Only status
2is not returned in the VALIDATE operation, as it indicates a challenge.
| Status | Meaning | Recommended action |
|---|---|---|
0 | Not authenticated | Evaluate the returned ECI to decide whether to proceed with the transaction. See the ECI table. |
1 | Authenticated | Proceed to transaction authorization. |
Common errors (400 Bad Request)
Possible errors in the VALIDATE operation response:
| Field with error | Scenario |
|---|---|
currency | Invalid format value or unsupported currency |
transactionId | Field absent or empty |
card | Object 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:
| Code | Meaning | Recommended action |
|---|---|---|
200 | Operation successful | — |
400 | Validation failure | Check the fields in the ErrorResult object returned in the response. |
401 | Unauthorized | Regenerate the access_token and restart the process. |
409 | Repeated or out-of-order operation | Follow the sequence: AUTH → INIT → ENROLL → VALIDATE. |
500 | Internal server error | Try again. If the issue persists, contact support. |
AUTH operation error codes
See the possible error codes for the AUTH operation:
| Code | Description |
|---|---|
605 | Establishment code not provided |
606 | Merchant name not provided |
607 | MCC not provided or invalid |
Reason codes
See the possible reason codes:
| Code | Description |
|---|---|
100 | Success |
101 | Required field missing |
102 | Invalid field value |
234 | Configuration error |
475 | Cardholder enrolled |
476 | Cardholder 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:
| Value | Description |
|---|---|
0 | Not authenticated |
1 | Authenticated |
2 | Challenge |
Test cards (sandbox)
Test cards without challenge
To simulate no-challenge authentications, submit requests with one of the following test cards:
| Card | Card brand | Result | Scenario | Status |
|---|---|---|---|---|
| 4000000000002701 5200000000002235 6505290000002000 | VISA MASTER ELO | Success | No-challenge authentication with cardholder successfully authenticated. | 1 |
| 4000000000002925 5200000000002276 6505290000002018 | VISA MASTER ELO | Failure | No-challenge authentication with cardholder authentication failure. | 0 |
Test cards with challenge
To simulate challenge authentications, submit requests with one of the following test cards:
| Card | Card brand | Result | Scenario | Status |
|---|---|---|---|---|
| 4000000000002503 5200000000002151 6505290000002190 | Visa Master Elo | Success | Challenge authentication with cardholder successfully authenticated. | 2 |
| 4000000000002370 5200000000002490 6505290000002208 | Visa Master Elo | Failure | Challenge authentication with cardholder authentication failure. | 0 |
| 4000000000002420 5200000000002664 6505290000002257 | Visa Master Elo | Unenrolled | Challenge authentication currently unavailable. | 0 |
| 4000000000002644 5200000000002656 6505290000002265 | Visa Master Elo | Error | System error during the authentication step. | 0 |
Test cards for DataOnly
To simulate DataOnly authentications, submit requests with one of the following test cards:
| Card | Card brand | Result | Scenario | Status |
|---|---|---|---|---|
| 5200000000002805 4000000000002024 | Mastercard Visa | Success | DataOnly authentication | 0 |
Updated 3 days ago