Signature
All requests to GLODIPAY must include a signature field. All responses and IPN payloads from GLODIPAY include a signature field so you can verify authenticity.
Signatures use RSA with MD5 (md5WithRSAEncryption).
Your RSA Private Key and the RSA Public Key are both available from the API Keys page in the Merchant Dashboard.
Generating a Signature (Merchant → GLODIPAY)
Sign request payloads with your RSA Private Key (obtained from the Merchant Dashboard).
Steps:
- Filter Payload: Collect all request parameters except
signature. Remove any keys with empty strings (""),null, orundefinedvalues. - Stringify Nested Data: If a field value is an object or array (e.g.,
metadata,browserDetails,transactionDocuments), convert it to a JSON string using your language's default encoder (no spaces). - Sort Top-Level Keys: Sort the resulting keys in natural ascending order (case-sensitive). Use
SORT_NATURALin PHP orlocaleCompare(b, undefined, { numeric: true, sensitivity: 'base' })in JavaScript. - Casting & Trimming:
- Convert numbers to strings.
- Convert booleans:
truebecomes"1",falsebecomes"". - Trim leading/trailing whitespace from all values.
- Canonicalization: Serialize the sorted object to a JSON string with all non-ASCII Unicode characters escaped to
\uXXXX(RFC 8259). In PHP,json_encode($data)does this by default — do not useJSON_UNESCAPED_UNICODE. In Node.js,JSON.stringify()does not escape Unicode by default — apply the fix below.- Node.js — forward slashes: Apply
.replace(/\//g, '\\/')to match PHP's default behavior. - Node.js — Unicode (RFC 8259): Apply
.replace(/[\u0080-\uffff]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))to escape all non-ASCII characters. This is required becauseJSON.stringify()keeps characters likeá,đ,ễas raw UTF-8, while PHP escapes them toá,đ,ễ. Without this fix, signatures will not match when the payload contains non-ASCII characters.
- Node.js — forward slashes: Apply
- Signing: Sign the canonical string with
md5WithRSAEncryption. - Encoding: Base64-encode the binary output and add it as the
signaturefield.
Verifying a Signature (GLODIPAY → Merchant)
Verify GLODIPAY responses and webhooks with the RSA Public Key (available in the Portal).
Steps:
- Extract the
signaturevalue and remove thesignaturekey from the payload. - Follow the same Filtering, Stringifying, Sorting, and Trimming steps as described above.
- Verify the canonical string against the extracted signature using the RSA Public Key and
md5WithRSAEncryption.
Code Examples
PHP
<?php
function generateSignature(array $data, string $privateKeyStr): string
{
unset($data['signature']);
// 1. Filter out empty/null values
$data = array_filter($data, function($v) {
return $v !== "" && $v !== null;
});
// 2. Stringify nested objects
foreach ($data as $k => $v) {
if (is_array($v)) $data[$k] = json_encode($v);
}
// 3. Sort keys
ksort($data, SORT_NATURAL);
// 4. Casting and trimming
array_walk_recursive($data, function (&$v) {
if (is_bool($v)) $v = $v ? "1" : "";
else $v = trim((string)$v);
});
// 5. Sign (PHP json_encode escapes '/' by default)
$key = openssl_pkey_get_private($privateKeyStr);
openssl_sign(json_encode($data), $signature, $key, 'md5WithRSAEncryption');
return base64_encode($signature);
}
Node.js
// Save as script.mjs and run: node script.mjs
import { createSign } from 'crypto';
function phpCast(v) {
if (typeof v === 'number') return String(v);
if (typeof v === 'boolean') return v ? '1' : '';
if (typeof v === 'string') return v.trim();
if (Array.isArray(v)) return v.map(phpCast);
if (v && typeof v === 'object') return Object.fromEntries(Object.entries(v).map(([k, val]) => [k, phpCast(val)]));
return v;
}
function generateSignature(data, privateKey) {
const sorted = {};
Object.keys(data)
.filter(k => k !== 'signature')
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }))
.forEach(k => { sorted[k] = data[k]; });
const canonical = JSON.stringify(phpCast(sorted))
.replace(/\//g, '\\/')
.replace(/[\u0080-\uffff]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));
const sign = createSign('md5WithRSAEncryption');
sign.update(canonical);
return sign.sign(privateKey, 'base64');
}