Bài này hướng dẫn tích hợp chuyển khoản cho web end-to-end với BeePay — 5 bước, ~30 phút làm xong cho dev đã quen Node/PHP.

Bước 1: Tạo Connected System

Lấy API key tại id.beepay.vn/account. Gọi:

POST https://id.beepay.vn/api/systems
X-Api-Key: YOUR_API_KEY
Content-Type: application/json

{
  "name": "Tên shop của bạn",
  "payment_prefix": "SHOP",
  "website": "https://yourshop.com"
}

payment_prefix = 2-10 ký tự A-Z0-9 (vd SHOP, BEELEARN) — sẽ là tiền tố mọi order_id.

Bước 2: Register Webhook

POST https://id.beepay.vn/api/webhooks
X-Api-Key: YOUR_API_KEY
{
  "name": "Shop webhook",
  "url": "https://yourshop.com/api/beepay-webhook",
  "system_id": <id từ bước 1>,
  "http_method": "POST",
  "filter_banks": [],
  "filter_type": "credit",
  "payload_format": "standard"
}

BeePay trả về secret_key (48 ký tự hex) — chỉ trả 1 lần. Lưu vào .env làm BEEPAY_WEBHOOK_SECRET.

Bước 3: Generate VietQR khi tạo đơn

Khi user checkout, sinh order_id duy nhất + VietQR URL:

const orderId = 'SHOP' + Date.now().toString(36).toUpperCase()
const qrUrl = `https://qr.beepay.vn/img/${BANK}-${ACCOUNT}.png?amount=${amount}&addInfo=${orderId}`

Hiển thị QR + thông tin TK + nội dung CK lên trang. Bắt đầu polling /api/order/:id ngay khi modal mở (3s/lần).

Bước 4: Verify Webhook HMAC

Khi BeePay POST về /api/beepay-webhook, verify chữ ký bằng raw body:

import crypto from 'crypto'

function verify(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  )
}

Sai chữ ký → return 401. Đúng → parse JSON, lookup order theo body.order_id, mark paid.

Bước 5: Polling status frontend

const interval = setInterval(async () => {
  const r = await fetch(`/api/order/${orderId}`).then(r => r.json())
  if (r.status === 'paid') {
    clearInterval(interval)
    showSuccess()
  }
}, 3000)

Đừng bắt user click "Tôi đã chuyển" mới poll — tự động phát hiện. UX đúng là người dùng chuyển khoản xong nhìn QR, vài giây sau thấy "✓ Thành công" tự bật lên.

Done

Test bằng cách chuyển 1.000đ với content đúng order_id → kiểm tra /admin/orders/:id. Nếu webhook không tới, check log dashboard BeePay (id.beepay.vn/webhook-logs) — thường là URL chưa public-reachable (cần ngrok / tunnel cho dev).

Source code mẫu Next.js + Postgres tại SaaS Boilerplate.