Um princípio acima de tudo: separe contexto de compra de resultado financeiro.A experiência do comprador (a
checkout.session) é descartável e só reflete o que aconteceu. Quem acompanha o estado da cobrança é o payment_intent. Quem registra cada tentativa concreta de mover dinheiro é a charge. Depois dela, cada movimento de dinheiro vira uma transaction no extrato — quanto entrou, o que foi descontado e quando liquida. Não trate a session — nem qualquer callback de frontend — como prova de pagamento.Os objetos e seus papéis
Use
payment_intent e os webhooks para decidir o resultado da cobrança. Use transaction depois, para explicar valores, taxas, parcelas e liquidação. Os objetos se complementam; um não substitui o outro.
A regra de identidade
Pense em três níveis:- a
checkout.sessionrepresenta uma experiência de compra; - o
payment_intentrepresenta a cobrança daquela compra ou daquele ciclo; - cada
chargerepresenta uma tentativa concreta de pagar essa cobrança.
O caminho completo
O fluxo padrão vai de uma intenção de compra até dinheiro liquidado, atravessando os objetos em ordem. Nem todo fluxo passa por todos — uma cobrança server-to-server pula a session; uma compra avulsa pula a invoice.
No fluxo recorrente, a renovação da
subscription cria uma invoice antes da etapa 2.
Compra avulsa não gera invoice por padrão. Em
mode=payment, o
payment_intent é o documento financeiro. A invoice só materializa se a
session foi criada com invoice_creation=true. Em assinatura, a invoice é
sempre o documento de cada ciclo.Síncrono vs. assíncrono
A grande bifurcação do ciclo é o método de pagamento. Cartão costuma resolver na hora; PIX e boleto ficam pendentes até a compensação chegar de forma assíncrona — e é aí que mora o erro mais comum de integração.Como os status se alinham
Cada objeto tem o próprio vocabulário de status. O quadro abaixo mostra como eles se movem juntos ao longo de uma compra. Os nomes são exatamente os do contrato público de cada objeto.status e payment_status da session são eixos independentes: complete
significa que o comprador terminou o formulário; paid significa que o
dinheiro entrou. Em PIX/boleto eles divergem temporariamente.Ciclo de vida do payment intent
Opayment_intent é o objeto que carrega o estado financeiro do início ao fim. Seus status:
PIX e boleto não pagos expiram sozinhos — e isso não cancela o intent.
Quando o código passa do
expires_at sem compensar, apenas aquela tentativa
termina: o intent volta a requires_payment_method com next_action: null e
payment.intent.updated é emitido. A expiração não emite
payment.intent.canceled nem qualquer evento de falha de intent, e um novo código pode
ser emitido no mesmo intent via regenerate.Se você precisa saber até quando o código era válido, persista
next_action.pix_display_qr_code.expires_at quando receber a confirmação ou o
payment.intent.updated — o payload da expiração não repete esse prazo.Charge: cada tentativa
Umpayment_intent pode produzir mais de uma charge, por exemplo quando
um PIX ou boleto é regenerado no mesmo intent. O intent aponta sempre a mais
recente em latest_charge. A charge é somente leitura e percorre
pending → processing → succeeded | failed | canceled. Use
paid/captured/amount_captured para saber se o dinheiro de fato entrou —
status: "succeeded" indica aprovação, não necessariamente captura.
Payment intent e transaction: venda e extrato
Depois do processamento, dois objetos respondem perguntas diferentes:
Uma venda parcelada em 10× gera 10 transactions — uma por parcela —, cada uma com o bruto daquela parcela, o detalhamento do que foi descontado e o líquido que cai na conta. Cada contexto público enxerga apenas os movimentos que lhe pertencem. Quando uma transaction chega a
paid, settled_at registra a liquidação automática na conta para saques cadastrada.
Invoices no ciclo recorrente
Para assinaturas, ainvoice é o documento de cada ciclo: ela congela os valores, cobra o método salvo via payment_intent e registra o resultado contábil.
O
billing_reason explica por que a invoice nasceu:
Como os webhooks sinalizam cada transição
Webhooks são a fonte confiável para reagir ao ciclo. Cada transição importante emite um evento cujodata.object carrega o objeto público completo no estado atual; eventos de update trazem data.previous_attributes com os valores anteriores dos campos alterados. A lista completa está em Webhook Events.
Mapa de evento por fluxo:
Idempotência e confirmação
Webhooks podem ser reenviados, e a mesma transição pode chegar mais de uma vez. Trate cada evento como idempotente:1
Persista o id do evento
Guarde o
id (evt_*) de cada webhook recebido. Se ele já foi processado,
ignore — é uma reentrega.2
Correlacione pelo ID público do recurso
Ao criar o recurso, grave no seu banco a relação entre o ID do pedido e o
ID público retornado pela Chargefy. No webhook, use esse mesmo ID para
recuperar o pedido correspondente.
3
Confirme o estado atual antes de rebaixar
data.object é o retrato completo quando o evento foi criado, mas pode
chegar atrasado. Antes de marcar uma cobrança como falha, consulte o
payment_intent ou a session atual e nunca rebaixe succeeded/paid.Próximos passos
Checkout Sessions
A tentativa de compra e seus dois eixos de estado.
Payment Intents
O livro-razão da cobrança ao longo do ciclo.
Charges
Cada tentativa concreta de mover dinheiro.
Conciliação
Conecte cobrança, matemática financeira e liquidação.
Webhook Events
A lista completa de eventos e o formato do payload.

