Hướng Dẫn Cấu Hình HMAC-SHA256 Cho SePay Webhook Trên WinSell
Hướng dẫn chi tiết cách tạo HMAC Secret trên SePay và cấu hình xác thực webhook bằng HMAC-SHA256 trên WinSell — phương thức bảo mật cao hơn API Key, chống giả mạo webhook hiệu quả.

SEPAY cho phép tự động xác nhận thanh toán chuyển khoản ngân hàng tức thì thông qua webhook, loại bỏ hoàn toàn việc nhân viên phải kiểm tra thủ công. WinSell đã tích hợp SEPAY vào module POS của nền tảng bán hàng — đây là hướng dẫn kỹ thuật từ kinh nghiệm thực tế.
SEPAY là dịch vụ tự động đọc và xử lý biến động số dư tài khoản ngân hàng theo thời gian thực. Thay vì nhân viên phải mở ứng dụng ngân hàng, kiểm tra từng giao dịch, rồi xác nhận thủ công — SEPAY nhận thông báo từ ngân hàng và gửi webhook đến hệ thống của bạn ngay lập tức khi có tiền về.
Vấn đề với quy trình thủ công:
Khách hàng chuyển tiền → nhân viên kiểm tra app ngân hàng → tìm đúng giao dịch → xác nhận trong hệ thống → mở khóa đơn hàng. Cả quy trình mất 2–5 phút mỗi đơn. Với POS bận rộn trong giờ cao điểm, 30 khách/giờ × 3 phút = 90 phút lãng phí thuần túy. Chưa kể sai sót do nhân viên xác nhận nhầm hoặc bỏ sót.
Với SEPAY:
Khách quét QR → chuyển tiền → trong vòng 3–5 giây, hệ thống tự nhận webhook → tự xác nhận đơn hàng → in hóa đơn. Nhân viên không cần làm gì.
Trước khi nhận webhook, cần kết nối tài khoản SEPAY với hệ thống qua OAuth 2.0:
client_id + client_secretauthorization_codeaccess_token và lưu vào database (mã hóa AES-256)Lưu ý quan trọng: access_token hết hạn định kỳ — hệ thống cần implement refresh token flow để tự gia hạn mà không cần merchant đăng nhập lại.
Sau khi có access_token, gọi SEPAY API để đăng ký webhook endpoint:
Endpoint của bạn sẽ nhận POST request mỗi khi có giao dịch. SEPAY hỗ trợ hai cơ chế xác thực webhook:
Cơ chế 1 — API Key trong header:
SEPAY gửi header Authorization: Apikey YOUR_WEBHOOK_SECRET. Backend kiểm tra header này để xác nhận request đến từ SEPAY, không phải từ bên ngoài giả mạo.
Cơ chế 2 — HMAC-SHA256 signature:
SEPAY ký request body bằng HMAC-SHA256 với shared secret. Backend tính lại chữ ký và so sánh. Đây là cơ chế an toàn hơn vì ngay cả khi secret bị lộ trong log, attacker không giả mạo được request nếu không có khả năng tính HMAC đúng.
WinSell triển khai cả hai cơ chế — API Key cho simplicity, HMAC-SHA256 cho merchant có yêu cầu bảo mật cao hơn.
Khi có tiền vào tài khoản, SEPAY gửi payload dạng JSON:
Các field quan trọng:
transferAmount: số tiền chuyển khoản (đơn vị VND)transferType: "in" = tiền vào, "out" = tiền ratransferContent: nội dung chuyển khoản (đây là field quan trọng nhất)accountNumber: số tài khoản nhận tiềntransaction_date: thời điểm giao dịchQuy trình xử lý trong backend:
Nhận webhook → verify chữ ký HMAC-SHA256 (hoặc API Key) → extract transferContent → tìm đơn hàng theo mã trong nội dung → kiểm tra số tiền khớp → cập nhật trạng thái đơn hàng → emit Socket.IO event → POS frontend tự động hiển thị "Đã thanh toán".
SEPAY có tính năng tài khoản ảo (VA): mỗi đơn hàng được gán một số tài khoản ảo khác nhau (dù thực ra đều chuyển vào cùng một tài khoản thật). Điều này cho phép đối soát chính xác 100% mà không cần khách hàng nhớ gõ mã đơn hàng vào nội dung chuyển khoản.
Khi không dùng VA: khách phải gõ đúng mã đơn hàng vào ô "nội dung chuyển khoản". Nếu gõ sai hoặc để trống → hệ thống không match được → nhân viên phải xác nhận thủ công.
Khi dùng VA: khách quét QR với số tài khoản ảo riêng → SEPAY tự map về đúng đơn hàng → không cần gõ gì.
WinSell implement VA flow cho merchant có nhu cầu loại bỏ hoàn toàn xác nhận thủ công.
Với merchant không dùng VA (phổ biến hơn), đối soát dựa trên nội dung chuyển khoản:
Quy tắc sinh mã: Mỗi đơn hàng được gán mã ngắn, dễ đọc, ít ký tự đặc biệt. Ví dụ: WS-20250601-0042. Mã này in lên QR code và nhắc khách gõ vào nội dung.
Quy tắc match: Backend dùng regex tìm mã trong transferContent. Không cần match 100% chính xác — "WS 20250601 0042" hay "ws-20250601-0042" đều được nhận dạng đúng nhờ normalize trước khi so sánh.
Xử lý edge case:
WinSell Platform (sản phẩm SaaS quản lý bán hàng) tích hợp SEPAY trực tiếp vào module POS:
Luồng tại quầy:
Hệ thống đối soát cuối ngày:
Dashboard báo cáo tổng hợp: tổng giao dịch SEPAY trong ngày, giao dịch tự xác nhận, giao dịch cần xác nhận thủ công, và giao dịch không match được đơn hàng nào (chuyển nhầm hoặc nhầm mã).
Xác nhận thủ công:
Tích hợp SEPAY tự động:
SEPAY hiện hỗ trợ hầu hết ngân hàng thương mại lớn tại Việt Nam: Vietcombank, VietinBank, BIDV, Techcombank, MB, ACB, Sacombank, TPBank, VPBank và nhiều ngân hàng khác. Danh sách đầy đủ và cập nhật nhất trên website sepay.vn.
Phí của SEPAY tính theo số giao dịch (xem trên sepay.vn để biết gói hiện tại). Chi phí lập trình tích hợp vào hệ thống có sẵn: 3–5 ngày công = 3–5 triệu đồng tại WinSell, tùy độ phức tạp của hệ thống cần tích hợp.
Có. Quy trình tương tự — tạo đơn với số tiền bằng giá trị đặt cọc, gán mã, khách chuyển khoản, hệ thống tự xác nhận và đánh dấu trạng thái "đã cọc".
Nên có fallback: khi không nhận được webhook trong X phút, hệ thống chuyển sang chế độ "xác nhận thủ công tạm thời." WinSell implement fallback này trong module POS để đảm bảo quầy không bị tắc.
Xem thêm về toàn bộ giải pháp tại trang dịch vụ phát triển phần mềm theo yêu cầu. Nếu bạn đang cần tư vấn chọn đối tác công nghệ, xem bài outsource hay tự xây đội ngũ phát triển.
Công ty TNHH WinSell (MST 1801831784). Địa chỉ: Số 79 Đường A4, KDC 91B, Phường An Khánh, Quận Ninh Kiều, TP Cần Thơ.
Liên hệ: Zalo / Điện thoại 0817771184 — Email [email protected] — Website winsell.vn — Facebook facebook.com/phanmembanhangwinsell.
Một câu hỏi kỹ thuật hay gặp: "Tại sao không chỉ whitelist IP của SEPAY là đủ?"
IP whitelist là lớp bảo vệ đầu tiên nhưng không đủ vì:
HMAC-SHA256 signature bảo vệ mạnh hơn vì chỉ SEPAY (biết shared secret) mới tạo được chữ ký đúng. Ngay cả khi attacker biết payload format và IP, họ không thể giả mạo được signature mà không có secret key.
Cách verify HMAC-SHA256 trong NestJS:
Nhận request body dạng raw buffer (không parse JSON trước), tính HMAC-SHA256 với shared secret, so sánh với signature trong header. Nếu không match, trả về 401 và không xử lý thêm. Đây là pattern chuẩn — cũng dùng tương tự với Stripe webhooks, GitHub webhooks, và nhiều provider khác.
Tình huống 1: Khách chuyển thiếu tiền
Webhook đến với transferAmount thấp hơn orderAmount. Hệ thống không tự confirm, đánh dấu "thiếu X đồng," notify nhân viên xử lý. Không nên tự động reject — khách có thể chuyển bổ sung.
Tình huống 2: Khách chuyển hai lần
Webhook đến lần 2 cho cùng đơn hàng (khách ấn chuyển khoản hai lần do hiểu lầm). Hệ thống detect đơn đã ở trạng thái "đã thanh toán," không double-confirm. Ghi nhận giao dịch thừa để admin hoàn tiền.
Tình huống 3: Webhook đến sau khi đơn đã hủy
Khách chờ lâu không thấy xác nhận, nghĩ lỗi nên hủy đơn và đặt lại. Webhook của đơn cũ đến muộn (do SEPAY retry). Hệ thống phải check trạng thái đơn trước khi confirm, không confirm đơn đã hủy.
Tình huống 4: Duplicate webhook (SEPAY gửi 2 lần)
SEPAY có cơ chế retry khi không nhận được 200 response trong timeout. Backend phải implement idempotency: lưu transaction ID của SEPAY, nếu đã xử lý rồi thì return 200 luôn mà không xử lý lại.
Bốn tình huống trên đều đã được WinSell test và implement handler đầy đủ trong module POS của WinSell Platform. Đây là loại chi tiết hay bị bỏ qua khi implement "happy path" chỉ và gây ra bug production sau khi launch.
Hướng dẫn chi tiết cách tạo HMAC Secret trên SePay và cấu hình xác thực webhook bằng HMAC-SHA256 trên WinSell — phương thức bảo mật cao hơn API Key, chống giả mạo webhook hiệu quả.

Hướng dẫn từng bước đăng ký tài khoản SePay, tạo API key và thiết lập webhook để tự động xác nhận thanh toán chuyển khoản trên WinSell POS — giúp shop online thu tiền nhanh hơn mà không cần kiểm tra thủ công.

Hướng dẫn chi tiết cách chọn phần mềm quản lý bán hàng phù hợp cho doanh nghiệp SME: tiêu chí đánh giá, so sánh giải pháp và quy trình triển khai hiệu quả.