Manual de Integração 3DS
Um guia completo para integrar o protocolo de segurança que protege suas vendas online, aumenta a aprovação e elimina chargebacks por fraude.
Manual de Integração 3DS
Índice
- Visão Geral
- O que é 3DS?
- Como Funciona o 3DS
- Começando: Configuração Inicial
- Guia de Integração Passo a Passo
- Objeto Order
- Ambientes de Teste
- Tratamento de Erros
- Solução de Problemas
Visão Geral
Para que serve esta documentação?
Esta documentação é seu guia completo para integrar a autenticação 3DS da Stone usando nossa biblioteca 3ds-nx-js. Se você é um desenvolvedor que precisa implementar 3DS em sua aplicação, você está no lugar certo.
O que você vai aprender?
- Conceitos fundamentais do 3DS de forma simples e clara
- Implementação prática com exemplos de código prontos para usar
- Como testar sua integração com cartões de teste específicos
- Tratamento de cenários tanto de fluxo simples quanto de desafio
- Boas práticas para uma implementação segura
O que é 3DS?
O 3D Secure (3DS) é um protocolo de segurança que adiciona uma camada extra de autenticação para transações com cartão de crédito e débito online. Pense nele como uma "segunda verificação" que confirma se é realmente o portador do cartão quem está fazendo a compra.
Por que usar 3DS?
✅ Proteção contra fraudes: Reduz significativamente transações fraudulentas
✅ Liability Shift: A responsabilidade por chargebacks passa para o emissor do cartão
✅ Aprovação aumentada: Emissores tendem a aprovar mais transações autenticadas
Como Funciona o 3DS
O processo de autenticação 3DS pode seguir dois caminhos principais:
🚀 Fluxo Frictionless (Sem Atrito)

O que acontece: O emissor aprova a transação automaticamente baseado no perfil de risco, sem interação do usuário.
🔐 Fluxo Challenge (Com Desafio)

O que acontece: O emissor solicita que o cliente confirme sua identidade através de SMS, app do banco, ou outro método.
Começando: Configuração Inicial
Pré-requisitos
- Secret Key da Stone (todos os clientes Stone possuem)
- Conhecimento básico em JavaScript
- Ambiente backend capaz de fazer requisições HTTPS
1. Incluir a biblioteca no HTML
URLs das bibliotecas por ambiente
| Ambiente | URL da Biblioteca |
|---|---|
| Sandbox | https://3ds-nx-js.stone.com.br/test/v2/3ds2.min.js |
| Produção | https://3ds-nx-js.stone.com.br/live/v2/3ds2.min.js |
Exemplo de como incluir a biblioteca no seu HTML:
<!-- Para o ambiente de sandbox: -->
<script src="https://3ds-nx-js.stone.com.br/test/v2/3ds2.min.js"></script>
<!-- Para o ambiente de produção: -->
<script src="https://3ds-nx-js.stone.com.br/live/v2/3ds2.min.js"></script>
2. Estrutura básica do HTML
A biblioteca requer alguns containers no HTML da sua aplicação para funcionar corretamente. Dentro deles ocorrerá o processo de 3DS Method e Challenge. Recomendamos que sejam elementos do tipo <div> para facilitar a manipulação. Dentro desses elementos a biblioteca irá inserir os iframes e forms necessários para o processo de autenticação. Portanto, sua aplicação não deve interferir na estrutura interna desses containers.
Um exemplo de como preparar os containers onde o 3DS vai operar:
<div id="myApplication">
<!-- Container invisível para o 3DS Method -->
<div id="tdsMethodContainer" style="display: none;"></div>
<!-- Container para o Challenge -->
<div id="challengeContainer"></div>
<!-- Botão para iniciar o processo de autenticação 3DS que será utilizado nos exemplos a seguir -->
<button id="submitButton">Finalizar Compra</button>
</div>
3. Geração do token 3DS
⚠️ IMPORTANTE: O token 3DS deve sempre ser gerado no seu backend por questões de segurança, dado que para gerá-lo é necessário utilizar a secret key da Stone, que deverá ser mantida em segredo e nunca exposta no frontend.
URLs base dos ambientes
| Ambiente | URL Base |
|---|---|
| Sandbox | https://3ds-sdx.stone.com.br/v2 |
| Produção | https://3ds.stone.com.br/v2 |
Como gerar o token 3DS no backend
Recomendamos criar um endpoint seguro em seu backend que seja responsável pela geração do token. Dessa forma sua aplicação pode obter o token de forma segura e evitar expor a secret key no frontend.
O token 3DS é obtido fazendo uma requisição para nosso endpoint GET /tds-token utilizando Basic Access Authentication com a secret key encodada em Base64 da seguinte forma:
Authorization: Basic <base64(secret_key:)>
Veja o exemplo abaixo da implementação de uma função em Node.js responsável por gerar o token 3DS:
// Exemplo em Node.js com a biblioteca axios
const axios = require('axios');
async function generateTDSToken(api, secretKey) {
try {
const response = await axios.get(`${api}/tds-token`, {
headers: {
'Authorization': 'Basic ' + Buffer.from(`${secretKey}:`).toString('base64'),
},
});
return response.data.tds_token;
} catch (error) {
throw new Error(`Failed to generate token: ${error.response?.data?.message || error.message}`);
}
}
O código acima faz uma requisição GET para o endpoint /tds-token, passando a secret key codificada em Base64 no cabeçalho de autorização. A resposta conterá o token 3DS necessário para iniciar o processo de autenticação.
E para que seu frontend possa obter esse token, você pode criar um endpoint em seu backend que retorne o token gerado:
// Exemplo em Node.js com a biblioteca Express.js
const TDS_API_URLS = {
sandbox: 'https://3ds-sdx.stone.com.br/v2',
production: 'https://3ds.stone.com.br/v2'
};
app.get('/tds-token', async (req, res) => {
try {
const environment = process.env.NODE_ENV === 'production' ? 'production' : 'sandbox';
const api = TDS_API_URLS[environment];
if (!api) {
throw new Error(`Invalid environment: ${environment}`);
}
const token = await generateTDSToken(api, process.env.STONE_SECRET_KEY);
res.json({ token });
} catch (error) {
console.error('Error generating 3DS token:', error.message);
res.status(500).json({ error: 'Failed to generate token' });
}
});
O código acima cria um endpoint /generate-3ds-token que, ao ser chamado, gera e retorna o token 3DS. O ambiente (sandbox ou produção) é determinado pela variável de ambiente NODE_ENV. O token é gerado utilizando a função generateTDSToken do exemplo anterior e a secret key da Stone que está sendo obtida da variável de ambiente STONE_SECRET_KEY.
Guia de Integração Passo a Passo
Passo 1: Obter o token 3DS no frontend
O seu frontend deve solicitar ao seu backend o token 3DS antes de iniciar o processo de autenticação 3DS. Recomendamos que o token seja obtido sempre antes de cada transação, para garantir que você esteja usando o token mais recente e válido, evitando problemas de expiração.
Abaixo está um exemplo de uma função em JavaScript que faz uma requisição para o endpoint POST /generate-3ds-token definido nos exemplos anteriores:
async function getToken() {
const response = await fetch('/generate-3ds-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
return data.token;
}
Passo 2: Preparar os dados do pedido
A biblioteca espera receber um objeto com os dados do pedido (order) que será processado. Esses dados são similares aos dados que você enviaria para criar um pedido na Stone, mas com algumas diferenças, consulte a seção Objeto Order para mais detalhes.
Para o ambiente de teste, é possível utilizar uma série de cartões de teste que simulam diferentes cenários de autenticação 3DS. Basta informar o número do cartão no campo number do objeto card dentro do pedido. Para uma lista completa de cartões de teste, consulte a seção Cartões de Teste Específicos para 3DS.
Exemplo da estrutura de dados do pedido:
const orderData = {
"payments": [
{
"payment_method": "credit_card",
"credit_card": {
"card": {
"number": "3000100811112072", // Cartão de teste que gera challenge manual
"holder_name": "JOAO SILVA",
"exp_month": 12,
"exp_year": 2028,
"billing_address": {
"country": "BR",
"state": "SP",
"city": "São Paulo",
"zip_code": "01234567",
"line_1": "123, Rua das Flores, Centro",
"line_2": "Apartamento 45"
}
},
},
"amount": 20000
}
],
"customer": {
"name": "João Silva",
"email": "[email protected]",
"code": "CUST-001",
"document": "12345678901",
"phones": {
"home_phone": {
"country_code": "55",
"area_code": "11",
"number": "987654321"
},
"mobile_phone": {
"country_code": "55",
"area_code": "11",
"number": "123456789"
}
}
},
"items": [
{
"amount": 15000,
"description": "Smartphone Samsung Galaxy",
"quantity": 1,
"code": "ITEM-001"
},
{
"amount": 5000,
"description": "Capa Protetora",
"quantity": 2,
"code": "ITEM-002"
}
],
"shipping": {
"recipient_name": "João Silva",
"address": {
"country": "BR",
"state": "SP",
"city": "São Paulo",
"zip_code": "01234567",
"line_1": "123, Rua das Flores, Centro",
"line_2": "Apartamento 45"
}
}
};
Passo 3: Iniciando o processo de autenticação 3DS
Assim que o cliente finalizar o preenchimento dos dados necessários para o pedido, você pode iniciar o processo de autenticação 3DS. A biblioteca 3ds-nx-js fornece uma função chamada TDS.init que gerencia todo o fluxo de autenticação com apenas uma chamada. Para isso você deve passar o token 3DS, os containers onde o 3DS Method e o Challenge serão exibidos, e os dados do pedido.
Abaixo está um exemplo de como implementar essa chamada:
async function process3DS() {
// Desabilita o botão para evitar duplo clique
document.getElementById('submitButton').disabled = true;
try {
// 1. Obter o token do backend
const token = await getToken();
// 2. Referências dos containers
const tdsMethodContainer = document.getElementById('tdsMethodContainer');
const challengeContainer = document.getElementById('challengeContainer');
// 3. Executar o fluxo 3DS
const response = await window.TDS.init({
token: token,
tds_method_container_element: tdsMethodContainer,
challenge_container_element: challengeContainer,
use_default_challenge_iframe_style: true, // Usar estilo padrão
challenge_window_size: '03' // Tamanho médio da janela
}, orderData);
// 4. Processar resposta
console.log('3DS Response:', response);
alert('Transação processada com sucesso!');
} catch (error) {
console.error('Erro no 3DS:', error);
alert('Erro ao processar pagamento: ' + error.message);
} finally {
// Reabilitar o botão
document.getElementById('submitButton').disabled = false;
}
}
// Vincular ao botão
document.getElementById('submitButton').addEventListener('click', process3DS);
Passo 4: CSS para o challenge
O challenge será renderizado em um iframe dentro do container informado em challenge_container_element. Se você definir use_default_challenge_iframe_style: true na chamada da função TDS.init, a biblioteca aplicará um estilo padrão ao iframe do challenge, que pode funcionar bem para alguns casos, mas pode não ser o ideal para todos os layouts. Por esse motivo, recomendamos que você personalize o estilo do iframe para melhor se adequar ao design da sua aplicação.
Para fazer isso, você deve definir use_default_challenge_iframe_style: false e aplicar seu próprio CSS ao iframe do challenge. Sempre que houver uma challenge para ser exibida, a biblioteca irá inserir um iframe dentro do container challenge_container_element. Você pode personalizar o estilo desse iframe para que ele se encaixe melhor no design da sua aplicação.
Veja um exemplo de como fazer isso:
/* Estilo personalizado para o challenge iframe */
#challengeContainer iframe {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60%;
max-width: 500px;
height: 600px;
border: 2px solid #ddd;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
background: white;
z-index: 9999;
}
Passo 5: Definindo o challenge_window_size
challenge_window_sizeO parâmetro challenge_window_size define o tamanho da janela do challenge e impacta diretamente na experiência do usuário. Por esse motivo é importante escolher um tamanho adequado para o seu público-alvo.
Recomendamos que você escolha o tamanho de janela baseado no dispositivo que o seu cliente estiver utilizando no momento da autenticação. Abaixo estão os tamanhos recomendados:
| Código | Dimensões | Recomendado para |
|---|---|---|
| '01' | 250x400px | Mobile portrait |
| '02' | 390x400px | Mobile landscape |
| '03' | 500x600px | Desktop (padrão) |
| '04' | 600x400px | Desktop wide |
| '05' | Fullscreen | Máxima compatibilidade |
Passo 6: Finalizando o pedido
Após o processo de autenticação 3DS, a função TDS.init retornará um array de objetos, um para cada cartão autenticado com o seguinte formato:
[
{
tds_server_trans_id: "<UUID>",
trans_status: "<Y|A|N|C|U|R|I>",
authenticated_card: "<card number>",
challenge_canceled: <true|false>,
}
]
| Nome do campo | Descrição | Tipo |
|---|---|---|
tds_server_trans_id | ID da transação autenticada | string |
trans_status | Status da transação 3DS | string |
authenticated_card | Número do cartão autenticado (mascarado) | string |
challenge_canceled | Indica se o challenge foi cancelado pelo usuário | boolean |
Status (trans_status) | Descrição | Responsabilidade em caso de fraude |
|---|---|---|
| Y | Transação autenticada | Emissor |
| A | Tentativa de autenticação | Lojista ou Emissor (depende da resposta do emissor) |
| N | Transação não autenticada | Lojista |
| C | Solicitação de desafio | Lojista ou Emissor (depende da resposta do emissor) |
| U | Autenticação indisponível | Lojista |
| R | Autenticação negada pelo emissor | Lojista |
| I | Apenas informação | Lojista |
Vale destacar que as transações que possuem Liability Shift são as transações com retorno "Y", e eventualmente os status "A" e "C", a depender da resposta do emissor.
Essas são as transações que quando autenticadas pelo emissor do cartão de crédito, ele se responsabiliza por eventuais fraudes.
Após receber a resposta, se desejar criar o pedido (order), utilize o tdsServerTransID retornado. No contrato de criação de pedidos, o campo correspondente ao tdsServerTransID é o ds_transaction_id, mais detalhes na seção Transações Autenticadas dessa documentação.
Objeto Order
O objeto Order representa os dados do pedido que será processado no fluxo de autenticação 3DS. Abaixo estão as especificações detalhadas de cada campo e tipo necessário.
Order
Customer
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
name | string | Não |
email | string | Sim |
code | string | Não |
document | string | Não |
phones | Phones | Não |
Phones
Phone
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
country_code | string | Sim |
area_code | string | Sim |
number | string | Sim |
Item
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
amount | number | Sim |
description | string | Sim |
quantity | number | Sim |
code | string | Sim |
Shipping
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
recipient_name | string | Sim |
electronic_delivery | boolean | Não |
address | Address | É obrigatório apenas se electronic_delivery for false |
Payment
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
payment_method | string | Não |
credit_card | CreditCard | Sim |
amount | number | Sim |
purpose | string | Não |
recurrence | Recurrence | Não |
CreditCard
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
card | Card | Sim |
Recurrence
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
deadline | string | Sim |
frequency_days | number | Sim |
Card
| Nome do campo | Tipo | Obrigatório? |
|---|---|---|
number | string | Sim |
holder_name | string | Sim |
exp_month | number | Sim |
exp_year | number | Sim |
billing_address | Address | Sim |
Address
| Nome do campo | Tipo | Obrigatório? | Formato |
|---|---|---|---|
country | string | Sim | Código do país no formato ISO 3166-1 alpha-2 (ex.: BR, US, FR) |
state | string | Sim | - |
city | string | Sim | - |
zip_code | string | Sim | - |
line_1 | string | Sim | number, street, district |
line_2 | string | Sim | - |
Ambientes de Teste
Cartões de Teste Específicos para 3DS
Use estes cartões em ambiente sandbox para testar diferentes cenários:
Cartões 3DS 2.2
| Número do Cartão | Versão 3DS | Comportamento | Descrição |
|---|---|---|---|
4000100511112003 | 2.2 | Frictionless Success | Aprovado automaticamente sem challenge |
5000100411112203 | 2.2 | 3DS Method Timeout | Testa timeout do 3DS Method |
6000100611112103 | 2.2 | Frictionless sem 3DS Method | Aprovado sem executar 3DS Method |
3000100811112072 | 2.2 | Manual Challenge | Exibe challenge manual para o usuário |
7000100911112070 | 2.2 | Auto Challenge Success | Challenge automático - aprovado |
3000101011112071 | 2.2 | Auto Challenge Fail | Challenge automático - negado |
Exemplo de Teste Completo
// Função para testar diferentes cenários
async function testScenario(cardNumber, description) {
console.log(`🧪 Testando: ${description}`);
const testOrder = {
...orderData,
payments: [{
...orderData.payments[0],
credit_card: {
...orderData.payments[0].credit_card,
card: {
...orderData.payments[0].credit_card.card,
number: cardNumber // Sobrescrever com cartão de teste
}
}
}]
};
try {
const token = await getToken();
const response = await window.TDS.init({
token,
tds_method_container_element: document.getElementById('tdsMethodContainer'),
challenge_container_element: document.getElementById('challengeContainer')
}, testOrder);
console.log(`✅ ${description} - Resultado:`, response);
} catch (error) {
console.log(`❌ ${description} - Erro:`, error.message);
}
}
// Executar testes
async function runAllTests3DS22() {
// Testes 3DS 2.2
await testScenario('4000100511112003', 'Frictionless Success (3DS 2.2)');
await testScenario('3000100811112072', 'Manual Challenge (3DS 2.2)');
await testScenario('7000100911112070', 'Auto Challenge Success (3DS 2.2)');
}
Tratamento de Erros
Erros Comuns e Soluções
1. "Invalid JWT for 3DS"
// ❌ Erro comum
catch (error) {
if (error.message.includes('Invalid JWT')) {
console.error('Token inválido ou expirado');
// Gerar um novo token
const newToken = await getToken();
// Tentar novamente
}
}
Solução: Verifique se o token está sendo gerado corretamente e se não expirou (20 segundos de validade).
2. "config.token is required"
// ✅ Validação correta
if (!token || typeof token !== 'string') {
throw new Error('Token é obrigatório e deve ser uma string');
}
3. "HTMLElement not found"
// ✅ Verificação dos containers
const tdsMethodContainer = document.getElementById('tdsMethodContainer');
const challengeContainer = document.getElementById('challengeContainer');
if (!tdsMethodContainer || !challengeContainer) {
throw new Error('Containers do 3DS não encontrados no DOM');
}
Exemplo de Tratamento Robusto
async function robustProcess3DS() {
const maxRetries = 3;
let attempt = 0;
while (attempt < maxRetries) {
try {
const token = await getToken();
const response = await window.TDS.init({
token,
tds_method_container_element: document.getElementById('tdsMethodContainer'),
challenge_container_element: document.getElementById('challengeContainer'),
use_default_challenge_iframe_style: true,
challenge_window_size: '03'
}, orderData);
return response; // Sucesso
} catch (error) {
attempt++;
console.warn(`Tentativa ${attempt} falhou:`, error.message);
if (attempt >= maxRetries) {
throw new Error(`Falha após ${maxRetries} tentativas: ${error.message}`);
}
// Aguardar antes de tentar novamente
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
Solução de Problemas
Cenário 1: Challenge não aparece
Sintomas: O processo parece executar mas nenhum challenge é exibido.
Possíveis causas:
- Container
challengeContainernão existe ou está oculto - CSS está impedindo a exibição
- Cartão de teste não requer challenge
Solução:
// Debug do container
const container = document.getElementById('challengeContainer');
console.log('Container exists:', !!container);
console.log('Container visible:', container && container.offsetHeight > 0);
// Testar com cartão que força challenge
const testCard = '3000100811111072'; // Manual challenge
Cenário 2: Timeout frequente
Sintomas: Erro "Token expirado" ou timeout.
Possíveis causas:
- Operações lentas no backend
- Problemas de conectividade
- Token gerado muito cedo
Solução:
// Gerar token just-in-time
async function justInTimeProcess() {
// Preparar tudo primeiro
const orderReady = validateOrder(orderData);
const containersReady = validateContainers();
if (orderReady && containersReady) {
// Só então gerar o token
const token = await getToken();
return window.TDS.init(/* ... */);
}
}
Cenário 3: Erro de CORS
Sintomas: Erro de Cross-Origin Request.
Solução:
// Backend deve incluir headers CORS
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://seudominio.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
Updated 2 days ago
