AAMLabs (atual Forge Labs) · Engenheiro full-stack, lead de backend

FortCred Financial

Plataforma multi-tenant de gestão de contratos de empréstimo para uma factory financeira, com 1.100+ contratos e R$1M+ transacionados em produção.

Cliente
AAMLabs (atual Forge Labs)
Papel
Engenheiro full-stack, lead de backend
Período
2024 ao presente

O problema

Uma factory financeira tocava a operação de empréstimos em planilhas e ferramentas improvisadas. O volume já era expressivo, com centenas de tomadores, milhares de contratos, ciclos mensais de fatura e conciliação manual de mora e cobrança. Cada passo manual era um ponto onde dinheiro escapava ou o compliance falhava.

O sistema precisava cobrir o ciclo completo: onboarding do cliente, aprovação do pedido, geração automática de parcelas, acúmulo diário de juros e mora, cobrança por WhatsApp, renegociação e uma trilha de auditoria por tomador, no estilo de um CRM. Com o tempo, passou também a atender mais de uma empresa na mesma base, o que trouxe a exigência de isolar os dados de cada uma.

Arquitetura

Monorepo com a API em NestJS 11 e o frontend em Next.js 15 com React 19, compartilhando o banco PostgreSQL via Prisma 6. O trabalho em segundo plano roda em BullMQ com Redis, e as mensagens de WhatsApp saem a partir das filas.

O sistema nasceu para uma empresa e evoluiu para multi-tenant. Cada empresa tem seus próprios clientes, contratos, modelos de mensagem e credenciais, sendo que as chaves do gateway de pagamento e da instância de WhatsApp ficam guardadas criptografadas. O isolamento acontece em duas camadas: um guard valida se o usuário pertence à empresa da requisição, e o Prisma injeta o filtro por companyId em cada consulta, de modo que uma empresa nunca enxerga os dados da outra. Um usuário pode pertencer a mais de uma empresa, através de uma tabela de associação, e um perfil SUPER_ADMIN transita entre todas.

   ┌─ Next.js 15 + React 19 ─────────────────────────┐
   │  TanStack Query · Radix UI · Tailwind 4         │
   │  JWT em cookies HTTP-only (withCredentials)     │
   └──────────────────┬──────────────────────────────┘
                      │ axios (tipado via Zod)
   ┌─ API NestJS 11 ──┴──────────────────────────────┐
   │  Auth · Clientes · Pedidos · Faturas · Pagtos   │
   │  Renegociações · Comunicação · Dashboard        │
   │  escopo por companyId (multi-tenant)            │
   └──────┬───────────────────────────────┬──────────┘
          │                               │
     PostgreSQL                  BullMQ + Redis ─→ WhatsApp
     (Prisma 6)                  (fila whatsapp-messages)
                                  ├─ atraso aleatório 5 a 15s
                                  ├─ 3 tentativas, backoff exp.
                                  └─ CommunicationLog

Os jobs diários (fuso do Brasil)

Três rotinas rodam em America/Sao_Paulo, nesta ordem:

  • 05:00, processamento de inadimplência: faturas vencidas recebem o lateFee% configurado, mudam para UNPAID e atualizam os totais do pedido.
  • 05:01, acúmulo de mora: (lateFeeMora% / 30) × valor_original × dias_em_atraso, somado diariamente. As renegociações sempre incidem sobre o valor original do pedido, não sobre o renegociado. É uma regra financeira pouco óbvia, mas que importa para o compliance.
  • 06:00, apenas em dias úteis, mensagens de cobrança: agrupa por pedido as faturas em atraso e as que vencem no dia e enfileira os lembretes de WhatsApp.

Cada rotina percorre todas as empresas configuradas e tem um ponto de entrada manual de recuperação, para reprocessar um dia sem disparar efeitos colaterais acidentais.

Modelagem de domínio

A máquina de estados financeira é mapeada em um enum tipado do Prisma: OPEN → APPROVED → ACTIVE → PAID / RENEGOTIATED / UNAPPROVED / LOST. Cada pedido tem uma modalidade de recorrência (diária, semanal, quinzenal ou mensal) que define a frequência das parcelas. O dashboard lê agregados em cache, com paginação e filtros por modalidade e tipo de contrato, o que mantém as visões de portfólio rápidas mesmo com milhares de contratos ativos.

O controle de acesso é por papel e por departamento, com perfis como LOAN_MANAGEMENT, STREET_SUPERVISOR e RENEGOTIATION_ANALYST, e cada rota passa por guards de autenticação e de papel.

Um ponto que rendeu trabalho recente foi a liquidação de faturas. Pagamento parcial, quitação e estorno precisam manter o saldo do pedido exato em qualquer ordem de operação. Um bug de dupla contagem corrompia o amountDue em certos cenários de estorno, e a correção exigiu tornar a liquidação atômica. Hoje a baixa de pagamento é registrada dentro do próprio sistema, e a integração com a AbacatePay, para confirmar pagamentos de forma automática, está em adoção. Fica o aprendizado: em sistema financeiro, a ordem e a atomicidade das operações de saldo não são detalhe, são o produto.

Cobrança no WhatsApp: da Evolution API para a API oficial

O canal de cobrança é o WhatsApp, e quem inicia a conversa é sempre a empresa: lembrete de vencimento, aviso de atraso, cobrança. A primeira versão usou a Evolution API, uma ponte não oficial sobre o WhatsApp. Ela serviu bem para validar o produto, mas tem um teto claro. Como o contato parte sempre da empresa, e em volume, esse padrão esbarra nas regras do WhatsApp e deixa o número em risco constante de bloqueio. Para uma operação que depende da cobrança diária, isso é inviável.

Por isso a migração em andamento é para a API oficial do WhatsApp, a Cloud API da Meta. Ela existe justamente para esse caso: mensagens iniciadas pela empresa passam por modelos aprovados com antecedência, o que torna o envio em escala legítimo e estável. A modelagem atual já facilita a transição, porque as mensagens de cobrança e lembrete já são tratadas como modelos por empresa, e não como texto solto, o que mapeia direto para o conceito de template aprovado da API oficial.

O aprendizado aqui é de produto, não só técnico: uma integração não oficial é ótima para provar uma ideia, mas qualquer canal de comunicação que vira dependência do negócio precisa ir para a via oficial antes de escalar.

Confiabilidade

Conforme o sistema virou a base de uma operação real, a malha de testes e a documentação acompanharam. Hoje são mais de 300 testes, entre unitários e de integração, cobrindo os serviços financeiros, os guards e os jobs, além de documentação OpenAPI em todos os controllers. Não é vaidade de cobertura: em cálculo de juros, mora e liquidação, o teste é o que permite mexer no código sem medo de quebrar o saldo de alguém.

Resultado

Em produção e em manutenção contínua. A factory opera inteiramente dentro do sistema, sem planilhas, e as filas dão conta do ciclo diário sem intervenção humana. A base evoluiu de uma empresa para várias na mesma instância, e sigo dando manutenção e evoluindo o produto. Os números abaixo são contagens vivas, não estimativas.

Números

  • R$1M+

    Transacionado

  • ~20 mil

    Faturas geradas

  • 1.100+

    Contratos cadastrados

  • 394+

    Clientes onboardados

Stack

  • NestJS 11
  • Next.js 15
  • React 19
  • Prisma 6
  • PostgreSQL
  • BullMQ
  • Redis
  • WhatsApp Cloud API
  • AbacatePay
  • JWT + cookies HTTP-only
  • TanStack Query
  • Zustand
  • Radix UI
  • Tailwind 4