R-001 — Expiração de pagamento PIX e cancelamento de sessão

TLDR: Um pagamento PIX não confirmado em 30 minutos é expirado pelo sistema, que destrói permanentemente todas as sessões pendentes ligadas àquela order.

Given / When / Then

Dado uma order com payment_method: pix e payment.status: pending Quando Payments::ExpireJob roda 30 minutos após a criação do pagamento sem webhook de confirmação Então o pagamento é marcado como expired, as sessões são canceladas com o motivo “Sistema: Cancelada por falta de pagamento.” e as sessões pendentes 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 / outro qualquer Job sai antecipadamente — nenhuma mudança

Restrições

  • A janela de expiração é exatamente 30 minutos a partir da criação do pagamento (Payments::QueueJobs)
  • Payments::Expire só roda se payment.pending? — idempotente por design
  • Sessões em estado pending são destruídas permanentemente (sem soft delete) via Meetings::DestroyAllByRelatedPayment
  • Sessões já scheduled são apenas canceladas, não destruídas
  • Se o webhook do gateway chegar depois da expiração e confirmar o pagamento como paid, o sistema atualiza payment.status para paid mas não reativa ou recria as sessões — UpdateMeetingsByGatewayStatus verifica order.meetings.pending.exists?

Risco de race condition

Existe uma janela crítica entre a criação do pagamento e a expiração de 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 há sessões

Nesse cenário o pagamento acaba paid no banco mas sem sessões ativas e sem payout para o terapeuta — exigindo intervenção manual.

Teste vinculado

spec/use_cases/payments/expire_spec.rb — (sem teste ainda)