# 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): 1. Para cada `empresa` com status='ativa': busca (ou cria) o `periodos_uso` do mês corrente. 2. Se `hoje > dia_vencimento` da assinatura E `periodos_uso.status != 'pago'`: marca `periodos_uso.status='bloqueado'`, `data_bloqueio=now()`, `empresas.status='bloqueada'`. 3. 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/pagamentos` gera 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/pagamentos` duas 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_id` não causa erro nem duplica nada. - [ ] `BloqueioJob` rodado 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 erro `pendencia_financeira`. ## Ao concluir Atualizar `10-tracker-progresso.md`: Prompt 07 concluído, data, resumo. Confirmar Prompts 01–06 seguem marcados.