Bunne Docs

3-D Secure

Trate cobranças requires_action: carregue o SDK, rode o desafio e confirme.

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.

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. Sem API key no browser: só emitimos chaves secretas  // (sk_), que nunca podem ser expostas. O browser só roda o desafio; o  // confirm-3ds é uma chamada do seu backend.  const client = Bunne.create({ baseUrl: "https://bunne.com.br" });</script>

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). Hoje, sem chave publishable, 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). Hoje emitimos apenas chaves secretas, então o browser só roda o desafio e o confirm-3ds é chamado pelo seu backend com sk_.