64 lines
4.3 KiB
Markdown
64 lines
4.3 KiB
Markdown
# 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.
|