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 é o livro-razão da cobrança é o payment_intent. Quem registra cada tentativa concreta de mover dinheiro é a charge. Não trate a session — nem qualquer callback de frontend — como prova de pagamento.Os objetos e seus papéis
| Objeto | Responsabilidade | Quem cria | Fonte da verdade para |
|---|---|---|---|
checkout.session | A tentativa de compra: coleta o comprador, mostra os métodos, confirma a escolha. Descartável e com expiração. | Seu backend (ou um clique em payment link). | Contexto e progresso da compra (status, payment_status). |
payment_intent | O ciclo de vida da cobrança: valor, moeda, método, status, próxima ação e desfecho. | Seu backend, a session ou uma invoice. | Estado financeiro da cobrança. |
charge | Cada tentativa concreta de mover dinheiro. Somente leitura. | O sistema, ao confirmar um intent. | O que foi cobrado e o que entrou. |
invoice | Documento de cobrança de assinatura (e de cobrança avulsa quando pedida). | O ciclo de assinatura ou a API. | Cobrança recorrente e contábil. |
payment_method | Instrumento salvo ou tokenizado. | Tokenização no checkout ou setup intent. | Como cobrar de novo no futuro. |
checkout.session, payment_intent, charge, invoice e webhooks.
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.| Origem | Como a cobrança nasce | Gera invoice? |
|---|---|---|
| Payment link | Cada clique materializa uma checkout.session nova. | Só se a session pedir (invoice_creation=true). |
| Checkout session (backend) | Seu servidor cria a session sob demanda; a confirmação dispara o payment_intent. | Recorrente: sempre. Avulso: só com invoice_creation=true. |
| Payment intent direto | Server-to-server, sem session — você já sabe valor, customer e método. | Não (a menos que o intent venha de uma invoice). |
| Assinatura | Cada ciclo materializa uma invoice, que cobra o método salvo via payment_intent. | Sempre. |
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.| Método | Confirmação | Estado intermediário | Quando o dinheiro entra |
|---|---|---|---|
credit_card | Síncrona | processing | Normalmente na própria resposta do confirm. |
pix | Assíncrona | pending + next_action (QR code) | Quando o comprador paga no banco/app. |
boleto | Assíncrona | pending + next_action (linha digitável) | Na compensação, dias depois. |
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.| Etapa | checkout.session.status | checkout.session.payment_status | payment_intent.status | charge.status |
|---|---|---|---|---|
| Sessão criada | open | unpaid | requires_payment_method / requires_confirmation | — |
| Comprador confirma (cartão) | complete | unpaid → paid | processing → succeeded | processing → succeeded |
| Comprador confirma (PIX/boleto) | complete | unpaid | pending | pending |
| Pagamento assíncrono liquida | complete | paid | succeeded | succeeded |
| Recusa de cartão | complete | unpaid | requires_payment_method | failed |
| Sessão sem confirmação por 24h | expired | unpaid | canceled | — |
| Total zero (ex.: trial ou preço recorrente gratuito) | complete | no_payment_required | — | — |
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:
| Status | Significado |
|---|---|
requires_payment_method | Falta definir o método, ou o anterior falhou. |
requires_confirmation | Método definido; pronto para confirmar. |
pending | Aguardando ação do comprador ou compensação assíncrona (PIX/boleto). |
processing | Pagamento em processamento. |
requires_capture | Cartão autorizado; falta capturar (somente captura manual). |
succeeded | Pagamento concluído. |
failed | Pagamento falhou. |
canceled | Cobrança cancelada. |
Boleto não pago expira sozinho. Um intent de boleto que passa de
expires_at sem compensar vira canceled automaticamente, com cancellation_reason: "abandoned", e emite payment.intent.canceled.Charge: cada tentativa
Umpayment_intent pode produzir mais de uma charge (uma recusa seguida de nova tentativa). 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.
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.
| Status | Significado |
|---|---|
open | Criada, pagável e com valores congelados. |
paid | Paga (ou marcada como paga fora do fluxo automático). |
void | Anulada. |
uncollectible | Baixa contábil manual: você marca a fatura como incobrável. Tentativas automáticas esgotadas deixam a fatura open e a assinatura unpaid — não vêm parar aqui sozinhas. |
billing_reason explica por que a invoice nasceu:
| Valor | Quando aparece |
|---|---|
subscription_create | Criação da assinatura. Trial gratuito, desconto total ou preço recorrente gratuito podem vir com total 0; caso contrário, vem com o valor da primeira cobrança. |
subscription_cycle | Renovação periódica (inclui a primeira cobrança real após um trial). |
subscription_update | Mudança no meio do ciclo. |
manual | Cobrança avulsa via API ou admin. |
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:
| Fluxo | Evento de referência para liberar produto |
|---|---|
| Checkout hospedado concluído (cartão) | checkout.session.completed |
| Método assíncrono pago depois | checkout.session.async.payment.succeeded |
| Método assíncrono falhou ou expirou | checkout.session.async.payment.failed |
| Cobrança direta paga | payment.intent.succeeded |
| Cobrança direta falhou | payment.intent.failed |
| Cobrança direta cancelada / boleto expirado | payment.intent.canceled |
| Tentativa de cobrança aprovada | charge.succeeded |
| Tentativa de cobrança recusada | charge.failed |
| Invoice de assinatura paga | invoice.paid |
| Invoice com tentativa falha | invoice.payment.failed |
Idempotência e reconciliação
Webhooks podem ser reenviados, e a mesma transição pode chegar mais de uma vez. Trate cada evento como idempotente:Persista o id do evento
Guarde o
id (evt_*) de cada webhook recebido. Se ele já foi processado, ignore — é uma reentrega.Correlacione pelo seu próprio identificador
Use
metadata (ex.: metadata.order_id) para ligar o evento ao seu pedido, sem depender de IDs internos da Chargefy. O metadata é ecoado em todos os webhooks do recurso.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.
Webhook Events
A lista completa de eventos e o formato do payload.

