API Reference

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:

  1. 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
  2. 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
  3. httpBrowserLanguage: var httpBrowserLanguage = navigator.language || navigator.userLanguage;
  4. httpBrowserJavaEnabled : navigator.javaEnabled() ?? false
  5. httpBrowserJavaScriptEnabled: true
  6. httpBrowserColorDepth: window.screen.colorDepth.toString()
  7. httpBrowserScreenHeight: window.innerHeight.toString()
  8. httpBrowserScreenWidth: window.innerWidth.toString()
  9. httpBrowserTimeDifference: new Date().getTimezoneOffset().toString()
  10. userAgentBrowserValue: navigator.userAgent