Skip to main content
As outras páginas cobrem o que você faz: catálogo, cobrança, entregável. Esta cobre o que acontece do lado do comprador — e é onde as integrações travam, porque é a parte com estados intermediários. O fio inteiro é este, e vale nos dois caminhos de venda:
Os dois polling são a parte que ninguém adivinha. Vamos por partes.

Caminho A: você tem a fatura (checkout())

Você já tem invoiceId desde o começo, então é o caminho curto:
Guarde o par invoiceId → seu usuário antes de mostrar a tela. É como você vai saber de quem era a compra quando o pagamento voltar. O link é a recomendação pra quem não quer montar tela. Se você quiser controlar a experiência mesmo usando link — ou testar o fluxo por HTTP — são três passos, e a fatura só existe no terceiro.
Três coisas que custam tempo se você não souber:
  • A fatura não existe antes do passo 3. A sessão não tem invoiceId — ele aparece na resposta do charge. Não procure antes.
  • O charge é na sessão, não na fatura. /sessions/{id}/charge, não /invoices/{id}/charge. A segunda existe e serve pro caminho A.
  • Sem email → 400 "E-mail is required."; sem taxId → 400 "A valid CPF or CNPJ is required." Os dois em português, os dois antes de qualquer cobrança acontecer.
Essas rotas não exigem Idempotency-Key. A regra “todo método que não é GET exige a chave” vale pra API autenticada. As rotas públicas de /pay/* — as que o navegador do comprador chama — aceitam sem.
O 422 do taxId chega no charge, não no checkout. checkout() aceita um cliente sem CPF/CNPJ e cria uma fatura finalizada e numerada. O 422 customer_tax_id_required só aparece no pay.charge, e aquela fatura não pode mais ser paga — ela fica open pra sempre no seu relatório.A validação vive no charge porque quem exige o documento é o provedor do método, e o método só é escolhido ali. Colete taxId antes de criar a fatura; se já criou uma sem, limpe:
O mesmo vale pra cliente sem nome e sem e-mail: um dos dois é obrigatório (422 customer_name_required).

Descobrir que pagou

Sempre passe timeoutMs. O default é 600000 — dez minutos — então numa fatura não paga, que é o caso normal, o seu handler trava em vez de responder. Em produção o webhook payment.confirmed é o caminho certo; em sandbox o registro responde 503, então é polling — detalhes em webhooks.
Não leia a fatura uma vez só. A confirmação chega pelo webhook do provedor, alguns instantes depois de você disparar o pagamento. Uma leitura única devolve open e você mostra “aguardando” pra alguém que já pagou. Medido: a fatura vira paid em menos de 1s às vezes, e em ~3s outras — a variação é a rede do provedor, não a sua.

Entregar — e o segundo polling

Aqui está o erro que dá pra cometer com a doc toda certa na mão: o grant não existe no mesmo instante que a fatura vira paid. A entrega roda depois do pagamento confirmar, então a primeira leitura devolve [].
Lista vazia é 200, nunca 404 — de propósito, justamente pra isso ser consultável num loop. O resto do entregável está em entregar o produto.

Testar tudo isso de ponta a ponta

Em sandbox você fecha o loop sozinho, sem chave de provedor:
Cheque o campo, nunca o formato do pixPayload — em produção o campo não vem e o payload é EMV. É o que separa um botão de teste de um bug em produção. Está detalhado em testar no sandbox.

A página inteira, junta

download vindo undefined não é erro: é produto sem entregável, ou entrega que ainda não rodou. Nos dois casos, mostrar “seu acesso chega por e-mail em instantes” é melhor do que uma tela vazia — e o e-mail realmente sai, desde que o endereço exista de verdade.

Se a venda voltar atrás

Estornar não é só devolver dinheiro: num produto digital o comprador já tem o arquivo, e o link continua no e-mail dele. Quem decide o que acontece com o acesso é o valor do estorno — total desliga, parcial não. Está em reembolso.