Skip to content

Instantly share code, notes, and snippets.

@Pushkraj19
Created February 2, 2026 07:35
Show Gist options
  • Select an option

  • Save Pushkraj19/ba6c9bc8a77ae780db015bd56148654c to your computer and use it in GitHub Desktop.

Select an option

Save Pushkraj19/ba6c9bc8a77ae780db015bd56148654c to your computer and use it in GitHub Desktop.
PayU Hash Generator Utility PHP Class
<?php
namespace App\Services\Gateways\PayU;
/**
* PayU Hash Generator Utility Class
*
* Handles all hash generation and validation scenarios for PayU payment gateway.
*
* @see https://docs.payu.in/docs/generate-hash-merchant-hosted
* @see https://docs.payu.in/docs/api-authentication-and-security
*/
class PayUHashGenerator
{
// Hash sequence constants
public const PAYMENT_HASH_SEQUENCE = 'key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5|udf6|udf7|udf8|udf9|udf10';
public const UDF_COUNT = 5;
// API command constants
public const CMD_VERIFY_PAYMENT = 'verify_payment';
public const CMD_CANCEL_REFUND = 'cancel_refund_transaction';
public const CMD_CHECK_OFFER_STATUS = 'check_offer_status';
public const CMD_GET_TRANSACTION_DETAILS = 'get_transaction_details';
public const CMD_GET_EMI_AMOUNT = 'get_emi_amount_according_to_interest';
public const CMD_CAPTURE_TRANSACTION = 'capture_transaction';
public const CMD_CHECK_ACTION_STATUS = 'check_action_status';
public const CMD_GET_TDR = 'get_TDR';
public const CMD_PAYMENT_DETAILS_SDK = 'payment_related_details_for_mobile_sdk';
/**
* Generate hash for payment transaction request.
*
* Formula: sha512(key|txnid|amount|productinfo|firstname|email|udf1|...|udf10||||||salt)
*
* @param array $params Transaction parameters
* @param string $salt Merchant salt
* @return string Generated hash (lowercase)
*/
public static function generatePaymentHash(array $params, string $salt): string
{
$hashString = self::buildPaymentHashString($params, $salt);
return self::computeHash($hashString);
}
/**
* Validate response hash from PayU callback (reverse hash).
*
* Formula: sha512([additionalCharges|]salt|status||||||udf10|...|udf1|email|firstname|productinfo|amount|txnid|key)
*
* @param array $responseParams Response parameters from PayU
* @param string $salt Merchant salt
* @return bool True if hash is valid, false otherwise
*/
public static function validateResponseHash(array $responseParams, string $salt): bool
{
if (empty($responseParams['hash'])) {
return false;
}
$hashString = self::buildResponseHashString($responseParams, $salt);
$computedHash = self::computeHash($hashString);
// Use constant-time comparison to prevent timing attacks
return hash_equals($computedHash, strtolower($responseParams['hash']));
}
/**
* Generate hash for PayU API operations.
*
* Formula: sha512(key|command|var1|salt)
*
* @param string $command API command name
* @param string $var1 Variable parameter (differs by command)
* @param string $key Merchant key
* @param string $salt Merchant salt
* @return string Generated hash (lowercase)
*/
public static function generateApiHash(string $command, string $var1, string $key, string $salt): string
{
$hashString = "{$key}|{$command}|{$var1}|{$salt}";
return self::computeHash($hashString);
}
/**
* Build hash string for payment request.
*
* @param array $params Transaction parameters
* @param string $salt Merchant salt
* @return string Hash string ready for hashing
*/
private static function buildPaymentHashString(array $params, string $salt): string
{
$hashString = self::getSafeValue($params, 'key').'|';
$hashString .= self::getSafeValue($params, 'txnid').'|';
$hashString .= self::getSafeValue($params, 'amount').'|';
$hashString .= self::getSafeValue($params, 'productinfo').'|';
$hashString .= self::getSafeValue($params, 'firstname').'|';
$hashString .= self::getSafeValue($params, 'email').'|';
// UDF fields 1-10
for ($i = 1; $i <= self::UDF_COUNT; $i++) {
$hashString .= self::getSafeValue($params, "udf{$i}").'|';
}
// 6 empty pipes for reserved fields (required by PayU)
$hashString .= '|||||';
$hashString .= $salt;
return $hashString;
}
/**
* Build reverse hash string for response validation.
*
* @param array $params Response parameters
* @param string $salt Merchant salt
* @return string Hash string ready for hashing
*/
private static function buildResponseHashString(array $params, string $salt): string
{
$hashString = '';
// Check for additional charges (may be named differently)
$additionalCharges = self::getSafeValue($params, 'additionalCharges')
?: self::getSafeValue($params, 'additional_charges');
if (! empty($additionalCharges)) {
$hashString .= $additionalCharges.'|';
}
$hashString .= $salt.'|';
$hashString .= self::getSafeValue($params, 'status').'|';
// 6 empty pipes for reserved fields (required by PayU)
$hashString .= '|||||';
// UDF fields in reverse order: udf10 to udf1
for ($i = self::UDF_COUNT; $i >= 1; $i--) {
$hashString .= self::getSafeValue($params, "udf{$i}").'|';
}
$hashString .= self::getSafeValue($params, 'email').'|';
$hashString .= self::getSafeValue($params, 'firstname').'|';
$hashString .= self::getSafeValue($params, 'productinfo').'|';
$hashString .= self::getSafeValue($params, 'amount').'|';
$hashString .= self::getSafeValue($params, 'txnid').'|';
$hashString .= self::getSafeValue($params, 'key');
return $hashString;
}
/**
* Compute SHA-512 hash from a string.
*
* @param string $hashString String to hash
* @return string Lowercase SHA-512 hash
*/
private static function computeHash(string $hashString): string
{
return strtolower(hash('sha512', $hashString));
}
/**
* Get safe value (empty string if null/missing).
*
* @param array $params Parameters array
* @param string $key Parameter key
* @return string Parameter value or empty string
*/
private static function getSafeValue(array $params, string $key): string
{
return isset($params[$key]) ? (string) $params[$key] : '';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment