A versão 2.0 do protocolo de autenticação 3DS está disponível em sua forma browser-based. Isso significa que a loja deve incorporar o script do 3DS no arquivo HTML da página de checkout. Ao processar esse script, o protocolo 3DS facilita a troca de dados entre a loja e o emissor, permitindo a autenticação do portador do cartão de forma segura.
Links e Downloads
Para implementar o 3DS, você encontrará abaixo os links para os arquivos JavaScript necessários:
Link de Download da Biblioteca JavaScript de Homologação: sandbox-3ds-min.js.
Link de Download da Biblioteca JavaScript de Produção: production-3ds-min.js.
É importante baixar esses arquivos e substituí-los pelos mais recentes sempre que houver uma atualização de código.
Observação: Recomenda-se fortemente evitar a utilização via referência de DNS, pois isso pode resultar em erros inesperados durante as atualizações.
Procedimentos
Implementação do Protocolo 3DS 2.0
A implementação do protocolo 3DS 2.0 envolve vincular uma chamada em JavaScript ao botão que executa a compra. Esse botão estará localizado em uma página do site de e-commerce, especificamente na tela onde o cliente final realiza a compra após inserir os dados do cartão de crédito.
Requisito
- Possuir um JQuery (versão 3.3.1 ou acima).
Checkpoints do Processo
- Injetar a biblioteca JavaScript.
Etapa 1 – Injetar Biblioteca Javascript:
- Para injetar a biblioteca JavaScript no seu diretório onde você carrega o
jquery.min.js
, é necessário baixar o arquivo sandbox-3ds-min.js durante a fase de homologação ou o arquivo production-3ds-min.js quando estiver em produção. Esse arquivo deve ser colocado na mesma localização onde está o checkout.
Etapa 2 – Desenvolvimento no Front-End:
- O segundo passo consiste em desenvolver o código no front-end, especificamente na página de pagamento do e-commerce, onde o botão "pagar" está disponível para os clientes finais. Essa é a mesma tela onde são inseridos os dados do cartão de crédito.
- Abaixo, apresentamos um exemplo de um arquivo “index.js” que simula uma página de pagamento. Preste atenção nos trechos que se referem à implementação do 3DS, que neste caso é o projeto base de exemplo:
// link para o backend
window.backend_url = window.location.protocol + "//" + window.location.host + "/";
// link para capturar o IP
window.get_ip_url = "https://api.ipify.org/?format=json";
let threeDs;
// Executando o pagamento
async function pagar() {
console.log("pagar ...");
threeDs = new Ultrapayments3ds('ccnum', validateChallengeCallback);
await threeDs.Authorization3ds();
var code3ds = threeDs.getThreeDsCode();
CallPayment(code3ds);
}
function validateChallengeCallback(jwt, statusChallenge) {
// status Approved/Cancelled
console.log("Status Challenge: " + statusChallenge);
if (statusChallenge == 'Cancelled') {
alert('O desafio foi cancelado pelo usuário.');
return;
}
// Recebe a resposta do desafio
// Levar o jwt para o backend para ser informado no api /payments/api/sales/validate do gateway
var validationData = getValidationData(jwt);
var settings = {
"async": true,
"crossDomain": true,
"url": window.backend_url + "Validation",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Accept": "*/*"
},
"processData": false,
"data": JSON.stringify(validationData),
};
$.ajax(settings).done(function (response, status) {
console.log("Status: " + status);
console.log("Validation Response: " + JSON.stringify(response));
ValidationResponse(response);
}).fail(function (failResponse) {
console.log("Problem during validation request.");
logMessage('Problem during validation request.', true);
});
}
// Resposta da validação
function ValidationResponse(validationResponse) {
console.log("treatValidationResponse ... ");
console.log(validationResponse);
window.datefin = performance.now();
if (validationResponse.success) {
alert(
`Pagamento efetuado com sucesso! \nPaymentId: ${validationResponse.paymentId}
\nAuthorizationCode: ${validationResponse.authorizationCode}
\nAmount: ${validationResponse.amount}
\nTime: ${((window.datefin - window.dateini) / 1000).toFixed(4)} seconds.`
);
} else {
alert(`${validationResponse.description} \nTime: ${(window.datefin - window.dateini)}`);
}
}
// Preparando o JSON para validação
function getValidationData(jwt) {
return {
code3ds: threeDs.getThreeDsCode(),
validateToken: jwt
};
}
// Processando o pagamento
async function CallPayment(code3ds) {
const payload = getPaymentData(code3ds);
try {
const r = await $.ajax({
type: 'POST',
dataType: 'json',
contentType: 'application/json',
url: window.payment_url,
data: JSON.stringify(payload)
});
treatEnrollmentResponse(r);
} catch (error) {
console.error("Erro durante a chamada de pagamento", error);
}
}
// Tratando a resposta da autorização
function treatEnrollmentResponse(objEnrollment: any) {
console.log("3DS protocol version: " + objEnrollment.threeDs.threeDsStatus);
console.log(objEnrollment);
switch (objEnrollment.status) {
case 3:
console.log("Pagamento efetuado com sucesso!");
window.datefin = performance.now();
alert(
`Pagamento efetuado com sucesso! \nPaymentId: ${objEnrollment.paymentId}
\nAuthorizationCode: ${objEnrollment.authorizationCode}
\nAmount: ${objEnrollment.amount}
\nTime: ${((window.datefin - window.dateini) / 1000).toFixed(4)} seconds.`
);
break;
case 7:
console.log("Operação falhou.");
alert("Falha: " + objEnrollment.message);
break;
default:
console.log("Step-up.");
if (confirm("Responda o desafio?")) {
threeDs.InitChallenge(
objEnrollment.threeDs.acsUrl,
objEnrollment.threeDs.pareq,
objEnrollment.threeDs.authenticationTransactionId
);
} else {
alert("Não foi autorizado o pagamento!");
}
break;
}
}
// Capturando o IP do cliente
function GetIpAddress() {
$.getJSON(window.get_ip_url, data => {
console.log("IP Address: " + data.ip);
const ipAddressField = document.getElementById("ipAddress");
ipAddressField.setAttribute('value', data.ip);
});
}
// Montagem do JSON para autorização
function getPaymentData(code3DS) {
return {
amount: (document.getElementById("total")).value,
payment: {
type: (document.getElementById("overridePaymentMethod")).value,
currency_code: (document.getElementById("currency")).value,
card: {
number: (document.getElementById("ccnum")).value,
holder: (document.getElementById("cname")).value,
cvv: (document.getElementById("cvv")).value,
brand: (document.getElementById("type")).value,
expiry_month: (document.getElementById("expmonth")).value,
expiry_year: (document.getElementById("expyear")).value
},
externalAuthentication: {
code3DS,
urlSite3DS: window.location.host,
codeAntiFraud: uuid()
}
},
customer: {
ip: (document.getElementById("ipAddress")).value
}
deviceInfo: {
httpBrowserLanguage: navigator.language || navigator.userLanguage,
httpBrowserJavaEnabled: navigator.javaEnabled() ?? false,
httpBrowserJavaScriptEnabled: true,
httpBrowserColorDepth: window.screen.colorDepth.toString(),
httpBrowserScreenHeight: window.innerHeight.toString(),
httpBrowserScreenWidth: window.innerWidth.toString(),
httpBrowserTimeDifference: new Date().getTimezoneOffset().toString(),
userAgentBrowserValue: navigator.userAgent,
}
};
}
window.onload = function () {
console.log("iniciando java...");
document.getElementById("pagarBtn").onclick = pagar;
GetIpAddress();
};
O trecho de código a seguir é um exemplo de um arquivo “index.cshtml” que simula a página HTML de pagamento. Preste atenção nos trechos destacados em cores, que se referem à implementação do 3DS. Os trechos em cinza representam os códigos esperados no site do e-commerce, sendo este um projeto base de exemplo:
Capturar Campos do navegador via JavaScript
Segue abaixo uma sugestão para capturar os campos abaixo via JavaScript:
- httpAcceptBrowserValue: Quais extensões que o navegador pode aceitar; Exemplo: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
- httpAcceptContent: Quais conteúdos vão ser aceitos; Exemplo: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
- httpBrowserLanguage: var httpBrowserLanguage = navigator.language || navigator.userLanguage;
- httpBrowserJavaEnabled : navigator.javaEnabled() ?? false
- httpBrowserJavaScriptEnabled: true
- httpBrowserColorDepth: window.screen.colorDepth.toString()
- httpBrowserScreenHeight: window.innerHeight.toString()
- httpBrowserScreenWidth: window.innerWidth.toString()
- httpBrowserTimeDifference: new Date().getTimezoneOffset().toString()
- userAgentBrowserValue: navigator.userAgent