Pular para o conteúdo

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:

  1. Atratividade pra consultor: ninguém quer pagar mensalidade pra usar plataforma. Mas todo consultor topa receber 50% por cada cliente trazido.
  2. Velocidade de aquisição: cada consultor credenciado vira agente comercial. Plataforma não vende; consultores vendem.
  3. Pricing simples: preço único definido por Alexandre no painel. Sem tiered pricing complexo.
  4. Marketplace ativo: clínicas órfãs no marketplace têm consultores motivados pra atender (recebem 50% do plano).
  5. 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 cumprida

Consultor 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 UUID
LANGUAGE 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ódigo
for (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 INT
LANGUAGE 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 API

6. 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ês

6.2 Consultor não paga plataforma (30 dias)

Dia 32: Karla recebe invoice B2B vencida
Dia 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 plataforma
CREATE 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 via register-consultor-asaas-webhook
  • register-consultor-asaas-webhook — quando consultor configura creds, plataforma usa API key dele pra registrar webhook próprio
  • platform-billing-cycle-cron — emite invoices B2B mensais
  • platform-billing-fallback-trigger — detecta consultor inadimplente e ativa fallback
  • platform-billing-restore-trigger — detecta consultor regularizado e restaura cobrança via consultor
  • health-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-cron precisa lidar com timezone, ciclo de cada consultoria

Riscos

  • Bug em who_receives_commission vaza 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.md
  • docs/edge-functions/consultor-asaas-webhook.md
  • docs/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