🪝

Webhooks

👨‍🍳 Chef

Eventos en tiempo real

Los webhooks permiten que servicios te notifiquen cuando algo pasa.


Cómo funcionan

Evento en servicio externo
         ↓
POST a tu endpoint
         ↓
Tu código procesa

Ejemplo: Stripe webhook

// app/api/webhooks/stripe/route.ts
export async function POST(request: Request) {
  const body = await request.text()
  const signature = request.headers.get('stripe-signature')!

  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  )

  switch (event.type) {
    case 'payment_intent.succeeded':
      // Marcar orden como pagada
      break
    case 'customer.subscription.deleted':
      // Cancelar suscripción
      break
  }

  return new Response('OK')
}

Verificar firma

Siempre verifica que el webhook viene del servicio real:

import crypto from 'crypto'

function verifySignature(payload: string, signature: string, secret: string) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  )
}

Servicios con webhooks

  • Stripe (pagos)
  • GitHub (commits, PRs)
  • Slack (mensajes)
  • Twilio (SMS, llamadas)

🔐 Webhooks de Pago: Seguridad Crítica

Los webhooks de pago son el punto más vulnerable de tu aplicación. Un atacante puede simular un pago exitoso.

Idempotencia: No proceses dos veces

Stripe puede reenviar el mismo webhook múltiples veces. Sin idempotencia, cobras/acreditas doble.

export async function POST(request: Request) {
  const event = await verifyAndParseWebhook(request)

  // ⚠️ CRÍTICO: Verificar si ya procesamos este evento
  const processed = await db.query(
    'SELECT 1 FROM processed_webhooks WHERE event_id = $1',
    [event.id]
  )

  if (processed.rows.length > 0) {
    // Ya procesado, responder OK sin hacer nada
    return new Response('Already processed', { status: 200 })
  }

  // Procesar el evento
  await processPaymentEvent(event)

  // Marcar como procesado DESPUÉS de procesar exitosamente
  await db.query(
    'INSERT INTO processed_webhooks (event_id, processed_at) VALUES ($1, NOW())',
    [event.id]
  )

  return new Response('OK')
}

Responde rápido, procesa después

Stripe espera respuesta en 20 segundos. Si tu procesamiento es lento, hazlo async:

export async function POST(request: Request) {
  const event = await verifyAndParseWebhook(request)

  // Responder inmediatamente
  // Procesar en background (cola, worker, etc.)
  await queue.add('process-payment', { eventId: event.id })

  return new Response('OK') // < 1 segundo
}

Errores comunes en Fintech

ErrorConsecuenciaSolución
No validar firmaFraude: pagos falsosconstructEvent() siempre
No manejar duplicadosCobro/acreditación dobleTabla de idempotencia
Timeout en procesamientoStripe reintenta = duplicadosResponder rápido, procesar async
No loguear eventosImposible debuggearLog completo con timestamp

Practica

Receptor de Webhooks

¿Quieres ir más lejos?

Esto es una pieza. GenAI Builder es el mapa completo.

Diez dominios, de la terminal a la energía y la ley, cinco niveles que se ganan con evidencia real y un mentor de IA. A tu ritmo, en español e inglés.

Ver el cursoAcceso de por vida · garantía de 30 días

© 2026 luxIA.us - Todos los derechos reservados

Creado por Alann Reyes