Bách Khoa Software
Trang chủ › Giải Pháp Phần Mềm

Cách xây dựng hệ thống thanh toán Payment Gateway an toàn

Ban biên tập Bách Khoa Software · · Cập nhật

Xây dựng hệ thống thanh toán an toàn cho doanh nghiệp, trong phần lớn trường hợp, nghĩa là tích hợp đúng cách với một cổng thanh toán (payment gateway) đã được cấp phép, chứ không phải tự xử lý dữ liệu thẻ. Phần việc của đội phát triển là thiết kế luồng giao dịch không bị trừ tiền hai lần, không mất trạng thái khi mạng chập chờn, xác thực mọi thông báo từ cổng thanh toán và đối soát hằng ngày. Bài viết trình bày các nguyên tắc kỹ thuật cốt lõi, áp dụng được cho cổng thanh toán trong nước lẫn quốc tế.

Các thành phần trong một luồng thanh toán

  • Merchant (bạn): website, ứng dụng bán hàng và backend quản lý đơn.
  • Payment gateway: trung gian nhận yêu cầu thanh toán, hiển thị trang nhập thông tin, kết nối ngân hàng, tổ chức thẻ, ví điện tử.
  • Ngân hàng phát hành, ngân hàng thanh toán, tổ chức thẻ: xử lý phê duyệt và chuyển tiền.
  • Người mua: chọn phương thức (thẻ nội địa, thẻ quốc tế, ví, chuyển khoản qua mã QR).

Tại Việt Nam, thanh toán bằng mã QR theo chuẩn VietQR qua ứng dụng ngân hàng đã rất phổ biến, bên cạnh thẻ và ví điện tử. Nhiều cổng thanh toán hỗ trợ tất cả trong một tích hợp.

Nguyên tắc số một: không chạm vào dữ liệu thẻ

Lưu, xử lý hoặc truyền số thẻ đầy đủ đặt doanh nghiệp vào phạm vi chuẩn bảo mật PCI DSS với yêu cầu rất nặng về hạ tầng, kiểm toán. Cách an toàn là để dữ liệu thẻ không bao giờ đi qua máy chủ của bạn:

  • Trang thanh toán của cổng (redirect): người mua được chuyển sang trang của cổng thanh toán, nhập thông tin ở đó rồi quay lại.
  • Trường nhập nhúng (hosted fields, iframe): ô nhập thẻ nằm trong trang của bạn nhưng thực chất được phục vụ từ cổng thanh toán.
  • Token hóa: khi cần lưu thẻ cho lần sau, chỉ lưu token do cổng cấp, không lưu số thẻ.

Với các cách này, phạm vi tuân thủ của doanh nghiệp được thu hẹp đáng kể. Hãy đọc tài liệu của cổng thanh toán để biết mức tự đánh giá PCI tương ứng.

Thiết kế trạng thái giao dịch

Mỗi giao dịch nên có máy trạng thái rõ ràng, chỉ cho phép chuyển theo những hướng hợp lệ:

Trạng tháiÝ nghĩaChuyển tiếp hợp lệ
CREATEDĐã tạo yêu cầu, chưa gửi cổngPENDING, CANCELLED
PENDINGĐang chờ người mua thanh toán hoặc cổng xử lýSUCCEEDED, FAILED, EXPIRED
SUCCEEDEDCổng xác nhận thành côngREFUNDED, PARTIALLY_REFUNDED
FAILED / EXPIREDThất bại hoặc quá hạnTrạng thái cuối

Lưu số tiền bằng số nguyên theo đơn vị nhỏ nhất (đồng với VND), kèm mã tiền tệ. Mọi thay đổi trạng thái ghi vào bảng lịch sử để truy vết. Cách thiết kế bảng và ràng buộc tham khảo bài thiết kế database schema chuẩn.

Idempotency: chống trừ tiền hai lần

Mạng có thể mất kết nối đúng lúc cổng đã trừ tiền nhưng phản hồi chưa về. Người dùng bấm lại, ứng dụng tự retry, hoặc webhook được gửi nhiều lần. Hệ thống phải đảm bảo cùng một ý định thanh toán chỉ tạo một giao dịch:

  • Sinh mã giao dịch duy nhất phía merchant (order reference) và gửi kèm mọi yêu cầu; nhiều cổng dùng mã này để từ chối giao dịch trùng.
  • Dùng idempotency key cho API nội bộ: request trùng khóa trả lại kết quả cũ thay vì xử lý lại.
  • Ràng buộc UNIQUE ở database cho mã giao dịch của cổng, để xử lý webhook trùng không tạo bản ghi mới.
  • Khi không chắc kết quả (timeout), không tự tạo giao dịch mới mà gọi API truy vấn trạng thái của cổng.

Xử lý webhook và trang quay về

Sau khi thanh toán, người mua được chuyển về trang kết quả (return URL) và cổng gửi thông báo máy chủ tới máy chủ (webhook/IPN). Quy tắc quan trọng:

  1. Không tin tham số trên return URL. Người dùng có thể sửa URL. Chỉ cập nhật trạng thái dựa trên webhook đã xác thực hoặc kết quả truy vấn trực tiếp tới cổng.
  2. Xác thực chữ ký của mọi webhook bằng khóa bí mật theo đúng thuật toán cổng quy định; từ chối nếu sai.
  3. Kiểm tra số tiền và mã đơn trong webhook khớp với đơn trong hệ thống.
  4. Phản hồi nhanh cho cổng rồi xử lý nghiệp vụ (gửi email, xuất kho) bất đồng bộ qua hàng đợi.
  5. Có job dự phòng định kỳ truy vấn các giao dịch PENDING quá lâu, phòng khi webhook bị mất.

Bảo mật hệ thống xung quanh

  • Lưu khóa bí mật của cổng trong secret manager, không đưa vào mã nguồn hay file cấu hình trong repository.
  • Tách môi trường sandbox và production với khóa riêng; tài khoản production chỉ vài người có quyền.
  • Bật HTTPS toàn bộ, HSTS, chính sách bảo mật nội dung để giảm nguy cơ chèn script đánh cắp dữ liệu nhập trên trang.
  • Giới hạn tần suất các API tạo thanh toán, hoàn tiền; hoàn tiền nên cần phê duyệt hai người với số tiền lớn.
  • Ghi log đầy đủ nhưng che thông tin nhạy cảm.

Người dùng cuối cũng là mục tiêu: kẻ gian giả mạo trang thanh toán, tin nhắn ngân hàng hoặc cài ứng dụng độc hại để chiếm OTP. Bài phân tích về mã độc ngân hàng và các chiêu lừa đảo giúp đội sản phẩm hiểu rủi ro phía khách hàng để thiết kế cảnh báo phù hợp.

Phòng gian lận

Các cổng thanh toán đều có lớp chống gian lận, nhưng merchant nên bổ sung quy tắc của riêng mình: giới hạn số lần thanh toán thất bại liên tiếp của một tài khoản hoặc thiết bị (dấu hiệu dò thẻ), cảnh báo đơn giá trị lớn từ tài khoản mới, đối chiếu địa chỉ giao hàng bất thường. Với thẻ quốc tế, bật xác thực 3-D Secure để chuyển trách nhiệm tranh chấp sang ngân hàng phát hành trong nhiều trường hợp.

Đối soát hằng ngày

Đối soát là so sánh giao dịch trong hệ thống của bạn với báo cáo của cổng thanh toán và sao kê ngân hàng:

  • Giao dịch có ở cổng nhưng hệ thống ghi thất bại (khách đã trả tiền nhưng đơn bị hủy) cần xử lý ngay.
  • Giao dịch hệ thống ghi thành công nhưng cổng không có là dấu hiệu lỗi nghiêm trọng hoặc gian lận.
  • Chênh lệch phí, hoàn tiền, tiền về tài khoản theo kỳ thanh toán.

Tự động hóa đối soát bằng job hằng ngày tải file từ cổng, ghép theo mã giao dịch, xuất danh sách chênh lệch cho kế toán. Dữ liệu này cũng cần chảy về ERP; bài triển khai ERP cho doanh nghiệp vừa và nhỏ mô tả cách tổ chức phân hệ kế toán nhận dữ liệu bán hàng.

Kiểm thử trước khi lên production

Dùng môi trường sandbox của cổng để thử: thanh toán thành công, thất bại, người dùng hủy giữa chừng, timeout, webhook đến trước khi người dùng quay lại, webhook đến hai lần, hoàn tiền toàn phần và một phần. Mỗi tình huống nên có integration test tự động để không bị phá vỡ khi sửa code về sau.

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

Doanh nghiệp nhỏ có cần chứng nhận PCI DSS không?

Nếu dùng trang thanh toán hoặc trường nhập do cổng cung cấp và không lưu dữ liệu thẻ, doanh nghiệp thường chỉ phải tự đánh giá theo mẫu đơn giản. Nghĩa vụ cụ thể phụ thuộc cách tích hợp và yêu cầu của cổng, ngân hàng.

Có nên tích hợp nhiều cổng thanh toán cùng lúc không?

Nhiều cổng giúp dự phòng khi một cổng gặp sự cố và tối ưu phí, nhưng tăng công tích hợp và đối soát. Nên thiết kế lớp trừu tượng nội bộ ngay từ đầu để thêm cổng sau dễ dàng.

Làm sao xác nhận chuyển khoản qua mã QR tự động?

Dùng dịch vụ của cổng thanh toán hoặc ngân hàng cung cấp thông báo biến động số dư qua API kèm nội dung chuyển khoản có mã đơn. Tránh cách đọc tin nhắn biến động số dư thủ công vì dễ sai và dễ bị giả mạo.

Webhook đến trước khi đơn hàng được lưu thì xử lý thế nào?

Luôn lưu đơn và giao dịch ở trạng thái PENDING trước khi chuyển người dùng sang cổng. Nếu vẫn gặp webhook không tìm thấy đơn, trả lỗi để cổng gửi lại sau, hoặc lưu tạm và xử lý lại bằng job.

Bài liên quan