Kiểm tra dữ liệu với signature
1. Kiểm tra thông tin bằng chữ ký
Giá trị signature
được dùng để kiểm tra tính chính xác của dữ liệu.
Cách tạo signature
- Sử dụng thuật toán HMAC_SHA256 để tạo signature.
- data tạo signature dạng: key1=value1&key2=value2... (key1: tên field, value1 = giá trị của key1).
- Dữ liệu tạo signature sắp xếp theo key thứ tự alphabet.
- Cấu trúc: hash_hmac("sha256", string $data , string $checksum_key)
Lưu ý:
Checksum Key được tạo ra sau khi tạo cổng thanh toán thành công
Code mẫu kiểm tra chính xác dữ liệu
- PHP
- Javascript
- Python
- Java
<?php
$checksum_key = "1a54716c8f0efb2744fb28b6e38b25da7f67a925d98bc1c18bd8faaecadd7675";
$webhookData = array(
"code" => "00",
"desc" => "success",
"success" => true,
"data" => array(
"orderCode" => 123,
"amount" => 3000,
"description" => "VQRIO123",
"accountNumber" => "12345678",
"reference" => "TF230204212323",
"transactionDateTime" => "2023-02-04 18:25:00",
"currency" => "VND",
"paymentLinkId" => "124c33293c43417ab7879e14c8d9eb18",
"code" => "00",
"desc" => "Thành công",
"counterAccountBankId" => "",
"counterAccountBankName" => "",
"counterAccountName" => "",
"counterAccountNumber" => "",
"virtualAccountName" => "",
"virtualAccountNumber" => ""
),
"signature" => "412e915d2871504ed31be63c8f62a149a4410d34c4c42affc9006ef9917eaa03"
);
function isValidData($transaction, $transaction_signature, $checksum_key)
{
ksort($transaction);
$transaction_str_arr = [];
foreach ($transaction as $key => $value) {
if (in_array($value, ["undefined", "null"]) || gettype($value) == "NULL") {
$value = "";
}
if (is_array($value)) {
$valueSortedElementObj = array_map(function ($ele) {
ksort($ele);
return $ele;
}, $value);
$value = json_encode($valueSortedElementObj, JSON_UNESCAPED_UNICODE);
}
$transaction_str_arr[] = $key . "=" . $value;
}
$transaction_str = implode("&", $transaction_str_arr);
dump($transaction_str);
$signature = hash_hmac("sha256", $transaction_str, $checksum_key);
dump($signature);
return $signature == $transaction_signature;
}
isValidData($webhookData['data'], $webhookData['signature'], $checksum_key);
import { createHmac } from "crypto";
const checksumKey =
"1a54716c8f0efb2744fb28b6e38b25da7f67a925d98bc1c18bd8faaecadd7675";
const webhookData = {
code: "00",
desc: "success",
success: true,
data: {
orderCode: 123,
amount: 3000,
description: "VQRIO123",
accountNumber: "12345678",
reference: "TF230204212323",
transactionDateTime: "2023-02-04 18:25:00",
currency: "VND",
paymentLinkId: "124c33293c43417ab7879e14c8d9eb18",
code: "00",
desc: "Thành công",
counterAccountBankId: "",
counterAccountBankName: "",
counterAccountName: "",
counterAccountNumber: "",
virtualAccountName: "",
virtualAccountNumber: "",
},
signature: "412e915d2871504ed31be63c8f62a149a4410d34c4c42affc9006ef9917eaa03",
};
function sortObjDataByKey(object) {
const orderedObject = Object.keys(object)
.sort()
.reduce((obj, key) => {
obj[key] = object[key];
return obj;
}, {});
return orderedObject;
}
function convertObjToQueryStr(object) {
return Object.keys(object)
.filter((key) => object[key] !== undefined)
.map((key) => {
let value = object[key];
// Sort nested object
if (value && Array.isArray(value)) {
value = JSON.stringify(value.map((val) => sortObjDataByKey(val)));
}
// Set empty string if null
if ([null, undefined, "undefined", "null"].includes(value)) {
value = "";
}
return `${key}=${value}`;
})
.join("&");
}
function isValidData(data, currentSignature, checksumKey) {
const sortedDataByKey = sortObjDataByKey(data);
const dataQueryStr = convertObjToQueryStr(sortedDataByKey);
const dataToSignature = createHmac("sha256", checksumKey)
.update(dataQueryStr)
.digest("hex");
return dataToSignature == currentSignature;
}
return isValidData(webhookData.data, webhookData.signature, checksumKey);
// Python3
import hmac
import hashlib
checksum_key = "1a54716c8f0efb2744fb28b6e38b25da7f67a925d98bc1c18bd8faaecadd7675"
webhook_data = {
"code": '00',
"desc": 'success',
"success": True,
"data": {
"orderCode": 123,
"amount": 3000,
"description": 'VQRIO123',
"accountNumber": '12345678',
"reference": 'TF230204212323',
"transactionDateTime": '2023-02-04 18:25:00',
"currency": 'VND',
"paymentLinkId": '124c33293c43417ab7879e14c8d9eb18',
"code": '00',
"desc": 'Thành công',
"counterAccountBankId": '',
"counterAccountBankName": '',
"counterAccountName": '',
"counterAccountNumber": '',
"virtualAccountName": '',
"virtualAccountNumber": '',
},
"signature": '412e915d2871504ed31be63c8f62a149a4410d34c4c42affc9006ef9917eaa03'
}
def convertObjToQueryStr(obj: dict) -> str:
query_string = []
for key, value in obj.items():
value_as_string = ""
if isinstance(value, (int, float, bool)):
value_as_string = str(value)
elif value in [None, 'null', 'NULL']:
value_as_string = ""
elif isinstance(value, list):
value_as_string = str([sortObjDataByKey(item) for item in value]).replace(": ", ":").replace('None', 'null').replace('\'', '"').replace(", ", ",")
else: value_as_string = value
query_string.append(f"{key}={value_as_string}")
return "&".join(query_string)
def sortObjDataByKey(obj: dict) -> dict:
return dict(sorted(obj.items()))
def isValidData(data,signature,key):
sorted_data_by_key = sortObjDataByKey(data)
data_query_str = convertObjToQueryStr(sorted_data_by_key)
print(data_query_str)
data_to_signature = hmac.new(key.encode("utf-8"), msg=data_query_str.encode("utf-8"), digestmod=hashlib.sha256).hexdigest()
return data_to_signature == signature
import java.util.*;
import org.apache.commons.codec.digest.HmacUtils;
import org.json.JSONObject;
public class Main {
static String checksumKey = "1a54716c8f0efb2744fb28b6e38b25da7f67a925d98bc1c18bd8faaecadd7675";
static String transaction = "{'orderCode':123,'amount':3000,'description':'VQRIO123','accountNumber':'12345678','reference':'TF230204212323','transactionDateTime':'2023-02-04 18:25:00','currency':'VND','paymentLinkId':'124c33293c43417ab7879e14c8d9eb18','code':'00','desc':'Thành công','counterAccountBankId':'','counterAccountBankName':'','counterAccountName':'','counterAccountNumber':'','virtualAccountName':'','virtualAccountNumber':''}";
static String transactionSignature = "412e915d2871504ed31be63c8f62a149a4410d34c4c42affc9006ef9917eaa03";
public static Boolean isValidData(String transaction, String transactionSignature) {
try {
JSONObject jsonObject = new JSONObject(transaction);
Iterator<String> sortedIt = sortedIterator(jsonObject.keys(), (a, b) -> a.compareTo(b));
StringBuilder transactionStr = new StringBuilder();
while (sortedIt.hasNext()) {
String key = sortedIt.next();
String value = jsonObject.get(key).toString();
transactionStr.append(key);
transactionStr.append('=');
transactionStr.append(value);
if (sortedIt.hasNext()) {
transactionStr.append('&');
}
}
String signature = new HmacUtils("HmacSHA256", checksumKey).hmacHex(transactionStr.toString());
return signature.equals(transactionSignature);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static Iterator<String> sortedIterator(Iterator<?> it, Comparator<String> comparator) {
List<String> list = new ArrayList<String>();
while (it.hasNext()) {
list.add((String) it.next());
}
Collections.sort(list, comparator);
return list.iterator();
}
public static void main(String[] args) {
System.out.println(isValidData(transaction, transactionSignature));
}
}
Xem chi tiết về API tại đây.
2. Thông tin dữ liệu mẫu
code required | string Mã lỗi |
desc required | string Thông tin lỗi |
success required | boolean Trạng thái thành công |
required | object |
signature required | string Chữ kí để kiểm tra thông tin, chi tiết dữ liệu mẫu |
{- "code": "string",
- "desc": "string",
- "success": true,
- "data": {
- "orderCode": 123,
- "amount": 3000,
- "description": "VQRIO123",
- "accountNumber": "12345678",
- "reference": "TF230204212323",
- "transactionDateTime": "2023-02-04 18:25:00",
- "currency": "VND",
- "paymentLinkId": "124c33293c43417ab7879e14c8d9eb18",
- "code": "00",
- "desc": "Thành công",
- "counterAccountBankId": "",
- "counterAccountBankName": "",
- "counterAccountName": "",
- "counterAccountNumber": "",
- "virtualAccountName": "",
- "virtualAccountNumber": ""
}, - "signature": "8d8640d802576397a1ce45ebda7f835055768ac7ad2e0bfb77f9b8f12cca4c7f"
}