R-003 — Expiração da assinatura PRO por inelegibilidade no CITRG

TLDR: Toda vez que o fluxo de criação do PRO roda (sign-in do terapeuta via AutoSubscribeJob ou POST /api/v1/subscriptions/pros), a filiação CITRG é revalidada; se estiver expirada, com documentação não aprovada ou ausente, a assinatura PRO local é expirada e a assinatura de terapeuta regular é restaurada.

Modelo de negócio

  • Terapeuta regular = possui certificado válido do Apolo (plataforma de aulas), verificado no login via isCertified.
  • Terapeuta PRO = terapeuta regular + filiação CITRG que está dentro da validade e com documentação aprovada, verificado via GET apolo_membership (valid_until_timestamp >= hoje e profile.status == "ok").

As duas condições são obrigatórias. Uma filiação cuja documentação nunca foi aprovada, está em análise ou foi suspensa não concede PRO mesmo dentro da validade; e uma filiação expirada não concede PRO mesmo com documentação aprovada. Os critérios de elegibilidade em si estão detalhados em R-004.

Given / When / Then

Dado um usuário com assinatura PRO ativa (não expirada) Quando o Subscriptions::Pro::Creation::CreateFlow roda e o ValidateEligibility calcula pro_eligible == false (filiação expirada, documentação não aprovada ou sem filiação) Então o Subscriptions::Pro::ExpireWhenIneligible define a assinatura PRO como status: :inactive com expires_at: Time.current e reativa a assinatura de terapeuta regular (status: :active, expires_at: nil) na mesma transação

Tabela de decisão

Resposta do CITRG pro_eligible citrg_unavailable
200, valid_until_timestamp >= hoje e profile.status == "ok" true false
200, valid_until_timestamp < hoje false false
200, profile.status em pending / analysis / suspended false false
200, sem a chave profile no payload false false
404 (sem usuário ou sem filiação paga) false false
qualquer outra falha (5xx, auth, timeout) false true — ninguém é rebaixado
pro_eligible Estado da assinatura PRO Resultado
false pending/active, não expirada PRO expirado + plano de terapeuta regular reativado
false inexistente ou já expirada Plano de terapeuta regular reativado (status: :active, expires_at: nil)
true qualquer Nada muda aqui; o fluxo segue (PRO existente bloqueia recriação; PRO expirado é reativado com expires_at limpo)

Restrições

  • O rebaixamento só acontece com uma resposta autoritativa do CITRG: HTTP 200 com filiação expirada ou não aprovada, ou HTTP 404 (sem filiação). Qualquer outra falha (5xx, auth mal configurada) marca citrg_unavailable e o ExpireWhenIneligible não toca na assinatura — uma indisponibilidade do CITRG nunca pode rebaixar terapeutas PRO pagantes.
  • Um payload 200 sem a chave profile é tratado como não aprovado (fail closed). O endpoint sempre serializa profile, então a ausência dela indica payload inesperado; um terapeuta rebaixado por engano é restaurado no próximo sign-in.
  • O status de pagamento não é verificado: apolo_membership só seleciona filiações pagas (scope :paid), então um 200 sempre carrega payment_status: "paid" e um filiado inadimplente responde 404.
  • A expiração é aplicada tanto aqui quanto pelo webhook (pro_downgrade_on_citrg_expiry). Isso é intencional, não redundante: o fluxo pega quem faz login, o webhook pega quem nunca mais faz. Os dois convergem no ExpireWhenIneligible, que é idempotente.
  • O fluxo roda em todo sign-in de terapeuta (Subscriptions::Pro::AutoSubscribeJob, pulado no ambiente de teste) e em POST /api/v1/subscriptions/pros.
  • Creation::CreateSubscription limpa expires_at ao reativar uma assinatura PRO existente, então renovar a filiação ou aprovar a documentação repromove o usuário no próximo sign-in.
  • O finder de pro_subscription em ExpireWhenIneligible não filtra por expires_at (busca só por subscription_plan e status: [:pending, :active]), de propósito: uma assinatura PRO que já esteja com expires_at no passado mas ainda status: active/pending (ex.: dado inconsistente por edição manual, ou qualquer estado anterior à correção) também precisa ser marcada inactive e logada aqui — do contrário fica presa nesse estado, invisível para esta rotina mas ainda vista como “PRO ativa” por ValidateExistingSubscription (que já buscava sem filtro de data), bloqueando qualquer nova tentativa de assinatura.
  • As verificações no login não cobrem usuários cujo token se renova automaticamente sem novo sign-in — um job de revalidação recorrente é um follow-up conhecido. A varredura pontual está em pro_eligibility_audit.
  • A janela de 12 meses da plataforma de aulas Apolo (Anjo/Tutelado) ainda não é representada neste sistema — também um follow-up.

Testes vinculados

  • spec/models/subscription_spec.rb
  • spec/requests/citrg/membership_expirations_spec.rb
  • spec/use_cases/subscriptions/pro/validate_eligibility_spec.rb
  • spec/use_cases/subscriptions/pro/expire_when_ineligible_spec.rb
  • spec/use_cases/subscriptions/pro/creation/create_subscription_spec.rb
  • spec/use_cases/subscriptions/pro/creation/create_flow_spec.rb
  • spec/requests/api/v1/subscriptions/pros_expire_when_ineligible_spec.rb