Hash calculation
Every Omniware request carries a hash field that signs the rest of the body. Responses that include sensitive state (payment outcome, refund status) also carry a hash you should verify before acting on the result.
The algorithm
- Start with the salt
Begin a string with your
saltas the first segment. - Sort parameters by key
Take all request parameters except
hashand sort by key, alphabetically. - Append non-empty values, pipe-delimited
For each parameter whose value is non-empty (
strlen(value) > 0), append|followed by the value. Empty parameters are skipped entirely. - SHA-512 and uppercase
Hash the resulting string with SHA-512, take the hex digest, and convert to uppercase. That string is your
hash.
The salt prefix is appended unconditionally — there is no leading | before it. The first | separator appears between the salt and the first parameter value.
Reference implementation
- PHP
- Node.js
- Python
- Java
- cURL
function generateHashKey(array $parameters, string $salt): ?string
{
$hashing_method = 'sha512';
$secure_hash = null;
ksort($parameters);
$hash_data = $salt;
foreach ($parameters as $key => $value) {
if (strlen($value) > 0) {
$hash_data .= '|' . $value;
}
}
if (strlen($hash_data) > 0) {
$secure_hash = strtoupper(hash($hashing_method, $hash_data));
}
return $secure_hash;
}
import crypto from "node:crypto";
export function generateHash(params, salt) {
const sortedKeys = Object.keys(params).sort();
let hashData = salt;
for (const key of sortedKeys) {
const value = params[key];
if (value !== undefined && value !== null && String(value).length > 0) {
hashData += "|" + value;
}
}
return crypto.createHash("sha512").update(hashData).digest("hex").toUpperCase();
}
import hashlib
def generate_hash(params: dict, salt: str) -> str:
hash_data = salt
for key in sorted(params):
value = params[key]
if value is None:
continue
s = str(value)
if len(s) > 0:
hash_data += "|" + s
return hashlib.sha512(hash_data.encode("utf-8")).hexdigest().upper()
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;
public static String generateHash(Map<String, String> params, String salt) throws Exception {
TreeMap<String, String> sorted = new TreeMap<>(params);
StringBuilder hashData = new StringBuilder(salt);
for (Map.Entry<String, String> e : sorted.entrySet()) {
String v = e.getValue();
if (v != null && !v.isEmpty()) {
hashData.append("|").append(v);
}
}
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] digest = md.digest(hashData.toString().getBytes("UTF-8"));
StringBuilder hex = new StringBuilder();
for (byte b : digest) hex.append(String.format("%02X", b));
return hex.toString();
}
# The hash is computed before you call curl. Use one of the language samples
# above to generate the value, then pass it on the request line:
HASH="$(php hash.php)" # or node, python, etc.
curl -X POST https://pgbiz.omniware.in/v2/paymentrequest \
-d "api_key=$API_KEY" \
-d "order_id=ORD-1001" \
-d "amount=149.00" \
-d "hash=$HASH"
Verifying a response hash
When Omniware sends you a response (server-to-server callback, return URL redirect, or API response body) that includes a hash, recompute it using the same algorithm and compare.
- PHP
- Node.js
- Python
function responseHashCheck(string $salt, array $response_array): bool
{
if (is_null($response_array['hash'] ?? null)) {
return true; // no hash to verify
}
$response_hash = $response_array['hash'];
unset($response_array['hash']);
$response_json = json_encode($response_array);
$calculated = strtoupper(hash('sha512', $salt . $response_json));
return hash_equals($response_hash, $calculated);
}
import crypto from "node:crypto";
export function verifyResponseHash(response, salt) {
if (!response.hash) return true;
const sent = response.hash;
const { hash, ...rest } = response;
const json = JSON.stringify(rest);
const computed = crypto.createHash("sha512").update(salt + json).digest("hex").toUpperCase();
return crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(computed));
}
import hashlib, hmac, json
def verify_response_hash(response: dict, salt: str) -> bool:
sent = response.get("hash")
if not sent:
return True
body = {k: v for k, v in response.items() if k != "hash"}
computed = hashlib.sha512((salt + json.dumps(body)).encode()).hexdigest().upper()
return hmac.compare_digest(sent, computed)
Two response-hash conventions exist in the Omniware platform:
- Body-as-JSON (Payment Status, Refund Status, S2S webhooks) —
hash = SHA512(salt + json_without_hash_field). - Sorted-pipe (form-post redirects) — same algorithm as the request: sort fields, pipe-delimit non-empty values, prefix
salt.
The endpoint reference for each API tells you which convention to use.
Use a constant-time comparison (hash_equals / crypto.timingSafeEqual / hmac.compare_digest) when comparing hashes to avoid leaking information through timing differences.