Saltar a contenido

Documento histórico

Este documento se conserva solo para trazabilidad. Consulte el índice maestro para la fuente vigente.

Documento histórico — no usar como fuente de decisiones actuales

Reemplazado por la versión canónica del 2026-07-11. Conservar solo como trazabilidad histórica.


CU-014: Procesar webhook de pago (Stripe)

Caso de uso detallado para el procesamiento del webhook de confirmación de pago de Stripe y la actualización del estado del envío.


Metadatos

Campo Valor
ID CU-014
Nombre Procesar webhook de pago
Actor primario Stripe (sistema externo)
Actores secundarios Sistema de Notificaciones, Auditoría
Módulo Pagos
Prioridad Media (Fase 2 — Stripe)
Frecuencia esperada Variable (cada vez que un usuario paga con Stripe)
Trazabilidad SRS RF-049, RN-070, RN-071, RN-072

Descripción

Cuando un usuario paga un envío a través de Stripe Checkout (Fase 2), Stripe envía un webhook al endpoint POST /api/v1/webhooks/stripe confirmando el pago. El sistema IslaBox:

  1. Verifica la firma del webhook (seguridad).
  2. Verifica idempotencia (evita procesar el mismo evento 2 veces).
  3. Actualiza el estado del payment a completado.
  4. Actualiza el estado de la consolidation y crea el shipment.
  5. Dispara notificaciones al usuario y al operador.
  6. Registra auditoría.

Este CU es crítico porque sin él, el sistema no puede confirmar pagos online. En MVP, los pagos son manuales (registrados por el operador), así que este caso se implementa completamente para Fase 2.


Precondiciones

  • El endpoint está expuesto públicamente con HTTPS válido.
  • La firma de Stripe se valida con STRIPE_WEBHOOK_SECRET.
  • El evento es de tipo payment_intent.succeeded o checkout.session.completed.
  • El payment_intent_id está registrado en la tabla payments con estado pendiente.

Postcondiciones

Éxito: - payment.status = completado. - consolidation.status = pagada. - shipment.status = en_preparacion (creado). - Notificación al usuario y al operador. - Evento en audit_logs.

Fallo: - HTTP 400 + log de error + alerta a Sentry/Datadog. - El evento queda en webhook_events con processed = false para reintento.


Flujo principal

  1. Stripe envía POST /api/v1/webhooks/stripe con:
    {
      "id": "evt_1234567890",
      "type": "payment_intent.succeeded",
      "data": {
        "object": {
          "id": "pi_1234567890",
          "amount": 5000,
          "currency": "usd",
          "metadata": {
            "consolidation_id": "55",
            "user_id": "42"
          }
        }
      }
    }
    
  2. El sistema lee el header Stripe-Signature.
  3. Verifica la firma con stripe.Webhook.construct_event().
  4. Si la firma es inválida → HTTP 400 + log de seguridad.
  5. Busca el evento en webhook_events por event_id.
  6. Si ya existe y está procesado → HTTP 200 (idempotencia, no reprocesar).
  7. Si no existe, lo inserta con processed = false (lock transaccional).
  8. Valida que el payment_intent_id exista en payments.
  9. Si no existe → log de error + alerta + HTTP 404.
  10. Transacción DB:
    • payments.status = completado, confirmed_at = now()
    • consolidations.status = pagada
    • shipments nuevo con status = en_preparacion
  11. Llama al servicio de notificaciones (push + email al usuario).
  12. Marca webhook_events.processed = true.
  13. Responde HTTP 200 a Stripe en < 5 segundos.

Flujos alternativos

FA-1: Evento duplicado

  • Stripe puede reenviar el mismo evento (reintento por timeout).
  • El sistema detecta por event_id y responde 200 sin reprocesar.

FA-2: Firma inválida

  • Posible ataque o configuración incorrecta.
  • HTTP 400 + log de seguridad + alerta.

FA-3: Pago no encontrado

  • Si el payment_intent_id no está en la base:
  • Log de error crítico.
  • Notificación al admin.
  • HTTP 404 (Stripe reintentará).

FA-4: Disputa / chargeback

  • Si llega charge.dispute.created:
  • Se crea incidencia automática pago_duplicado o cobro_indebido.
  • Se notifica al admin.
  • Se congela el envío relacionado hasta resolución.

FA-5: Reembolso desde Stripe

  • Si llega charge.refunded:
  • Se actualiza payment.status = reembolsado.
  • Se notifica al usuario.
  • Si el envío ya estaba despachado, se abre incidencia para investigar.

Reglas de negocio

  • RN-070: La firma del webhook es obligatoria; sin firma válida, se rechaza el request.
  • RN-071: La idempotencia se garantiza por webhook_events.event_id con UK constraint.
  • RN-072: El procesamiento de un webhook es transaccional: si falla cualquier paso, se hace rollback completo.
  • RN-073: El endpoint responde a Stripe en < 5 segundos (SLA de Stripe).
  • RN-074: Si el procesamiento tarda > 30s, se delega a un worker asíncrono y se responde 200 inmediato.

Endpoints API

  • POST /api/v1/webhooks/stripe — endpoint público, valida firma
  • GET /api/v1/admin/webhook-events — vista admin de eventos
  • POST /api/v1/admin/webhook-events/{id}/replay — reprocesar evento manualmente

Configuración requerida

# Variables de entorno
STRIPE_API_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_SUCCESS_URL=https://app.islabox.com/payments/success
STRIPE_CANCEL_URL=https://app.islabox.com/payments/cancel

Eventos generados

  • PAYMENT_CONFIRMED
  • CONSOLIDATION_PAID
  • SHIPMENT_CREATED
  • NOTIFICATION_DISPATCHED

Notificaciones generadas

  • Push al usuario: "¡Pago confirmado! Tu envío está en preparación"
  • Email al usuario con factura PDF adjunta
  • Push al operador: "Nuevo envío listo para despacho #1234"