To ensure data integrity, authenticity, and tamper protection, AMWAL Payment Gateway uses HMAC SHA-256 hashing with a Merchant Secure Key.
Every request sent to AMWAL — and every response received — must be verified using a secure hash.
⚠️ Important:
Secure hash calculation must NEVER be done on the client side (JavaScript, mobile apps, browser).
It must always be calculated on the backend and then sent to the frontend if needed.
🔐 What is a Secure Hash? #
A Secure Hash:
- Protects data from being modified in transit
- Ensures the request/response is coming from a trusted source
- Prevents replay, injection, and tampering attacks
AMWAL uses:
HMAC + SHA-256
🧮 Secure Hash Calculation – Request Side #
Step 1: Prepare Parameters #
Collect all required request parameters.
Example:
{
"Amount": "10",
"CurrencyId": "512",
"MerchantId": "48804",
"MerchantReference": "",
"RequestDateTime": "2024-12-31T15:27:10Z",
"SessionToken": "",
"TerminalId": "113176"
}
Step 2: Sort Parameters Alphabetically #
Sort parameters by key name (A → Z).
Step 3: Concatenate Parameters #
Format:
key=value&key=value&key=value
✔️ Do NOT add spaces
✔️ Do NOT URL encode
✔️ Do NOT include secureHashValue
Example:
Amount=10&CurrencyId=512&MerchantId=48804&MerchantReference=&RequestDateTime=2024-12-31T15:27:10Z&SessionToken=&TerminalId=113176
Step 4: Generate HMAC SHA-256 Hash #
Use the Merchant Secure Key as the HMAC key.
💻 Code Examples – Request Hash #
Node.js / Backend #
import crypto from "crypto";
function generateSecureHash(dataString, hexKey) {
const key = Buffer.from(hexKey, "hex");
return crypto
.createHmac("sha256", key)
.update(dataString, "utf8")
.digest("hex")
.toUpperCase();
}
PHP #
function generateSecureHash($data, $hexKey) {
$binaryKey = hex2bin($hexKey);
return strtoupper(hash_hmac('sha256', $data, $binaryKey));
}
.NET Core #
using System;
using System.Security.Cryptography;
using System.Text;
namespace YourProject.Helpers
{
public static class AmwalHashHelper
{
public static string EncryptWithSHA256(string input, string hexKey)
{
if (string.IsNullOrEmpty(input))
throw new ArgumentException("Input cannot be null or empty");
if (string.IsNullOrEmpty(hexKey))
throw new ArgumentException("Hex key cannot be null or empty");
byte[] keyBytes = HexStringToByteArray(hexKey);
using (var hmac = new HMACSHA256(keyBytes))
{
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
byte[] hashBytes = hmac.ComputeHash(inputBytes);
return ByteArrayToHexString(hashBytes);
}
}
private static byte[] HexStringToByteArray(string hex)
{
int length = hex.Length;
if (length % 2 != 0)
throw new ArgumentException("Invalid hex key length");
byte[] bytes = new byte[length / 2];
for (int i = 0; i < length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
private static string ByteArrayToHexString(byte[] bytes)
{
var sb = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
}
}
//Example in a Controller or Service
using YourProject.Helpers;
public class TestService
{
public void TestHash()
{
string inputText = "Amount=36&CurrencyId=512&MerchantId=1369217&MerchantReference=26_23122645&RequestDateTime=2023-12-26T09:42:46Z&SessionToken=&TerminalId=6942344";
string hexKey = "9FFA1F36D6E8A136482DF921E856709226DE5A974DB2673F84DB79DA788F7E19";
string hash = AmwalHashHelper.EncryptWithSHA256(inputText, hexKey);
Console.WriteLine(hash);
}
}
React / JavaScript (Backend or Trusted Server Only) #
import CryptoJS from "crypto-js";
function generateSecureHash(data, hexKey) {
const key = CryptoJS.enc.Hex.parse(hexKey);
return CryptoJS.HmacSHA256(data, key)
.toString(CryptoJS.enc.Hex)
.toUpperCase();
}
TypeScript
let paramsObj = {
Amount: "10",
CurrencyId: "512",
MerchantId: "48804",
MerchantReference: "",
RequestDateTime: "121123103839",
TerminalId: "113176",
SessionToken: "YOUR_SESSION_TOKEN"
};
function calcHash(obj: any, secret: string) {
try {
let objSorted = Object.keys(obj)
.sort()
.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {});
let dataPreparedForHashing = Object.entries(objSorted)
.map(([key, value]) => `${key}=${value}`)
.join("&");
const hmac = crypto.createHmac("sha256", Buffer.from(secret, "hex"));
const hashValue = hmac.update(dataPreparedForHashing, "utf-8").digest("hex");
return hashValue.toUpperCase();
} catch (error) {
return "";
}
}
let hash = calcHash(paramsObj, "YOUR_SECRET_KEY");
✅ Request Hash Example #
Secure Key
64373939653761352D343730352D343666632D623264312D3436323532346361615564654
Data String
Amount=10&CurrencyId=512&MerchantId=48804&MerchantReference=&RequestDateTime=121123103839&SessionToken=&TerminalId=113176
Result
8A8E9F1BC2979D6D89A947008831199E76331689D5B28D41395FA1DA65FFDE7B
🔁 Response Integrity Validation #
Every AMWAL response contains a field:
“secureHashValue”
You must recalculate the hash on your backend and compare.
Callback Response Parameters Used for Hash #
{
amount,
currencyId,
customerId,
customerTokenId,
merchantId,
merchantReference,
responseCode,
terminalId,
transactionId,
transactionTime
}
Cloud Notification Response Parameters Used for Hash #
{
Amount,
AuthorizationDateTime,
CurrencyId,
DateTimeLocalTrxn,
MerchantId,
MerchantReference,
Message,
PaidThrough,
ResponseCode,
SystemReference,
TerminalId,
TxnType
}
Sorted Response String Example #
amount=1¤cyId=512&customerId=82383bce-6e32-4f5b-b1ea-7e00d5c446ed&customerTokenId=aacd0817-2246-4521-a3df-9f3971c63a22&merchantId=7921&merchantReference=201204&responseCode=00&terminalId=221143&transactionId=6b75efb6-84ab-46f2-8a32-351a23490f45&transactionTime=2024-12-10T15:56:37.1099636Z
Expected Hash Output #
4E21F6F06C188F38B0B6A3CD2EFD9DC93EE83E3D5284C708B1C8930D9BCF0D11
✔️ If calculated hash matches response hash → response is valid
❌ If mismatch → reject response
