4.3 KiB
Prompt 07 — Financeiro: assinaturas, período de uso e AbacatePay
Contexto
Prompts 01–06 concluídos: CRUD completo do escritório funcionando. Leia 10-tracker-progresso.md antes de começar.
Este prompt implementa o menu Cliente → Financeiro (pagar mensalidade) e a automação de bloqueio/liberação de acesso conforme pagamento, usando AbacatePay para gerar cobrança PIX.
Objetivo
Criar/consultar periodos_uso do mês corrente, gerar cobrança PIX via AbacatePay, processar o webhook de confirmação, e rodar o job diário que bloqueia empresas inadimplentes.
Especificação
1. Cliente AbacatePay
Consultar a documentação oficial da AbacatePay (https://docs.abacatepay.com) para confirmar o formato exato do endpoint de criação de cobrança PIX e do payload do webhook — não assumir o formato sem checar, a API pode ter mudado. Implementar AbacatePayClient:
criarCobranca(valorCentavos, descricao, identificadorExterno)→ retorna{chargeId, qrCodePayload, qrCodeBase64}- Validação de assinatura do webhook, se a AbacatePay oferecer esse mecanismo (verificar na doc).
2. Endpoints do escritório
GET /api/financeiro/periodo-atual
→ busca (ou cria, se não existir) o periodos_uso do mes_referencia = mês corrente;
retorna status, valor da assinatura vigente, se já tem pagamento pendente/gerado
POST /api/financeiro/pagamentos
→ se já existe um pagamento 'pendente' pro período atual, retorna ele (não duplica
cobrança); senão, chama AbacatePayClient.criarCobranca com o valor da assinatura,
grava em `pagamentos`, retorna qr_code_base64 + qr_code_payload pro frontend renderizar
GET /api/financeiro/pagamentos/{id}
→ status atual (útil pro frontend fazer polling enquanto o QR Code está na tela)
3. Webhook
POST /api/master/webhooks/abacatepay
→ SEM autenticação de master/funcionário (é a AbacatePay chamando) — validar por
assinatura/segredo do webhook, conforme a documentação da AbacatePay
→ identifica o pagamento pelo abacatepay_charge_id, marca status='pago', pago_em=now()
→ marca periodos_uso.status='pago', data_liberacao=now()
→ marca empresas.status='ativa' (caso estivesse 'bloqueada')
→ idempotente: se o webhook chegar duplicado (mesma cobrança já processada), não fazer
nada de errado, só retornar 200 de novo
4. Job de bloqueio diário
BloqueioJob, rodando uma vez por dia (implementar com um ScheduledExecutorService simples dentro da própria aplicação Java — não precisa de cron externo nesta fase):
- Para cada
empresacom status='ativa': busca (ou cria) operiodos_usodo mês corrente. - Se
hoje > dia_vencimentoda assinatura Eperiodos_uso.status != 'pago': marcaperiodos_uso.status='bloqueado',data_bloqueio=now(),empresas.status='bloqueada'. - Logar quantas empresas foram bloqueadas na execução.
5. Bloqueio de acesso
No middleware exigirFuncionario (Prompt 04), adicionar a checagem: se empresas.status='bloqueada', retornar 403 com corpo {"erro": "pendencia_financeira", "mensagem": "..."} em vez de deixar passar — mas sempre permitir o próprio GET /api/financeiro/periodo-atual e POST /api/financeiro/pagamentos mesmo bloqueado, senão o escritório não consegue nem pagar pra desbloquear.
Critérios de aceite
POST /api/financeiro/pagamentosgera de fato uma cobrança na AbacatePay (testar em ambiente sandbox, se a AbacatePay oferecer) e retorna QR Code válido.- Chamar
POST /api/financeiro/pagamentosduas vezes seguidas não cria duas cobranças, retorna a mesma pendente. - Simular o webhook (chamada manual com um payload de teste) marca o período como pago e desbloqueia a empresa.
- Rodar o webhook duas vezes com o mesmo
charge_idnão causa erro nem duplica nada. BloqueioJobrodado manualmente (chamar o método direto, sem esperar o schedule) bloqueia corretamente uma empresa de teste com vencimento no passado e sem pagamento.- Empresa bloqueada consegue logar e acessar só
/api/financeiro/**; qualquer outro endpoint retorna 403 com o erropendencia_financeira.
Ao concluir
Atualizar 10-tracker-progresso.md: Prompt 07 concluído, data, resumo. Confirmar Prompts 01–06 seguem marcados.