Pular para o conteúdo principal

Quando dispara

O evento refund_out é disparado quando um cash-in que você recebeu é devolvido ao pagador. O saldo correspondente é debitado da sua conta. Cenários comuns:
  • Você acionou um estorno por motivo de fraude ou erro
  • Cliente solicitou cancelamento dentro do prazo SPEI
  • Disputa OXXO em que o NTX Pay devolve o valor

Payload

{
  "event": "refund_out",
  "deliveryId": "7d2c9e8f-5b34-4c19-aa18-99b3c4d5e6f7",
  "createdAt": "2026-05-14T11:45:00.000Z",
  "transaction": {
    "id": 78901,
    "externalId": "order-abc-123-refund",
    "paymentMethod": "SPEI",
    "direction": "out",
    "type": "refund_out",
    "status": "CONFIRMED",
    "provider": "smartfastpay",
    "amountCentavos": 50000,
    "clabe": "012180001234567890",
    "createdAt": "2026-05-14T11:44:50.000Z",
    "confirmedAt": "2026-05-14T11:45:00.000Z"
  },
  "originalTransactionId": 12345
}
originalTransactionId aponta para o id do cash-in original que foi devolvido.

Resposta Esperada

HTTP 200 OK.

Exemplos de Handler

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

const app = express();
app.use(express.raw({ type: 'application/json' })); // raw body for 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)
    .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 === 'refund_out') {
    // Balance already debited
    markOrderAsRefunded({
      originalCashInId: event.originalTransactionId,
      refundId: event.transaction.id,
      amount: event.transaction.amountCentavos,
    });
  }

  res.json({ received: true });
});