Saltar al contenido principal

Cuándo dispara

El evento cash_in se dispara cuando:
  • Una transferencia SPEI llega a la CLABE desechable emitida por POST /api/spei/cash-in y es liquidada
  • Un pago OXXO emitido por POST /api/oxxo/cash-in es pagado en la caja de la tienda y confirmado

Payload

{
  "event": "cash_in",
  "deliveryId": "8e2c5b6f-3a12-4b9c-9a18-77a2b3c4d5e6",
  "createdAt": "2026-05-12T14:31:05.000Z",
  "transaction": {
    "id": 12345,
    "externalId": "order-abc-123",
    "paymentMethod": "SPEI",
    "direction": "in",
    "type": "cash_in",
    "status": "CONFIRMED",
    "provider": "smartfastpay",
    "amountCentavos": 50000,
    "clabe": "012180001234567890",
    "createdAt": "2026-05-12T14:30:00.000Z",
    "confirmedAt": "2026-05-12T14:31:05.000Z"
  }
}

Headers

HeaderValor
X-NTXPay-Eventcash_in
X-NTXPay-Signaturesha256=<hmac>
X-NTXPay-TimestampUnix epoch (segundos)
X-NTXPay-DeliveryUUID único del envío

Diferencias SPEI vs OXXO

AspectoSPEIOXXO
paymentMethodSPEIOXXO
clabeCLABE de origennull
Tiempo hasta confirmaciónSegundos a minutosMinutos a horas tras pagar en caja
Identificador adicionalreferenceNumericalreferenceNumerical

Respuesta Esperada

Responde 200 OK en menos de 10 segundos. Ante cualquier status distinto, NTX Pay reintenta hasta 5 veces en backoff exponencial.
HTTP/1.1 200 OK
Content-Type: application/json

{"received": true}

Ejemplo de Handler (Node.js / Express)

import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.raw({ type: 'application/json' })); // raw body para HMAC

const SECRET = process.env.NTXPAY_WEBHOOK_SECRET!;

app.post('/webhooks/ntxpay', (req, res) => {
  const sig = req.header('X-NTXPay-Signature') ?? '';
  const expected = 'sha256=' + crypto
    .createHmac('sha256', SECRET)
    .update(req.body) // req.body es Buffer
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body.toString());
  if (event.event === 'cash_in' && event.transaction.status === 'CONFIRMED') {
    enqueue(event); // procesa async
  }

  res.json({ received: true });
});
Ver el guía de implementación para Python y PHP.