Pular para o conteúdo

Decisions

Estas decisões refletem o estado canonical da Versão Atual (07/05/2026). Cada ADR resume o que, por que e status atual. Pendências e gaps do audit ficam ao final.

  • What — Integração exclusiva via aplicativo Omie AABC (CNPJ 50.162.682/0001-07). LNG fora do escopo desta versão.
  • Why — Foco MVP. Reduz superfície de teste e evita conflito de regras fiscais entre orgs.
  • StatusDecidido 07/05/2026 · Confluence 3813310465.
  • What — Bubble dispara exatamente 1 trigger automático (POST /v1/orders/{bubble_order_id}/sync-omie em “Pedido criado”) e expõe N botões backoffice/website chamando endpoints do backend (retry, cancel-nf, mark-nf-manual, approve-billing-hold, override-day25, transfer-cc, use-cc). Bubble não recebe webhook Pagar.me nem chama Brasil API.
  • Why — Bubble continua dono dos dados e do front; backend é orquestrador. Não recriar funcionalidades existentes; centralizar side-effects e regras fiscais onde há capacidade de teste e observability.
  • StatusDecidido · canonical em AGENTS.md (“Premissa Mínimo Bubble”).
  • What — Bubble chama sync-omie síncrono e espera resposta. Não há outbox pattern. Backend escreve status writeback de volta no Bubble Data API dentro da mesma transação lógica.
  • Why — Simplicidade. Evita consistency lag visível ao backoffice. Deferir outbox até segundo consumer aparecer ou até volume justificar.
  • StatusDecidido para MVP · revisar pós primeiro produção.
  • What — Pedido carrega status_pedido ∈ cancelado (3 valores) e status_faturamento ∈ faturamento_manual (9 valores), evoluídos independentemente.
  • Why — Lifecycle de pagamento ≠ lifecycle de faturamento. Carta de crédito, holds manuais, regra dia 25 e cancelamento de NF têm transições próprias que não mapeiam 1:1 em estado de pedido.
  • StatusDecidido · Confluence 3812950022 (Modelo de Estados).
  • WhatBLU-766 (integração HubSpot Fase 2) removido do escopo da Versão Atual.
  • Why — Scope reduction. Sem caso de uso bloqueante para MVP.
  • StatusMover para wontfix-mvp · ação pendente no board.
  • WhatBLU-781 (consolidação de pedidos) fora do escopo da Versão Atual.
  • Why — Complexidade incremental sem caso de uso confirmado no MVP. Reabrir se demanda comercial surgir pós-launch.
  • StatusMover para wontfix-mvp · ação pendente no board.
  • WhatPOST /webhooks/pagarme no backend, HMAC mandatory. Idempotência por charge_id. Bubble não processa webhook.
  • Why — HMAC verification e idempotência cabem no backend; Bubble não tem capacity nativa para assinatura HMAC nem para gating de retry com janela de 31min do rate-limit Omie.
  • StatusDecidido · Confluence 3812327443 (Módulo Pagar.me).
  • What — Backend chama Brasil API CNPJ lookup e escreve resultado no Bubble via Data API.
  • Why — Rate limit e cache no backend; Bubble não é HTTP client capable para integração externa com retry policy e TTL.
  • StatusDecidido · Confluence 3813539856 (Mapeamento Payload).
  • What — Backend roda como reconciler autônomo com cron ARQ cdc_tick (hourly, 0 * * * *) que puxa Orders modificados via Bubble Data API com cursor (GET /api/1.1/obj/order :search constraint modified_date > last_cursor). Cursor persistido em Postgres backend (tabela bubble_sync_cursor). Shadow store local (order_shadow) detecta state delta sem segundo round-trip. Reconciler decide via billing_service.decide_billing + utils/dia25.compute_billing_date e executa sequência Omie. Único endpoint Bubble-facing: POST /v1/orders/{id}/sync-omie (manual force re-sync, mesmo handler do tick parametrizado pra 1 order). Endpoints /retry, /cancel-nf, /mark-nf-manual, /override-day25, /payment-confirmed removidos — todas as ações viraram edições em campos do Order Bubble que o reconciler observa.
  • Why — Tentativas iniciais (Opção A: webhook push Bubble→backend; Opção B: cron polling 60s; Opção C híbrido) todas envolvem workflows Bubble novos acoplados ao backend (frágil, sem retry nativo, falha silenciosa) ou gasto desnecessário de Bubble Data API (1440 Searches/dia em polling 60s). Throughput real é ~10 pedidos/dia — fluxo fiscal tolera latência horária. CDC pull com cursor: zero workflow Bubble novo, 24 calls/dia, cursor garante avanço monotônico, idempotente e reentrante. Webhook Pagar.me continua no Bubble (legado HMAC funcionando) — reconciler descobre status_pedido = confirmado via modified_date no próximo tick.
  • StatusDecidido 2026-06-09 · pendente implementação (Slice F) · referência: Integration · Bubble × Backend e Omie Sync State. Implementação requer: (a) Alembic migration bubble_sync_cursor + order_shadow, (b) job ARQ cdc_tick (0 * * * *) + função core _reconcile_one(order), (c) handler POST /v1/orders/{id}/sync-omie chamando _reconcile_one direto (sem mexer no cursor), (d) deprecation dos endpoints /retry, /cancel-nf, /mark-nf-manual, /override-day25 + remoção do OpenAPI, (e) novos campos no Order Bubble (billing_scheduled_date_override, faturamento_manual, omie_nfse_numero_manual, omie_nfse_codigo_manual) que substituem os antigos endpoints. Migração futura (V2) pode mover webhook Pagar.me para o backend e fazer Bubble consumir via Omie Sync State — sem ROI hoje.
OpçãoPor que NÃO
Webhook push Bubble→backend (Opção A)Workflow Bubble pode falhar silenciosamente; sem retry nativo; acopla backend a config no-code.
Cron polling 60s (Opção B)1440 Searches/dia desnecessários pra throughput ~10 pedidos/dia.
Híbrido push + reconcile 15min (Opção C original)Mantém dependência de workflow Bubble + endpoint /payment-confirmed extra. CDC pull pure é mais simples e dispensa workflow.
  • P11 · formato vendedor na O.S. Omie (consultar /geral/vendedores/) — Aguardando Luiz
  • P12 · estratégia homologação NFS-e (acesso 7d ambiente demo) — Aguardando Luiz
  • PAGARME-1 · PAGARME_WEBHOOK_SECRET rotacionado? — Aguardando Luiz
  • BUBBLE-1 · Bubble tem pagarmedetails + pagarmewebhookcall no schema — substitui ou convive com handler atual? — Aguardando Luiz
  • OMIE-1 · cache strategy /servicos/listaservico/ (TTL? refresh diário?) — Discovery
  • BLU-783 · rate-limit Omie discovery existe, falta ticket filho de implementação — Bloqueia retry policy

Top-5 gaps identificados no audit docs/reports/order-to-billing-flow-audit.html, em ordem de implementação:

  1. Gap 4 (1h) — Unificar enum status writeback Bubble: sync_service.py:430 escreve "NF emitida" (PT) vs invoice_service.py:222 escreve "invoiced" (EN). Confirmar Option Set ns_status canonical antes de unificar. Quick win
  2. Gap 2 (1d) — Alembic migration adicionando billing_scheduled_date + billing_trigger em billing_schedule. Pré-requisito Gap 1
  3. Gap 1 (1d) — run_billing_job deve chamar InvoiceService.emit_invoice (depende de Gap 2). Bloqueia faturamento agendado
  4. Gap 5 (4h) — BanError específico → HTTP 503 com Retry-After: 1860 (31min). Higiene de retry
  5. Gap 3 — outbox pattern. Defer pós-MVP (alinhado ADR-3).

Decisões adiadas conscientemente. Registradas pra revisita após MVP estabilizar.

Auth Bubble → Backend:

  • Static bearer token em header Authorization: Bearer <token>
  • Header obrigatório X-Request-Timestamp (unix epoch ms) com janela de 5 min
  • Header obrigatório Idempotency-Key (UUID por request)
  • Token armazenado no Bubble API Connector (server-side) e em .env do backend
  • Rotação trimestral manual aspiracional.env.example documenta mecanismo, sem automação

Auth Backend → Bubble:

  • BUBBLE_API_KEY bearer já existente, rotação anual manual
#ItemAvaliado em MVP — descartado porTrigger pra revisitar
9.1HMAC body signature (X-Signature: HMAC-SHA256(body, secret))Bubble no-code não tem builtin HMAC; exige plugin/custom code = scope creepDetecção de replay/MITM em produção; ou Bubble lançar HMAC nativo
9.2Rotação automatizada via secret manager (AWS Secrets Manager / HashiCorp Vault)MVP roda em infra simples, sem secret manager. Bubble API Connector exige update manual mesmo com manager.Adoção de secret manager pela infra; ou compliance audit
9.3IP allowlist complementar a bearerBubble usa AWS dinâmico, sem range estável publicado. User-Agent filter descartado (ruído, fácil falsificar).Bubble publicar lista oficial de IPs egress; ou compliance audit
9.4mTLSBubble não suporta client certBubble lançar suporte; ou requisito B2B explícito
9.5JWT short-lived com claims (per-call, exp 60s)Bubble não gera JWT signed sem pluginPlugin oficial Bubble ou Bubble flow code
9.6Audit de autenticação (log de tentativas falhas, alerta após N)MVP escopo small, sem SOCVolume real justifica monitoramento ativo

Itens decididos na arquitetura mas fora do escopo do primeiro smoke test green (happy path PJ AABC sync-omie). Cada um vira teste vertical posterior ou tarefa de infra dedicada.

#ItemDecisão arquiteturaQuando endereçar
10.1Postgres + Alembic para action_log, idempotency_cache, omie_id_map, omie_project_mapMVP smoke usa in-memory dict — comportamento vem primeiro, persistência depoisApós smoke green; teste vertical “restart preserva idempotency cache”
10.2Retry policy 1s/5s/30s (sync inline) + is_retriable policyDecidido. Smoke só exercita happy path.Teste vertical: “Omie 503 transitório → retry → green”
10.3Timeout per-call Omie 10sDecidido. Smoke não exercita timeout.Teste vertical: “Omie hang → 10s timeout → retry”
10.4CI cron diário pra drift detection schema Bubble (re-fetch /meta vs commitado)Drift on-demand pra MVP. CI cron descartado por simplicidade.Quando schema Bubble começar a mudar com freq
10.5Codegen automation (pre-commit hook + CI gate)MVP roda manual python -m bubble_sdk.codegenQuando codegen quebrar build silenciosamente ao menos 1×
10.6Rotação automatizada bearer token via secret managerPendência seção §Roadmap SegurançaAdoção de secret manager
10.7Circuit breaker (5 falhas Omie consecutivas em 10min → 503 fast-fail por 1min)Não-MVP. Decidido.Após observar pattern real de falhas
10.8Endpoints manuais cancel-nf, mark-nf-manual, retryDecididos. Smoke cobre só sync-omie.1 teste vertical por endpoint
10.9Validação timestamp window 5min + Idempotency-Key obrigatória em prodDecidido. Smoke desabilita validação (BUBBLE_AUTH_REQUIRED=false).Teste vertical: “request timestamp >5min → 401”
10.10option.* como Literal (após enum values pendência ns_status / nf_invoice)Codegen gera str no MVP. Regenera com Literal quando enum values vierem.Quando dev Bubble preencher option types
10.11Dispatcher AABC/LNG cross-check (validar mismatch payment_special_condition × empresa escolhida)Decidido — pode emitir 422 erro_roteamentoTeste vertical: “PJ flexível sem empresa escolhida → 422”
10.12omie_id_map cache cross-request (2ª chamada usa código cacheado)Decidido — PK composta. Smoke limpa state entre testes.Teste vertical: “2ª sync mesmo cliente → não chama IncluirCliente”
10.13omie_project_map cache cross-requestDecididoTeste vertical: “2ª sync mesmo evento → não chama IncluirProjeto”
10.14Mock Bubble UI (L2 + L3)Não-MVP — só L1 HTTP-only statefulQuando QA manual exigir
10.15Frontend Bubble real chamando APIBubble dev separado, fora do backendApós backend smoke green
10.16Deploy infra realStack decidida no AGENTS.md mas não scaffoldedApós primeiros 3-4 testes verticais