Expiração
Duas coisas acontecem com uma cobrança depois que ela sai da sua tela: ela expira, ou ela é estornada. As duas têm armadilha.
O prazo que você pede é um pedido
pix.expires_in_seconds na criação é o prazo que você gostaria. Quem decide é o processador de
pagamentos, e ele pode encurtar, estender ou simplesmente ignorar o seu pedido. Alguns processadores
expiram tudo à meia-noite, por exemplo.
Por isso existe uma regra só:
Use
pix.expires_atda resposta. Nunca calcule a expiração somando o seu prazo ao horário da criação.
O contador na sua tela de checkout sai de expires_at. O seu job de limpeza também. A faixa aceita
para o pedido vai de 5 minutos a 30 dias, e o padrão é uma hora (Limites).
A expiração não é instantânea
A cobrança vira expired pouco depois de expires_at, não exatamente nele. A folga existe de
propósito, para um pagamento que chegou no último segundo não ser perdido por corrida de relógio.
Então: expires_at no passado e status ainda pending é normal por alguns minutos. Não trate
isso como erro nem como expirada; espere o status mudar ou o evento transaction.expired chegar.
Pagamento tardio existe
Uma cobrança expired pode virar paid. Acontece quando o processador aceitou um pagamento que
entrou na janela dele, mais larga que a nossa.
O seu sistema precisa aguentar isso. Concretamente:
- Não apague o pedido quando o
transaction.expiredchegar. Marque como não pago e siga. - Continue aceitando
transaction.paiddaquela cobrança depois disso, e libere o produto. - Se o seu fluxo não puder entregar depois do prazo, trate como um caso de estorno, não ignorando o evento.
Os eventos podem até chegar fora de ordem no seu endpoint. A verdade final está sempre na consulta pelo id.
Expirada some das listagens
Cobrança expired não aparece na listagem por padrão, nem entra nos totais. Foi uma decisão de
produto: a lista de cobranças é uma lista de coisas que importam, e expirada é ruído em volume.
Pedir status=expired explicitamente não é permitido e responde 400 validation_failed. A
contagem de expiradas nos totais sai sempre zero.
Isso vale só para as listagens. A consulta pelo id, a timeline e o evento transaction.expired
continuam mostrando tudo normalmente. Detalhes em Consultar.
Estorno
Estorno é sempre do valor total; não existe estorno parcial. Ele só é possível em cobrança
paid, e leva o status para refunded.
O pedido de estorno é feito pelo painel, pela pessoa dona da conta. Não existe rota de estorno por chave de API, e isso é proposital: estorno é operação de dinheiro saindo, e exige a confirmação extra que só a sessão do painel tem.
Pela API você observa o estorno, e isso é tudo o que a sua integração precisa:
- O evento
transaction.refundedchega no seu webhook. - A consulta pelo id passa a trazer
statusemrefundede a data emrefunded_at. - A timeline registra o lançamento correspondente.
O valor sai do saldo no mesmo instante, e o saldo disponível pode ficar negativo por causa disso.
chargeback segue o mesmo caminho, com o evento transaction.chargeback, com a diferença de ser
iniciado pelo cliente e não por você.
Próximo passo
Referência: Consulta uma cobrança e Timeline de uma cobrança.