Hash mismatch error after a transaction is paid


#1

Good day team,
I have recently integrated with paynow and I am now doing the production tests. I am getting hash mismatch error after a payment is done which is the last step to finalize the transaction. I tried to generate the new integration key on the portal but still nothing is changing. Kindly help, @paynow team kindly confirm if there is another key specifically for production that you are using to generate the callback hash for my merchant ID. Below is my code and logs :

`public function paynowResult(Request $request)
{
// Loging raw data from Paynow
Log::info(“Paynow Result callback raw data”, $request->all());

// Gather all parameters
$data = $request->all();

// The 'hash' sent by Paynow, if any
$providedHash = $data['hash'] ?? '';

// Remove 'hash' from $data before we do our own hashing
unset($data['hash']);

// Sort & build the string to hash
ksort($data);

$string = '';
foreach ($data as $key => $value) {
    // Exclude empty values
    if (strlen($value) > 0) {
        // Converting key to uppercase, URL-encode the value, then append "KEY=VALUE&"
        $string .= strtoupper($key) . '=' . urlencode($value) . '&';
    }
}
// Removing trailing ampersand
$string = rtrim($string, '&');

// Generating our own hash
$ourHash = hash('sha512', $string . env('PAYNOW_INTEGRATION_KEY'));


Log::info("Computed string for hash: " . $string);
Log::info("Computed ourHash: " . strtoupper($ourHash));
Log::info("Paynow-sent hash: " . strtoupper($providedHash));

// Comparing (case-insensitive) your computed hash vs Paynow's
if (strtoupper($ourHash) !== strtoupper($providedHash)) {
    Log::error('Paynow callback hash mismatch. Potential tampering.');
    abort(403, 'Hash mismatch');
}

// If the hash matched proceed.
$reference  = $data['reference']       ?? '';
$paynowRef  = $data['paynowreference'] ?? '';
$amount     = $data['amount']          ?? 0;
$status     = $data['status']          ?? 'Unknown';
$pollUrl    = $data['pollurl']         ?? '';

Log::info("Paynow callback verified for reference: $reference, status=$status");

// Payment ref. 
$payment = Payment::where('reference', $reference)->first();

if (!$payment) {
    Log::error("No Payment found for reference: $reference or pollUrl: $pollUrl");
    // Return 200 so Paynow doesn't keep retrying
    return response('Payment not found', 200);
}

//Handling statuses
if (strtolower($status) === 'paid') {
    $payment->status         = 'completed';
    $payment->transaction_id = $paynowRef;
    $payment->save();

    $trip = $payment->trip; 
    if ($trip) {
        $trip->payment_status = 'completed';
        $trip->save();
    }
    Log::info("Payment #{$payment->id} completed on Paynow.");
}
elseif (strtolower($status) === 'cancelled') {
    $payment->status         = 'cancelled';
    $payment->transaction_id = $paynowRef;
    $payment->save();
}
else {
    $payment->status         = strtolower($status);
    $payment->transaction_id = $paynowRef;
    $payment->save();
}

return response('OK', 200);

}`

[2025-03-19 09:12:48] production.INFO: processPayment() invoked {"_token":“pz0MLlYtNbLjYhIYGfQzGT676by6PATJaZ8iPhwF”,“bid_amount”:“1”,“payment_method”:“paynow”,“trip_id”:“98”} [2025-03-19 09:12:50] production.INFO: Paynow response data: {“status”:“Ok”,“browserurl”:“https://www.paynow.co.zw/Payment/ConfirmPayment/22673706/rodnkamamera@gmail.com//",“pollurl”:“https://www.paynow.co.zw/Interface/CheckPayment/?guid=fe3cafa1-5a28-4702-bed2-ac42eda36222”,“hash”:"0752ADC44E65F8B2327203F8BD33ECF81502DC14E08C0492AAF09F95AF891CB9155A052C7F6E174D66852E82740776E700865ADE5F97B7AE05982291264BC5C4”} [2025-03-19 09:12:50] production.INFO: Paynow redirect URL: https://www.paynow.co.zw/Payment/ConfirmPayment/22673706/gd123****@gmail.com// [2025-03-19 09:13:55] production.INFO: Paynow Result callback raw data {“reference”:“Trip-98-Payment-34”,“paynowreference”:“22673706”,“amount”:“1.00”,“status”:“Paid”,“pollurl”:“https://www.paynow.co.zw/Interface/CheckPayment/?guid=fe3cafa1-5a28-4702-bed2-ac42eda36222",“hash”:"F30EC739767D1E1DDEABDB8F1D457042729B27105092FB1A431BD6EB3ABB9E4B0CFE82FC524E153EC9E1D95221419FEDE65ADEEE3C198F13CB942605F7EC3B2F”} [2025-03-19 09:13:55] production.INFO: Computed string for hash: AMOUNT=1.00&PAYNOWREFERENCE=22673706&POLLURL=https%3A%2F%2Fwww.paynow.co.zw%2FInterface%2FCheckPayment%2F%3Fguid%3Dfe3cafa1-5a28-4702-bed2-ac42eda36222&REFERENCE=Trip-98-Payment-34&STATUS=Paid [2025-03-19 09:13:55] production.INFO: Computed ourHash: 7DB2064C3071EFB36FE97C8BB7DCCDA0B58EDE76ADB24C1E1150B20E40CEEEF343747F68765DE554A768158072647E702387A6F6BA38800B924A2AAD8A121B5A [2025-03-19 09:13:55] production.INFO: Paynow-sent hash: F30EC739767D1E1DDEABDB8F1D457042729B27105092FB1A431BD6EB3ABB9E4B0CFE82FC524E153EC9E1D95221419FEDE65ADEEE3C198F13CB942605F7EC3B2F [2025-03-19 09:13:55] production.ERROR: Paynow callback hash mismatch.


#2

Good day @Mudza

Please use the Paynow helper function to verify hash

use Paynow\Util\Hash;

Hash::verify( $request->all(), env(‘PAYNOW_INTEGRATION_KEY’))

The docs for the method is found here


#3

Noted Elphas, let me try that one