Skip to Content
Documentación de integración del API de Seif. ¿Dudas? soporte@pagosripei.com
EndpointsWebhooks

Webhooks

Los webhooks le avisan a tu servidor, en tiempo real, cuando algo ocurre: un cobro aprobado, una tarjeta guardada, una sesión que expiró. Es la forma recomendada de reaccionar a los pagos (mejor que hacer polling).


Eventos disponibles

EventoCuándo se emite
charge.approvedUn cobro fue aprobado.
charge.declinedUn cobro fue rechazado por el banco/tarjeta.
charge.failedUn cobro no se pudo procesar.
card.storedSe almacenó una tarjeta tras un cobro aprobado.
card.deletedSe eliminó una tarjeta guardada.
session.otp_requestedLa sesión solicitó un OTP (flujos con segundo factor).
session.otp_confirmedEl OTP fue confirmado.
session.expiredLa sesión expiró sin pagarse.

Registrar un endpoint

POST /v1/webhooks/endpoints

Autenticación: clave secreta (Authorization: Bearer sk_…).

CampoTipoDescripción
urlstring (https)A dónde Seif enviará los eventos. Debe ser https.
enabledEventsstring[]Eventos a recibir. Omítelo o envía [] para recibir todos.
descriptionstringNota interna (opcional).
curl -X POST https://api.seif.pagosripei.com/v1/webhooks/endpoints \ -H "Authorization: Bearer sk_test_tu_clave" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.mitienda.com/seif/webhooks", "enabledEvents": ["charge.approved", "charge.declined", "charge.failed"] }'

La respuesta incluye un secret (whsec_…), mostrado una sola vez. Guárdalo: con él verificas la firma de cada evento.


Autenticación saliente (opcional)

Además de la firma Seif-Signature (que va siempre), puedes pedir que Seif autentique la llamada a tu endpoint para que tu receptor la valide. Se configura con authType al crear o actualizar:

authTypeCamposHeader que Seif envía
none (default)(solo la firma)
bearerauthTokenAuthorization: Bearer <authToken>
basicauthUsername, authPasswordAuthorization: Basic base64(usuario:contraseña)
api_keyauthHeaderName, authToken<authHeaderName>: <authToken>
oauth2oauthTokenUrl, oauthClientId, oauthClientSecret, oauthScope (opc.)Authorization: Bearer <token> — Seif obtiene el token con client_credentials y lo cachea
curl -X POST https://api.seif.pagosripei.com/v1/webhooks/endpoints \ -H "Authorization: Bearer sk_test_tu_clave" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.mitienda.com/seif/webhooks", "authType": "bearer", "authToken": "tu-token" }'

Las credenciales se guardan cifradas y nunca se devuelven: las respuestas solo indican hasAuthToken, hasBasicAuth, etc. Para rotarlas, envía el nuevo valor con PATCH. La firma HMAC sigue activa con cualquier authType.


Gestionar endpoints

GET /v1/webhooks/endpoints # listar GET /v1/webhooks/endpoints/{id} # ver uno PATCH /v1/webhooks/endpoints/{id} # actualizar (URL, eventos, estado, auth) DELETE /v1/webhooks/endpoints/{id} # eliminar

En PATCH, omite un campo para conservarlo; en una credencial, envía "" para borrarla. Cambiar status a disabled detiene los envíos sin borrar el endpoint (el historial de entregas se conserva).


Probar un endpoint (ping)

POST /v1/webhooks/endpoints/{id}/test

Encola un evento sintético webhook.test hacia ese endpoint (con su firma y autenticación), aunque esté inactivo o no suscrito a ese evento. Devuelve { deliveryId, eventId }; el resultado aparece en el historial de entregas. Úsalo para verificar conectividad, firma y autenticación sin esperar a una transacción real.


Formato del evento

Cada entrega es un POST con este cuerpo:

{ "id": "evt_1a2b3c...", "type": "charge.approved", "createdAt": "2026-06-09T18:05:12.000Z", "env": "production", "data": { "transactionReference": "txn_2nFp9Zk", "sessionReference": "cs_8sKd2pQ1Aa", "amountMinor": 105000, "currency": "VES", "status": "approved", "declineReason": null, "errorCode": null } }

Cuerpo de data según el evento:

Eventodata
charge.*{ transactionReference, amountMinor, currency, status, declineReason, errorCode }
card.stored{ seifToken, brand, lastFour, customerRef }
session.expired{ sessionReference }

Los eventos charge.* traen además el origen del cobro: sessionReference si vino del checkout, o seifToken si fue un cobro con tarjeta guardada.

Por qué falló un cobro

En charge.declined y charge.failed vienen los dos campos que explican el resultado, así no hace falta consultar la transacción aparte:

CampoPara qué
declineReasonTexto en español, apto para mostrarle al comprador.
errorCodeCódigo del procesador. Es el que debes usar para ramificar en tu código — el texto puede cambiar.

Ambos son null en charge.approved.

Si necesitas más detalle del que trae el webhook (el motivo técnico completo, la traza de eventos), consulta GET /v1/transactions/{reference} con la transactionReference del evento.


Verificar la firma

Cada entrega trae el header Seif-Signature:

Seif-Signature: t=1717954512,v1=5f8e...c3

Recalcula HMAC-SHA256 sobre `${t}.${cuerpoCrudo}` con tu secret (whsec_…) y compara con v1. Verifica además que t sea reciente para rechazar reenvíos.

import crypto from 'crypto'; function verifySeifWebhook(rawBody, header, secret) { const parts = Object.fromEntries(header.split(',').map((p) => p.split('='))); const expected = crypto .createHmac('sha256', secret) .update(`${parts.t}.${rawBody}`, 'utf8') .digest('hex'); const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)); const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // 5 min return ok && fresh; }

Verifica la firma usando el cuerpo crudo (sin parsear). Si tu framework parsea el JSON antes, re-serializar cambiará los bytes y la firma no coincidirá.


Entregas y reintentos

  • Responde 2xx rápido (idealmente < 5 s). Cualquier otra respuesta se considera fallida.
  • Seif reintenta hasta 5 veces con backoff exponencial.
  • Puedes consultar el historial de entregas y reintentar manualmente:
GET /v1/webhooks/deliveries GET /v1/webhooks/deliveries/{id} POST /v1/webhooks/deliveries/{id}/retry

Diseña tu endpoint para ser idempotente: usa el id del evento (evt_…) para no procesar dos veces el mismo evento si llega repetido tras un reintento.

Last updated on