# EcomSmart Hub por Módulos — Especificação para desenvolvimento

Versão 1 · 11/09/2026 · acompanha o protótipo navegável (artefato "EcomSmart Hub Módulos")

## 1. Modelo comercial

| Módulo | key | O que entrega | Base existente no Hub |
|---|---|---|---|
| Recuperação de Carrinho | `carrinho` | Captura híbrida (webhook + sync), régua de até 3 mensagens, receita confirmada por vínculo com pedido. Inclui recuperação de cancelados. | `models/carrinho.py`, `pedido_cancelado.py`, `api_admin/carrinho*.py`, tela `recuperacao-carrinho/` e `recuperacao-cancelados/` |
| Disparo para Base (RFV) | `disparo` | Segmentação Ozcan automática, campanhas one-shot (reativação, sazonais, informativos, novidades, giftback, manuais), template por segmento, cadência segura. | `core/rfm/`, `campaigns.py`, `campaigns_generic.py`, telas `reativacao/ sazonais/ informativos/ novidades/ giftback/ campanhas/`, `analise-rfv/` |
| Retenção | `retencao` | Boas-vindas + Aniversariantes do dia, sempre rodando, com anti-duplicação no banco. | `AutomacaoAniversario`, `AutomacaoBoasVindas`, `tasks/campaign_birthday.py`, `campaign_welcome.py`, telas `aniversariantes/ boas-vindas/` |
| E-commerceFlow | `flow` | Catálogo no WhatsApp, IA vendedora (5 nós B2C já existentes), WhatsApp Flows para variação/entrega/pagamento, pedido criado na loja. | `core/agentes/`, prompts B2C (`b2c.intent`, `b2c.recommend_*`, `b2c.suggestions_intro`, `b2c.validate_complements`), `Produto/Categoria`, `Integracao`. **Flows é greenfield.** |
| Campanhas Inteligentes | `campanhas` | Motor de priorização de ofertas (objetivos O1–O14), recomendação de produto/categoria, cupons controlados, aprovação pelo lojista. | `core/ai/cesta.py`, `gerador_recomendacao_oferta.py`, `services/oferta.py`, `novidades_bi.agregar_cruzado`, tela "Ofertas sugeridas". **Inferência fuzzy (Mamdani) é greenfield no Hub** (hoje só no prompt `panel.marketing_assistant`). |

Preço e regra comercial, iguais para os 5 módulos:

- Teste grátis de 7 dias, sem cobrança (cartão opcional no cadastro, obrigatório para continuar após o trial).
- R$ 99/mês nos 3 primeiros ciclos pagos, depois R$ 299/mês.
- Cota de 10.000 mensagens/mês **por módulo**. Custo Meta por conversa repassado a preço de tabela (já existe em `CustoMensagemWhatsapp`).
- Cada módulo é contratado, testado e cancelado de forma independente.

## 2. Revisão da arquitetura atual

### 2.1 Django (`ecomsmart-hub-django`)

O que já existe e será reaproveitado:

- **Planos e assinatura**: `models/planos.py` com `Plano`, `PlanoPreco`, `Assinatura` (status trial/ativa/suspensa/cancelada, `trial_ate`, `referencia_externa`, `origem`), `Pagamento` (append-only, idempotente por `referencia_externa`), `ConsumoMensal`. Services em `services/planos.py` (`garantir_assinatura`, `assinatura_vigente`, `modulos_efetivos`, `sincronizar_modulos`, `registrar_consumo_mensagem`, `pode_enviar`).
- **Catálogo de módulos**: `MODULOS_CATALOGO` em `models/tenant.py` (hoje `principal`, `prospeccao`, `meta_leads`, `agentes_ia`) e `Empresa.modulos_habilitados`.
- **Onboarding self-service**: só via Shopify (`services/shopify_onboarding.py`, `ShopifyOnboardingService.bootstrap/confirmar/status/retry`) e provisionamento por MCP (`/api/admin/empresas/provisionar/`, tool `criar_empresa`).
- **SmartConversas**: `ContaSmartConversas → ApiSmartConversas → CanalSmartConversas`, endpoints `smartconversas/associar`, `apis/criar`, `canais`; status por polling (30 min).
- **Chokepoint de envio**: `services/whatsapp_dispatch.enviar_mensagem_canal` (único lugar para aplicar cota).
- **MCP**: `mcp_server/server.py` (FastMCP, ~70 tools, quase todas de leitura), `MCPToken` por usuário, `MCPMultiTenantMiddleware`.
- **IA**: `core/ai/claude_client.py`, `core/agentes/runtime.py`, prompts versionados por nó.

Lacunas (zero ocorrências no repositório): Stripe; lookup de CNPJ; WhatsApp Flows; matching/inferência fuzzy; signup fora da Shopify; enforcement de cota (`pode_enviar` devolve sempre True); gating de endpoint por módulo (o JSON `modulos_habilitados` só monta menu).

Pontos de refatoração obrigatórios:

1. `MODULOS_CATALOGO` → 5 keys comerciais. Migração de dados traduz `principal` em `carrinho + disparo + retencao` para empresas atuais. Consumidores a ajustar: `Plano.clean`, `Assinatura.clean`, `/api/admin/modulos/`, tool `modulos_disponiveis`, `modulos.config.ts` no Angular.
2. `TemplateWhatsapp.MODULOS_CHOICES` (11 valores) passa a ser **sub-uso dentro de um módulo comercial** (ex.: `aniversario` e `boas-vindas` pertencem a `retencao`). Criar mapa `SUBMODULO → MODULO` em `models/tenant.py` e usá-lo no gating de templates.
3. Constraint `uma_assinatura_vigente_por_empresa` deixa de valer: ver §3.

### 2.2 Angular (`ecomsmart-hub-angular`)

Existe e será reaproveitado: Angular 21 standalone + signals + Tailwind 4 + biblioteca própria (~60 componentes em `shared/components/`, incluindo `bottom-sheet`, `responsive-table`, `rfm-gate`, `funil`, `stat-card`); infra de módulos (`ModuloKey`, `MODULOS`, `ModuleService`, `moduleGuard`, switcher, `modulos_habilitados`); telas de negócio dos 5 módulos dentro de `features/comunicacoes/`, `analise-rfv/`, `agentes-ia/`; layout com sidebar off-canvas em telas menores que 1024px; onboarding embutido da Shopify (`features/shopify/shopify-app.component.ts`) como molde de máquina de estados.

Lacunas: nenhuma rota pública além de `login` e `nova-senha`; sem cadastro; sem landing; zero referências a Stripe; `moduleGuard` redireciona com toast em vez de mostrar tela bloqueada; os 5 módulos comerciais não existem como `ModuloKey`; sem assistente conversacional embutido nas telas; sem layout público.

Tokens a manter: `brand-600 #4f54e4`, `brand-950 #1d1e4c` (sidebar), superfícies `surface-50 #f8f9fc`, texto `#0f1117 / #5b6478 / #9aa3b2`, fonte Inter. O protótipo introduz **Sora** apenas para títulos de landing/painel (opcional) e uma cor por módulo: carrinho `#E8890C`, disparo `#3B6FE0`, retenção `#D64C8C`, flow `#7C3AED`, campanhas `#0E9F8A`.

## 3. Modelo de domínio (novo e alterado)

```
Empresa (existente)
  + cnpj validado, razao_social, nome_fantasia, cnae, endereco_json   (preenchidos pelo lookup)
  + onboarding = JSON { passo, modulo_origem, utm, flags: {whatsapp, loja, stripe, smartconversas} }
  + stripe_customer_id

Assinatura (existente)  → passa a ser a CONTA DE COBRANÇA da empresa
  origem: direta | shopify | stripe        (+ 'stripe' em METODO_PAGAMENTO_CHOICES)
  (remover constraint uma_assinatura_vigente_por_empresa OU manter 1 Assinatura e mover
   o ciclo de vida para o item — recomendação: manter 1 Assinatura, ciclo de vida no item)

AssinaturaModulo (NOVO)
  assinatura FK · modulo (5 keys) · status: trial | ativa | inadimplente | cancelada
  trial_ate · fase: intro (3 ciclos R$99) | cheia (R$299) · ciclos_pagos
  stripe_subscription_id (UMA Subscription Stripe por módulo)
  cota_mensagens_mes = 10000 · cancelada_em
  UniqueConstraint (assinatura, modulo) WHERE status != cancelada

ConsumoMensal (existente) + coluna modulo  → UniqueConstraint (empresa, competencia, modulo)

Pagamento (existente) · referencia_externa = id do evento/invoice Stripe

MCPToken (existente) → key_hash (SHA-256), prefixo visível, escopos JSON, revogado_em
LogMcp (NOVO) usuario · origem (painel|claude_desktop|claude_ai|chatgpt) · tool · args · resultado · duracao

OnboardingEvento (NOVO) empresa · etapa · status · detalhe · criado_em   (auditoria do provisionamento)
```

Regra de gating (fonte única): `services/modulos.py::modulos_ativos(empresa) -> set[key]` lendo `AssinaturaModulo` com status `trial|ativa`. Cache por empresa (`@cache_empresa`), invalidado pelos webhooks Stripe.

## 4. Fluxo de cadastro e onboarding

Mobile first: uma decisão por tela, estado persistido em `Empresa.onboarding`, retomável.

1. **Landing** `/lp/:slug` → CTA grava `modulo_origem` + UTM → `/cadastro?modulo=carrinho`.
2. **Cadastro**: CNPJ com autocomplete (`GET /api/publica/cnpj/?q=`, proxy para o serviço `listacnpj` já em produção no cluster; debounce 300 ms, mínimo 8 dígitos), nome, WhatsApp, e-mail, senha ou Google. `POST /api/auth/cadastro/` cria `User + Empresa + UserPerfil(admin_empresa) + Assinatura + AssinaturaModulo(trial)`; loga por sessão.
3. **Passo Empresa**: confirma razão social, fantasia, endereço, CNAE, plataforma da loja, faixa de faturamento.
4. **Passo Módulos**: o módulo de origem já incluído; oferta dos outros 4 com uma linha de argumento cada; qualquer um pode ser adicionado ao trial.
5. **Passo Pagamento (Stripe)**: `POST /api/billing/setup-intent/` → Stripe Elements salva o cartão sem cobrar → `POST /api/billing/assinar/` cria as Subscriptions (§5). Pode ser pulado ("Cadastrar cartão depois"); nesse caso o trial segue e o painel mostra aviso a partir do dia 5.
6. **Passo WhatsApp** (roda em paralelo com o provisionamento): Meta Embedded Signup via JS SDK → `POST /api/canais-whatsapp/embedded-signup/` troca o `code` por token, cria `CanalWhatsapp(tipo=oficial)`. Alternativa: informar WABA ID + Phone Number ID.
7. **Passo Loja**: escolha da `Integracao` (Shopify em 1 clique pelo app existente; Magazord/Nuvemshop/VTEX/Woo/CSV) → `sync_integration` backfill 24 meses. RFV nasce após o primeiro sync completo (guard já existente).
8. **Pronto**: três atalhos que acionam o assistente ("Ativar régua padrão", "Só capturar", "Personalizar").

Provisionamento SmartConversas (task Celery `provisionar_smartconversas`, fila `express_queue`, idempotente por etapa, registra `OnboardingEvento`):

```
1 login super → 2 criar estabelecimento → 3 créditos iniciais → 4 buscar/cadastrar usuário admin
5 autenticar → 6 usuário do cliente → 7 depto "Vendas" → 8 depto "Atendimento" → 9 pipeline padrão
10 campo customizado lead_id → 11 origem do lead → 12 senha padrão
13 (Hub) smartconversas/associar → 14 apis/criar (webhook_token) → 15 canais sync → 16 templates padrão do módulo enviados à Meta
```

`GET /api/onboarding/status/` devolve a lista de etapas com `done|running|todo|erro` para a tela de progresso; `POST /api/onboarding/retry/` reexecuta a etapa que falhou.

## 5. Stripe

Catálogo (criado uma vez, ids guardados em `ParametroSistema`):

- 5 Products (um por módulo) · 1 Price recorrente mensal R$ 299 BRL cada.
- 1 Coupon `INTRO-99`: R$ 200 off, `duration=repeating`, `duration_in_months=3`.
- Uma **Subscription por módulo** com `trial_period_days=7` e o coupon aplicado. Vantagens: trial independente por módulo, cancelamento sem prorrata cruzada, fatura mostra um item por módulo. Cliente Stripe único por `Empresa`.

Endpoints:

| Método | Rota | Função |
|---|---|---|
| POST | `/api/billing/setup-intent/` | SetupIntent para salvar cartão |
| POST | `/api/billing/assinar/` `{modulos:[]}` | cria Subscriptions (trial) e `AssinaturaModulo` |
| POST | `/api/billing/modulos/` `{modulos:[]}` | adiciona módulos a uma conta existente (checkout in-app) |
| DELETE | `/api/billing/modulos/:key/` | cancela ao fim do ciclo (`cancel_at_period_end`) |
| GET | `/api/billing/resumo/` | módulos, status, uso no ciclo, próxima fatura, faturas |
| POST | `/api/billing/portal/` | link do Customer Portal (trocar cartão, baixar faturas) |
| POST | `/webhook/stripe/` | eventos abaixo, assinatura verificada, idempotente por `event.id` |

Webhooks → efeito:

- `customer.subscription.trial_will_end` → notificação no painel + e-mail (dia 5).
- `customer.subscription.updated` → sincroniza `status`, `fase` (conta ciclos pagos para virar `cheia` após 3), `trial_ate`.
- `invoice.paid` → grava `Pagamento` (idempotente), `status=ativa`.
- `invoice.payment_failed` → `status=inadimplente`, grace de 7 dias, depois módulo bloqueado (as automações pausam via guard de task).
- `customer.subscription.deleted` → `status=cancelada`, tools MCP do módulo saem do registro.

Shopify continua como origem alternativa de cobrança (`origem=shopify`): a empresa vinda da Shopify recebe `AssinaturaModulo` conforme o `shopify_plan_handle`.

## 6. Gating por módulo

Backend:

- Decorator `@require_modulo('carrinho')` em `api_admin/_helpers.py`, ao lado de `_require_empresa_admin`; aplicado por família de rota em `core/urls.py` (tabela rota → módulo). Resposta `403 {codigo:'modulo_bloqueado', modulo, argumento}`.
- Guard nas tasks: `tasks/_guards.py::modulo_ativo_ou_pula(empresa, modulo)` chamado no início de cada task de disparo; campanha agendada de módulo cancelado vira `pausada` com motivo.
- Cota: `pode_enviar(empresa, modulo)` passa a comparar `ConsumoMensal(modulo)` com a cota; ao atingir 100% bloqueia com `codigo='cota_excedida'`; 80% e 100% geram notificação.
- Templates: filtro `?modulo=` usa o mapa submódulo → módulo comercial.

Frontend:

- `moduleGuard(key)` deixa de redirecionar e resolve para a mesma rota com `bloqueado=true`; o componente do módulo renderiza KPIs de exemplo + overlay de paywall contextual (`GET /api/modulos/:key/argumento/` devolve um insight real da base, ex.: "610 clientes a recuperar").
- `GET /api/auth/me/` devolve `modulos: [{key, status, trial_fim, uso_ciclo, cota}]`.

## 7. Frontend Angular: nova estrutura

```
src/app/
  layouts/
    public-layout/        header marketing (links dos 5 módulos, Entrar, Testar grátis) + footer
    onboarding-layout/    barra de passos, sem sidebar, mobile first
    app-shell/            layout atual, sidebar = Início · Assistente · Meus módulos (5, com lock) · Minha base · Conta
  features/
    public/               landing/:slug (pré-render estático p/ SEO) · cadastro · login
    onboarding/           empresa · modulos · pagamento · whatsapp · loja · pronto  (+ status polling)
    home/                 início: KPIs consolidados, cards dos 5 módulos, sugestão do assistente
    modulos/
      carrinho/           dashboard · configurar · mensagens · historico   (move recuperacao-carrinho + cancelados)
      disparo/            dashboard · campanhas (reativação/sazonal/informativo/novidades/giftback/manual) · rfv · mensagens · historico
      retencao/           dashboard · boas-vindas · aniversario · mensagens · historico
      flow/               dashboard · catalogo · ia-vendedora (nós B2C) · flows · conversas
      campanhas/          dashboard · objetivos (O1–O14) · cupons · aprovacoes · historico
      _shared/            modulo-shell (cabeçalho, abas, banner de trial, painel do assistente) · modulo-bloqueado · modulo-checkout
    assistente/           hub-assistente (painel dockado à direita / bottom-sheet no mobile; contexto = módulo ativo)
    conta/                assinatura-uso (Stripe) · conectar-mcp · configuracoes (existente)
  core/
    config/modulos.config.ts   ModuloKey = 'carrinho'|'disparo'|'retencao'|'flow'|'campanhas'
                               ModuloDef += cor, slugLanding, argumentoCurto, feats[], preco
    services/billing.service.ts · assistente.service.ts (SSE) · cnpj.service.ts · onboarding.service.ts
```

Rotas: `/lp/:slug` · `/cadastro` · `/login` · `/onboarding/:passo` · `/app` (início) · `/app/m/:modulo/(dashboard|configurar|mensagens|historico|assistente)` · `/app/m/:modulo/ativar` (checkout) · `/app/assistente` · `/app/conta/(assinatura|mcp)`.

Mobile: tab bar inferior (Início · Módulos · Assistente · Conta) usando `bottom-sheet` existente para o assistente; KPIs em grade 2×2; abas roláveis; formulários de uma coluna. Web: sidebar como hoje, assistente fixo à direita (360 px) em toda tela de módulo.

Regra de UX mantida das telas atuais: toda tela de envio tem Dashboard com KPIs + funil + mensal + histórico, e a aba Configurar segue a sequência de cards numerados (Nome → Canal → Mensagem → Regra → Agendamento → Revisão).

## 8. Assistente conversacional (MCP)

Arquitetura em duas portas sobre o mesmo servidor de tools:

```
Angular <hub-assistente [modulo]>  ──SSE──▶  POST /api/assistente/conversar/
                                              │  monta system prompt (empresa, módulo, config atual)
                                              │  tools = mcp_server filtradas por modulos_ativos ∩ escopo do usuário
                                              ▼
                                         Claude API (tool use, streaming)
                                              │  tool de escrita → devolve ToolCard {tool, diff, status:'pendente'}
                                              │  usuário confirma no chat → POST /api/assistente/confirmar/{id}
                                              ▼
                                         executa a tool (mesma função do mcp_server) → LogMcp

Claude Desktop / claude.ai / ChatGPT  ──Streamable HTTP──▶  https://mcp.hub.ecomsmart.com.br/:slug
                                              OAuth 2.1 (Django como authorization server), escopos <modulo>:read|write
                                              lista de tools dinâmica por tenant: módulo bloqueado = tool não registrada
```

Regras: toda escrita exige confirmação (elicitation); nenhuma tool envia mensagem, só agenda; `assinatura.manter_modulo` e `assinatura.ativar_trial` abrem a confirmação de cobrança, nunca cobram sozinhas; `MCPToken.key` migra para hash.

Tools de escrita a criar (o `mcp_server` hoje é quase todo leitura):

| Módulo | Tools |
|---|---|
| comum | `hub.resumo_semana`, `hub.modulos_status`, `templates.listar/arquivar`, `canais.status`, `assinatura.ativar_trial`, `assinatura.manter_modulo` |
| carrinho | `carrinho.regua_atual`, `carrinho.atualizar_regua`, `carrinho.trocar_template`, `carrinho.pausar/retomar`, `carrinho.kpis` |
| disparo | `rfv.resumo`, `rfv.contar_publico`, `rfv.listar_clientes`, `campanhas.criar_{reativacao,sazonal,informativo,novidade}`, `campanhas.agendar/cancelar`, `campanhas.desempenho` |
| retencao | `retencao.boas_vindas.configurar/ativar`, `retencao.aniversario.configurar/ativar`, `retencao.kpis`, `retencao.previa_dia` |
| flow | `flow.regras.atualizar`, `prompts.nova_versao`, `flow.simular_conversa`, `catalogo.status`, `flow.horario_humano` |
| campanhas | `campanhas.analisar_estoque`, `campanhas.sugerir_ofertas`, `campanhas.aprovar_objetivo`, `campanhas.simular_margem`, `cupons.regras` |

## 9. Módulos: telas e reaproveitamento

Cada módulo usa o mesmo `modulo-shell`: cabeçalho (ícone, nome, status, banner de trial), abas Dashboard · Configurar · Mensagens · Histórico · Assistente, painel do assistente à direita.

- **Carrinho**: move `recuperacao-carrinho/` e `recuperacao-cancelados/` para dentro; aba Configurar = régua (3 regras com delay/template/cupom) + origens (webhooks) + canal.
- **Disparo RFV**: `analise-rfv/` vira a aba RFV; as famílias one-shot viram sub-abas de Campanhas; `informativos` e `giftback` seguem aqui.
- **Retenção**: `aniversariantes/` e `boas-vindas/` como sub-abas; `RetencaoAutomacaoState` mantido.
- **E-commerceFlow**: Catálogo (produtos sincronizados, status), IA vendedora (editor de prompts B2C já existente, versionado), Flows (Flow JSON por empresa, publicar/testar, endpoint de dados criptografado `/api/publica/flows/data/`), Conversas (bate-papo existente filtrado pelo canal do Flow). Pedido criado via provider da `Integracao`.
- **Campanhas Inteligentes**: tela "Ofertas sugeridas" (objetivos O1–O14 com clientes elegíveis, tier RFV, horário e mensagem sugerida) migrada para o Hub; aba Cupons (regras por faixa, validade, mínimo); aba Aprovações (fila diária às 07:00). Motor: portar o Mamdani do assistente de marketing para `core/campanhas_inteligentes/` (entradas: recência, frequência, valor, afinidade `cesta.py`, giro de estoque; saída: prioridade por objetivo × cliente).

## 10. Fases de entrega

| Fase | Escopo | Dependências |
|---|---|---|
| 0 | `MODULOS_CATALOGO` 5 keys + migração; `AssinaturaModulo`; `ConsumoMensal.modulo`; `@require_modulo`; `pode_enviar` real; `me` com `modulos` | nenhuma |
| 1 | Landing ×5 (pré-render), cadastro com CNPJ, login público, `public-layout` | listacnpj exposto via `/api/publica/cnpj/` |
| 2 | Onboarding 6 passos + provisionamento SmartConversas + Embedded Signup + integração loja | credenciais Meta (Embedded Signup app review) |
| 3 | Stripe (catálogo, setup-intent, assinar, webhooks, portal), tela Assinatura & uso, checkout in-app, módulo bloqueado | conta Stripe BRL |
| 4 | Novo app-shell + `modulo-shell` + migração das telas existentes para os 3 módulos maduros (carrinho, disparo, retenção) | fase 0 |
| 5 | Assistente MCP interno (SSE + confirmação) + tools de escrita dos 3 módulos maduros; MCP externo com OAuth 2.1 | fase 4 |
| 6 | Campanhas Inteligentes no Hub (motor fuzzy, cupons, aprovações) | fase 4 |
| 7 | E-commerceFlow (Flows, catálogo, pedido via integração) | canal oficial com Flows habilitado |

## 11. Decisões abertas

- Trial exige cartão? Protótipo assume **opcional** (aumenta conversão; risco de trial sem conversão). Alternativa: cartão obrigatório no passo 3.
- Uma Subscription por módulo (recomendado) versus uma Subscription com vários itens (fatura única, mas trial e cancelamento por item ficam complexos).
- Empresas atuais em plano fechado: mapear `principal` → 3 módulos sem cobrança adicional até renegociação, marcando `origem=direta`.
- Cota por módulo ou pool compartilhado quando há mais de um módulo? Protótipo mostra por módulo; pool simplifica a leitura para o cliente.
- Fuzzy: portar a lógica que hoje vive no prompt para código determinístico (auditável) e usar a IA só para redigir a mensagem.
