Skip to content

Webhooks — Notificaciones de eventos

Wepago notifica a tu servidor cuando ocurre un evento importante (cobro aprobado, rechazado, etc.) mediante un HTTP POST a la URL que configuraste en tu cuenta.

Configuración

En comercios.wepago.com → Configuración → API registra:

CampoDescripción
Webhook URLEndpoint HTTPS de tu servidor que recibirá los eventos
Webhook SecretClave aleatoria (mín. 32 chars) para verificar la firma

HTTPS obligatorio

Wepago solo envía webhooks a URLs con https://. Endpoints HTTP son rechazados.

Formato del payload

json
{
  "event": "subscription.charge.approved",
  "data": {
    "subscriptionTransactionId": "01JR9H0BKMCAZ6PY1GR65B91J6",
    "subscriptionPlanId": "01JR9H0BKMCAZ6PY1GR65B91J7",
    "externalReference": "tu-referencia-interna",
    "transactionId": "1200969-1775447756-67089",
    "amount": 49900,
    "status": "APPROVED",
    "chargeDate": 1712534400000
  },
  "timestamp": 1712534401234
}

Eventos disponibles

EventoCuándo ocurre
subscription.charge.approvedCobro procesado y aprobado por el banco
subscription.charge.declinedCobro rechazado (fondos insuficientes, tarjeta bloqueada, etc.)
subscription.charge.errorError técnico durante el cobro
subscription.charge.voidedCobro anulado

Próximamente

subscription.activated y subscription.cancelled están en roadmap.

Verificar la firma

Cada webhook incluye el header X-Webhook-Signature con una firma HMAC-SHA256 del cuerpo del request. Siempre verifica esta firma antes de procesar el evento.

Node.js

javascript
const crypto = require('crypto')

function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)           // rawBody = string sin parsear
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  )
}

// Express
app.post('/webhooks/wepago', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature']
  const isValid = verifyWebhookSignature(req.body, signature, process.env.WEPAGO_WEBHOOK_SECRET)

  if (!isValid) {
    return res.status(401).send('Invalid signature')
  }

  const event = JSON.parse(req.body)
  
  switch (event.event) {
    case 'subscription.charge.approved':
      // Activar servicio, enviar confirmación al usuario, etc.
      await activateSubscription(event.data.externalReference)
      break
    case 'subscription.charge.declined':
      // Notificar al usuario, reintentar cobro, etc.
      await handleDeclinedPayment(event.data.externalReference)
      break
  }

  res.status(200).json({ received: true })
})

Python

python
import hmac, hashlib

def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

PHP

php
function verifySignature(string $rawBody, string $signature, string $secret): bool {
    $expected = hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $signature);
}

No uses el body parseado

La firma se calcula sobre el body como string, antes de parsear JSON. Si usas el objeto JSON ya parseado, obtendrás una firma diferente.

Responder correctamente

Tu endpoint debe responder con HTTP 200 en menos de 10 segundos. Wepago no reintenta si el endpoint falla o tarda más.

javascript
// ✅ Responde rápido, procesa en background si es necesario
app.post('/webhooks/wepago', async (req, res) => {
  res.status(200).json({ received: true })  // Responde PRIMERO
  await processEvent(req.body)              // Luego procesa
})

Polling como alternativa

Si tu servidor no puede recibir webhooks (firewalls, desarrollo local), puedes consultar el estado de un plan directamente:

bash
GET https://merchant.api.wepago.com/merchants/plans/{subscriptionPlanId}/status
Authorization: Bearer {api_key}:{api_secret}

Ver Referencia API → Polling de estado.

Probar webhooks en desarrollo

Usa webhook.site o ngrok para exponer tu servidor local:

bash
# ngrok
ngrok http 3000
# → https://abc123.ngrok.io

# Registra https://abc123.ngrok.io/webhooks/wepago como tu Webhook URL

URL pública con token en la URL

En el pasado, algunas integraciones usaban una URL con token en el path (ej: /webhook/{token}) como "autenticación". No hagas esto: el token queda en logs de servidores y proxies. Usa siempre la verificación HMAC con el header X-Webhook-Signature.