Retry & Signature
HMAC Signature
Mỗi webhook có header X-Webhook-Signature:
X-Webhook-Signature: sha256=abcdef1234...Verify bằng HMAC-SHA256 với Webhook Secret của bạn (lấy trong Dashboard → Webhook).
Node.js
js
const crypto = require('crypto')
function verify(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}PHP
php
function verify($rawBody, $signature, $secret) {
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}Sử dụng raw body
Verify trên raw bytes của body trước khi parse JSON. Nếu re-serialize JSON rồi verify sẽ mismatch.
Retry schedule
BeePay retry khi endpoint trả khác 2xx hoặc timeout > 10s:
| Attempt | Delay sau lần trước |
|---|---|
| 1 (gửi lần đầu) | 0 |
| 2 | 1 phút |
| 3 | 5 phút |
| 4 | 30 phút |
| 5 | 2 giờ |
| 6 | 6 giờ |
| 7 (cuối) | 24 giờ |
Sau 6 lần retry fail → webhook_status = failed. Admin có thể retry thủ công trong Dashboard.
Idempotency
Khách có thể chuyển khoản cùng order_id nhiều lần (nhầm, double pay) → BeePay gửi webhook mỗi lần.
Endpoint của bạn nên idempotent:
- Check order đã paid chưa trước khi update
- Trả 200 OK kể cả khi skip (tránh BeePay retry vô nghĩa)
js
app.post('/webhook', (req, res) => {
const order = db.findOrder(req.body.order_id)
if (order.status === 'paid') {
// Đã xử lý rồi — vẫn trả 200
return res.json({ ok: true, already_paid: true })
}
order.status = 'paid'
order.amount_received = req.body.amount
db.save(order)
res.json({ ok: true })
})Timeout khuyến nghị
Xử lý webhook ≤ 3 giây. Nếu cần xử lý lâu (gửi email, sync inventory...):
- Enqueue job vào queue (Redis, SQS...)
- Trả 200 ngay
- Worker xử lý background