RF45: Sistema valida pago
Descripción
El sistema valida que un pago de suscripción se haya completado correctamente antes de activar o mantener el acceso premium, usando como fuente de verdad los webhooks firmados de Stripe (no la respuesta del cliente).
Procesa invoice.payment_succeeded (pago confirmado) e invoice.payment_failed (fallo → dunning), siempre tras verificar la firma con stripe.webhooks.constructEvent() (Stripe §Webhooks).
| Campo | Valor |
|---|---|
| Módulo | Subscription Module |
| Actor | Sistema (handler de webhooks) |
| Endpoint | POST /webhooks/stripe |
| Precondiciones | Stripe emite un evento de pago |
| Prioridad | Alta (MVP) |
| Etapa | MVP |
| Requisitos relacionados | RF38, RF41, RF42, RF46 |
Reglas de negocio
- RN-45.1 — Todo evento se valida con la firma del webhook (
STRIPE_WEBHOOK_SECRET) antes de procesarse. - RN-45.2 — El acceso premium se concede/mantiene solo con un pago confirmado por webhook.
- RN-45.3 — Un pago fallido activa el dunning y la notificación (RF41); no concede acceso.
- RN-45.4 — El procesamiento es idempotente: reintentos del mismo evento no duplican efectos.
- RN-45.5 — Los eventos se registran (auditoría) sin datos de tarjeta.
Validaciones / consideraciones
| Aspecto | Regla |
|---|---|
| Firma | constructEvent obligatorio. |
| Idempotencia | Por event.id (no reprocesar). |
| Tipos | Solo eventos soportados; el resto se ignora seguro. |
Criterios de aceptación
Escenario 1: Pago confirmado
Dado que Stripe emite invoice.payment_succeeded con firma válida,
Cuando el backend lo procesa,
Entonces confirma el pago, mantiene/activa el acceso premium,
Y registra el pago (RF41 notifica).
Escenario 2: Pago fallido
Dado que Stripe emite invoice.payment_failed con firma válida,
Cuando el backend lo procesa,
Entonces marca la suscripción en gracia (dunning) y notifica al usuario (RF41),
Y no concede acceso premium adicional.
Escenario 3: Firma inválida (seguridad)
Dado que llega un webhook con firma inválida o cuerpo manipulado,
Cuando el backend lo verifica,
Entonces lo rechaza con 400 y no altera ningún estado.
Escenario 4: Evento duplicado (idempotencia)
Dado que Stripe reintenta el mismo event.id,
Cuando el backend lo recibe de nuevo,
Entonces no duplica la activación ni la notificación.
Criterios no funcionales
- Respuesta rápida al webhook (procesamiento pesado en segundo plano si aplica).
- Sin datos de tarjeta en logs; comunicación TLS 1.2+.