Alguns cartões pedem uma autenticação do comprador no navegador, o 3D Secure: um código no app do banco, uma tela do emissor ou um redirecionamento. Quem usa o checkout próprio faz essa etapa com o SDK de 3DS.
O SDK consulta a configuração da sua loja e só age quando o cartão precisa. Quando não precisa, as funções não fazem nada. Por isso você pode chamar o fluxo completo em toda venda com cartão.
Ele usa a mesma chave de tokenização do tokenizador. A chave de API continua só no seu backend.
navegador do compradorseu servidorAPISeta tracejada é resposta.
Passos | O que acontece |
|---|---|
| 1 | init guarda a chave de tokenização. |
| 2 a 4 | prepare recebe a venda e busca o que o cartão da sua loja exige. Chame ao abrir a página de pagamento, não no clique em pagar. |
| 5 e 6 | start recebe o cartão, abre a autenticação para o comprador quando o cartão pede e devolve o threeDS, com o resultado da autenticação e os dados de antifraude. Quando não há nada a devolver, devolve undefined. |
| 7 e 8 | O encrypt do tokenizador recebe só o cartão e devolve o token. |
| 9 a 12 | Sua página manda o token e o threeDS ao seu backend. Ele cria o pagamento com os dois dentro de card e devolve a resposta inteira para a página. |
| 13 | finish recebe a resposta e leva o comprador à autenticação quando ela acontece depois da venda. |
Adicione o script na sua página, depois do tokenizador. Ele é um módulo ES, então a tag precisa de type="module". O SDK fica em window["AvenPayThreeDS"].
<script type="module" src="https://api.avenpayments.com/v1/scripts"></script>
<script type="module" src="https://api.avenpayments.com/v1/scripts/3ds"></script>Chame o init e o prepare assim que a página de pagamento abrir, com o que já souber da venda, e deixe do start em diante para o clique em pagar. Em alguns cartões, o prepare começa a coletar em segundo plano os dados do aparelho do comprador para a análise de fraude, e isso leva alguns segundos. Se o start vier logo depois do prepare, a venda segue sem esses dados e tem mais chance de ser recusada.
<button id="pay" disabled>Pagar</button>
<script type="module">
const pk = "CHAVE_DE_TOKENIZACAO";
const tokenizer = window["AvenPay"];
const sdk = window["AvenPayThreeDS"];
const payButton = document.querySelector("#pay");
// a venda é o corpo do POST /v1/payment, sem o token
const sale = {
amount: 10000,
currency: "BRL",
payer: {
name: "Maria Souza",
email: "maria@email.com",
phone: "+5511999998888",
},
card: {
installments: 3,
billingAddress: {
street: "Rua A",
number: "100",
city: "São Paulo",
state: "SP",
zipCode: "01000000",
country: "BR",
},
},
};
// ao abrir a página: o prepare começa a coletar os dados do aparelho, o que leva alguns segundos
try {
await tokenizer.init(pk);
await sdk.init(pk);
await sdk.prepare(sale);
payButton.disabled = false; // o botão nasce desabilitado e só liga com o prepare pronto
} catch (error) {
if (error.name === "PaymentError") showError(error.code, error.description);
}
// no clique em pagar
payButton.addEventListener("click", async (event) => {
event.preventDefault(); // o botão dentro do <form> faria o submit
payButton.disabled = true; // sem isto, dois cliques fazem dois start e dois pagamentos
const card = {
number: "5555444433331111",
holder: "Maria Souza",
expMonth: "08",
expYear: "2027",
cvv: "789",
};
let payment;
try {
const threeDS = await sdk.start(card); // 3DS e antifraude, ou undefined
const token = await tokenizer.encrypt(card); // só o cartão
payment = await fetch("/meu-backend/pagar", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token, threeDS }),
}).then((response) => response.json());
await sdk.finish(payment); // a resposta inteira, sem tirar campos
} catch (error) {
if (error.name === "PaymentError") showError(error.code, error.description);
if (!payment) payButton.disabled = false; // com a venda criada, religar deixaria pagar de novo
}
});
</script>No seu backend, o threeDS vai em card.threeDS, ao lado do token, sem mudar nada:
// seu backend recebe { token, threeDS } da página
const { token, threeDS } = request.body;
const { installments, billingAddress } = sale.card;
const response = await fetch("https://api.avenpayments.com/v1/payment", {
method: "POST",
headers: { Authorization: "Bearer CHAVE_DE_API", "Content-Type": "application/json" },
body: JSON.stringify({
...sale, // a mesma venda do prepare, com method, description, items e metadata
ip: "203.0.113.10", // o IP do comprador, não o do seu servidor
card: { token, installments, billingAddress, threeDS }, // o threeDS vai como veio
}),
});
// devolva a resposta inteira para a páginaO prepare recebe a venda no mesmo formato do POST /v1/payment. Monte o objeto uma vez e use nas duas pontas.
Campo | Obrigatório | Para que serve |
|---|---|---|
| amount | sim | Valor da venda na menor unidade da moeda (centavos no BRL), igual ao do pagamento. |
| currency | sim | Moeda da venda, ex.: "BRL". |
| payer | sim | Nome, e-mail e telefone do comprador. O telefone ajuda o banco a autenticar. Pode ser preenchido depois do prepare, no mesmo objeto da venda: o start e o finish leem o comprador desse objeto. |
| card.installments | não | Número de parcelas. Sem ele, vale 1. Com card.chargeInterest true, o SDK autentica o valor da parcela com juros, que é o valor cobrado. |
| card.billingAddress | não | Endereço de cobrança. Sem ele, o SDK usa o delivery.address. |
| delivery.address | não | Endereço de entrega. |
Chame o prepare de novo sempre que o valor, a moeda, as parcelas ou o endereço mudarem, e quando passar outro objeto de venda: o start usa a venda do último prepare.
No POST /v1/payment, mande em ip o IP do comprador, que o seu backend recebe na requisição da página, e não o IP do seu servidor. Alguns bancos usam esse IP na autenticação. Sem um IP válido, a venda segue sem ele.
O threeDS que o start devolve traz o resultado da autenticação e os dados de antifraude da venda. Seu backend manda esse objeto em card.threeDS no POST /v1/payment. O token do cartão leva só o cartão.
Todos os campos são opcionais. O SDK preenche só o que o cartão da sua loja usa. Quando o start devolve undefined, o campo fica de fora. Se a coleta dos dados de antifraude falhar, a venda segue sem eles.
Mande o objeto como veio, sem montar nem mudar campos. A API recusa com 400 um campo que ela não conhece. Só o `device` é diferente: fora do formato, ele é descartado e a venda segue sem ele.
Campo | Para que serve |
|---|---|
| authentication | Resultado da autenticação feita antes da venda: id, eci, cavv, dsTransactionId, version e status. |
| device | Dados de antifraude: sessionId e fingerprint da análise de fraude, e os dados do navegador do comprador (userAgent, language, screenWidth, screenHeight, colorDepth e timeZoneOffset). |
| cardReference | O cartão no formato que a cobrança de alguns cartões pede, gerado no navegador. Não substitui o card.token. |
Em alguns cartões, a autenticação acontece depois que o pagamento é criado. Em parte deles, o finish abre a tela do banco dentro da sua página, e o comprador não sai dela. Nos outros, o finish leva o comprador até o banco, e ele volta pelo returnUrl. Nos dois casos, o resultado chega pelo webhook.
O diagrama mostra o comprador saindo para o banco. Quando a tela do banco abre dentro da sua página, não há ida nem volta: o comprador autentica ali, e o resultado chega pelo webhook do mesmo jeito.
No POST /v1/payment, mande sempre metadata.returnUrl, na mesma origem da sua página de pagamento. É para esse endereço que o banco devolve o comprador.
Passe ao finish a resposta inteira do pagamento, sem tirar campos. Se o cartão pedir, ele abre a tela do banco na sua página ou leva o comprador até o banco.
O resultado da autenticação chega pelo webhook do pagamento, não pela volta do comprador.
{
// ...resto da venda
"metadata": {
"provider": "sualoja",
"orderId": "123",
"sellerTaxId": "12345678000190",
"sellerEmail": "contato@sualoja.com",
"returnUrl": "https://sualoja.com/pedido/123"
}
}O metadata exige provider, orderId, sellerTaxId e sellerEmail. Os campos estão em Criar Pagamento.
Quando o comprador sai da sua página para o banco, o endereço de volta é escolhido assim:
Você enviou | O que acontece |
|---|---|
returnUrl com https:// | O comprador autentica no banco e volta para esse endereço. |
Sem returnUrl | Vale o checkoutUrl do metadata, se ele tiver https://. |
Nenhum dos dois com https:// | O pagamento segue sem a autenticação. |
Quando a tela do banco abre dentro da sua página, o finish mostra uma janela por cima dela e fecha sozinho quando o banco devolve o comprador ao returnUrl. Para isso, o returnUrl precisa estar na mesma origem da página de pagamento (protocolo, domínio e porta) e poder abrir em iframe. Fora disso, a janela não fecha sozinha.
Poder abrir em iframe quer dizer que a página do returnUrl não responde com X-Frame-Options: DENY nem com frame-ancestors 'none'. X-Frame-Options: SAMEORIGIN serve.
Se o comprador fechar a janela, o finish falha com THREEDS_AUTHENTICATION_FAILED e detail igual a "cancelled". Se passar de 15 minutos, o detail é "timeout".
Em alguns cartões, a resposta do POST /v1/payment traz o threeDSToken, e o finish usa esse token para confirmar a autenticação na API, com a chave de tokenização. Você não chama essa confirmação nem lê o token: basta passar a resposta inteira ao finish. O token vale 15 minutos e não vem nos webhooks.
Como alguns cartões autenticam depois da venda com os dados do cartão, o SDK guarda o cartão só em memória até o fim do finish e nunca grava no navegador.
A Content Security Policy é uma lista de permissões que o seu site manda para o navegador. Ela diz de quais endereços a página pode carregar scripts, abrir janelas internas (iframes) e buscar dados. O que não está na lista, o navegador bloqueia, mesmo que o código peça.
Ela serve para proteger o comprador: se alguém injetar um script malicioso na página, o navegador não deixa ele rodar.
Ela costuma vir num cabeçalho da resposta do seu servidor, assim:
Content-Security-Policy: script-src 'self' https://api.avenpayments.com; connect-src 'self' https://api.avenpayments.com; frame-src 'self'script-src lista de onde podem vir scripts, connect-src, para onde a página pode fazer requisições, e frame-src, de onde podem vir as janelas internas. 'self' é o próprio domínio do seu site.
Como saber se você usa: abra a página de pagamento, abra as ferramentas do navegador (F12), aba Rede, clique na própria página e procure Content-Security-Policy nos cabeçalhos da resposta. Se não aparecer, sua página não usa CSP e você pode pular esta seção.
Libere o que o SDK precisa carregar. Se um script for bloqueado, o prepare falha com THREEDS_SCRIPT_LOAD_FAILED, exceto o de análise de fraude. Se a busca da configuração for bloqueada, ele falha com THREEDS_CONFIG_FAILED.
Diretiva | Libere | Por quê |
|---|---|---|
| script-src | https://api.avenpayments.com | Carrega o tokenizador e o SDK. |
| connect-src | https://api.avenpayments.com | O prepare busca a configuração da sua loja na API. Em alguns cartões, o finish também chama a API para confirmar a autenticação. |
| script-src | O script de autenticação do banco | O SDK carrega no prepare, conforme o cartão da sua loja. |
| script-src | O script de análise de fraude | Em alguns cartões, o SDK carrega no prepare para coletar dados do aparelho do comprador. Se ele for bloqueado, a venda segue. |
| frame-src | A tela de autenticação do banco | Em alguns cartões, a tela do banco abre numa janela dentro da sua página. |
| frame-src | 'self' | Quando a tela do banco abre dentro da sua página, o banco devolve o comprador ao seu returnUrl dentro dessa janela. |
Os endereços do banco e da análise de fraude mudam conforme o cartão. Se não puder liberar endereços que não conhece, deixe script-src, connect-src e frame-src sem restrição na página de pagamento.
Igual ao tokenizador: o SDK faz throw de um PaymentError com name, code, description e field. O THREEDS_AUTHENTICATION_FAILED traz também detail, com o motivo da falha.
code | field | Quando acontece | O que fazer |
|---|---|---|---|
| INVALID_TOKENIZATION_KEY | O init recebeu a chave vazia. | Use a chave de tokenização de Financeiro → Integrações. | |
| THREEDS_NOT_INITIALIZED | O prepare foi chamado antes do init. | Chame await init(pk) antes. | |
| THREEDS_NOT_PREPARED | O start ou o finish foi chamado antes do prepare, ou o último prepare falhou. | Chame await prepare(sale) antes. | |
| THREEDS_CONFIG_FAILED | A configuração da loja não carregou: chave errada, moeda sem cartão ativo, falha de rede ou CSP bloqueando a busca na API. | Confira a chave e a moeda, e tente de novo. Se sua página usa CSP, veja Se sua página usa CSP. | |
| THREEDS_SCRIPT_LOAD_FAILED | Um script da autenticação não carregou, em geral por CSP. | Veja Se sua página usa CSP. | |
| THREEDS_INSTALLMENT_NOT_FOUND | installments | O número de parcelas não está disponível para esse valor. | Ofereça outro número de parcelas. |
| THREEDS_AUTHENTICATION_FAILED | A autenticação não foi concluída. O motivo vem em detail: o que o emissor devolveu, cancelled (o comprador fechou a janela do banco), timeout (passou de 15 minutos), missing_token (a resposta passada ao finish não tem o threeDSToken), network_error (a confirmação não chegou à API), o code do erro da API ou http_ seguido do status HTTP. | Peça para tentar de novo ou usar outro cartão. Com missing_token, passe ao finish a resposta inteira do pagamento. | |
| THREEDS_ENCRYPT_CARD | Os dados do cartão foram recusados na autenticação. | Peça ao comprador para conferir número, validade e CVV. |