Bloquear terapeuta logado de acessar o funil de checkout
TLDR: um terapeuta logado (regular ou pro) consegue hoje completar o checkout e agendar sessão com outro terapeuta; a correção estende o
routesGuardpara bloquear qualquer terapeuta logado nas 3 páginas do funil de checkout, mantendo cliente e visitante anônimo funcionando como hoje.
Status: proposed Created: 2026-08-07 Owner: @diegoFranciscoo
Context
A regra de negócio do produto é que o fluxo de agendamento/checkout é exclusivo para clientes (isCustomer) e visitantes anônimos (que se cadastram como cliente durante o próprio checkout). Terapeutas não deveriam conseguir agendar sessão com outro terapeuta por esse fluxo.
Essa regra já existe e funciona corretamente na página de perfil público /terapeuta/[slug]: o backend (TherapistSectionsManager, documentado em .project/docs/specs/20260806105144_professional_profile_contact_card.md) retorna a seção personal_contact (só o card de informações) em vez de scheduling (Agenda) quando quem está visualizando o perfil é um terapeuta logado. O frontend consome isso em PublicProfile.tsx via showSchedulling, então um terapeuta logado normalmente nunca vê o widget de Agenda.
O problema é que essa checagem nunca foi replicada nas páginas do funil de checkout. Reprodução do bug:
- Visitante anônimo escolhe uma sessão com um terapeuta e é redirecionado para
/checkout/{slug}/info?sessions=...sem estar logado. - Em vez de logar inline nessa mesma aba (fluxo normal do checkout), o usuário abre uma nova aba e loga com uma conta de terapeuta já existente (regular ou pro).
- Ele cola a mesma URL de checkout na aba autenticada.
- A página carrega normalmente — nenhuma das páginas do funil valida o tipo da sessão logada — e ele consegue completar o pagamento e agendar a sessão com o terapeuta-alvo.
Nenhuma das 3 páginas do funil (pages/checkout/[slug]/info.tsx, pages/app/checkout/[slug]/cartao.tsx, pages/app/checkout/[slug]/pix.tsx) valida isso hoje. info.tsx fica fora do matcher de middleware atual (["/app/:path*", "/cadastro/:path*"]), por isso o routesGuard nunca roda para ela.
Objetivos
- Bloquear qualquer terapeuta logado (regular ou pro) de acessar
/checkout/{slug}/info,/app/checkout/{slug}/cartaoe/app/checkout/{slug}/pix, redirecionando para/buscar-terapeuta. - Manter o acesso de visitante anônimo a
/checkout/{slug}/infofuncionando exatamente como hoje (sem exigir login). - Manter o acesso de cliente logado (
isCustomer) funcionando exatamente como hoje nas 3 páginas. - Consolidar a checagem num único validator do
routesGuard, seguindo o padrão já usado porminhaCarteiraValidator,meusClientesValidator, etc., em vez de duplicar a lógica em cadagetServerSideProps.
Non-goals
- Nenhuma mudança de backend/API.
- Nenhuma diferenciação entre terapeuta regular e pro — a regra bloqueia terapeuta de qualquer tier, sem exceção.
- Nenhuma mudança na página
/terapeuta/[slug]ou noPublicProfile.tsx— já funcionam corretamente. - Nenhuma mudança de UX para o cliente ou visitante anônimo.
Changes
-
middleware.ts— adicionar"/checkout/:path*"ao arraymatcher, que hoje é["/app/:path*", "/cadastro/:path*"]. -
src/infra/routesGuard/callbacks.ts(authGuard) — adicionar uma exceção pontual, no mesmo padrão da exceção já existente para/cadastro/certificado-invalido, que retornatruepara o pathname que casa com/^\/checkout\/[^/]+\/info$/, permitindo que o visitante anônimo continue acessando essa página sem login. Fora dessa exceção, mantémBoolean(token). - Novo validator
src/infra/routesGuard/validators/checkoutValidator/— implementaGuardValidator(mesmo formato dominhaCarteiraValidator:checkoutValidator.tsxcom a classe +index.tsreexportando).shouldValidate(req): testareq.nextUrl.pathnamecontra/^\/(app\/)?checkout\/[^/]+\/(info|cartao|pix)$/.validate(req): extraisessiondoreq.nextauth.token(mesmo padrão dominhaCarteiraValidator). Sesessionexistir e!isCustomer({ user: session } as TUserSession)(helper já existente em@/infra/permissionsManager), definethis.redirectPath = new URL("/buscar-terapeuta", req.url)e retornafalse. Caso contrário, retornatrue.- Visitante anônimo (sem token) nunca chega a rodar o validator, pois
middleware.tsjá retorna cedo quando!req.nextauth.token.
src/infra/routesGuard/validators/index.ts— registrarnew CheckoutValidator()emgetValidators(), junto dos demais validators existentes.
How to verify
- Teste automatizado do novo
checkoutValidator(seguindo o padrão derestrictedRoutesForRolesValidator.test.ts, já que é o único validator existente com teste): cobrir os 3 pathnames (info,cartao,pix) × 3 tipos de sessão (sem sessão/anônimo — não deve nem chegar ao validate via middleware, mas ovalidate()isolado deve retornartruesesessionforundefined; cliente; terapeuta regular; terapeuta pro). - Manual: como visitante anônimo, escolher uma sessão e chegar em
/checkout/{slug}/infosem estar logado — deve carregar normalmente. - Manual: reproduzir o cenário do bug (abrir nova aba, logar como terapeuta regular, colar a URL de checkout) — deve redirecionar para
/buscar-terapeuta. - Manual: repetir o mesmo teste logado como terapeuta pro — deve redirecionar para
/buscar-terapeutatambém. - Manual: como cliente logado, completar o fluxo de checkout normalmente até
cartao/pix— deve funcionar sem bloqueio.
Documentation
Nenhuma mudança de documentação de regra de negócio necessária — a regra em si (“agendamento é só para cliente/anônimo”) já está documentada em .project/docs/specs/20260806105144_professional_profile_contact_card.md; esta spec apenas fecha uma lacuna de enforcement que não seguia essa regra já existente.