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
AutoSubscribeJobouPOST /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 >= hojeeprofile.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_unavailablee oExpireWhenIneligiblenã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 serializaprofile, 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_membershipsó seleciona filiações pagas (scope :paid), então um 200 sempre carregapayment_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 emPOST /api/v1/subscriptions/pros. Creation::CreateSubscriptionlimpaexpires_atao 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_subscriptionemExpireWhenIneligiblenão filtra porexpires_at(busca só porsubscription_planestatus: [:pending, :active]), de propósito: uma assinatura PRO que já esteja comexpires_atno passado mas aindastatus: active/pending(ex.: dado inconsistente por edição manual, ou qualquer estado anterior à correção) também precisa ser marcadainactivee logada aqui — do contrário fica presa nesse estado, invisível para esta rotina mas ainda vista como “PRO ativa” porValidateExistingSubscription(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.rbspec/requests/citrg/membership_expirations_spec.rbspec/use_cases/subscriptions/pro/validate_eligibility_spec.rbspec/use_cases/subscriptions/pro/expire_when_ineligible_spec.rbspec/use_cases/subscriptions/pro/creation/create_subscription_spec.rbspec/use_cases/subscriptions/pro/creation/create_flow_spec.rbspec/requests/api/v1/subscriptions/pros_expire_when_ineligible_spec.rb