O que é igual às outras adquirentes#
A integração é a mesma do guia de cartão: POST /elements/checkout na moeda do comprador, Throne Elements na página, webhook transaction.paid. Campos, respostas, erros, moedas e formato do webhook não mudam. Esta página cobre só o que é próprio da ShadowBank.
Para usar a ShadowBank, mande "acquirer": "shadowbank" na criação do checkout. Se a ShadowBank for a padrão da sua conta, o campo é opcional. A ShadowBank é habilitada pelo suporte, conta a conta. Veja Escolher a adquirente.
Nome e e-mail do comprador são obrigatórios na criação. Mande customer.name (até 120 caracteres) e customer.email (e-mail válido, até 180). Sem eles, ou fora desses tamanhos, a chamada volta 422 customer_required_for_shadowbank e nada é criado. O elemento nunca pergunta nome nem e-mail ao comprador: colete os dois na sua página antes de chamar a API. A regra vale sempre que a venda fica com a ShadowBank, inclusive sem o campo acquirer, quando ela é a padrão da conta. Nas outras adquirentes, customer continua opcional. O exemplo abaixo já manda os dois campos.
curl -X POST https://app.thronecorporate.com/api/v1/elements/checkout \
--max-time 60 \
-H "Authorization: Bearer $THRONE_PK:$THRONE_SK" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "amount_cents": 4990, "currency": "eur", "acquirer": "shadowbank", "customer": { "name": "Lena Fischer", "email": "lena@example.de" } }'A resposta 201 traz "acquirer": "shadowbank", e a venda fica na ShadowBank até o fim: pagamento, estorno e contestação.
A moeda funciona exatamente como nas outras adquirentes. Você manda o valor na moeda do comprador (por exemplo, "currency": "eur") e ele paga nessa moeda. A cotação que credita o seu saldo é fixada na criação, e o webhook traz currency, native_amount_cents e fx_rate no mesmo formato. Detalhes em Moedas e limites.
No checkout comum (Throne Elements), a ShadowBank não faz recorrência nem upsell em um clique e não atende o Checkout Brasil. Com "acquirer": "shadowbank", o campo recurrence volta 422 recurrence_not_supported, o one_click_from volta one_click: false, sem erro, e o POST /elements/checkout/br volta 422 acquirer_not_enabled.
Na integração direta, que é outra modalidade e precisa de liberação à parte, as duas coisas funcionam: cobrança recorrente e upsell em um clique, e o upsell também pode ser recorrente. O que não existe lá é o parcelamento — a cobrança é sempre à vista.
Resumo#
| Item | Na ShadowBank |
|---|---|
| Verificação do banco | Não tem: o banco aprova ou recusa na hora, e o resultado aparece dentro do próprio elemento |
| Navegador de app (Instagram, Facebook, Google, TikTok) | Tudo dentro do elemento; os eventos chegam normalmente |
| Eventos na sua página | Em todos os caminhos: computador, celular e navegador de app |
| Valor máximo por venda | Varia com a moeda e com a cotação do dia. Veja Limites |
| Tentativas por checkout | 5, contadas no servidor. Recarregar a página não devolve tentativas |
| Validade do checkout | 24 horas |
| Fechamento por recusa | Na 5ª recusa, ou 10 minutos depois de uma recusa sem nova tentativa (failed) |
| Parcelas | À vista |
| Recorrência | Não |
| Upsell em um clique | Não |
| Dados do comprador | customer.name e customer.email obrigatórios na criação. O elemento não os pergunta |
| Idioma do checkout | Pelo país do comprador; Brasil e Portugal veem em inglês. ?nl= no checkout_url mostra outro idioma, para teste |
| Idioma da mensagem de recusa | O mesmo do checkout, pelo país do comprador (ou pelo ?nl=), na tela e em e.detail.message. Não é preciso enviar Accept-Language na criação |
Códigos em throne-elements:error |
declined, attempts_exhausted, token, network, timeout, expired, ended |
Limites#
| Limite | Na ShadowBank |
|---|---|
| Mínimo por venda | Varia com a moeda e com a cotação do dia, ou o mínimo próprio da ShadowBank, se houver |
| Máximo por venda | Depende da adquirente e varia com a moeda e com a cotação do dia. Consulte GET /elements/acquirers?currency= (campo limits.max_amount_cents) |
| Acima do máximo | 422 invalid_amount com max_amount_cents na sua moeda e alternatives. Nada é criado |
| Capacidade de criação | Em pico ou instabilidade, 503 temporarily_unavailable com Retry-After |
Como o comprador paga#
Em qualquer aparelho#
- Preenche número, validade, CVV e nome impresso no cartão.
- Clica em pagar.
- O resultado sai em poucos segundos, dentro do próprio elemento. Não há etapa do banco nem janela nova.
- Aprovado: o elemento mostra a confirmação e dispara
throne-elements:paid. - Recusado: o elemento mostra o motivo, quantas tentativas restam e um botão para tentar de novo. A venda continua
pending.
Em navegador de app#
Instagram, Facebook, o app do Google, TikTok e outros apps abrem links num navegador próprio, que não abre uma segunda janela. Na ShadowBank isso não faz diferença: o pagamento acontece todo dentro do elemento, o comprador termina na sua página e throne-elements:paid chega como no computador. Mesmo assim, libere o pedido pelo webhook ou pelo GET, nunca só pelo evento da página.
O que o comprador vê#
- O endereço. Nada muda: o comprador fica na sua página do começo ao fim.
- Nenhuma confirmação do banco. Não há código por mensagem, aplicativo do banco nem pergunta de segurança: o cartão é autorizado na hora. Um aviso ao lado do botão de pagar é opcional.
- A fatura. Na fatura do cartão, a compra aparece sem o nome da sua loja.
Textos sugeridos, se quiser avisar:
Secure payment. You will see the result right here, on this page.Sichere Zahlung. Das Ergebnis erscheint direkt hier auf dieser Seite.Pago seguro. Verás el resultado aquí mismo, en esta página.Requisitos da página#
- Não fixe a altura do container e não esconda o
overflow: as telas de resultado e de análise mudam a altura do elemento. - Sem
sandboxem volta do elemento. Se não der para tirar, use a lista mínima de Throne Elements. - O clique no botão de pagar precisa ser do comprador. Não dispare o pagamento por código.
- Se o seu site usa Content-Security-Policy, libere
https://app.thronecorporate.comemscript-srce emimg-src, e emframe-srclibere esse domínio e o domínio docheckout_url, como em Throne Elements.
Pagamento em análise#
Às vezes o pagamento entra em análise antifraude. O comprador vê "Pagamento em análise", sem formulário, e a venda fica pending até a decisão:
- aprovado, vira
paide o webhooktransaction.paidé enviado; - reprovado, conta como uma recusa: a tentativa é gasta, o elemento mostra o motivo e, com tentativas sobrando, o comprador pode tentar de novo. A venda continua
pending; na 5ª recusa o checkout fecha comofailed.
Se a decisão demorar, o elemento avisa que a confirmação pode levar alguns minutos e envia timeout em throne-elements:error. Isso não é recusa. Não libere o pedido enquanto não chegar transaction.paid, e não cancele o pedido só pelo timeout.
Erros e o que fazer#
Na criação do checkout:
| Situação | Resposta | O que fazer |
|---|---|---|
| Valor acima do máximo da ShadowBank | 422 invalid_amount, com max_amount_cents e alternatives |
Não repita. Crie com o acquirer de uma das alternatives ou divida o pedido |
| Valor abaixo do mínimo | 422 invalid_amount, com min_amount_cents |
Aumente o valor |
Sem customer.name ou customer.email, e-mail inválido ou fora dos tamanhos (nome até 120 caracteres, e-mail até 180) |
422 customer_required_for_shadowbank |
Mande os dois campos na criação, dentro dos tamanhos. Nada é criado |
| ShadowBank não habilitada na conta | 422 acquirer_not_enabled, com as habilitadas no message |
Use uma das habilitadas, ou fale com o suporte para habilitar a ShadowBank |
| ShadowBank em pausa, instável ou sem capacidade | 503 temporarily_unavailable, com Retry-After e "retryable": true |
Espere o Retry-After e repita com uma Idempotency-Key nova, ou use outra adquirente habilitada |
| Criação recusada | 502 checkout_failed, com "retryable": false |
Não repita igual. Se continuar, fale com o suporte |
| Falha passageira de conexão | 502 checkout_failed, com "retryable": true |
Repita com uma Idempotency-Key nova |
| Sem resposta (timeout do seu lado) | Nenhuma | Repita com a mesma Idempotency-Key (regra) |
Na página, em throne-elements:error (e.detail.code):
code |
O que aconteceu | O que fazer |
|---|---|---|
declined |
O banco não autorizou | O comprador pode tentar de novo ou usar outro cartão. Vale sugerir que ele libere compras online e internacionais no app do banco |
attempts_exhausted |
O checkout fechou: acabaram as 5 tentativas | Ofereça outro meio de pagamento ou um checkout novo |
token |
Os dados do cartão não passaram na validação, antes da cobrança | O comprador confere os dados e tenta de novo. Nenhuma tentativa é gasta |
network |
Falha de conexão, ou o formulário não carregou | O comprador pode tentar de novo em alguns instantes |
timeout |
A análise antifraude está demorando e o pagamento segue em análise | Espere o webhook ou consulte GET /transactions/{id}. Não é recusa; não crie outro checkout por isso |
expired, ended |
O checkout passou da validade ou foi encerrado | Crie um checkout novo |
A tabela completa dos códigos, nas seis adquirentes, está em Throne Elements. Duas tentativas ao mesmo tempo no mesmo checkout (duas abas ou duplo clique) não geram cobrança dupla: a segunda recebe "Já existe um pagamento em andamento. Aguarde." e o elemento acompanha o pagamento que já está em andamento.
Tratar os eventos na página#
Na ShadowBank, um código pede tratamento próprio: timeout é espera, não recusa.
window.addEventListener('throne-elements:paid', (e) => {
// e.detail.transaction_id: mostre o "obrigado". A liberação do pedido vem pelo webhook.
});
window.addEventListener('throne-elements:error', (e) => {
switch (e.detail.code) {
case 'timeout':
// o pagamento segue em análise: mostre "estamos confirmando" e espere o webhook
break;
case 'attempts_exhausted':
case 'expired':
case 'ended':
// este checkout acabou: ofereça outro meio ou crie um checkout novo
break;
default:
// declined, token, network: o próprio elemento já oferece a nova tentativa
break;
}
});Quando chega o webhook#
O resultado sai na hora e o webhook transaction.paid costuma sair logo depois da aprovação. Numa venda que entra em análise, ele sai quando a análise aprova. Se a confirmação da ShadowBank se perder no caminho, a conferência automática do Throne a recupera alguns minutos depois. Não cancele o pedido por falta de webhook antes da validade de 24 horas; na dúvida, consulte GET /transactions/{id}.
O webhook é o mesmo das outras adquirentes: currency e native_amount_cents com a venda na moeda do comprador, amount_cents com o valor creditado no seu saldo e fx_rate com a cotação fixada na criação. Veja Webhooks.
Depois da venda#
- Estorno: não é feito pela API; fale com o suporte. O estorno total deixa a venda
refunded. O parcial mantémpaid, com os valores reduzidos. - Contestação: o Throne debita da sua carteira o valor líquido da venda, e ela vira
chargeback. Se a contestação for revertida a seu favor, ela volta parapaid. - Nenhum desses eventos gera webhook. Acompanhe pelo
GET /transactions/{id}.
Testar#
Use um cartão de verdade com valor baixo. Para ver como o comprador de outro país enxerga o checkout, abra o checkout_url da resposta com ?nl= e o código do idioma (por exemplo, ?nl=de para alemão): o formulário, os números e as telas do elemento saem nesse idioma. Teste também dentro do navegador do Instagram ou do Facebook, no celular: na ShadowBank o resultado aparece na sua página e o evento chega. Os erros que dá para provocar sem custo e o roteiro completo estão em Como testar.
Checklist#
- Checkout criado no servidor, com
"acquirer": "shadowbank"(ou a ShadowBank como padrão da conta),Idempotency-Keynova por venda e timeout de 60 segundos. -
customer.nameecustomer.email(e-mail válido) enviados na criação, coletados na sua página antes de chamar a API. - Moeda do comprador em
currency, sem ajustar o valor no seu site. - Máximo da ShadowBank conferido na moeda do seu público, com plano para
422 invalid_amount(outra adquirente ou pedido dividido). - Container sem altura fixa e sem
sandboxem volta do elemento (ou com a lista mínima). - Tratamento de
e.detail.codeemthrone-elements:error, comtimeouttratado como espera. - Pedido liberado só com
transaction.paid(assinatura validada) ou com oGET. - Novo checkout para cada nova tentativa de compra depois de
failedouexpired.