Simulador de pasarela incluido (tu propio script firma y envía). No mires solutions.md hasta entregar.
Ejercicio 1 — El webhook sin defensas (roto a propósito)
- Implementa
POST /api/payments/webhook/que confíe en el cuerpo ({"event_id", "payment_id", "status", "timestamp"}) y marque el pago como SUCCEEDED. Pruébalo con curl. ¿Cuánto tardaste en hackearte? (cualquiera con tu URL ya cobró). - Añade firma HMAC-SHA256:
X-Signature = hex(hmac(secret, timestamp + "." + raw_body)). Verifica conhmac.compare_digest(¿por qué no==?). - Ataques a probar: cuerpo válido sin firma → 401; firma de OTRO cuerpo → 401; mismo cuerpo+firma con timestamp de hace 1 hora → 400 (replay). Añade la ventana de 5 min.
Ejercicio 2 — ACK rápido + procesamiento diferido
- Simula la cola:
process_gateway_event(payload)que tarda 3s (sleep) + crea la entrada. Primero síncrono: mide el tiempo de respuesta del webhook con curl (¿cuánto? ¿qué haría la pasarela con 5s de timeout?). - Envuelve en
transaction.on_commit+ cola simulada (thread de fondo odelay()si ya montaste Celery). Responde 200 en <100ms y procesa igual. - Demuestra idempotencia: mismo event_id 3 veces → 1 entrada creada, 2 respuestas 200.
Ejercicio 3 — Fuera de orden
- Envía
payment.succeededy DESPUÉSpayment.pendingdel mismo pago. ¿Tu handler regresa el pago a PENDING? (si sí: bug de máquina de estados — elconfirm()/transiciones de la 00b deben rechazar la transición ilegal). - ¿Qué status respondes a cada uno? (el ilegal: ¿400/409/200-ignorado? Justifica contra la política de reintentos del proveedor).
Ejercicio 4 — Tu webhook saliente
- Crea
notify_organizer(event_id, payload): firma igual que recibes, POST a una URL de test (requestbin o tu propio endpoint de prueba), reintentos con backoff 1m/5m/30m/2h/6h (tablaoutgoing_webhook_delivery(attempts, next_retry, status)). - Simula la URL caída (503): verifica que reintenta según el schedule y que tras 5 intentos queda marcado FAILED para revisión manual.
- ¿Qué haces con una URL que responde 410 Gone permanentemente? (pista: no reintentes para siempre — desactiva suscripción y notifica).
Ejercicio 6 — La fortaleza del webhook entrante
- Implementa la checklist del §6 sobre tu endpoint de la pasarela fake: las 7 piezas, cada una con su test (verificación, ventana, dedup, ACK+cola, rate limit, filtrado del secret en logs, métrica de firma inválida).
- El ataque de replay: captura un webhook válido y reenvíalo tal cual a los 10 min: ¿lo rechaza la ventana? Y a los 2 min con otro
event_id: ¿lo acepta el dedup o lo duplica? Documenta los dos límites. - El outbox de salientes: migra los webhooks salientes del 17 al mecanismo outbox→poller (25): test de atomicidad (transacción fallida → cero webhooks encolados) y del reintentante (pasarela ajena caída → backoff → convergencia).
Entrega
Pega código, curl outputs y la tabla de reintentos. Cierra el módulo 3; después Lección 18 — Sesiones vs tokens.