Bank account linking result webhook
When a customer completes linking a bank account through the SDK (Frontend Web or Mobile SDK), the TINGEE system automatically sends a webhook notification (event type bank-link) to the webhook URL you configured on the Portal.
Notification that syncs the successfully linked bank account information from Tingee to the partner system.
Webhook authentication types
Tingee supports 3 security authentication types when sending webhooks to your server (configured directly on the Portal):
- Signature authentication (Recommended):
If you configure a Secret Token for the webhook endpoint on the Portal, Tingee includes the
x-signatureheader. The signature is generated withHMAC_SHA512(timestamp + ":" + json_body, secretKey). - API Key authentication (custom header): If you choose API Key as the authentication type, Tingee attaches a header with the name and value you set in advance on the Portal.
- API Credentials authentication (Client ID & Secret Token):
If you choose API Credentials as the authentication type, Tingee attaches the
x-client-idheader containing your Client ID so you can match it.
Headers sent
These headers help you identify the request and verify the integrity of the received data.
| Header | Description |
|---|---|
Content-Type | Always application/json. |
x-request-id | Unique identifier of each webhook request (UUID). |
x-request-timestamp | Time the notification is sent (format: yyyyMMddHHmmssSSS, UTC+7). |
x-signature | HMAC SHA512 signature used to verify the data (sent if a Secret Token is configured). |
x-client-id | Your Client ID (sent if API Credentials authentication is selected). |
Body Parameter
The details of the linked bank account are sent in the body.
| Field | Type | Description |
|---|---|---|
merchantId | number | Partner ID (Merchant ID). |
type | string | Webhook event type. Fixed value: bank-link. |
merchantAccountNumberId | number | ID of the newly linked bank account on the Tingee system. |
accountType | string | Linked account type (e.g. PersonalAccount, BusinessAccount). |
vaAccountNumber | string | Virtual account (VA) number or actual receiving account number that was created/linked. |
bankName | string | Code / short name of the linked bank (e.g. BIDV, VCB, ACB...). |
bankBin | string | BIN of the linked bank (e.g. 970418 for BIDV). |
shopId | number | ID of the shop attached to this bank account. |
status | string | Bank linking status. Value: success. |
metadata | string | Additional data (metadata) you passed when creating the linking session (if any). |
Sample 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"
}
Webhook authentication
Depending on the Webhook authentication type you choose on the Portal, your server authenticates requests as follows:
1. No authentication
The system sends no authentication information other than the default headers (x-request-id, x-request-timestamp).
2. Custom API key
Tingee attaches a custom header (e.g. x-api-key) containing the secret key value you set on the Portal. You only need to check that this header matches.
3. API Credentials (Recommended)
This is the most secure method, using the Client ID and Secret Token pair:
- Headers sent:
x-client-id,x-request-timestampand thex-signaturesignature. - Match
x-client-id, then recompute thex-signaturesignature withHMAC-SHA512over the string${x-request-timestamp}:${body}using the correspondingSecret Tokenas the key, and compare.
Signature verification code samples (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);
}
Responding to the request
After receiving and processing the webhook successfully (e.g. activating the linked bank account status in your system), your server must respond with HTTP 200 OK and the following JSON:
{
"code": "00",
"message": "Success"
}
⚠️ Important notes
- Scope configuration: To receive this webhook, your webhook endpoint on the Portal must be registered with the
bank-linkscope. - Latency: The webhook is sent immediately after the bank linking succeeds on the client.
- Idempotency: Store
merchantAccountNumberIdto avoid duplicate processing if the system resends the webhook because of a temporary network issue.