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
https://bankhub-api-sandbox.sepay.vn/v1/webhookRequest Body
URL webhook endpoint (HTTPS required in production)
Loại xác thực
Secret key (sẽ gửi trong header X-Secret-Key)
1 = bật, 0 = tắt
Mặc định:1["*"] = tất cả events, [] = không events nào
- 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 RequestURL 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ệ
401UnauthorizedAccess token không hợp lệ hoặc đã hết hạn
API Response
{
"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": [
"*"
]
}
}Mã trạng thái HTTP
Mã lỗi (nếu có)
Thông báo kết quả
Cấu hình webhook (secret key đầy đủ - chỉ trong POST response)
Code mẫu
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
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": ["*"]}'
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": []}'
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"]}'
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ặcnull: Không gửi bất kỳ sự kiện nào
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_CHANGEDLINK_SESSION_COMPLETED,LINK_SESSION_FAILED,LINK_SESSION_ABANDONED,LINK_SESSION_EXPIRED
Bank Account Events (3):
BANK_ACCOUNT_LINKED,BANK_ACCOUNT_UNLINKED,BANK_ACCOUNT_INACTIVATED
- 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
- 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ó configallow_events - Immediate Effect: Thay đổi cấu hình áp dụng ngay lập tức cho tất cả events mới
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:
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 eventconst event = req.body;console.log('Event:', event.event);res.status(200).send('OK');});