Flujo de Reservas
Estados de una reserva
Section titled “Estados de una reserva”Jugador selecciona slot │ ▼┌─────────────────┐│ PENDIENTE_PAGO │ ← Slot BLOQUEADO para otros jugadores│ (TTL: 15 min) │└────────┬────────┘ │ ┌────┴────┐ │ │ ▼ ▼┌───────┐ ┌──────────┐│Pago OK│ │Timeout / ││ │ │Cancelar │└───┬───┘ └─────┬─────┘ │ │ ▼ ▼┌──────────┐ ┌──────────┐│CONFIRMADA│ │CANCELADA │ ← Slot LIBERADO└────┬─────┘ └──────────┘ │ ▼┌──────────┐│COMPLETADA│ (fecha/hora ya pasó)└──────────┘Reglas de bloqueo
Section titled “Reglas de bloqueo”-
Cuando un jugador selecciona un slot para reservar, el sistema crea una reserva en estado
PENDIENTE_PAGO. -
El slot queda BLOQUEADO — ningún otro jugador puede reservar ese mismo horario en esa cancha mientras exista una reserva
PENDIENTE_PAGOactiva. -
TTL de 15 minutos: si el pago no se confirma en 15 minutos, la reserva expira y el slot se libera automáticamente.
-
Validación de pago: la reserva pasa a
CONFIRMADAcuando:- El jugador completa el pago en MercadoPago (vía webhook), O
- El dueño marca manualmente el pago como verificado desde su panel
-
Reservas manuales del dueño: cuando el owner crea una reserva manual, pasa directamente a estado
CONFIRMADA(no requiere pago).
TTL — Liberación automática
Section titled “TTL — Liberación automática”Las reservas en estado PENDIENTE_PAGO tienen un tiempo máximo de vida de 15 minutos. Si el pago no se confirma en ese lapso, la reserva se cancela automáticamente y el slot se libera.
Mecanismo
Section titled “Mecanismo”El TTL usa un enfoque de limpieza oportunista — sin cron jobs ni procesos externos:
- Cada vez que un usuario consulta disponibilidad (
GET /api/disponibilidad) - Cada vez que se crea una nueva reserva (
POST /api/reservas) - Manualmente vía
POST /api/reservas/liberar-expiradas
En cualquiera de estos casos, el sistema barre las reservas PENDIENTE_PAGO con más de 15 minutos de antigüedad y las marca como CANCELADA.
Notificaciones de expiración
Section titled “Notificaciones de expiración”Cuando una reserva expira:
- Jugador: recibe notificación in-app + email informando que su reserva expiró
- Dueño: recibe notificación de que el slot fue liberado
Notificaciones
Section titled “Notificaciones”El sistema de notificaciones cubre todos los cambios de estado:
| Evento | Jugador | Dueño |
|---|---|---|
| Reserva creada | ✅ in-app + email | ❌ |
| Pago confirmado | ✅ in-app + email | ✅ in-app + email |
| Reserva cancelada | ✅ in-app + email | ✅ in-app + email |
| Partido completado | ✅ in-app + email | ❌ |
| Reserva expirada (TTL) | ✅ in-app + email | ✅ in-app + email |
Canales
Section titled “Canales”- In-app: campanita con badge + drawer de notificaciones (persistente, marca como leída)
- Email: vía Resend (requiere
RESEND_API_KEYen.env)
Bloqueo por solapamiento
Section titled “Bloqueo por solapamiento”El sistema valida que no existan dos reservas activas (PENDIENTE_PAGO o CONFIRMADA) para la misma cancha con horarios solapados:
SELECT * FROM reservasWHERE cancha_id = $1 AND estado IN ('PENDIENTE_PAGO', 'CONFIRMADA') AND slot_inicio < $fin AND slot_fin > $inicioSi existe un conflicto, la API devuelve 409 Conflict.
Próximas mejoras
Section titled “Próximas mejoras”- Webhook de MercadoPago: actualización automática de estado al recibir pago
- Recordatorio al jugador antes del partido