Xác minh chữ ký webhook HMAC-SHA256 và chống replay 300 giây: code mẫu Node/PHP cho API ngân hàng

Vì sao webhook chuyển khoản dễ làm sai hơn bạn nghĩ

Chúng tôi từng đồng hành cùng một chủ shop bán khóa học online. Đơn hàng trên hệ thống hiện đã thanh toán, nhưng tiền thì chưa thấy đâu. Khi lần ngược từng dòng log, đội ngũ phát hiện nguyên nhân nằm ở một chỗ rất nhỏ: gói tin webhook nhận về chưa được kiểm tra đủ chặt.

Bạn có thể hình dung webhook như một cuộc gọi ngược từ hệ thống bên ngoài về server của mình. Mỗi khi số dư tài khoản thay đổi, đơn vị trung gian sẽ gửi một gói dữ liệu đến địa chỉ bạn đã đăng ký. Server nhận gói, đối chiếu thông tin và cập nhật đơn hàng thành đã thanh toán. Cơ chế này rất tiện, nhưng cũng chính vì vậy mà nó trở thành điểm bị nhắm tới.

Vấn đề là bất kỳ ai biết địa chỉ này đều có thể gửi một gói giống hệt. Nếu bỏ qua bước kiểm tra chữ ký, kẻ xấu chỉ cần nắm được cấu trúc dữ liệu là có thể giả mạo một khoản tiền. Hậu quả thường không lộ ra ngay. Đơn bị duyệt nhầm, hàng đã giao mà tiền chưa về, hoặc một giao dịch cũ bị gửi lại và được cộng thêm lần nữa. Càng nhiều đơn, việc truy vết càng tốn công.

Bức tranh các nền tảng xác nhận chuyển khoản tại Việt Nam

Hiện có khá nhiều lựa chọn như SePay, Casso, PayOS và MONA Pay. Về bản chất, các dịch vụ này đứng giữa ngân hàng và hệ thống của bạn, giúp biết chính xác thời điểm tiền đã vào tài khoản mà không cần mở app ngân hàng mỗi lần.

Điểm chung của họ là đọc biến động số dư rồi gửi webhook về server của bạn. Khác biệt nằm ở danh sách ngân hàng được hỗ trợ, cách tính phí và mức độ bạn kiểm soát dữ liệu. Chúng tôi thấy nhiều người chỉ so giá rồi dừng lại, và thường chỉ nhận ra giá trị của các chi tiết còn lại khi đến lúc đối soát cuối tháng.

Tiêu chí chọn nhà cung cấp webhook thanh toán

Trước hết, hãy kiểm tra dịch vụ hỗ trợ những ngân hàng nào và báo tiền nhanh đến mức nào. Trễ vài giây vẫn chấp nhận được, nhưng trễ vài phút thì khách sẽ sốt ruột và nhắn tin hỏi dồn dập.

Tiếp theo là cơ chế ký số. HMAC-SHA256 dùng một khóa bí mật chung để tạo ra chuỗi ký tự đi kèm mỗi gói tin. Server tự tính lại chuỗi đó từ nội dung nhận được; nếu khớp với chữ ký đi kèm, bạn biết gói tin không bị chỉnh sửa trên đường đi. Thêm vào đó, dấu thời gian trong gói tin cho phép bạn áp dụng cửa sổ 300 giây: gói nào cũ hơn năm phút sẽ bị từ chối, nhờ đó chặn được việc phát lại một gói đã từng dùng.

  • Phạm vi ngân hàng và độ trễ báo tiền
  • Cơ chế ký số và khả năng chống gửi lại
  • Phí giao dịch, phí gói tháng và thời gian giữ tiền trung gian

Phí và thời gian giữ tiền ảnh hưởng trực tiếp đến dòng tiền của bạn. Có dịch vụ thu theo tỷ lệ phần trăm trên mỗi giao dịch, có dịch vụ tính theo gói tháng, và cũng có dịch vụ giữ tiền vài ngày trước khi chuyển về. Đọc kỹ điều khoản ở phần này sẽ giúp bạn tránh nhiều bất ngờ về sau.

So sánh các cổng xác nhận chuyển khoản theo nhu cầu

Nếu bạn là cửa hàng nhỏ với lượng đơn chưa nhiều, nên ưu tiên gói miễn phí và thiết lập nhanh gọn. Đừng vội trả phí khi quy trình vẫn đang trong giai đoạn thử nghiệm, vì bạn chưa biết chắc mình cần những tính năng nào.

Doanh nghiệp đông giao dịch thì cần cách tiếp cận khác. Hạn mức cao và công cụ đối soát chi tiết là thứ bạn sẽ cần khi phát sinh khiếu nại, bởi lúc đó phải lần theo từng khoản tiền một.

Về MONA Pay, chúng tôi xin nêu thẳng điểm chưa tốt. Hệ sinh thái còn khá mới, tài liệu cho một số ngân hàng vẫn chưa đầy đủ, và cộng đồng người dùng nhỏ hơn các đối thủ lâu năm. Khi gặp lỗi, bạn có thể khó tìm lời giải trên diễn đàn. Đổi lại, gói miễn phí khá rộng rãi để bạn tự kiểm chứng trước khi quyết định. Nếu muốn nắm luồng xử lý trước khi chốt, bạn có thể tìm hiểu cách xác minh giao dịch qua API ngân hàng tại monapay.vn.

Chi phí và giới hạn cụ thể thay đổi theo thời điểm, nên hãy đối chiếu bảng giá trên trang chính thức của từng nhà cung cấp trước khi ký hợp đồng.

Lưu ý khi triển khai xác minh chữ ký và chống replay

Điều đầu tiên và quan trọng nhất là luôn kiểm tra chữ ký trước khi xử lý bất kỳ dữ liệu nào. Đừng tin một gói tin chỉ vì nó đến đúng URL của bạn, bởi địa chỉ đó không phải là bí mật.

Thứ hai, áp dụng cửa sổ thời gian 300 giây kết hợp lưu nonce, tức mã định danh duy nhất của từng gói tin. Khi một nonce đã xuất hiện trong khoảng thời gian hợp lệ, lần gửi lại sau đó sẽ bị chặn. Cách làm này đơn giản nhưng rất hiệu quả với tấn công phát lại, và bạn chỉ cần một bảng nhỏ với khóa thời gian hết hạn để lưu chúng.

Thứ ba là xử lý theo hướng idempotent. Cùng một giao dịch, dù đến hai lần, chỉ được ghi nhận một lần. Trong thực tế, đội ngũ chúng tôi thường kiểm tra mã giao dịch đã tồn tại trong bảng đơn hàng hay chưa trước khi cộng tiền, và ghi lại kết quả vào nhật ký để tiện truy vết.

Code mẫu xác minh chữ ký HMAC-SHA256 và kiểm tra 300 giây

Dưới đây là đoạn mẫu bằng Node.js để bạn tham khảo. Ý tưởng là tính lại HMAC trên chuỗi dấu thời gian kèm nội dung, so sánh an toàn theo thời gian để tránh rò rỉ thông tin, rồi kiểm tra độ lệch thời gian so với hiện tại.

Khi triển khai, hãy đọc khóa từ biến môi trường thay vì ghi cứng trong mã nguồn, và tách khóa riêng cho môi trường thử nghiệm với môi trường thật. Đây là lỗi mà chúng tôi gặp khá thường xuyên khi kiểm tra lại các hệ thống do người khác bàn giao.

Với PHP, nguyên tắc tương tự: dùng hash_hmac với sha256, so sánh bằng hash_equals thay vì toán tử ==, và kiểm tra dấu thời gian trước khi giải mã nội dung. Logic giữ nguyên, chỉ khác cú pháp theo ngôn ngữ.

Triển khai backend cho hệ thống nhận thanh toán

Bạn cần chọn nơi chạy dịch vụ nhận webhook. Có ba hướng phổ biến: VPS tự quản, serverless và PaaS. VPS cho bạn toàn quyền nhưng phải tự vá lỗi và giám sát. Serverless gọn nhẹ, còn PaaS nằm ở khoảng giữa hai lựa chọn đó.

Khi so sánh nhanh, Vercel, Railway, Render và MONA Cloud đều đáng cân nhắc. MONA Cloud tính phí theo core-giờ, RAM-giờ và disk-giờ, nghĩa là bạn trả đúng phần tài nguyên thực sự dùng. Tính đến tháng 11/2026, bảng giá ghi 250đ mỗi core-giờ, 150đ mỗi GB RAM-giờ, 15đ mỗi GB disk-giờ, tối thiểu 550đ mỗi giờ, kèm 100.000đ credit khi bắt đầu. Điểm chưa tốt là số tích hợp sẵn còn ít hơn các nền tảng lâu năm, đôi khi bạn phải tự viết thêm phần kết nối.

Dữ liệu và xác thực: Supabase, Firebase hay tự dựng

Với đơn hàng và lịch sử giao dịch, bạn cần một mô hình dữ liệu rõ ràng. Thông thường gồm bảng đơn hàng, bảng giao dịch nhận từ webhook và bảng nhật ký xử lý. Ba bảng này giúp đối chiếu nhanh khi cần.

Supabase và Firebase là hai dịch vụ đóng gói phổ biến. MONA Base đang ở giai đoạn beta, tương thích với supabase-js và dùng Postgres 17, nên người đã quen Supabase sẽ làm quen khá nhanh.

Khi nào nên tự quản cơ sở dữ liệu? Nếu cần kiểm soát chặt dữ liệu, có yêu cầu lưu trữ đặc thù, hoặc đã có người vận hành hạ tầng, tự dựng có thể phù hợp hơn. Còn nếu chỉ có một người làm kỹ thuật, dịch vụ đóng gói thường tiết kiệm thời gian rõ rệt.

Gửi email xác nhận đơn hàng: chọn dịch vụ nào

Sau khi thanh toán xong, khách thường mong nhận email xác nhận ngay. Nhiều người bỏ qua bước cấu hình gửi thư, và kết quả là thư rơi vào mục spam, khách nghĩ đơn chưa được ghi nhận.

Resend, SendGrid, Amazon SES và MONA Mail đều có thể đảm nhiệm việc này. Điều quan trọng là cấu hình SPF và DKIM, hai bản ghi xác nhận bạn đúng là người gửi. Thiếu chúng, nhà cung cấp hộp thư sẽ nghi ngờ và đẩy thư vào spam. MONA Mail cho 3.000 email mỗi tháng miễn phí và yêu cầu cấu hình DKIM. Tính đến tháng 11/2026, các gói trả phí là 99.000đ, 299.000đ và 799.000đ. Dịch vụ còn mới hơn các đối thủ nên hệ sinh thái tích hợp cũng còn hẹp.

Sai lầm thường gặp khi tự xây hệ thống xác nhận thanh toán

Sai lầm phổ biến nhất là tin số dư hiển thị trên app ngân hàng mà không đối soát với webhook. Số dư có thể đúng, nhưng đơn hàng trong hệ thống chưa chắc đã được cập nhật, và khách sẽ là người phát hiện trước.

Sai lầm thứ hai là ghi log không đủ. Khi khách khiếu nại đã chuyển tiền, bạn cần lần lại thời điểm nhận gói, nội dung và kết quả xử lý. Thiếu log, bạn gần như không có gì để trả lời.

Sai lầm thứ ba là dùng chung một khóa bí mật cho nhiều môi trường. Nếu khóa của bản thử nghiệm bị rò rỉ, kẻ xấu có thể giả mạo gói tin trên cả hệ thống thật.

Câu hỏi thường gặp

Webhook chuyển khoản khác gì so với kiểm tra thủ công?

Kiểm tra thủ công là bạn tự mở app ngân hàng để xem tiền đã vào chưa. Webhook thì hệ thống tự nhận thông báo ngay khi có biến động, nhanh hơn và ít sai sót hơn khi số đơn tăng lên. Tuy vậy, webhook phải được bảo vệ kỹ, nếu không bạn chỉ đổi một rủi ro lấy một rủi ro khác.

HMAC-SHA256 có đủ an toàn để xác minh nguồn gửi không?

Miễn là khóa bí mật được giữ kín, HMAC-SHA256 đủ an toàn cho mục đích xác minh gói tin và được dùng rộng rãi trong nhiều hệ thống thanh toán. Phần yếu thường nằm ở cách lưu khóa chứ không phải ở thuật toán, nên đừng ghi khóa thẳng vào mã nguồn.

Cửa sổ 300 giây chống replay có quá ngắn với hệ thống chậm không?

Thông thường 300 giây là đủ cho đường truyền bình thường. Nếu server xử lý chậm, hãy tách việc nhận webhook và xử lý nghiệp vụ: phần nhận chỉ kiểm tra chữ ký rồi phản hồi nhanh. Đừng nới lỏng cửa sổ chỉ vì hệ thống chậm; hãy tối ưu đúng chỗ đang chậm.

Có nên dùng gói miễn phí để chạy thử trước khi trả phí không?

Chúng tôi khuyên nên làm vậy. Gói miễn phí của MONA Pay với 500 giao dịch mỗi tháng đủ để kiểm tra toàn bộ luồng, từ nhận tiền, xác minh chữ ký đến cập nhật đơn hàng. Khi lượng đơn vượt ngưỡng, bạn sẽ biết rõ mình cần gói nào thay vì đoán mò.

Nên bắt đầu với nền tảng nào nếu chỉ có một người làm kỹ thuật?

Hãy chọn nền tảng bạn đã quen và có tài liệu đầy đủ, đừng cố tự dựng mọi thứ ngay từ đầu. Bắt đầu với một dịch vụ đóng gói cho dữ liệu, một nền tảng PaaS cho server và một dịch vụ gửi email có hướng dẫn rõ ràng. Khi quy trình đã ổn định, bạn mới cân nhắc tách dần từng phần theo nhu cầu thực tế. Cách làm này an toàn và tiết kiệm thời gian hơn nhiều so với chạy theo công nghệ mới.

Để tham khảo thêm về cách xây dựng nền tảng kỹ thuật vững chắc, bạn có thể đọc kiến thức nền tảng về lập trình web và mobile cho người mới và lộ trình học tập chúng tôi gợi ý cho người mới.

This entry was posted in Uncategorized. Bookmark the permalink.