Saltearse al contenido

La búsqueda solo está disponible en las versiones de producción. Intenta construir y previsualizar el sitio para probarlo localmente.

Webhooks

Cashela notifica a tu servidor con un POST firmado cuando una transacción cambia de estado. Pay-In y Pay-Out usan las mismas cabeceras y el mismo esquema de firma; el cuerpo es propio de cada producto.

ProductoDe dónde sale la URLCuerpo y eventos
Pay-Innotification_url de cada depósitoWebhooks de Pay-In
Pay-Outcallback_url de cada transacciónCrear una transacción

Cabeceras

CabeceraDescripción
X-Cashela-EventEl evento, por ejemplo partner.payin.transaction.updated o partner.payout.transaction.updated. El cuerpo no lo lleva.
X-Cashela-TimestampHora Unix (segundos) de la firma.
X-Cashela-NonceValor único por entrega (UUID).
X-Cashela-SignatureHMAC-SHA256 de {timestamp}.{nonce}.{body} con tu api_secret, en hexadecimal en minúsculas.
X-Cashela-Trace-IdIdentificador de correlación; cítalo a soporte.

Verificar la firma

  1. Lee las tres cabeceras de firma; rechaza la petición si falta alguna.
  2. Calcula HMAC-SHA256 sobre {timestamp}.{nonce}.{cuerpo en bruto} con el api_secret de la clave que creó la transacción mientras siga activa (o la clave activa más reciente de tu cuenta si fue eliminada). Usa el cuerpo exactamente como llegó.
  3. Compara con X-Cashela-Signature en tiempo constante.
  4. Rechaza timestamps de más de 300 segundos y nonces que ya hayas visto.
// Express: monta esta ruta con express.raw({ type: "application/json" })
// para que req.body sea el Buffer en bruto.
const crypto = require("crypto");
function isValidCashelaNotification(req, apiSecret) {
const timestamp = req.get("X-Cashela-Timestamp");
const nonce = req.get("X-Cashela-Nonce");
const signature = req.get("X-Cashela-Signature");
if (!timestamp || !nonce || !signature) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = crypto
.createHmac("sha256", apiSecret)
.update(`${timestamp}.${nonce}.${req.body.toString("utf8")}`)
.digest("hex");
return expected.length === signature.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Prueba tu implementación con los vectores de prueba de la firma.

Entrega

  • Responde cualquier 2xx rápido. Cualquier otro estado, un timeout o un error de conexión es una entrega fallida. Pay-In reintenta las entregas fallidas; en Pay-Out no cuentes con reintentos automáticos: consulta la transacción o pide un reenvío.
  • La misma notificación puede llegar más de una vez: haz tu handler idempotente.
  • Las notificaciones nunca se envían sin firma.
  • ¿Perdiste una? Pide que se reenvíe: POST /api/v1/pay-in/transactions/{reference}/callback (Pay-In) o POST /api/v1/pay-out/transactions/{reference}/callback (Pay-Out), o consulta la transacción.
  • Que tu cliente vuelva a tu sitio nunca confirma un pago; solo lo confirman la notificación o una consulta de la transacción.