API Tạo hoặc cập nhật Webhook

API tạo webhook mới hoặc cập nhật webhook hiện có (upsert). Webhook được dùng để nhận thông báo sự kiện từ Bank Hub.


API Endpoint

POST
https://bankhub-api-sandbox.sepay.vn/v1/webhook

Request Body

webhook_urlstring

URL webhook endpoint (HTTPS required in production)

auth_typeenum

Loại xác thực

secret_keystring

Secret key (sẽ gửi trong header X-Secret-Key)

activeenum

1 = bật, 0 = tắt

Mặc định: 1
allow_eventsarray<object>

["*"] = tất cả events, [] = không events nào

Lưu ý
  • API này yêu cầu Bearer Token trong header Authorization
  • Pattern upsert: tạo mới nếu chưa có, cập nhật nếu đã tồn tại
  • Chỉ cho phép một webhook trên mỗi merchant
  • Secret key đầy đủ được trả về trong response để tham khảo

Xử lý lỗi

400Bad Request

URL không hợp lệ, IP nội bộ bị chặn (SSRF protection), hoặc tên sự kiện không hợp lệ

401Unauthorized

Access token không hợp lệ hoặc đã hết hạn

API Response

Response 200 - Upsert thành công
{
  "status": 200,
  "error": "string",
  "messages": {
    "success": [
      "string"
    ],
    "error": [
      "string"
    ]
  },
  "data": {
    "webhook_url": "https://example.com",
    "auth_type": "SECRET_KEY",
    "secret_key": "string",
    "request_content_type": "string",
    "active": 0,
    "allow_events": [
      "*"
    ]
  }
}
statusinteger

Mã trạng thái HTTP

errorstring

Mã lỗi (nếu có)

messagesobject

Thông báo kết quả

dataobject

Cấu hình webhook (secret key đầy đủ - chỉ trong POST response)

Code mẫu

1
2
3
4
5
curl --request POST \
--url https://bankhub-api-sandbox.sepay.vn/v1/webhook \
--header 'Authorization: Bearer REPLACE_BEARER_TOKEN' \
--header 'content-type: application/json' \
--data '{"webhook_url":"http://example.com","auth_type":"SECRET_KEY","secret_key":"string","active":0,"allow_events":["*"]}'

Ví dụ sử dụng

JSĐăng ký tất cả sự kiện
1
2
3
4
5
6
7
8
9
10
curl --location 'https://bankhub-api-sandbox.sepay.vn/v1/webhook' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{
"webhook_url": "https://merchant.com/webhook",
"auth_type": "SECRET_KEY",
"secret_key": "your-secret-key-here",
"active": 1,
"allow_events": ["*"]
}'
JSTắt tất cả sự kiện
1
2
3
4
5
6
7
curl --location 'https://bankhub-api-sandbox.sepay.vn/v1/webhook' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{
"webhook_url": "https://merchant.com/webhook",
"allow_events": []
}'
JSĐăng ký sự kiện cụ thể
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl --location 'https://bankhub-api-sandbox.sepay.vn/v1/webhook' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{
"webhook_url": "https://merchant.com/webhook",
"auth_type": "SECRET_KEY",
"secret_key": "your-secret-key-here",
"active": 1,
"allow_events": [
"BANK_ACCOUNT_LINKED",
"BANK_ACCOUNT_UNLINKED",
"LINK_TOKEN_CREATED",
"LINK_SESSION_COMPLETED"
]
}'

Validation Rules

Webhook URL:

  • Phải là HTTPS trong production
  • Tối đa 2000 ký tự
  • Không được sử dụng IP nội bộ (SSRF protection)
  • Dải IP bị chặn: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16

Secret Key:

  • Tối đa 500 ký tự
  • Khuyến nghị: string ngẫu nhiên, độ dài 32-64 ký tự

Allow Events:

  • Chỉ chấp nhận tên sự kiện hợp lệ từ whitelist (13 events)
  • ["*"]: Wildcard để đăng ký tất cả sự kiện
  • [] hoặc null: Không gửi bất kỳ sự kiện nào
13 Webhook Events

Link Token Events (3):

  • LINK_TOKEN_CREATED, LINK_TOKEN_REVOKED, LINK_TOKEN_EXPIRED

Link Session Events (7):

  • LINK_SESSION_INITIALIZED, LINK_SESSION_REINITIALIZED, LINK_SESSION_STATE_CHANGED
  • LINK_SESSION_COMPLETED, LINK_SESSION_FAILED, LINK_SESSION_ABANDONED, LINK_SESSION_EXPIRED

Bank Account Events (3):

  • BANK_ACCOUNT_LINKED, BANK_ACCOUNT_UNLINKED, BANK_ACCOUNT_INACTIVATED
Upsert Behavior
  • Tạo mới: Nếu merchant chưa có webhook, tạo webhook mới
  • Cập nhật: Nếu merchant đã có webhook, cập nhật cấu hình hiện tại
  • Một webhook per merchant: Chỉ được có một webhook config per merchant
  • Partial update: Chỉ gửi các field cần update
Lưu ý quan trọng
  • SSRF Protection: IP nội bộ và localhost bị chặn để bảo vệ khỏi SSRF attacks
  • Secret Key Display: Secret key đầy đủ chỉ hiển thị trong POST response, GET response sẽ che (mask)
  • Event Validation: Tên sự kiện không hợp lệ sẽ bị reject với error 400
  • Active Flag: Nếu active = 0, webhook sẽ không gửi bất kỳ event nào dù có config allow_events
  • Immediate Effect: Thay đổi cấu hình áp dụng ngay lập tức cho tất cả events mới
Webhook Authentication

Khi auth_type = "SECRET_KEY", mỗi webhook request sẽ có header: X-Secret-Key: your-secret-key-here

Verify webhook trong endpoint của bạn:

JSJavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.post('/webhook', (req, res) => {
const receivedSecret = req.headers['x-secret-key'];
const expectedSecret = 'your-secret-key-here';
 
if (receivedSecret !== expectedSecret) {
return res.status(401).send('Unauthorized');
}
 
// Process webhook event
const event = req.body;
console.log('Event:', event.event);
 
res.status(200).send('OK');
});