Invoices
Invoices (visão geral)
Invoice é a unidade atômica de cobrança da Pagnovo: toda cobrança que existe — manual, vinda de checkout público ou gerada por subscription recorrente — eventualmente é uma Invoice. Ela representa "essa pessoa deve esse valor até essa data".
Cada Invoice tem uma URL pública (hostedUrl) onde o cliente final acessa, escolhe método de pagamento (PIX) e paga sem precisar de login.
3 origens possíveis:
POST /v2/invoices ────────────────────────┐
(cobrança avulsa, você cria diretamente) │
│
PaymentLink + checkout do cliente ────────┼──→ Invoice ──→ cliente paga ──→ status PAID
(URL pública pay.pagnovo.com/<slug>) │
│
Subscription + cron de billing ───────────┘
(gerada automaticamente a cada ciclo)
Estados da Invoice
| Status | O que significa | Editável? | Cancelável? |
|---|---|---|---|
DRAFT | Rascunho. Cliente ainda não vê. (Pouco usado — criação padrão já vai pra PENDING.) | ✓ | ✓ |
PENDING | Aguardando pagamento. hostedUrl ativa. | ✓ (sem attempt APPROVED) | ✓ |
PAID | Pago. paidAt preenchido, amountPaid > 0. | — | — (use /mark-refunded) |
OVERDUE | Venceu sem ser pago. Multa/juros podem aplicar. hostedUrl ainda funciona até expiresAt. | — | ✓ |
REFUNDED | Marcada como estornada (parcial ou total). | — | — |
CANCELED | Cancelada manualmente. hostedUrl para de aceitar pagamento. | — | — |
EXPIRED | Passou de expiresAt. Cliente final não consegue mais pagar. | — | — |
Importante: a Invoice pode ser editada só em
DRAFTouPENDING, e somente se não existir umInvoicePaymentAttemptcom statusAPPROVED. Se existir attemptPENDING, dá pra editar campos não-monetários, mas nãoamountnemdueDate(cancele os attempts primeiro).
Items
Items são opcionais — você pode criar uma Invoice só com amount e description. Mas se você enviar items[], a soma de totalPrice precisa bater exatamente com amount (ambos em centavos), senão a API rejeita com 400.
{
"description": "Plano Pro Mensal - Outubro/2026",
"amount": 4900,
"dueDate": "2026-10-15T00:00:00.000Z",
"items": [
{ "description": "Assinatura mensal", "quantity": 1, "unitPrice": 4900, "totalPrice": 4900 }
]
}
E também: totalPrice == quantity * unitPrice em cada item (validado no servidor). Se você mandar item com quantity 2, unitPrice 1000 e totalPrice 1500, a API rejeita.
Items aparecem renderizados no hostedUrl pra o cliente entender o que está pagando.
Multas e juros
Igual a Subscription: lateFeeType define o tipo, lateFeeValue o valor (polimórfico — centavos se FIXED, basis points se PERCENTAGE), interestRatePerMonth os juros mensais proporcionais.
Quando a Invoice entra em OVERDUE, esses campos definem quanto será somado ao amount original em cada tentativa de cobrança. Cada InvoicePaymentAttempt armazena:
amount— valor cobrado nessa tentativa (em centavos)lateFeeApplied— quanto de multa foi somado nessa tentativainterestApplied— quanto de juros foi somado nessa tentativa
Veja todos os attempts no detalhe da Invoice (GET /v2/invoices/:id).
hostedUrl
Toda Invoice tem uma URL pública no formato:
https://<INVOICE_CHECKOUT_BASE_URL>/i/<invoiceId>
O cliente final acessa essa URL sem precisar de login, vê os items da fatura, escolhe método de pagamento e paga. A hostedUrl é gerada automaticamente no momento da criação e fica disponível em todos os retornos da API.
Quando o cliente paga via PIX no checkout, a Pagnovo cria um InvoicePaymentAttempt interno, gera o BR Code, espera o callback do Bacen e, ao confirmar, marca a Invoice como PAID. Você recebe um webhook cashin.paid (detalhes) com o transaction_id correspondente.
Notificações
Cada Invoice tem um notificationConfig opcional que controla quando o sistema envia notificações automáticas (criação, lembrete, vencido, pago, cancelado). O canal disponível hoje é EMAIL (SMS e WhatsApp estão no roadmap). Se omitido, o default é enviar e-mail nos momentos chave (criação + lembrete + pago).
Pra reenviar manualmente uma notificação (cliente diz "não recebi"), use POST /v2/invoices/:id/notifications/resend:
{
"type": "INVOICE_REMINDER",
"channel": "EMAIL"
}
Tipos válidos: INVOICE_CREATED, INVOICE_REMINDER, INVOICE_OVERDUE, INVOICE_PAID, INVOICE_CANCELED. Canal: EMAIL (SMS e WhatsApp no roadmap).
Reenvio só funciona em Invoices com Customer associado (Invoice "pública" sem customer não tem destinatário). E só se o Customer tiver o e-mail preenchido (canal EMAIL; SMS e WhatsApp no roadmap).
Estorno parcial
Pra marcar uma Invoice paga como estornada (total ou parcial), use POST /v2/invoices/:id/mark-refunded passando refundedAmount em centavos.
Regras:
- Invoice precisa estar em
PAID. refundedAmountdeve ser> 0e<= amountPaid.- A invoice passa pra status
REFUNDED, preencherefundedAmounterefundedAt.
⚠️ Esse endpoint apenas atualiza o status da Invoice no sistema. O estorno PIX em si é responsabilidade do módulo de Refund (transação separada). Use após confirmar com seu time que o estorno bancário foi de fato executado.
Ex: invoice paga de R$ 100,00 e você quer estornar R$ 30,00: refundedAmount: 3000. Status vira REFUNDED, mas amountPaid permanece 10000 (o que o cliente pagou originalmente).