Validación de hash
El hash es tu candado anti-fraude. Cada IPN viene con un “sello” que solo Yappy puede generar con tu clave secreta. Si el hash que calculas no coincide con el que recibes, alguien está tratando de engañarte, rechaza esa notificación. Validarlo es obligatorio antes de confirmar cualquier pago.
¿Cómo se calcula?
- Decodifica tu
CLAVE_SECRETA(la que guardaste de Yappy Comercial WEB) de base64 a texto UTF-8. - Divide ese texto por el punto
.y usa solo el primer pedazo como llave HMAC. - Junta
orderId + status + domainsin espacios ni separadores. Ejemplo:abc123Ehttps://tudominio.com - Calcula
HMAC-SHA256(mensaje, llave)y conviértelo a hexadecimal en minúsculas. - Compara ese hash con el que recibiste. Si coinciden, todo bien. Si no, rechaza.
Implementaciones
const crypto = require('crypto');
function validateHash(orderId, status, domain, receivedHash) {
const decoded = Buffer
.from(process.env.CLAVE_SECRETA, 'base64')
.toString('utf-8');
const key = decoded.split('.')[0];
const signature = crypto
.createHmac('sha256', key)
.update(orderId + status + domain)
.digest('hex');
// Comparación de tiempo constante (resiste timing attacks)
return crypto.timingSafeEqual(
Buffer.from(receivedHash, 'hex'),
Buffer.from(signature, 'hex')
);
}
using System;
using System.Security.Cryptography;
using System.Text;
public static bool ValidateHash(string orderId, string status,
string domain, string receivedHash)
{
var secretKey = Environment.GetEnvironmentVariable("CLAVE_SECRETA");
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(secretKey));
var key = decoded.Split('.')[0];
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(orderId + status + domain));
var signature = Convert.ToHexString(hash).ToLower();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(receivedHash),
Encoding.UTF8.GetBytes(signature)
);
}
<?php
function validateHash($orderId, $status, $domain, $receivedHash) {
$decoded = base64_decode(getenv('CLAVE_SECRETA'));
$key = explode('.', $decoded)[0];
$signature = hash_hmac(
'sha256',
$orderId . $status . $domain,
$key
);
return hash_equals($receivedHash, $signature);
}
import os, base64, hmac, hashlib
def validate_hash(order_id: str, status: str,
domain: str, received_hash: str) -> bool:
secret = os.environ['CLAVE_SECRETA']
decoded = base64.b64decode(secret).decode('utf-8')
key = decoded.split('.')[0]
signature = hmac.new(
key.encode(),
f"{order_id}{status}{domain}".encode(),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(received_hash, signature)
NO actualices el estado de la orden. Pasos:
- Devuelve HTTP 200 con
{"success": false} - Registra el evento en tu log de seguridad con todos los parámetros recibidos
- Si es sistemático (>3 fallos consecutivos), coloca un ticket a soporte
yappy@bgeneral.com
Errores de hash más comunes:
- No decodificaste la
CLAVE_SECRETAdesde base64 antes de usarla - Usaste toda la clave en lugar del primer segmento (texto antes del
.) - La concatenación tiene espacios extras o separadores incorrectos
- Estás usando
sha256en lugar dehmac-sha256
Usa el Validador de Hash para reproducir
el cálculo HMAC-SHA256 paso a paso en el navegador. Toma los valores orderId,
status, domain y hash que llegaron en tu IPN y
verás exactamente en qué paso difiere tu implementación.