0008 — Modelo Financeiro Revenue Share + Attribution Permanente
Status
Accepted · 2026-04-26 · Autor: Alexandre + Claude. Após 4 turnos de refinamento na sessão de geração do docs/bootstrap-archive/.
Esta é a decisão financeira mais crítica do produto. Mudança aqui = mudança de modelo de negócio = ADR explícito + 30 dias de comunicação prévia aos consultores.
Contexto
Modelos B2B SaaS típicos cobram do consultor uma assinatura para usar a plataforma. Alexandre escolheu modelo invertido (revenue share / affiliate) por 5 razões:
- Atratividade pra consultor: ninguém quer pagar mensalidade pra usar plataforma. Mas todo consultor topa receber 50% por cada cliente trazido.
- Velocidade de aquisição: cada consultor credenciado vira agente comercial. Plataforma não vende; consultores vendem.
- Pricing simples: preço único definido por Alexandre no painel. Sem tiered pricing complexo.
- Marketplace ativo: clínicas órfãs no marketplace têm consultores motivados pra atender (recebem 50% do plano).
- Escalável globalmente: modelo replicável internacionalmente.
Decisão
1. Plano único: R$ 700/mês
Definido em platform_settings:
INSERT INTO public.platform_settings (key, value) VALUES ('platform.plan_price_brl_cents', '70000'), -- R$ 700,00 ('platform.consultant_commission_percent', '50'), ('platform.platform_billing_grace_days', '30'), ('platform.client_billing_grace_days', '30'), ('platform.programa_acolhe_credit_brl_cents', '40000'); -- R$ 400 por consulta cumpridaConsultor não pode alterar o preço do plano (regra inviolável). Pode cobrar a consultoria que quiser (100% dele).
2. Attribution diferenciada
-- clinics.attribution_type:-- - consultancy_acquisition → consultor captou via lead; recebe 50% PERMANENTE-- - marketplace_acquisition → clínica entrou direto + escolheu consultor; recebe 50% ENQUANTO atender-- - platform_direct → clínica direta sem consultor; ninguém recebe (plataforma 100%)
-- Função canônica:CREATE OR REPLACE FUNCTION public.who_receives_commission(p_clinic_id UUID)RETURNS UUIDLANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT CASE WHEN c.attribution_type = 'consultancy_acquisition' THEN c.attribution_consultancy_id -- permanente WHEN c.attribution_type = 'marketplace_acquisition' THEN c.current_consultancy_id -- segue quem atende ELSE NULL -- platform_direct: 100% plataforma END FROM public.clinics c WHERE c.id = p_clinic_id AND c.deleted_at IS NULL;$$;attribution_consultancy_id é IMUTÁVEL após primeiro set. Trigger prevent_attribution_mutation bloqueia UPDATE (regra inviolável §5.7.2).
3. Pacotes Avulsos: 0% comissão
Se clínica órfã compra pacote avulso de qualquer consultor credenciado, plataforma cobra 0%. Consultor recebe 100%. Razão: pacote avulso é consultoria; plataforma não cobra consultoria. Detalhes em ADR 0010.
4. Cobrança plataforma → consultor (B2B mensal)
Cron platform-billing-cycle-cron rodando 1x/dia:
// Pseudo-códigofor (const consultancy of activeConsultancies) { if (today === consultancy.subscription.current_period_end) { const billableClinics = await getBillableClinics(consultancy.id); const totalCharge = billableClinics.length * 200; // R$ 200 por clínica (R$ 700 × 50% = R$ 350; depois -R$ 100 do crédito Acolhe quando vaga cumprida; depois -R$ 50 outros descontos)
const adjustments = calculateAcolheCredits(consultancy.id, currentPeriod); const finalCharge = totalCharge - adjustments;
await createAsaasInvoice({ account: 'platform', // cobra do CONSULTOR via Asaas DA PLATAFORMA customer: consultancy.asaas_customer_id_in_platform_account, value_cents: finalCharge, due_date: addDays(today, 7), }); }}Função consultancy_billable_clinic_count(consultancy_id, at_timestamp) conta clínicas elegíveis:
CREATE OR REPLACE FUNCTION public.consultancy_billable_clinic_count( p_consultancy_id UUID, p_at_timestamp TIMESTAMPTZ DEFAULT NOW()) RETURNS INTLANGUAGE SQL STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT COUNT(*)::INT FROM public.clinics c WHERE public.who_receives_commission(c.id) = p_consultancy_id AND c.status = 'active' AND c.deleted_at IS NULL AND c.provisioned_at IS NOT NULL AND c.provisioned_at <= p_at_timestamp;$$;5. Fluxo financeiro mensal típico
Mês N: Clínica X (active, attribution=consultancy_acquisition, who_receives=Karla)
Dia 1: Asaas DA KARLA cobra R$ 700 da clínica Dia 5: Cliente paga → Webhook 'consultor-asaas-webhook' notifica plataforma: PAYMENT_CONFIRMED → Plataforma registra em billing_events (idempotência) → Karla recebe R$ 700 - taxas Asaas (digamos R$ 670 líquido)
Dia 25 (fim do ciclo da Karla na plataforma): → platform-billing-cycle-cron roda → consultancy_billable_clinic_count(Karla.id) = 7 clínicas → 7 × R$ 200 = R$ 1.400 (cobrança bruta) → Aplicar créditos Acolhe: 3 clínicas cumpriram vaga este mês = -3×R$100 = -R$ 300 (Karla absorve metade do crédito Acolhe) → Aplicar outros ajustes → Total: R$ 1.100 (cobrança líquida) → Asaas DA PLATAFORMA emite invoice pra Karla
Dia 32: Karla paga R$ 1.100 → Plataforma recebe; aplica em consultancy_invoices.paid_at
Resultado mensal de Karla: Recebeu: 7 × R$ 670 = R$ 4.690 do plano + valor da consultoria que ela cobra Pagou: R$ 1.100 pra plataforma Margem do plano: R$ 4.690 - R$ 1.100 = R$ 3.590 (~51% do que cobrou da clínica em plano) + Margem da consultoria: 100% do que cobrou separadamente
Resultado mensal da plataforma: Receita líquida: R$ 1.100 da Karla (× N consultorias ativas) Custos: Supabase (~$50), Netlify (~$19), Asaas fees, Resend, Sentry, Anthropic API6. Fallback de cobrança (situações de inadimplência)
6.1 Cliente não paga consultor (30 dias)
Dia 5: Asaas do consultor envia "PAYMENT_OVERDUE" via webhook→ clinics.platform_subscription_state = 'past_due'→ Email pra clínica + cópia pro consultor
Dia 35 (30 dias após vencimento):→ clinics.platform_subscription_state = 'suspended'→ Acesso da clínica BLOQUEADO (RequireClinicSubscriptionActive bloqueia)→ Consultor NÃO recebe os R$ 200 (plataforma não cobra)→ Plataforma também não recebe nada do mês6.2 Consultor não paga plataforma (30 dias)
Dia 32: Karla recebe invoice B2B vencidaDia 62 (30 dias após vencimento):→ consultancies.platform_billing_state = 'suspended_for_billing'→ Cron 'platform-billing-fallback-trigger' roda: 1. Carrega clínicas ativas de Karla (com billing_via='consultancy_asaas') 2. Pra cada clínica: - Cancela subscription no Asaas DA KARLA (usa API key dela ainda válida) - Cria customer + subscription no Asaas DA PLATAFORMA - clinics.billing_via = 'platform_fallback' - clinics.current_asaas_subscription_id = novo ID - clinics.current_asaas_account = 'plataforma' - Email pra clínica: "a partir do próximo ciclo, sua cobrança vem direto da plataforma" 3. Karla é bloqueada da plataforma (não acessa /consultancy/*)
Comissões do(s) mês(es) durante atraso são PERDIDAS (regra contratual; sem ressarcimento).6.3 Consultor regulariza
Dia 90: Karla paga atrasos→ consultancies.platform_billing_state = 'current'→ Cron 'platform-billing-restore-trigger' detecta no próximo ciclo: 1. Pra cada clínica em billing_via='platform_fallback' attribuída a Karla: - Cancela subscription no Asaas DA PLATAFORMA - Cria de volta no Asaas DA KARLA (cred ainda em consultancy_billing_accounts) - clinics.billing_via = 'consultancy_asaas' - Email pra clínica: "cobrança volta a vir do consultor X" 2. Karla volta ao acesso normal
NUNCA há ressarcimento retroativo dos meses durante atraso.7. Crédito narrativo do Programa Acolhe (Opção D)
Detalhes em ADR 0014. Resumo:
- Clínica paga R$ 700/mês cheio sempre
- Quando cumpre vaga social: fatura SUBSEQUENTE tem 2 linhas:
Plano Mensal: R$ 700+Crédito Programa Acolhe: -R$ 400 - Total: R$ 300 nesse mês
- 6 meses de vaga cumprida + 6 meses sem = R$ 300×6 + R$ 700×6 = R$ 6.000/ano (igual a R$ 500 fixo, mas com efeito psicológico de “ganhar”)
- Plataforma e consultor absorvem 50/50 o crédito (cada um perde R$ 200)
8. Tabelas implicadas
-- Subscription B2B do consultor com a plataformaCREATE TABLE public.consultancy_subscriptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), consultancy_id UUID UNIQUE NOT NULL REFERENCES public.consultancies(id), asaas_customer_id TEXT, -- ID do consultor como customer no Asaas DA PLATAFORMA status TEXT NOT NULL DEFAULT 'trialing' CHECK (status IN ('trialing', 'active', 'past_due', 'paused', 'canceled')), trial_ends_at TIMESTAMPTZ, current_period_start TIMESTAMPTZ, current_period_end TIMESTAMPTZ, next_billing_date DATE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW());
-- Faturas B2B (plataforma → consultor)CREATE TABLE public.consultancy_invoices ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), consultancy_id UUID NOT NULL REFERENCES public.consultancies(id), period_year_month DATE NOT NULL, -- 1º dia do mês de competência base_amount_brl_cents INT NOT NULL, acolhe_credit_brl_cents INT DEFAULT 0, other_adjustments_brl_cents INT DEFAULT 0, total_charge_brl_cents INT NOT NULL, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'paid', 'overdue', 'cancelled')), asaas_invoice_id TEXT UNIQUE, due_date DATE NOT NULL, paid_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (consultancy_id, period_year_month));
-- Snapshot de quantas clínicas billable por mês (auditoria)CREATE TABLE public.consultancy_billing_snapshots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), consultancy_id UUID NOT NULL REFERENCES public.consultancies(id), period_year_month DATE NOT NULL, billable_clinic_count INT NOT NULL, acolhe_vagas_cumpridas INT DEFAULT 0, snapshot_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (consultancy_id, period_year_month));9. Edge functions
consultor-asaas-webhook— recebe eventos do Asaas dos consultores (B2C clínicas) — registrado dinamicamente viaregister-consultor-asaas-webhookregister-consultor-asaas-webhook— quando consultor configura creds, plataforma usa API key dele pra registrar webhook próprioplatform-billing-cycle-cron— emite invoices B2B mensaisplatform-billing-fallback-trigger— detecta consultor inadimplente e ativa fallbackplatform-billing-restore-trigger— detecta consultor regularizado e restaura cobrança via consultorhealth-check-consultor-webhooks— semanal, valida que webhooks ainda estão ativos no Asaas dos consultores
Consequências
Positivas
- Pricing transparente: clínica paga R$ 700, consultor recebe 50%, plataforma 50%
- Attribution permanente justa: quem capta é recompensado pra sempre
- Marketplace dinâmico: comissão flutuante pra órfãos incentiva consultor a aceitar atendimento
- Sem complexidade fiscal: split é abatimento na fatura B2B; crédito Acolhe idem (Opção D — não é pagamento)
- Resiliente a inadimplência: fallback automático protege plataforma sem perder cliente
- Escalável: modelo replicável internacionalmente
Negativas
- Complexidade técnica: 6+ edge functions só pra billing
- Dependência de Asaas: uptime do Asaas afeta plataforma (mitigado por idempotência)
- Carga de cron diário:
platform-billing-cycle-cronprecisa lidar com timezone, ciclo de cada consultoria
Riscos
- Bug em
who_receives_commissionvaza dinheiro pra consultor errado- Mitigação: tests unitários extensivos da função; auditoria em snapshots mensais
- Bug em fallback cobra em duplicidade
- Mitigação: cancelamento de subscription primeiro, criação depois (transação Postgres + side-effect compensável)
- Asaas API key revogada durante fallback
- Mitigação: graceful degradation — alerta + email pra consultor regularizar; não cobra retroativo
Alternativas consideradas
1. Plataforma cobra consultor (B2B SaaS clássico)
- Prós: padrão de mercado
- Contras: consultor não topa; barreira de entrada alta; modelo affiliate seria sobreposto. Rejeitado (decisão estratégica do Alexandre).
2. Tiered pricing (Starter / Growth / Scale)
- Prós: escala granular
- Contras: pricing complicado; venda confusa
- Rejeitado — começar simples; reavaliar em F1.5 com dados reais.
3. Split payment via Asaas (em vez de invoice mensal B2B)
- Prós: zero risco de inadimplência B2B (split automático)
- Contras: requer Asaas Plus; cada consultor configura split (UX complexa)
- Rejeitado pra F1; reavaliar em F1.5+ com 5+ consultores.
4. Plataforma como cobrador master (clínica paga plataforma direto, plataforma transfere consultor)
- Prós: plataforma controla cashflow
- Contras: muda quem é cobrador titular (clínica vê “pago pra plataforma”); contradiz intenção de “consultor é o cobrador” do Alexandre
- Rejeitado.
Atualizações de documentos
- AGENTS.md §5.7 (Modelo Financeiro), §inventário
- BLUEPRINT.md §4 (Modelo financeiro)
docs/edge-functions/platform-billing-cycle-cron.mddocs/edge-functions/consultor-asaas-webhook.mddocs/configuracoes-externas/asaas.md
Referências
- ADR 0001 (multi-consultancy)
- ADR 0010 (marketplace)
- ADR 0011 (cancelamento bilateral + fallback)
- ADR 0014 (Programa Acolhe — crédito narrativo)
- AGENTS.md §5.7