DRF + tu BD. No mires solutions.md hasta entregar.
Ejercicio 1 — El cobro doble, reproducido
- Simula el reintento: en shell, llama dos veces a la creación de pago de una misma reserva (o curl dos veces). ¿Dos pagos? (la UNIQUE de idempotency_key debe impedirlo si generaste la clave igual... ¿o estás generando uuid4 en cada POST? Anota lo que pase).
- Discute: ¿dónde está el bug semántico — la BD rechaza el segundo, pero ¿con qué status responde tu API hoy?
Ejercicio 2 — Idempotency-Key real
- Crea el modelo
IdempotencyKey(key PK, request_hash, status, body JSONB, created_at)+ migración segura (11). - Implementa el mecanismo (decorador o helper en la vista de pagos): 1ª vez procesa y guarda; 2ª vez con mismo cuerpo devuelve lo guardado; mismo key + cuerpo distinto → 422.
- Demuéstralo con curl: mismo POST dos veces con la misma
Idempotency-Key→ misma respuesta y UN solo Payment en BD.
Ejercicio 3 — Los idempotentes por diseño
- Haz que
DELETE /reservations/{uuid}/sea idempotente: cancelar dos veces → 204 ambas (elcancel()de la 00b ya devuelve False; conecta). PUT /api/events/{uuid}/con reemplazo completo: demuestra que enviar el mismo PUT dos veces deja el mismo estado (y que PATCH con{"price": "+2"}dos veces NO es idempotente).
Ejercicio 4 — Webhook idempotente
- En tu handler del webhook (simulado): busca el pago por el
event_idde la pasarela antes de crear; si existe, responde 200 sin reprocesar. Demuéstralo con el mismo payload tres veces. - ¿Qué status le respondes a la pasarela en el reintento duplicado y por qué NUNCA 4xx ahí?
Ejercicio 5 — Versionado sin romper
- Activa
URLPathVersioning(o rutas manuales/api/v1/) y duplica un endpoint con un cambio rompedor (p. ej. v2 devuelvestarts_atISO con zona y v1 naive). - Documenta la deprecación: cabecera
Deprecation: trueen v1 cuando exista uso, y escribe la consulta de métrica que mediría el tránsito de clientes v1→v2 (qué contaría: por endpoint y por día).
Entrega
Pega código y curl outputs. Después: Lección 15 — Validación y contratos con OpenAPI.