P0.3 · Modelo de dados, Stripe e captura de atribuição
Depende de P0.1. É a base comercial e de medição de tudo.
O prompt
Implemente o núcleo comercial do radar. A parte de atribuição é a que quebra em 90% dos projetos assim — capriche nela.
1. Modelos · app tenants
- Loja:
nome,dominio,plataforma(nuvemshop, shopify, tray, woocommerce, mercadolivre),plataforma_loja_id,criada_em,status - Usuario: custom user com e-mail como username,
telefoneem E.164, FK para Loja - Plano:
slug,nome,preco_centavos,intervalo,stripe_price_id,limites(JSON) - Assinatura: FK Loja, FK Plano,
stripe_subscription_id,stripe_customer_id,status,periodo_fim,cancelada_em,trial_fim
Semear via migração de dados:
| slug | nome | preço | intervalo | limites |
|---|---|---|---|---|
raiox-free | Raio-X da Loja | 0 | — | relatório 1× por mês |
preco-essencial | Radar de Preço Essencial | R$ 97,00 | anual | 30 SKUs, 3 concorrentes, 1×/dia, sem WhatsApp |
preco-pro | Radar de Preço Pro | R$ 49,00 | mensal | 200 SKUs, 10 concorrentes, 4×/dia, com WhatsApp |
anuncios | Radar de Anúncios | R$ 49,00 | mensal | 10 anunciantes |
combo | Combo Radar | R$ 197,00 | anual | essencial mais anúncios |
hub | EcomSmart Hub | R$ 299,00 | mensal | trial 7 dias, R$ 99 nos meses 1 a 3 |
Todo modelo com FK para Loja usa o manager escopado.
2. App atribuicao — leia com atenção
Toque: sessao_id, utm_source, utm_medium, utm_campaign, utm_content, utm_term, gclid, wbraid, gbraid, fbclid, fbp, fbc, referrer, landing_path, ip, user_agent, criado_em.
Middleware que, em toda requisição pública:
- Lê ou cria o cookie
rdr_sid— uuid4, 90 dias,SameSite=Lax,Secure, não HttpOnly (o JS do pixel precisa ler) - Se houver qualquer parâmetro de atribuição na querystring, grava um
Toquenovo - Constrói
fbca partir dofbclidcomofb.1.<timestamp_ms>.<fbclid>quando o cookie_fbcnão existir
Lead: FK Loja (nulo até conectar), e-mail, telefone, primeiro_toque, ultimo_toque, score, faturamento_90d_centavos, qualificado, fila (A/B/C), origem.
Guarde primeiro e último toque. O padrão do painel é último toque não direto; o primeiro fica para comparação.
3. App billing · Stripe
- Stripe Checkout Session em modo
subscription. Sempre cartão payment_method_collection: alwaysecustomer_creation: always— o cartão arquivado é o ativo mais importante desta operação- No
metadatada Session e do Customer:sessao_id, todos osutm_*,gclid,fbclid,lead_id,origem - Plano
hub:subscription_data.trial_period_days=7mais desconto de 3 ciclos levando a R$ 99. Documente a escolha de implementação - Webhooks com verificação de assinatura e idempotência por
event.idgravado em tabela:checkout.session.completed,customer.subscription.created/updated/deleted,invoice.paid,invoice.payment_failed,charge.refunded - Em
checkout.session.completed, dispare a tarefa Celery de conversão offline (implementada em P3.3). Deixe o gancho comTODOclaro e um teste que verifica que a tarefa foi enfileirada
Não use dj-stripe. Modelo próprio, enxuto, alimentado pelos webhooks acima.
4. Upgrade em um clique
POST /billing/upgrade/hub/ que, para Loja que já tem cartão na Stripe, cria a assinatura do Hub com o payment method salvo, sem novo checkout. Retorna 200 e o subscription_id. É o que o vendedor aciona ao telefone.
Critérios de aceite
- Teste criando Session com metadata completo, verificando todos os campos de atribuição
- Webhook com assinatura inválida retorna 400; evento repetido processa uma vez só
Lead.primeiro_toquenão muda em visitas posteriores;ultimo_toquemuda- Fluxo manual em modo teste: checkout do
preco-essencial, webhook, assinatura ativa, cartão salvo, upgrade para Hub sem novo checkout. Mostre os logs - Loja no
preco-essencialnão consegue cadastrar o SKU 31
Crie o Secret radar-stripe no namespace producao com STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PUBLISHABLE_KEY. Peça as chaves a mim — não invente.