Callback Security
Security measures and best practices for webhook notifications.
Hash Validation (MD5)
MVPAY callbacks include a hash field in the body. This hash is created by concatenating specific fields in a specific order and taking the MD5.
Format
Concatenate in the following order:
processID + "|" + amount + "|" + userID + "|" + type + "|" + apiKey
Example string:
TEST-PROCESS-ID-T1|100|2|withdraw|YOUR_API_KEY
Note: Order matters. The amount value must be treated as a string. Prefer timing-safe comparison functions (e.g., hash_equals) when comparing.
Verification Code
- 🟢 Node.js
- 🐘 PHP
- 🔷 C#
- 🐍 Python
- ☕ Java
import crypto from "crypto";
export function validateMVPayCallback(callbackData, yourApiKey) {
const hashString = `${callbackData.processID}|${callbackData.amount}|${callbackData.userID}|${callbackData.type}|${yourApiKey}`;
const expectedHash = crypto.createHash("md5").update(hashString).digest("hex");
// Timing-safe comparison
return crypto.timingSafeEqual(Buffer.from(expectedHash), Buffer.from(callbackData.hash));
}
function validateMVPayCallback(array $callbackData, string $yourApiKey): bool {
if (!isset($callbackData['hash'])) {
return false;
}
$hashString = $callbackData['processID'] . '|' .
$callbackData['amount'] . '|' .
$callbackData['userID'] . '|' .
$callbackData['type'] . '|' .
$yourApiKey;
$expectedHash = md5($hashString);
return hash_equals($expectedHash, $callbackData['hash']);
}
using System;
using System.Security.Cryptography;
using System.Text;
using System.Collections.Generic;
public static bool ValidateMVPayCallback(Dictionary<string, object> callbackData, string yourApiKey)
{
if (!callbackData.ContainsKey("hash"))
{
return false;
}
string hashString = $"{callbackData["processID"]}|{callbackData["amount"]}|{callbackData["userID"]}|{callbackData["type"]}|{yourApiKey}";
using (MD5 md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(hashString));
string expectedHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
return expectedHash == callbackData["hash"].ToString();
}
}
import hashlib
def validate_mvpay_callback(callback_data: dict, your_api_key: str) -> bool:
if 'hash' not in callback_data:
return False
hash_string = f"{callback_data['processID']}|{callback_data['amount']}|{callback_data['userID']}|{callback_data['type']}|{your_api_key}"
expected_hash = hashlib.md5(hash_string.encode()).hexdigest()
return expected_hash == callback_data['hash']
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
public static boolean validateMVPayCallback(Map<String, Object> callbackData, String yourApiKey) {
if (!callbackData.containsKey("hash")) {
return false;
}
String hashString = callbackData.get("processID") + "|" +
callbackData.get("amount") + "|" +
callbackData.get("userID") + "|" +
callbackData.get("type") + "|" +
yourApiKey;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashBytes = md.digest(hashString.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
String expectedHash = sb.toString();
return expectedHash.equals(callbackData.get("hash").toString());
} catch (NoSuchAlgorithmException e) {
return false;
}
}