Chuyển tới nội dung chính

Chữ ký

Tất cả các yêu cầu gửi đến GLODIPAY phải bao gồm trường signature. Tất cả các phản hồi và payload IPN từ GLODIPAY cũng bao gồm trường signature để bạn có thể xác minh tính xác thực.

Chữ ký sử dụng thuật toán RSA với MD5 (md5WithRSAEncryption).

RSA Private KeyRSA Public Key đều có sẵn trong trang API Keys trong Dashboard của người bán.


Tạo chữ ký (Merchant → GLODIPAY)

Ký các payload yêu cầu bằng RSA Private Key của bạn (lấy từ Merchant Dashboard).

Các bước:

  1. Lọc Payload: Thu thập tất cả các tham số yêu cầu ngoại trừ signature. Loại bỏ bất kỳ khóa nào có giá trị là chuỗi rỗng (""), null hoặc undefined.
  2. Chuyển đổi dữ liệu lồng nhau thành chuỗi: Nếu giá trị của một trường là một đối tượng hoặc mảng (ví dụ: metadata, browserDetails, transactionDocuments), hãy chuyển đổi nó thành chuỗi JSON bằng bộ mã hóa mặc định của ngôn ngữ bạn đang dùng (không có khoảng trắng).
  3. Sắp xếp các khóa cấp cao nhất: Sắp xếp các khóa kết quả theo thứ tự tăng dần tự nhiên (phân biệt chữ hoa chữ thường). Sử dụng SORT_NATURAL trong PHP hoặc localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }) trong JavaScript.
  4. Ép kiểu và Cắt bỏ khoảng trắng (Casting & Trimming):
    • Chuyển đổi số thành chuỗi.
    • Chuyển đổi kiểu boolean: true thành "1", false thành chuỗi rỗng "".
    • Cắt bỏ khoảng trắng ở đầu và cuối (trim) tất cả các giá trị.
  5. Chuẩn hóa (Canonicalization): Tuần tự hóa đối tượng đã sắp xếp thành một chuỗi JSON với tất cả các ký tự Unicode không phải ASCII được thoát thành \uXXXX (RFC 8259). Trong PHP, json_encode($data) thực hiện điều này theo mặc định — không sử dụng JSON_UNESCAPED_UNICODE. Trong Node.js, JSON.stringify() không thoát Unicode theo mặc định — áp dụng sửa lỗi bên dưới.
    • Node.js — dấu gạch chéo xuôi: Áp dụng .replace(/\//g, '\\/') để khớp với hành vi mặc định của PHP.
    • Node.js — Unicode (RFC 8259): Áp dụng .replace(/[\u0080-\uffff]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0')) để thoát tất cả các ký tự không phải ASCII. Điều này bắt buộc vì JSON.stringify() giữ nguyên các ký tự như á, đ, dưới dạng UTF-8 thô, trong khi PHP thoát chúng thành á, đ, . Nếu không có sửa lỗi này, chữ ký sẽ không khớp khi payload chứa các ký tự không phải ASCII.
  6. Ký (Signing): Ký chuỗi chuẩn hóa bằng thuật toán md5WithRSAEncryption.
  7. Mã hóa (Encoding): Mã hóa Base64 đầu ra nhị phân và thêm nó vào trường signature.

Xác minh chữ ký (GLODIPAY → Merchant)

Sử dụng RSA Public Key (có trong Portal) để xác minh các phản hồi và webhook của GLODIPAY.

Các bước:

  1. Trích xuất giá trị signature và loại bỏ khóa signature khỏi payload.
  2. Thực hiện các bước Lọc, Chuyển đổi chuỗi, Sắp xếpCắt bỏ khoảng trắng tương tự như mô tả ở trên.
  3. Xác minh chuỗi chuẩn hóa với chữ ký đã trích xuất bằng RSA Public Key và thuật toán md5WithRSAEncryption.

Ví dụ mã nguồn

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');
}

Bookmarks

No bookmarks yet.
Hover over a heading and click to save a section.