Cấu hình webhook qua API
Mọi thứ làm được ở dashboard mục Webhooks đều có API tương ứng: POST /api/v1/client-webhooks tạo cấu hình (URL nhận, kiểu xác thực HMAC_SHA256 / API_KEY / NONE, định dạng payload, gắn theo 1 VA hoặc mọi tài khoản), PUT và DELETE /api/v1/client-webhooks/{config_id} để sửa, xoá, POST /api/v1/client-webhooks/test bắn 1 payload giả lập 500.000đ để thử endpoint trước khi có tiền thật. Lịch sử từng lần gửi (mã HTTP, thời gian phản hồi duration_ms, nhãn lỗi) đọc ở GET /api/v1/webhook-logs, thống kê 7-30 ngày ở GET /api/v1/webhook-logs/stats.
Cách MONA Pay ký và gửi payload xem ở Tích hợp webhook và Bảo mật; trang này là tham chiếu API.
Đối tượng cấu hình webhook
| Trường | Kiểu | Bắt buộc khi tạo | Ghi chú |
|---|---|---|---|
name |
string | có | Tên gợi nhớ (“Web bán hàng”, “Phần mềm kế toán”) |
webhook_url |
string | có | URL HTTPS nhận POST của anh chị |
auth_type |
NONE / API_KEY / HMAC_SHA256 |
không, mặc định NONE |
Nên dùng HMAC_SHA256 |
secret_key |
string | cần khi auth_type khác NONE |
Secret để ký HMAC hoặc gửi trong header API key |
api_key_name |
string | không, mặc định X-Webhook-Secret |
Tên header mang secret khi auth_type = API_KEY |
payload_format |
application/json / application/x-www-form-urlencoded / multipart/form-data |
không, mặc định application/json |
Kiểu body MONA Pay gửi |
virtual_account_id |
uuid | không | Gắn cấu hình với 1 VA. Bỏ trống = mọi tài khoản, mọi VA |
is_active |
boolean | chỉ khi PUT | Tắt tạm không cần xoá |
Mỗi giao dịch tiền vào sẽ bắn tới mọi cấu hình đang bật khớp điều kiện: cấu hình “mọi tài khoản” luôn nhận, cấu hình gắn VA chỉ nhận giao dịch của VA đó. Giữ số cấu hình gọn (dưới 20) để dễ theo dõi log.
GET /api/v1/client-webhooks
Danh sách cấu hình của tài khoản. Cần Bearer.
curl https://api.monapay.vn/api/v1/client-webhooks -H "Authorization: Bearer $MONA_TOKEN"
{
"success": true,
"message": "Success",
"data": [
{ "id": "0190e0f1-...", "name": "Web ban hang", "webhook_url": "https://shop.vn/webhook/monapay", "auth_type": "HMAC_SHA256", "api_key_name": "X-Webhook-Secret", "payload_format": "application/json", "virtual_account_id": null, "is_active": true, "max_retries": 7, "created_at": "2026-08-28T11:00:00" }
]
}
secret_key không trả về trong danh sách. max_retries là số lần gửi lại tối đa dành cho cơ chế gửi lại tự động đang triển khai; hiện chưa có tác dụng.
POST /api/v1/client-webhooks
Cần Bearer + X-Client-Secret.
cURL
curl -X POST https://api.monapay.vn/api/v1/client-webhooks \
-H "Authorization: Bearer $MONA_TOKEN" -H "X-Client-Secret: $MONA_SECRET" \
-H 'Content-Type: application/json' \
-d '{"name":"Web ban hang","webhook_url":"https://shop.vn/webhook/monapay","auth_type":"HMAC_SHA256","secret_key":"hmac-secret-dai-va-ngau-nhien","payload_format":"application/json"}'
PHP
<?php
$ch = curl_init('https://api.monapay.vn/api/v1/client-webhooks');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . getenv('MONA_TOKEN'), 'X-Client-Secret: ' . getenv('MONA_SECRET')],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Web ban hang',
'webhook_url' => 'https://shop.vn/webhook/monapay',
'auth_type' => 'HMAC_SHA256',
'secret_key' => getenv('MONA_WEBHOOK_SECRET'), // cùng secret server nhận webhook dùng để verify
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($res['success'])) throw new RuntimeException($res['message'] ?? 'Tạo webhook thất bại');
$configId = $res['data']['id'];
Node
const r = await fetch('https://api.monapay.vn/api/v1/client-webhooks', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.MONA_TOKEN}`, 'X-Client-Secret': process.env.MONA_SECRET },
body: JSON.stringify({
name: 'Web ban hang',
webhook_url: 'https://shop.vn/webhook/monapay',
auth_type: 'HMAC_SHA256',
secret_key: process.env.MONA_WEBHOOK_SECRET, // cùng secret server nhận webhook dùng để verify
}),
});
const { success, data, message } = await r.json();
if (!success) throw new Error(message);
console.log('config id', data.id);
PUT /api/v1/client-webhooks/{config_id}
Sửa một phần, chỉ gửi trường cần đổi. Cần Bearer + X-Client-Secret.
curl -X PUT https://api.monapay.vn/api/v1/client-webhooks/0190e0f1-... \
-H "Authorization: Bearer $MONA_TOKEN" -H "X-Client-Secret: $MONA_SECRET" \
-H 'Content-Type: application/json' -d '{"is_active":false}'
DELETE /api/v1/client-webhooks/{config_id}
Xoá cấu hình. Log cũ vẫn giữ để tra cứu.
curl -X DELETE https://api.monapay.vn/api/v1/client-webhooks/0190e0f1-... \
-H "Authorization: Bearer $MONA_TOKEN" -H "X-Client-Secret: $MONA_SECRET"
POST /api/v1/client-webhooks/test
Bắn 1 payload giả lập tới URL bất kỳ, không cần tạo cấu hình trước. Dùng để thử endpoint và code verify HMAC. Cần Bearer + X-Client-Secret.
| Trường | Kiểu | Bắt buộc | Ghi chú |
|---|---|---|---|
webhook_url |
string | có | URL cần thử |
auth_type |
string | không, mặc định NONE |
Thử đúng kiểu anh chị sẽ dùng |
secret_key |
string | khi auth khác NONE | |
api_key_name |
string | không | |
payload_format |
string | không | |
is_dummy |
boolean | không | Gửi payload giao dịch giả lập (nên đặt true) |
Payload giả lập MONA Pay gửi:
{"amount":500000,"description":"DUMMY TRANSACTION MONAPAY","transfer_date":"11:05:00 28/08/2026","transaction_code":"DUMMY123","account_number":"1900636648","bank_name":"ACB","type":"income"}
Server anh chị nên nhận ra transaction_code = DUMMY123 để không tạo đơn thật.
curl -X POST https://api.monapay.vn/api/v1/client-webhooks/test \
-H "Authorization: Bearer $MONA_TOKEN" -H "X-Client-Secret: $MONA_SECRET" \
-H 'Content-Type: application/json' \
-d '{"webhook_url":"https://shop.vn/webhook/monapay","auth_type":"HMAC_SHA256","secret_key":"hmac-secret-dai-va-ngau-nhien","is_dummy":true}'
Response: success là true khi server anh chị trả 200/201/202 trong 10 giây; false kèm message mô tả lỗi (timeout, SSL, 4xx/5xx).
GET /api/v1/webhook-logs
Lịch sử từng lần gửi, mới nhất trước. Cần Bearer.
| Tham số (query) | Kiểu | Ghi chú |
|---|---|---|
page |
integer ≥1 | mặc định 1 |
limit |
integer 1-100 | mặc định 20 |
status |
success / failed |
success = mã 2xx |
from_date, to_date |
YYYY-MM-DD |
Lọc theo ngày tạo (UTC) |
curl "https://api.monapay.vn/api/v1/webhook-logs?status=failed&from_date=2026-08-01&limit=50" \
-H "Authorization: Bearer $MONA_TOKEN"
{
"success": true,
"message": "Webhook logs retrieved successfully",
"data": {
"items": [
{
"id": "0190f0a1-...",
"client_id": "0190a0f0-...",
"event_type": "webhook",
"endpoint_url": "https://shop.vn/webhook/monapay",
"request_payload": "{\"amount\":2500000,...}",
"request_headers": "{\"X-Mona-Timestamp\":\"1756350312\",\"X-Mona-Signature\":\"sha256=...\"}",
"status_code": 500,
"response_text": "Internal Server Error",
"duration_ms": 842,
"error_label": "HTTP_5XX",
"created_at": "2026-08-28T10:45:13+00:00"
}
],
"total": 3,
"page": 1,
"limit": 50
}
}
error_label nhận một trong: OK, HTTP_3XX, HTTP_4XX, HTTP_5XX, TIMEOUT, SSL, DNS, CONNECTION, ERROR. event_type là webhook cho giao dịch thật, test cho lần gửi thử. Ý nghĩa từng nhãn và cách sửa ở Gửi lại và xử lý lỗi.
GET /api/v1/webhook-logs/stats
Thống kê theo số ngày gần nhất (tham số days, ví dụ 7 hoặc 30). Cần Bearer.
curl "https://api.monapay.vn/api/v1/webhook-logs/stats?days=7" -H "Authorization: Bearer $MONA_TOKEN"
{
"success": true,
"message": "Success",
"data": {
"days": 7,
"total": 412,
"success": 409,
"failed": 3,
"success_rate": 99.27,
"p95_duration_ms": 610,
"daily": [ { "date": "2026-08-22", "total": 58, "failed": 0 } ],
"errors": [ { "label": "HTTP_5XX", "count": 2 }, { "label": "TIMEOUT", "count": 1 } ]
}
}
p95_duration_ms là thời gian server anh chị phản hồi ở phân vị 95; con số này vượt 5.000 là nên xem lại endpoint, vì MONA Pay cắt ở 10.000 ms.
Lỗi thường gặp
| HTTP | Nguyên nhân | Cách xử lý |
|---|---|---|
| 401 | Token hết hạn hoặc thiếu X-Client-Secret ở POST/PUT/DELETE |
Login lại, thêm header |
422 auth_type |
Giá trị ngoài NONE / API_KEY / HMAC_SHA256 |
Viết hoa đúng |
422 payload_format |
Ngoài 3 giá trị cho phép | Dùng application/json |
test trả success: false |
Server anh chị không phản hồi 2xx trong 10 giây, hoặc SSL/DNS lỗi | Đọc message, thử curl chính URL đó từ máy khác |