Skip to content

Assinaturas e Faturamento

Última atualização: 2026-04-08

Adapter centralizado: Toda comunicação HTTP com o Mercado Pago está em _shared/payment-gateway.ts. Para trocar de provedor, alterar apenas esse arquivo.

URLs dinâmicas: Links de pagamento e back URLs são gerados via _shared/environment.ts, derivando o domínio base (olp.digital ou staging.olp.digital) automaticamente a partir do SUPABASE_URL.

Canal de notificação: Lembretes e avisos de faturamento são enviados via WhatsApp (Wasender). Ref: docs/architecture/THIRD_PARTY_INTEGRATIONS.md


1. Planos

Tabela: planos

CampoTipoDescrição
nometextNome do plano
preco_mensalnumericPreço base mensal
preco_anualnumericPreço anual (se aplicável)
valor_por_aluno_extranumericValor por aluno contratado (multiplicado pelo total)
alunos_minimo / alunos_maximointFaixa de elegibilidade
trial_diasintDuração do trial em dias
is_trialboolSe é plano de teste
tipo_cobrancatextmensal ou anual
featuresjsonbFeatures habilitadas

Fórmula de Cálculo

Valor Mensal = Preço Base + (Alunos Contratados × Valor por Aluno Extra)

2. Assinaturas

Tabela: escola_assinaturas

Ciclo Contratual

  • data_inicio → início do contrato
  • meses_contratados → duração total (calculado a partir do range mes_iniciodata_fim_contrato)
  • data_fim_contrato → último dia do mês final do contrato
  • dia_vencimento → dia do mês para faturas (1-28, default 10)
  • renovacao_automatica → se renova ao expirar

Criação de Assinatura (CreateAssinaturaSchema)

A criação de assinatura usa validação Zod com os seguintes campos:

CampoTipoRegra
escola_idUUIDObrigatório
plano_idUUIDObrigatório
status_assinaturaenum'ativa' ou 'trial' (default: 'ativa')
alunos_contratadosintMínimo 1
dia_vencimentoint1-28 (default 10)
mes_iniciostringYYYY-MM, deve ser posterior ao mês atual
data_fim_contratostringYYYY-MM, >= mes_inicio
valor_personalizadonumberOpcional, override do cálculo automático
renovacao_automaticabooleanDefault true

Geração de faturas por range: Ao criar a assinatura, são geradas faturas skeleton (sem link_pagamento) para cada mês do range [mes_inicio, data_fim_contrato]. O cron faturamento-cron completa esses skeletons.

Status

StatusSignificado
trialPeríodo de teste (validado por trial_ate)
ativaAssinatura paga ativa
suspensaPagamento atrasado — acesso bloqueado
canceladaCancelamento solicitado
encerradaContrato finalizado

Gateway MercadoPago

CampoUso
gateway_customer_idID do cliente no MercadoPago
gateway_subscription_idID da assinatura (se recorrente)

3. Faturas

Tabela: escola_faturas

Numeração Sequencial

Trigger gerar_numero_fatura gera automaticamente: OLP-2026-0001, OLP-2026-0002, etc.

Status de Pagamento

StatusDescrição
pendenteGerada, aguardando pagamento
pagoPagamento confirmado
atrasadoVencimento ultrapassado
canceladoFatura cancelada

Integração MercadoPago

CampoDescrição
gateway_preference_idPreference ID do Checkout Pro
link_pagamentoURL init_point para pagamento (uso interno do painel)
gateway_payment_idID do pagamento confirmado
pix_qrcode / pix_copia_colaDados PIX
boleto_pdf_url / codigo_barrasDados do boleto
lembrete_d5_enviado_emTimestamp do envio do lembrete D-5 (evita reenvio)

Fluxo de Pagamento

1. Admin/cron gera link → Edge Function cria Preference no MercadoPago
2. preference_id salvo em gateway_preference_id
3. init_point salvo em link_pagamento (uso exclusivo do painel)
4. WhatsApp enviado SEM link — apenas informativo direcionando ao painel
5. Usuário acessa painel → "Pagamentos" → botão "Efetuar Pagamento" → MercadoPago
6. Webhook recebe notificação → valida pagamento → atualiza fatura

Nota: O pipeline anterior de URL curta (olp.digital/pagar/{token}) foi descontinuado em 2026-04-16. Mensagens WhatsApp não contêm links de pagamento — o link_pagamento é consumido apenas pelo CheckoutLinkDialog dentro do painel autenticado.


4. Geração Automática de Faturas

Job: gerar-faturas-mensal

  • Edge Function: faturamento-cron (action gerar_faturas)
  • Schedule: 0 6 28-31 * * (dias 28-31 de cada mês, 03:00 BRT / 06:00 UTC)
  • Lógica: Roda nos dias 28-31 mas só executa se amanhã.getDate() === 1 (último dia do mês)
  • Paradigma: O cron completa faturas existentes (skeleton → com link MP + WhatsApp + notificação). Se não existir skeleton, cria nova fatura (fallback).
  • Guard: Uma fatura é considerada completa quando link_pagamento IS NOT NULL. Skeletons (criados pelo create_assinatura) são elegíveis para processamento.
  • Regra: Ciclo anual encerra em dezembro — não gera faturas além desse mês
  • Alerta: Se existem assinaturas ativas mas nenhuma fatura foi processada, dispara alerta faturamento.alerta_faturas_sem_link via ntfy

Regra de Ouro: "Sempre Mês Seguinte"

A primeira fatura de novos planos ou alterações é gerada para o primeiro dia do mês subsequente, evitando cobranças imediatas.

Recálculo Automático

Ao alterar alunos_contratados ou dia_vencimento, faturas pendentes futuras são recalculadas automaticamente.


5. Lembrete D-5

Job: lembrete-d5-diario

  • Edge Function: faturamento-cron (action lembrete_d5)
  • Schedule: 0 9 * * * (diariamente, 06:00 BRT / 09:00 UTC)
  • Lógica: Busca faturas pendentes com vencimento em 5 dias que ainda não receberam lembrete (lembrete_d5_enviado_em IS NULL)
  • Canal: Mensagem enviada via WhatsApp (Wasender) para o contato da escola
  • Controle: Após envio bem-sucedido, marca lembrete_d5_enviado_em = now() para evitar reenvios

Comportamento

CenárioAção
Fatura pendente, vence em 5 dias, sem lembreteEnvia WhatsApp + marca timestamp
Fatura pendente, vence em 5 dias, já lembradaIgnora (idempotente)
Fatura paga ou canceladaIgnora

6. Trial

Verificação: subscription-helper.ts

typescript
const resultado = await verificarAssinaturaEscola(supabase, escolaId, 'sistema');
if (!resultado.valida) {
  // resultado.code: 'TRIAL_EXPIRADO' | 'SEM_ASSINATURA'
}

Comportamento

CenárioResultado
Assinatura ativa ou trial válidovalida: true
Trial expiradovalida: false, code: 'TRIAL_EXPIRADO'
Sem assinaturavalida: false, code: 'SEM_ASSINATURA'
Erro técnicovalida: true (fail-open)

Onde é Verificado

ContextoEdge Function
Login sistemasend-otp, verify-otp
Portal alunoportal-escola (lookup)

7. Ciclo Contratual Multi-Ano

Action: get_ciclo_contratual (admin-faturas)

Retorna dados completos do ciclo contratual de uma escola, com filtro por ano.

Parâmetros: escola_id (obrigatório), ano (opcional — default: ano corrente)

Retorno:

CampoDescrição
faturasLista de faturas do ano filtrado
metricasCálculos do ano: total_faturas, pagas, pendentes, valor_total, valor_pago, valor_pendente, taxa_adimplencia
metricas_geraisCálculos de todo o contrato (sem filtro de ano)
assinaturaDados da assinatura ativa
planoDados do plano
anos_disponiveisLista de anos com faturas

Nota: No frontend, metricas_gerais é independente do filtro de ano — ao trocar de ano, apenas a seção superior recarrega.


8. Administração

Edge Functions

FunçãoAções Principais
admin-assinaturasCRUD de assinaturas, recálculo de faturas
admin-faturasListagem, get_ciclo_contratual, geração manual de links, marcação de pago
admin-planosCRUD de planos
admin-escola-dadoscreate_assinatura (validação Zod CreateAssinaturaSchema) — criação na tela de detalhes
escola-pagamentosVisão da escola: faturas, links de pagamento (usado por gestão/diretor)
mercadopago-preferenceGerar link de pagamento (Checkout Pro)
mercadopago-webhookReceber notificações de pagamento
faturamento-cronGeração mensal de faturas + lembrete D-5

Hooks Frontend

HookUso
useAdminAssinaturasGestão de assinaturas (admin)
useAdminFaturasGestão de faturas (admin)
useAdminPlanosGestão de planos (admin)
useMercadoPagoGeração de links e polling (admin)
useEscolaPagamentosFaturas e pagamentos (visão escola — gestão/diretor)

9. Faturas Migradas

Faturas importadas de sistemas legados são marcadas com migrada = true. Não possuem gateway_preference_id e servem apenas como histórico financeiro.


10. Auto-Ativação de Escola

Ao ativar uma assinatura paga (status_assinatura = 'ativa'), se a escola estiver com status = 'em_analise', seu status é automaticamente alterado para ativa.

Regra conservadora: Só ativa escolas em em_analise. Escolas já ativa, suspensa ou encerrada não são alteradas.

Pontos de ativação (3 caminhos):

Edge FunctionActionContexto
admin-escola-dadoscreate_assinaturaTela de detalhes da escola
admin-assinaturascreateTela de assinaturas
admin-assinaturasconvert_trial_to_paidConverter trial para pago

11. Referências