Cliente fiel indica um amigo. Quando o amigo aparece e corta, os dois ganham — corte grátis ou desconto, configurável pela barbearia. Esta é a documentação visual de tudo que foi implementado.
A feature combina configuração por barbearia, um histórico imutável de prêmios, regras no banco e telas no app.
Tabela referral_program_configs. O dono define, por lado, o tipo de recompensa, valores, o modo do referidor e a validade.
Tabela referral_rewards. Uma linha por prêmio ganho, com snapshot do valor no momento — mudar a config depois não mexe no que já foi ganho.
credit_referral_reward emite os prêmios, close_appointment resgata o desconto de forma atômica, get_referral_program_stats calcula o custo realizado.
Configuração em /configuracoes/indicacoes, badge de resgate no fechamento da comanda, banner no agendamento e link enviado pelo bot do WhatsApp.
O gatilho é sempre a presença física: o crédito só acontece quando a comanda do indicado fecha como concluída.
Após cortar, Carlos recebe no WhatsApp seu link único ?ref=….
Manda pro amigo André.
André abre o link, vê o banner e marca o primeiro horário.
Corte concluído → o gatilho dispara.
André ganha boas-vindas; Carlos ganha conforme o modo.
Réplica de /configuracoes/indicacoes. Tudo independente por lado.
Quando o cliente da comanda tem um desconto de indicação pendente, aparece o badge. Mexa nos controles e clique em Aplicar — repare que o desconto manual fica intacto (não soma duas vezes) e o valor é "calculado no servidor".
A regra mais importante da fase 2. Clique em "André comparece" várias vezes e veja como o referidor é (ou não) premiado. No modo recorrente vale a regra "1 pendente por vez".
Depois que o corte é concluído, o worker do bot dispara (fire-and-forget) a mensagem com o link de indicação. O indicado abre o link e cai no agendamento com o banner.
O ?ref=ABC123 carrega o código de indicação do Carlos. Ele é opaco (sem PII) e validado dentro do tenant.
referred_by) só é gravado se o André for cliente novo.after() pra não travar a resposta.Cada combinação possível, com um exemplo concreto.
O padrão (igual fase 1). Os dois ganham um corte grátis no primeiro corte do indicado.
O carro-chefe da fase 2. X ganha desconto toda vez que Y volta, com a regra 1-pendente.
Em vez de %, um valor cravado em reais.
O % incide no total da comanda, mas limitado a um teto em R$.
Badge de um toque; valor calculado no servidor; resgate atômico com o fechamento.
Configurável; NULL = sem validade. Vale só pro desconto.
Mudar a config não altera prêmios já ganhos.
Resgatar indicação é aplicar desconto — exige a permissão apply_discount.
O desconto de indicação é bancado pela barbearia.
Várias guardas combinadas.
Onde cada coisa vive.
referral_program_configs (1 por barbearia)| Coluna | O que é |
|---|---|
enabled | liga/desliga o programa |
referrer_reward_type / referred_reward_type | free_service ou discount (por lado) |
*_service_id / *_uses | serviço grátis + qtd de usos (quando corte grátis) |
*_discount_mode / *_discount_value / *_discount_cap_cents | pct|fixed, valor e teto (quando desconto) |
referrer_mode | one_time ou recurring |
validity_days | dias de validade · NULL = sem validade |
referral_rewards (1 por prêmio ganho)| Coluna | O que é |
|---|---|
beneficiary_customer_id + role | quem recebe (referrer = X / referred = Y) |
source | welcome (boas-vindas) ou recurring |
reward_type + snapshot (discount_mode/value/cap, free_service_id/uses) | o prêmio congelado no momento que foi ganho |
earned_at / expires_at / redeemed_at / redeemed_amount_cents | ciclo de vida: ganho → expira ou é resgatado |
redeemed_at IS NULL AND (expires_at IS NULL OR expires_at > agora). A regra 1-pendente do modo recorrente só emite um novo prêmio de referidor se não houver nenhum pendente.referral_programMira só o tenant do ProBarbers (pelo group key). Outras barbearias recebem false → não veem a tela nem o badge.
Só um false explícito desabilita. Se o PostHog estiver inerte, mostra (comportamento seguro).
8 eventos cobrem o ciclo inteiro, do convite ao prêmio. Zero PII: só ids de tenant, contagens, valores agregados e enums.
?ref=referral_landing_viewedreferral_program_configured → referral_stats_viewed (recorrência de abertura dos relatórios).| Evento | Quando dispara | Propriedades | Lado |
|---|---|---|---|
referral_program_enabled | Dono ativa o toggle do programa | — | client |
referral_program_configured | Dono salva a configuração | referrer_uses, referred_uses | client |
referral_stats_viewed | Dono abre Indicações ou Relatórios | source: config | relatorios | client |
referral_link_viewed | Cliente abre a própria área | has_existing_code | client |
referral_link_shared | Cliente compartilha o link | share_method: native | clipboard | client |
referral_landing_viewed | Novo cliente abre um ?ref= válido | — | client |
referral_reward_credited | Crédito concedido (1º corte do indicado) | tenant_id, referrer_uses, referred_uses | server |
referral_discount_redeemed | Desconto de indicação resgatado no checkout | tenant_id, amount_cents | server |
tenant
server captureServerEvent — disparado em server actions
reward_credited, discount_redeemed) saem de dentro do after() no fechamento da comanda — nunca travam a ação principal e nunca carregam nome/telefone/email.