R-004 — Elegibilidade PRO exige filiação válida e documentação aprovada
TLDR: A elegibilidade PRO exige duas condições na mesma resposta do CITRG — filiação dentro da validade e documentação aprovada (
profile.status == "ok"); antes desta regra só a validade era verificada.
Contexto
O Subscriptions::Pro::ValidateEligibility decidia a elegibilidade PRO a partir de um único campo do payload apolo_membership do CITRG: valid_until_timestamp. Um terapeuta com filiação paga e não expirada continuava PRO mesmo com a documentação nunca aprovada, em análise ou suspensa.
Given / When / Then
Dado um terapeuta em processo de promoção ou revalidação PRO
Quando o ValidateEligibility consulta GET apolo_membership no CITRG
Então pro_eligible só é true se, na mesma resposta, valid_until_timestamp >= hoje e profile.status == "ok"
Tabela de decisão
| Resposta do CITRG | Elegível | Motivo |
|---|---|---|
valid_until_timestamp >= hoje e profile.status == "ok" |
sim | — |
valid_until_timestamp < hoje |
não | expired |
profile.status em pending / analysis / suspended |
não | documentation_not_ok |
profile ausente do payload |
não | documentation_not_ok (fail closed) |
404 (sem usuário ou sem filiação paga) |
não | no_membership |
| qualquer outra falha, timeout, exceção | indefinido | citrg_unavailable |
A validade já era verificada antes desta mudança; a condição de documentação é o que é novo. As duas são avaliadas sobre o mesmo payload, então uma única consulta responde às duas.
Restrições
citrg_unavailablenunca pode rebaixar ninguém. OExpireWhenIneligiblejá retorna cedo comcontext.citrg_unavailable, e o guia de auditoria mantém essas linhas em um balde separado.- O status de pagamento deliberadamente não é verificado:
apolo_membershipsó seleciona filiações pagas (scope :paid, -> { where(payment_status: :paid) }na citrg-api, aplicado tanto emvalid_paid_membershipsquanto emnewest_paid_membership), então um200sempre carregapayment_status: "paid"e um filiado inadimplente responde404. - A expiração é aplicada em dois lugares, e isso é intencional — não são redundantes:
- o fluxo (
ValidateEligibility, esta regra) pega no sign-in do terapeuta e emPOST /api/v1/subscriptions/pros - o webhook (
POST /citrg/memberships/expired, ver pro_downgrade_on_citrg_expiry) pega os terapeutas que nunca mais fazem login
Os dois convergem no
ExpireWhenIneligible, que é idempotente, então uma dupla execução é inofensiva. - o fluxo (
Rebaixamento
Rebaixar para regular-terapeuta exige dois passos, não um:
Subscriptions::Pro::ExpireWhenIneligible— define a assinatura PRO comoinactive/expires_at: Time.currente reativa umregular-terapeutajá existente.Subscriptions::RegularTerapeuta::Creation::CreateSubscription— necessário quando o usuário nunca teve assinatura regular, porque o passo 1 usafind_by(...)&.update!e é no-op nesse caso. Sem ele o terapeuta fica sem nenhum plano ativo.
O índice único em subscriptions (user_id, subscription_plan_id) torna o passo 2 seguro de tentar incondicionalmente, mas o guia o protege mesmo assim para manter o log legível.
Rollout
A base PRO existente foi promovida sob a regra antiga, então terapeutas com documentação não aprovada já estão PRO e só são reavaliados no próximo sign-in. A varredura pontual é um script de console em duas fases (pro_eligibility_audit): a fase 1 audita e imprime, a fase 2 rebaixa uma lista explícita com dry-run por padrão. Um terapeuta rebaixado por engano é restaurado no próximo login pelo auto_subscribe_pro.
Testes vinculados
spec/use_cases/subscriptions/pro/validate_eligibility_spec.rbspec/use_cases/subscriptions/pro/expire_when_ineligible_spec.rb
Referências
app/use_cases/subscriptions/pro/validate_eligibility.rb- R-003 — Expiração da assinatura PRO por inelegibilidade no CITRG