Skip to content

Loong-ERP/loong-api-docs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

Loong API — Documentação Técnica

API REST multi-tenant para gestão financeira com bot de WhatsApp e IA integrada.

Django DRF Celery Docker License


Visão Geral

O Loong API é o backend de uma plataforma fintech para pequenos negócios. Gerencia múltiplos usuários e workspaces isolados, processa webhooks do Stripe e do WhatsApp em tempo real, executa tarefas assíncronas com Celery e expõe uma API REST consumida por clientes web e pelo bot.

O código-fonte é privado. Este repositório documenta a arquitetura, os módulos, as decisões de design e os contratos de API.


Arquitetura Geral

graph TD
    subgraph Clientes
        WA[WhatsApp User]
        WEB[Frontend React]
        STRIPE[Stripe]
    end

    subgraph Infra
        NGINX[Nginx<br/>reverse proxy]
    end

    subgraph Django API
        GUNICORN[Gunicorn<br/>WSGI server]
        ACCOUNTS[accounts<br/>auth & users]
        COMPANIES[companies<br/>workspaces & tenancy]
        BILLING[billing<br/>Stripe & planos]
        POCKET[pocket<br/>finanças pessoais]
        WHATSAPP[whatsapp<br/>bot & webhooks]
        BLOG[blog<br/>conteúdo com IA]
    end

    subgraph Async
        REDIS[(Redis<br/>broker & cache)]
        WORKER[Celery Worker]
        BEAT[Celery Beat<br/>agendador]
    end

    subgraph Dados
        PG[(PostgreSQL)]
    end

    subgraph Externas
        META[Meta Cloud API]
        STRIPE_API[Stripe API]
        GROQ[Groq / LLaMA 3.3 70B]
        GEMINI[Google Gemini]
        SENTRY[Sentry]
    end

    WA -->|webhook| NGINX
    WEB -->|HTTPS| NGINX
    STRIPE -->|webhook| NGINX
    NGINX --> GUNICORN
    GUNICORN --> ACCOUNTS & COMPANIES & BILLING & POCKET & WHATSAPP & BLOG
    ACCOUNTS & COMPANIES & BILLING & POCKET & WHATSAPP & BLOG --> PG
    WHATSAPP --> REDIS
    POCKET --> REDIS
    WORKER --> REDIS
    BEAT --> REDIS
    WORKER --> META & GROQ & GEMINI
    BILLING --> STRIPE_API
    GUNICORN --> SENTRY
Loading

Módulos

accounts — Autenticação e Usuários

Gerencia o modelo customizado de usuário (email como username), registro, login, logout e fluxos de senha.

Principais decisões:

  • AbstractBaseUser customizado — email como campo de login em vez de username
  • Hashing de senha com Argon2 (vencedor do Password Hashing Competition)
  • JWT via djangorestframework-simplejwt com rotação de tokens e blacklist
  • Autenticação de dois fatores com TOTP (compatível com Google Authenticator)
  • AuditLog imutável — registra login, logout, mudança de senha, IP e user agent
accounts/
├── models.py        # User, AuditLog
├── serializers.py   # CustomTokenObtainPairSerializer, RegisterSerializer
├── views.py         # Login, Register, Logout, PasswordChange
├── validators.py    # PasswordStrengthValidator
├── sanitizers.py    # Bleach — sanitização de inputs
├── audit.py         # Helper para registrar AuditLog
└── phone_otp.py     # Verificação de telefone via OTP

companies — Workspaces e Multi-Tenancy

O Workspace é o tenant central do sistema. Toda entidade (transação, conta, categoria, configuração) pertence a um workspace. Um usuário pode ter múltiplos workspaces.

# Toda entidade do sistema segue este padrão:
class AnyModel(BaseModel):
    workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE)
    # ... outros campos

Tipos de workspace: pocket (pessoal, criado automaticamente no cadastro), personal, business, enterprise.

BaseModel — classe abstrata base de todos os modelos:

Campo Tipo Descrição
id UUIDField PK em UUID v4 — impossível de adivinhar, sem coordenação central
created_at DateTimeField Preenchido automaticamente na criação
updated_at DateTimeField Atualizado a cada save()

billing — Pagamentos e Assinaturas

Integração completa com Stripe para assinaturas recorrentes (planos FREE, PRO, POCKET).

Fluxo de assinatura:

sequenceDiagram
    participant U as Usuário
    participant API as Loong API
    participant S as Stripe

    U->>API: POST /billing/checkout/
    API->>S: Cria checkout session
    S-->>API: session_url
    API-->>U: Redireciona para Stripe

    U->>S: Preenche cartão e confirma
    S->>API: POST /billing/webhook/ (checkout.session.completed)
    API->>API: Valida HMAC-SHA256
    API->>API: select_for_update() — lock para idempotência
    API->>API: Ativa assinatura no banco
    S->>API: POST /billing/webhook/ (subscription events)
    API->>API: Atualiza status da assinatura
Loading

Decisão técnica chave: select_for_update() no _activate_subscription garante que dois webhooks concorrentes (o Stripe pode reenviar) não ativem a assinatura duas vezes.


pocket — Gestão Financeira

Core financeiro do produto. Gerencia contas, transações, categorias, contas a pagar, metas e transações recorrentes.

pocket/
├── models.py           # Account, Transaction, Category, Bill, RecurringTransaction, FinancialGoal
├── analytics.py        # Engine de analytics: budget diário, projeções, insights
├── jarvis_engine.py    # Regra do silêncio, janela 24h WhatsApp, insights proativos
├── charts.py           # Geração de gráficos com Matplotlib (PNG → WhatsApp)
├── tasks.py            # Celery: lembretes, recorrentes, radar diário do Jarvis
├── importers/          # OFX, PDF, XLSX — importação de extratos bancários
└── services/           # Lógica de negócio isolada da view

Soft Delete — transações nunca são deletadas do banco:

class SoftDeleteManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)

Analytics Engine — cálculos financeiros comportamentais em Python puro:

  • Budget diário disponível (levando em conta teto de gastos, metas e recorrentes pendentes)
  • Projeção de gastos até o fim do mês
  • Detecção de anomalias de gasto por categoria

whatsapp — Bot e Integração Meta

Webhook que recebe mensagens do WhatsApp, roteia para o handler correto e responde via Meta Cloud API.

Fluxo de mensagem:

flowchart LR
    MSG[Mensagem recebida] --> VERIFY{Verifica\nassinatura Meta}
    VERIFY -->|inválida| IGNORE[Ignora]
    VERIFY -->|válida| PARSE[Parser]
    PARSE --> CMD{Comando\nsimples?}
    CMD -->|sim| HANDLER[Handler direto\nsaldo/resumo/etc]
    CMD -->|não| TXPARSE{É uma\ntransação?}
    TXPARSE -->|sim| SAVE[Salva transação]
    TXPARSE -->|não| LLM[Groq / LLaMA 3.3 70B\nconsultor financeiro]
    HANDLER & SAVE & LLM --> REPLY[Responde via\nMeta Cloud API]
Loading

Parser de linguagem natural:

O parser usa regex para capturar os padrões mais comuns antes de acionar a IA. Isso mantém custo de API em menos de 1% das interações.

"+50 gasolina"     → transação receita R$50, descrição "gasolina"
"-30 mercado"      → transação despesa R$30, descrição "mercado"
"saldo"            → retorna saldo consolidado de todas as contas
"resumo"           → retorna resumo do mês com gráfico
[foto de boleto]   → OCR (pyzbar + pytesseract) → extrai valor e vencimento
[qualquer pergunta aberta] → LLM com snapshot financeiro do usuário

Regra do silêncio (Jarvis):

  • Máximo 1 mensagem proativa por dia por workspace
  • Verifica janela de 24h da Meta API antes de enviar (evita custo de template)
  • Estado armazenado no Redis com TTL de 24h

blog — Geração de Conteúdo com IA

Gera artigos automaticamente toda segunda-feira às 9h via Celery Beat + Google Gemini. Os artigos entram como rascunho para revisão humana antes de publicar.


Tarefas Agendadas (Celery Beat)

Task Horário Descrição
send_bill_reminders 8h diário Notifica usuários sobre contas a vencer
mark_overdue_bills 8h05 diário Marca contas vencidas como overdue
process_recurring_transactions 6h diário Gera transações das recorrências ativas
jarvis_daily_radar 8h15 diário Envia insight financeiro proativo pelo WhatsApp
generate_blog_article 9h segunda Gera artigo com Gemini (publica como rascunho)

Segurança em Camadas

# Camada Implementação
1 Hashing de senha Argon2 (Password Hashing Competition winner)
2 Autenticação JWT com access (15min) + refresh (7d) + blacklist
3 2FA TOTP via django-otp (Google Authenticator)
4 Rate limiting Scoped throttling por endpoint (login: 5/min, register: 10/h)
5 Sanitização Bleach — remove HTML/JS de todos os inputs
6 CSP django-csp — Content Security Policy via header HTTP
7 Admin restrito Path ofuscado + restrição por IP (ADMIN_ALLOWED_IPS)
8 X-Forwarded-For Só confia no header se REMOTE_ADDR for proxy confiável
9 Audit logs Imutáveis — login, logout, mudança de senha, IP, user agent
10 Prompt injection Input do usuário isolado em bloco <USER_INPUT> no system prompt

Endpoints — Visão Geral

Todos os endpoints (exceto auth) exigem Authorization: Bearer <access_token>. Base URL: /api/v1/

Auth (/auth/)

Método Endpoint Descrição
POST /auth/register/ Cria conta e workspace padrão
POST /auth/login/ Retorna access + refresh tokens
POST /auth/logout/ Invalida o refresh token (blacklist)
POST /auth/token/refresh/ Troca refresh por novo access token
POST /auth/password/change/ Altera senha (requer senha atual)
POST /auth/password/reset/ Envia email de reset

Pocket (/pocket/)

Método Endpoint Descrição
GET/POST /pocket/transactions/ Lista e cria transações
GET/PATCH/DELETE /pocket/transactions/{id}/ Detalhe, edição e soft-delete
GET/POST /pocket/accounts/ Contas bancárias/carteiras
GET/POST /pocket/categories/ Categorias de transação
GET/POST /pocket/bills/ Contas a pagar
GET/POST /pocket/recurring/ Transações recorrentes
GET /pocket/analytics/summary/ Resumo financeiro do mês
GET /pocket/analytics/daily-budget/ Budget disponível hoje
POST /pocket/import/ofx/ Importa extrato OFX
POST /pocket/import/pdf/ Importa extrato PDF

Billing (/billing/)

Método Endpoint Descrição
POST /billing/checkout/ Cria sessão de checkout Stripe
GET /billing/subscription/ Status e detalhes da assinatura atual
POST /billing/cancel/ Cancela assinatura
POST /billing/webhook/ Webhook Stripe (público, validado por HMAC)

WhatsApp (/whatsapp/)

Método Endpoint Descrição
GET /whatsapp/webhook/ Verificação do webhook (Meta)
POST /whatsapp/webhook/ Recebe mensagens (Meta)

Infraestrutura

┌─────────────────────────────────────────────────────────┐
│                    Docker Compose                        │
│                                                          │
│  ┌─────────┐   ┌──────────┐   ┌──────────────────────┐ │
│  │  nginx  │──▶│ gunicorn │──▶│  Django Application  │ │
│  │  :80    │   │  :8000   │   │  (4 workers)         │ │
│  └─────────┘   └──────────┘   └──────────────────────┘ │
│                                         │               │
│  ┌──────────────────┐     ┌─────────────▼────────────┐ │
│  │  celery-beat     │     │        redis             │ │
│  │  (agendador)     │────▶│  broker + cache + rate   │ │
│  └──────────────────┘     └─────────────┬────────────┘ │
│                                         │               │
│  ┌──────────────────┐                   │               │
│  │  celery-worker   │◀──────────────────┘               │
│  │  (executor)      │                                   │
│  └──────────────────┘                                   │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │             PostgreSQL                           │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘

Decisões de Design

Por que UUID como chave primária?

IDs inteiros sequenciais (1, 2, 3...) permitem que um atacante adivinhe IDs de outros recursos (/transactions/1, /transactions/2). UUID v4 são 128 bits gerados aleatoriamente — impossível de enumerar. Também permitem gerar IDs em qualquer nó sem coordenação central, facilitando escalabilidade.

Por que Celery + Redis em vez de processar na requisição?

Webhooks da Meta têm timeout de poucos segundos. Processar OCR de boleto, chamar LLM e responder ao WhatsApp dentro desse tempo é arriscado e bloqueia o processo Gunicorn. Com Celery, a requisição retorna 200 OK imediatamente, e o processamento acontece em background — melhor UX e sem risco de timeout.

Por que parser regex antes do LLM?

Mais de 95% das interações no WhatsApp são comandos simples (+50 gasolina, saldo, resumo). Processar cada um com um LLM seria lento (latência de rede) e caro (custo por token). O parser regex captura esses casos em microssegundos. O LLM só é acionado para perguntas abertas onde regex não resolve.

Por que Soft Delete em vez de DELETE real?

Transações financeiras deletadas precisam existir para auditoria e para o caso de "desfazer". Com soft delete, deleted_at é marcado e o registro fica invisível para queries normais, mas recuperável. Também preserva integridade referencial de registros relacionados.

Por que select_for_update() no billing?

O Stripe pode reenviar o mesmo webhook se não receber confirmação a tempo. Dois workers Celery processando o mesmo evento de checkout.completed em paralelo poderiam ativar a mesma assinatura duas vezes. select_for_update() coloca um lock no banco — o segundo worker espera o primeiro terminar e, ao verificar o estado, descobre que a assinatura já está ativa.


Sobre

Desenvolvido por Caio Fiori Martins — Desenvolvedor Full-Stack · Python / Django / React

LinkedIn GitHub

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors