R-001 — Expiração de pagamento PIX e cancelamento de sessões
TLDR: Um pagamento PIX não confirmado em 30 minutos é expirado pelo sistema, que destrói permanentemente todas as sessões
pendingvinculadas àquela order.
Given / When / Then
Dado uma order com payment_method: pix e payment.status: pending
Quando o Payments::ExpireJob roda 30 minutos após a criação do pagamento sem que o webhook de confirmação tenha chegado
Então o pagamento passa a expired, as sessões são canceladas com o motivo “Sistema: Cancelada por falta de pagamento.” e as sessões pending são destruídas permanentemente
Tabela de decisão
| Status do pagamento na execução do job | Status das sessões | Resultado |
|---|---|---|
pending |
pending |
Pagamento → expired, sessões destruídas |
pending |
scheduled |
Pagamento → expired, sessões canceladas (não destruídas) |
paid / expired / qualquer outro |
qualquer | Job sai cedo — nada muda |
Restrições
- A janela de expiração é de exatamente 30 minutos a partir da criação do pagamento (
Payments::QueueJobs) Payments::Expiresó roda sepayment.pending?— idempotente por construção- Sessões em
pendingsão destruídas permanentemente (sem soft delete) viaMeetings::DestroyAllByRelatedPayment - Sessões já
scheduledsão apenas canceladas, não destruídas - Se o webhook do gateway chegar depois da expiração confirmando o pagamento como
paid, o sistema atualizapayment.statusparapaidmas não reativa nem recria as sessões —UpdateMeetingsByGatewayStatusprotege comorder.meetings.pending.exists?
Risco de condição de corrida
Existe uma janela crítica entre a criação do pagamento e a expiração em 30 minutos:
00:24 — Order + Payment criados (pending)
00:25 — Cliente paga o PIX
00:54 — ExpireJob roda → pagamento expirado, sessões destruídas
~depois — Webhook chega → pagamento atualizado para paid, mas não existe mais nenhuma sessão
Nesse cenário o pagamento fica paid no banco, sem nenhuma sessão ativa e sem repasse ao terapeuta — exigindo intervenção manual. O caso real está registrado em payments_pix_expiration_race_condition.
Teste vinculado
spec/use_cases/payments/expire_spec.rb — (sem teste ainda)