Configuras una URL desde la app y te enviamos un POST con
Content-Type: application/json cada vez que entra un pago.
Cabeceras que recibes
| Cabecera | Contenido |
X-Tingu-Signature | HMAC-SHA256 del cuerpo, en hexadecimal |
X-Tingu-Event | alerta.nueva o test |
X-Tingu-Timestamp | Milisegundos desde epoch |
Cuerpo del envío
{
"Id": 1842,
"IdUsuarioNegocio": 17,
"Tipo": "yape",
"NombreCliente": "Juan Carlos R.",
"Monto": 50.00,
"CodigoSeguridad": "482",
"Estado": "Completado",
"FechaHora": "2026-08-09T15:42:11.000Z",
"IdBilletera": 3,
"IdSucursal": 1,
"PackageName": "com.bcp.innovacxion.yapeapp",
"NombreNegocio": "Bodega Central",
"FechaRegistro": "2026-08-09T15:42:12.000Z"
}
Verificar la firma (obligatorio)
Sin verificar la firma, cualquiera que conozca tu URL puede inyectarte pagos falsos.
Calcula el HMAC-SHA256 del cuerpo crudo con tu secret y compáralo en
tiempo constante.
// Node.js + Express
const crypto = require('crypto');
app.post('/webhook-tingu',
express.raw({ type: 'application/json' }),
(req, res) => {
const firma = req.headers['x-tingu-signature'];
const esperada = crypto
.createHmac('sha256', process.env.TINGU_SECRET)
.update(req.body)
.digest('hex');
const a = Buffer.from(firma || '');
const b = Buffer.from(esperada);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('Firma invalida');
}
const pago = JSON.parse(req.body);
// Responde 200 YA. Procesa despues, en segundo plano.
res.sendStatus(200);
procesarEnSegundoPlano(pago);
}
);
# Python + Flask
import hmac, hashlib, os
from flask import request, abort
@app.route('/webhook-tingu', methods=['POST'])
def webhook():
esperada = hmac.new(
os.environ['TINGU_SECRET'].encode(),
request.get_data(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(esperada, request.headers.get('X-Tingu-Signature', '')):
abort(401)
pago = request.get_json()
return '', 200
// C# / .NET — para integrar con un POS Windows
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var esperada = Convert.ToHexString(
hmac.ComputeHash(Encoding.UTF8.GetBytes(cuerpoCrudo))
).ToLowerInvariant();
if (!CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(esperada),
Encoding.UTF8.GetBytes(firmaRecibida)))
return Unauthorized();