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?

  1. Decodifica tu CLAVE_SECRETA (la que guardaste de Yappy Comercial WEB) de base64 a texto UTF-8.
  2. Divide ese texto por el punto . y usa solo el primer pedazo como llave HMAC.
  3. Junta orderId + status + domain sin espacios ni separadores. Ejemplo: abc123Ehttps://tudominio.com
  4. Calcula HMAC-SHA256(mensaje, llave) y conviértelo a hexadecimal en minúsculas.
  5. 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)
¡Hash inválido! ¿Qué hago?

NO actualices el estado de la orden. Pasos:

  1. Devuelve HTTP 200 con {"success": false}
  2. Registra el evento en tu log de seguridad con todos los parámetros recibidos
  3. 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_SECRETA desde 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 sha256 en lugar de hmac-sha256
¿Tu hash sigue sin coincidir?

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.

¿Te fue útil esta página?