Redirect kết quả giao dịch Deep Link
Sau khi người dùng hoàn tất thao tác trên ứng dụng ngân hàng, hệ thống Tingee sẽ tự động chuyển hướng người dùng quay trở lại ứng dụng hoặc website của đối tác kèm theo thông tin kết quả giao dịch.
Hệ thống Tingee sẽ append kết quả giao dịch trực tiếp vào redirectUrl dưới dạng Query String.
Query Parameters
Các tham số được đính kèm vào URL chuyển hướng.
| Tham số | Kiểu | Mô tả |
|---|---|---|
amount | number | Số tiền giao dịch thực tế. |
bankName | string | Tên ngân hàng nhận. |
bankBin | string | Mã BIN ngân hàng nhận. |
accountNumber | string | Số tài khoản người nhận. |
content | string | Nội dung chuyển khoản. |
deepLinkStatus | string | Trạng thái (Success hoặc Failed). |
transactionDate | long | Thời gian giao dịch (Timestamp ms). |
clientId | string | Client ID của đối tác. |
signature | string | Chữ ký xác thực HMAC SHA512 đảm bảo dữ liệu không bị thay đổi. |
Quy tắc xác thực
Để đảm bảo an toàn, đối tác phải kiểm tra chữ ký signature nhận được. Chữ ký này được tạo bằng cách ký HMAC SHA512 của "toàn bộ các tham số (trừ signature) được sắp xếp theo bảng chữ cái" với secretKey.
Ví dụ mã nguồn xác thực
- NestJS
- C#
- Java
- PHP
import { createHmac } from 'crypto';
function verifySignature(query: any, secretKey: string): boolean {
const { signature, ...data } = query;
// Sắp xếp các key theo alphabet
const sortedKeys = Object.keys(data).sort();
const canonicalString = sortedKeys
.map(key => `${key}=${data[key]}`)
.join('&');
const computedSig = createHmac('sha512', secretKey)
.update(canonicalString)
.digest('hex');
return computedSig === signature;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public static bool VerifySignature(IDictionary<string, string> query, string secretKey)
{
if (!query.ContainsKey("signature")) return false;
var signature = query["signature"];
var data = query.Where(x => x.Key != "signature")
.OrderBy(x => x.Key);
var canonicalString = string.Join("&", data.Select(x => $"{x.Key}={x.Value}"));
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var messageBytes = Encoding.UTF8.GetBytes(canonicalString);
using (var hmac = new HMACSHA512(keyBytes))
{
var hash = hmac.ComputeHash(messageBytes);
var computedSig = BitConverter.ToString(hash).Replace("-", "").ToLower();
return computedSig == signature;
}
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
public class SignatureUtil {
public static boolean verifySignature(Map<String, String> query, String secretKey) throws Exception {
String signature = query.get("signature");
if (signature == null) return false;
List<String> sortedKeys = query.keySet().stream()
.filter(key -> !key.equals("signature"))
.sorted()
.collect(Collectors.toList());
String canonicalString = sortedKeys.stream()
.map(key -> key + "=" + query.get(key))
.collect(Collectors.joining("&"));
Mac sha512_HMAC = Mac.getInstance("HmacSHA512");
SecretKeySpec secret_key = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
sha512_HMAC.init(secret_key);
byte[] hash = sha512_HMAC.doFinal(canonicalString.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : hash) sb.append(String.format("%02x", b));
return sb.toString().equals(signature);
}
}
function verifySignature($query, $secretKey) {
$signature = $query['signature'] ?? '';
unset($query['signature']);
ksort($query);
$canonicalString = [];
foreach ($query as $key => $value) {
$canonicalString[] = "$key=$value";
}
$canonicalString = implode('&', $canonicalString);
$computedSig = hash_hmac('sha512', $canonicalString, $secretKey);
return hash_equals($computedSig, $signature);
}
Ví dụ URL Redirect
Hệ thống sẽ chuyển hướng người dùng về địa chỉ sau:
https://partner.vn/callback?amount=100000&bankBin=970422&deepLinkStatus=Success&signature=91a2a8b7d...
Ghi chú quan trọng
- Nếu
redirectUrlban đầu của bạn đã có sẵn query string, Tingee sẽ tự động thêm dấu&để không làm hỏng cấu trúc URL cũ của bạn. - Luôn kiểm tra trường
deepLinkStatusđể biết giao dịch thành công hay thất bại trước khi xử lý nghiệp vụ tiếp theo. - Khuyến cáo log lại toàn bộ URL callback để phục vụ việc tra soát (audit) khi cần thiết.