R-001 — Resposta do apolo_membership é composta por duas filiações
TLDR:
GET /api/v1/apolo_membershipmonta a resposta a partir de duas filiações: os campos de situação (apolo_access_status,card_status,card_picture) vêm da filiação vigente elegível, e a data final (valid_until/valid_until_timestamp) vem sempre da filiação paga mais distante. Quando nenhuma filiação vigente passa no gate de elegibilidade, a resposta cai na filiação paga mais recente — o comportamento anterior.
Given / When / Then
Dado um usuário com mais de uma Membership paga (histórico de renovação, possivelmente com renovação antecipada ainda no futuro)
Quando GET /api/v1/apolo_membership?email=... é chamado
Então os campos de situação vêm da filiação vigente (valid_until >= hoje) mais recente que passa em Membership#apolo_access_permitted?, e valid_until/valid_until_timestamp vêm da filiação paga de maior valid_until — independente de qual filiação forneceu a situação
Tabela de decisão
| Cenário | Base dos campos de situação | apolo_access_status |
valid_until_timestamp |
|---|---|---|---|
| Vigente aprovada + renovação antecipada pendente | vigente | true |
da renovação (mais distante) |
| Vigente pendente + renovação aprovada | renovação | true |
da renovação |
| Nenhuma filiação vigente passa no gate | fallback: paga mais recente | false |
da paga mais recente |
| Todas as filiações vencidas | fallback: paga mais recente | false |
da paga mais recente |
| Nenhuma filiação paga | — | 404 |
404 |
Restrições
- A data final nunca anda para trás:
valid_until/valid_until_timestampsempre saem da filiação paga de maiorvalid_until. Essa garantia existe porque otrg-club-apidecide elegibilidade PRO lendo apenasvalid_until_timestamp— uma data mais próxima rebaixaria a assinatura de quem está pago. - O fallback para a filiação paga mais recente é obrigatório: sem ele o endpoint devolveria
404quando nenhuma candidata passa no gate, e404faz otrg-club-apirebaixar a assinatura. Membership#apolo_access_permitted?continua sendo a única fonte da verdade do gate — a seleção o consome, não o reimplementa em SQL.- A mudança é estritamente aditiva: nenhum usuário sai de
apolo_access_status: trueparafalse. Validado em produção sobre 18.262 usuários com filiação paga (192 corrigidos, 0 regressões). - Usuário sem nenhuma filiação paga continua recebendo
404. - A vigência é avaliada pelo scope existente
valid_gte_today, que inclui a filiação vencendo hoje: o literal de tempo vai sem tipo explícito, o Postgres resolve o operador comodate >= datee trunca a hora. Nenhum scope novo de vigência foi criado por causa disso. - A revogação automática é preservada: quando a filiação vigente vencer e a renovação continuar sem aprovação, o acesso volta a ser negado sem intervenção.
profile.status/profile_statuscontinuam vindo deuser_profile.documentation_status(user-level) — não são recompostos por esta regra. Atenção: desde#700dotrgclub-api(04/08/2026) esse campo é gate de PRO —Subscriptions::Pro::ValidateEligibility#documentation_approved?exige== "ok"eExpireWhenIneligibledesativa a assinatura quando falha. Não é mais um campo de display. Ver R-006.
Código
app/controllers/api/v1/apolo_membership_controller.rb
```ruby def membership @membership ||= valid_paid_memberships.detect(&:apolo_access_permitted?) || newest_paid_membership end
def apolo_payload membership.public_serialize.merge( valid_until: newest_paid_membership.valid_until.strftime(“%m/%Y”), valid_until_timestamp: newest_paid_membership.valid_until ) end ```
Teste vinculado
test/controllers/api/v1/apolo_membership_controller_test.rb — seleção da vigente elegível, guarda dos casos “vigente pendente + renovação aprovada”, override das datas, borda de vigência e os três fallbacks
test/models/membership_test.rb — valid_gte_today inclui filiação vencendo hoje; public_serialize expõe valid_until/valid_until_timestamp mesmo com apolo_access_permitted? falso
Histórico
- 2026-07-17: seleção trocada de
admin_approvedparapaidevalid_until/valid_until_timestampdeixaram de ser condicionados aapolo_access_permitted?— quem renovou e ainda não foi reaprovado tinha o PRO rebaixado notrg-club-api. Ver spec. - 2026-07-29: seleção passou a compor a resposta a partir de duas filiações — a troca para
paidfez a renovação antecipada (futura, ainda não aprovada) ser sempre escolhida, bloqueando o acesso de 192 membros no Apolo. Ver spec.