Webhook thông báo kết quả liên kết ngân hàng
Khi khách hàng hoàn tất quá trình liên kết tài khoản ngân hàng thông qua SDK (Frontend Web hoặc Mobile SDK), hệ thống TINGEE sẽ tự động gửi một thông báo Webhook (loại sự kiện bank-link) tới địa chỉ URL webhook mà bạn đã cấu hình trên Portal.
Thông báo đồng bộ thông tin tài khoản ngân hàng liên kết thành công từ Tingee về hệ thống đối tác.
Kiểu chứng thực Webhooks
Hệ thống Tingee hỗ trợ 3 kiểu chứng thực bảo mật khi gửi webhook về server của bạn (được cấu hình trực tiếp trên Portal):
- Xác thực bằng Chữ ký (Signature - Khuyến nghị):
Nếu đối tác cấu hình Secret Token cho Webhook endpoint trên Portal, Tingee sẽ gửi kèm header
x-signature. Chữ ký được tạo bằng thuật toánHMAC_SHA512(timestamp + ":" + json_body, secretKey). - Xác thực bằng API Key (Header tuỳ chỉnh): Nếu đối tác cấu hình kiểu chứng thực là API Key, Tingee sẽ đính kèm header với Tên và Giá trị do đối tác thiết lập trước trên Portal.
- Xác thực bằng API Credentials (Client ID & Secret Token):
Nếu đối tác cấu hình kiểu chứng thực là API Credentials, Tingee sẽ đính kèm header
x-client-idchứa Client ID của đối tác để bạn đối chiếu.
Header gửi kèm
Các header này giúp bạn định danh và xác thực tính toàn vẹn của dữ liệu nhận được.
| Header | Mô tả |
|---|---|
Content-Type | Luôn là application/json. |
x-request-id | Mã định danh duy nhất cho mỗi yêu cầu webhook (UUID). |
x-request-timestamp | Thời gian gửi thông báo (format: yyyyMMddHHmmssSSS, múi giờ UTC+7). |
x-signature | Chữ ký HMAC SHA512 dùng để kiểm tra tính hợp lệ của dữ liệu (gửi kèm nếu cấu hình Secret Token). |
x-client-id | Client ID của đối tác (gửi kèm nếu chọn chứng thực API Credentials). |
Body Parameter
Dữ liệu chi tiết về tài khoản ngân hàng liên kết được gửi trong phần body.
| Trường | Kiểu | Mô tả |
|---|---|---|
merchantId | number | ID của đối tác (Merchant ID). |
type | string | Loại sự kiện webhook. Giá trị cố định: bank-link. |
merchantAccountNumberId | number | ID của tài khoản ngân hàng liên kết vừa tạo trên hệ thống Tingee. |
accountType | string | Loại tài khoản liên kết (ví dụ: PersonalAccount, BusinessAccount). |
vaAccountNumber | string | Số tài khoản ảo (VA) hoặc số tài khoản thực nhận được tạo/liên kết. |
bankName | string | Mã/tên viết tắt của ngân hàng liên kết (ví dụ: BIDV, VCB, ACB...). |
bankBin | string | Mã BIN của ngân hàng liên kết (ví dụ: 970418 đối với BIDV). |
shopId | number | ID của cửa hàng (Shop) được gắn với tài khoản ngân hàng này. |
status | string | Trạng thái liên kết ngân hàng. Giá trị: success. |
metadata | string | Dữ liệu bổ sung (metadata) do đối tác tự truyền vào khi tạo session liên kết (nếu có). |
Ví dụ Payload Body:
{
"merchantId": 1001,
"type": "bank-link",
"merchantAccountNumberId": 456,
"accountType": "business-account",
"vaAccountNumber": "1234567890",
"bankName": "BIDV",
"bankBin": "970418",
"shopId": 12,
"status": "success",
"metadata": "custom_data_here"
}
Xác thực Webhook (Authentication)
Tùy thuộc vào cấu hình Kiểu chứng thực Webhooks bạn chọn trên Portal, máy chủ của bạn sẽ thực hiện xác thực theo các cách sau:
1. Không cần chứng thực
Hệ thống không gửi kèm thông tin xác thực nào khác ngoài các header mặc định (x-request-id, x-request-timestamp).
2. API key tuỳ chỉnh
Tingee sẽ đính kèm header tùy chỉnh (ví dụ: x-api-key) chứa giá trị khóa bí mật mà bạn đã thiết lập trên Portal. Đối tác chỉ cần kiểm tra sự trùng khớp của header này.
3. API Credentials (Khuyến nghị)
Đây là phương thức bảo mật cao nhất sử dụng cặp Client ID và Secret Token:
- Header gửi kèm bao gồm:
x-client-id,x-request-timestamp, và chữ kýx-signature. - Bạn cần đối chiếu
x-client-idvà tính toán lại chữ kýx-signaturebằng thuật toánHMAC-SHA512dựa trên chuỗi${x-request-timestamp}:${body}với khóa bí mậtSecret Tokentương ứng để so sánh.
Ví dụ mã nguồn xác thực chữ ký (API Credentials)
- NestJS
- C#
- Java
- PHP
import { createHmac } from 'crypto';
function verifyWebhook(headers: any, body: any, secretKey: string): boolean {
const signature = headers['x-signature'];
const timestamp = headers['x-request-timestamp'];
const message = `${timestamp}:${JSON.stringify(body)}`;
const computedSig = createHmac('sha512', secretKey).update(message).digest('hex');
return computedSig === signature;
}
using System;
using System.Security.Cryptography;
using System.Text;
public class WebhookVerifier
{
public static bool VerifyWebhook(string body, string timestamp, string signature, string secretKey)
{
var message = $"{timestamp}:{body}";
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var messageBytes = Encoding.UTF8.GetBytes(message);
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;
public class WebhookVerifier {
public static boolean verifySignature(String body, String timestamp, String signature, String secretKey) throws Exception {
String message = timestamp + ":" + body;
Mac hmac = Mac.getInstance("HmacSHA512");
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
hmac.init(keySpec);
byte[] hash = hmac.doFinal(message.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(String.format("%02x", b));
}
return sb.toString().equals(signature);
}
}
function verifyWebhook($headers, $body, $secretKey) {
$timestamp = $headers['x-request-timestamp'] ?? '';
$signature = $headers['x-signature'] ?? '';
$message = $timestamp . ':' . $body;
$computedSig = hash_hmac('sha512', $message, $secretKey);
return hash_equals($computedSig, $signature);
}
Phản hồi yêu cầu
Sau khi nhận và xử lý webhook thành công (ví dụ: kích hoạt trạng thái tài khoản ngân hàng liên kết trên hệ thống của bạn), máy chủ của bạn cần phản hồi với mã HTTP 200 OK và nội dung JSON như sau:
{
"code": "00",
"message": "Success"
}
⚠️ Lưu ý quan trọng
- Cấu hình Scope: Để nhận được webhook này, Endpoint Webhook của bạn trên Portal cần được đăng ký với scope
bank-link. - Độ trễ: Webhook sẽ được gửi ngay lập tức sau khi giao dịch liên kết ngân hàng thành công ở client.
- Idempotency: Hãy lưu trữ
merchantAccountNumberIdđể tránh việc xử lý trùng lặp nếu hệ thống gửi lại webhook do sự cố mạng tạm thời.