Bunne Docs

3-D Secure

Trate 3DS e Risk Session: carregue o SDK, rode o desafio e envie contexto de risco na cobrança.

Uma cobrança de cartão pode nascer requires_action pedindo autenticação 3-D Secure. O SDK hospedado roda o desafio no browser e você conclui em POST /charges/{id}/confirm-3ds, resolvendo a charge canônica final.

Quando o 3DS acontece

Quando uma regra de 3DS exige autenticação E a adquirente resolvida suporta 3DS, a charge nasce requires_action e inclui next_action em vez de autorizar na hora. Sem 3DS, next_action fica ausente e o fluxo segue normal.

Uma chave com a capacidade charges:bypass_3ds nunca pausa: a cobrança autoriza na hora, sem next_action, mesmo com a regra exigindo 3DS. É uma capacidade sensível, concedida caso a caso pela plataforma — e sem 3DS não há liability shift, então o risco de fraude fica com a operação.

O next_action da charge

next_action só aparece enquanto a charge está requires_action. sdk_payload é opaco, específico da adquirente e ligado ao browser — repasse ao SDK sem inspecionar.

{  "id": "ch_xxx",  "object": "charge",  "status": "requires_action",  "amount_cents": 10990,  "currency": "BRL",  "payment_method": "credit_card",  "next_action": {    "type": "three_ds_challenge",    "three_ds": {      "authentication_id": "tds_xxx",      "acquirer": "cielo",      "sdk_payload": {}    }  },  "request_id": "req_xxx"}

Inclua o SDK hospedado

Carregue o script hospedado e crie um cliente com create() — ele instala o global Bunne. Aponte baseUrl para a sua API (ou o seu proxy).

<script src="https://bunne.com.br/sdk/v1/bunne-3ds.js"></script><script>  // Instala window.Bunne. Crie o cliente com a pk_ na próxima etapa.</script>

Inicie a Risk Session

Assim que a página carregar, crie a instância com uma pk_ seller-scoped. Bunne inicia a sessão imediatamente; antes de criar a cobrança, riskSession() devolve o id.

// Bunne.create inicia e coleta a Risk Session agora.// baseUrl pode ser omitido quando a SDK e a API estão na mesma origem.const client = Bunne.create({  baseUrl: "https://bunne.com.br",  apiKey: "pk_test_xxx",});// Antes de criar a cobrança, aguarde e pegue o id dessa mesma sessão.const riskSession = await client.riskSession();// Envie só riskSession.id ao SEU backend junto com o pedido de checkout.// Não envie a pk_, client_secret ou dados de browser para o seu servidor.

Envie a sessão na cobrança

Envie riskSession.id ao seu backend e inclua-o como risk_session_id em POST /charges usando a sk_.

curl -X POST https://bunne.com.br/api/v1/charges \  -H "Authorization: Bearer sk_test_xxx" \  -H "Idempotency-Key: order_123" \  -H "Content-Type: application/json" \  -d '{    "amount": 10990,    "payment_method": "credit_card",    "card_token": "tok_xxx",    "installments": 1,    "risk_session_id": "rks_xxx",    "buyer": { "...": "campos normais da cobrança" }  }'

Recomendado: desafio no browser, confirm no backend

No browser, rode initThreeDS(next_action): ele roda o desafio SEM chave e resolve { authentication_id, acquirer, result }. Seu backend faz o POST em /charges/{id}/confirm-3ds com a chave sk_.

// 1. No browser, rode só o desafio (agnóstico de adquirente). Sem rede ainda.//    Resolve { authentication_id, acquirer, result }.const resolved = await client.initThreeDS(charge.next_action);// 2. Envie `resolved` ({ authentication_id, result }) para o SEU backend, que//    confirma com a chave sk_ (exemplo abaixo). Nenhuma chave passa pelo browser.

Confirm no seu backend

POST /charges/{id}/confirm-3ds com { authentication_id, result } retorna a charge canônica. É idempotente — um confirm duplicado é replay e nunca autoriza duas vezes.

curl -X POST https://bunne.com.br/api/v1/charges/ch_xxx/confirm-3ds \  -H "Authorization: Bearer sk_test_xxx" \  -H "Idempotency-Key: order_123_confirm_3ds" \  -H "Content-Type: application/json" \  -d '{    "authentication_id": "tds_xxx",    "result": { "authenticated": true }  }'

O atalho de uma chamada

threeDSecure(charge) faz tudo: roda o desafio e chama o confirm-3ds. Como o confirm exige a chave sk_, use só onde a chave fica segura (backend ou render no servidor). A pk_ de Risk Session não autoriza esse confirm; no browser, prefira o fluxo em duas etapas acima.

// charge é o JSON retornado por POST /charges (criado no seu backend).// Pode vir "requires_action" com next_action, ou já final.const finalCharge = await client.threeDSecure(charge);// finalCharge.status agora é final: "paid" / "authorized" / "refused".// Sem 3DS? threeDSecure resolve a mesma charge, sem chamada de rede.

Nunca exponha sk_ no browser

Não coloque uma chave secreta sk_… no browser (o SDK avisa se detectar). A pk_ é pública, mas só inicia Risk Session: nunca autoriza cobrança, tokenização, leitura de dados ou confirm-3ds.