Skip to content

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:

AttemptDelay sau lần trước
1 (gửi lần đầu)0
21 phút
35 phút
430 phút
52 giờ
66 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...):

  1. Enqueue job vào queue (Redis, SQS...)
  2. Trả 200 ngay
  3. Worker xử lý background