Thanh toán chuyển khoản bằng mã QR
Khách quét một mã QR bằng app ngân hàng, số tiền và nội dung đã điền sẵn, tiền vào thẳng tài khoản ngân hàng của cửa hàng. MARZI không giữ tiền của cửa hàng và không thu phí cho cách thanh toán này. Hình minh họa dùng dữ liệu giả của "Cửa hàng Mẫu".
1. Cách hoạt động
- Khách chọn "Thanh toán bằng mã QR" ở chi tiết đơn (app người mua). MARZI vẽ mã QR có sẵn số tiền của đơn và mã thanh toán dạng
MZ12345678trong nội dung chuyển khoản. - Khách quét mã bằng app ngân hàng và chuyển tiền. Tiền vào tài khoản của cửa hàng.
- MARZI biết tiền đã về theo một trong hai cách: dịch vụ SePay theo dõi tài khoản rồi báo về MARZI (tự động), hoặc nhân viên cửa hàng nhìn thấy tiền và bấm Đánh dấu đã thanh toán.
- Đơn chuyển sang đã thanh toán, màn QR của khách tự đổi thành "Đã nhận thanh toán".
Mã QR chỉ là lệnh chuyển tiền do app ngân hàng của khách thực hiện, tiền đi thẳng từ ngân hàng này sang ngân hàng kia nên MARZI không nhìn thấy gì. Muốn biết đã trả phải có một bên đọc biến động số dư của tài khoản cửa hàng. SePay làm việc đó và báo về MARZI.
2. Nhập tài khoản ngân hàng
Có hai nơi nhập, kết quả như nhau:
- Bảng điều khiển (đại lý hoặc chủ hệ thống): Khách hàng → chọn cửa hàng → thẻ Thanh toán chuyển khoản QR. Cửa hàng cần có mã doanh nghiệp.
- App quản lý (nhân viên có quyền sửa): Cài đặt → Thanh toán chuyển khoản QR. Tài khoản chỉ được xem thì không sửa được.

| Ô | Điền gì |
|---|---|
| Ngân hàng | Chọn trong danh sách 18 ngân hàng lớn (Vietcombank, VietinBank, BIDV, Agribank, MB, Techcombank, ACB, VPBank, TPBank, Sacombank, HDBank, VIB, SHB, OCB, MSB, SeABank, LPBank, Eximbank). Ngân hàng khác: chọn "Ngân hàng khác", nhập mã ngân hàng 6 số (mã NAPAS) và tên. |
| Số tài khoản | Chỉ chữ và số, từ 4 đến 19 ký tự. |
| Tên chủ tài khoản | Đúng như ngân hàng hiện ra khi quét mã. |
Trong app quản lý có đường tắt: bấm Quét mã QR ngân hàng của cửa hàng rồi quét mã QR mà ngân hàng in sẵn hoặc hiện trong app của họ. MARZI đọc ra ngân hàng và số tài khoản, nhân viên chỉ gõ tên chủ tài khoản. Cách này tránh gõ sai số.


3. Quét thử (bắt buộc)
Vì vậy máy chủ từ chối lưu nếu người nhập chưa xác nhận "Tôi đã quét thử bằng app ngân hàng và thấy đúng tên chủ tài khoản". Mọi lần nhập, đổi, bật, tắt hoặc xóa được ghi lại kèm tên người làm và làm từ đâu (bảng điều khiển hay app quản lý). Đại lý xem ở mục "Thay đổi gần đây" của thẻ này; chủ hệ thống xem thêm ở Nhật ký.
- Nhập ngân hàng và số tài khoản, bấm Hiện mã QR để quét thử (bảng điều khiển). Mã này chưa có số tiền.
- Mở app ngân hàng, quét mã và xem tên chủ tài khoản hiện ra. Không cần chuyển tiền.
- Đúng tên thì tích ô xác nhận rồi bấm Lưu tài khoản.
Trong app quản lý, người dùng đã quét mã QR của chính ngân hàng nên số tài khoản đã đúng; vẫn phải tick xác nhận tên chủ tài khoản trước khi lưu.
4. Tự xác nhận bằng SePay
Không bắt buộc. Không dùng SePay thì nhân viên bấm "Đánh dấu đã thanh toán" khi thấy tiền về (xem mục 6). Muốn tự động:
- Ở SePay (
my.sepay.vn): liên kết tài khoản ngân hàng của cửa hàng. - Ở SePay, thêm webhook: dán địa chỉ webhook mà bảng điều khiển hiện ở thẻ này (dạng
https://<tên miền>/v1/payments/sepay/<mã cửa hàng>), chỉ nhận tiền vào, chọn kiểu xác thực HMAC-SHA256 (khuyên dùng) hoặc API Key, và ghi lại khóa bí mật (hoặc khóa API). - Ở bảng điều khiển: mục "Cách biết đã nhận tiền", chọn SePay (tự động), chọn đúng kiểu xác thực, dán khóa, bấm Lưu cách xác nhận. Khóa được mã hóa trong cơ sở dữ liệu và không hiện lại; để trống ô khóa khi chỉ đổi kiểu xác thực thì khóa cũ được giữ.
- Thử: dùng chế độ thử (Test mode) của SePay để bắn một giao dịch thử, hoặc chuyển một khoản nhỏ có nội dung chứa mã thanh toán của một đơn thử. Bảng "Tiền vào gần đây" của thẻ ghi từng thông báo nhận được và kết quả.
SePay không gọi được vào máy thử (localhost, địa chỉ trong mạng nội bộ). Chỉ dùng được khi MARZI đã chạy trên máy chủ có tên miền và HTTPS; máy chủ cần đặt biến môi trường PUBLIC_BASE_URL (địa chỉ gốc, ví dụ https://app.mazimar.com) để bảng điều khiển hiện đúng địa chỉ webhook. Mã thanh toán nằm sẵn trong nội dung chuyển khoản (MZ + 8 số) nên không cần đặt tiền tố mã thanh toán ở SePay. Các bước giao diện ở SePay có thể đổi; xem tài liệu tại developer.sepay.vn.
Webhook của tài khoản nhận tiền của sàn MARZI có địa chỉ /v1/payments/sepay/_marzi; xem hướng dẫn cho chủ hệ thống.
5. Khách thanh toán
Khi cửa hàng đã nhập tài khoản và bật nhận chuyển khoản, đơn chưa thanh toán có thêm nút Thanh toán bằng mã QR ở chi tiết đơn trong app người mua.



Khách có ba cách trả: quét mã trên màn hình bằng điện thoại khác; bấm Lưu mã QR để tải ảnh mã về rồi quét từ thư viện ảnh của app ngân hàng; hoặc bấm Mở app ngân hàng, chọn ngân hàng của mình và app mở ra với số tiền và nội dung đã điền sẵn. Ảnh mã QR được tạo từ cùng chuỗi VietQR với mã trên màn hình, nên ngân hàng nào đọc được mã thì đọc được ảnh.
Khách mở app ngân hàng, chọn quét mã QR rồi quét mã trên màn hình. Số tiền và nội dung đã có trong mã; khách giữ nguyên nội dung để cửa hàng biết đơn nào đã trả.
6. Cửa hàng theo dõi
Trong chi tiết đơn ở app quản lý, MARZI hiện một dòng cho biết khách đã chọn trả bằng mã QR:


- Cửa hàng có website: MARZI ghi nhận tiền đã về và màn QR của khách báo "đã thanh toán", nhưng cờ "đã thanh toán" trên website chưa tự đổi. Nhân viên bấm Đánh dấu đã thanh toán để website cũng ghi nhận (bấm xong, màn QR của khách cũng đóng lại nếu chưa tự đóng).
- Cửa hàng không có website (MARZI giữ đơn): đơn tự đổi sang đã thanh toán.
- Không dùng SePay: khách thấy dòng "Cửa hàng sẽ xác nhận khi thấy tiền về"; nhân viên mở ngân hàng, thấy tiền (nội dung có mã
MZ…), rồi bấm Đánh dấu đã thanh toán. - Đại lý xem ở thẻ trong bảng điều khiển: bảng Tiền vào gần đây với kết quả từng thông báo.
7. Quy tắc xử lý tiền
| Tình huống | MARZI làm gì |
|---|---|
| Chuyển đủ hoặc dư số tiền, nội dung có mã | Đơn đã thanh toán. Tiền dư MARZI không hoàn; cửa hàng tự xử lý với khách. |
| Chuyển thiếu | Đơn vẫn chờ; thông báo ghi "Thiếu tiền" trong bảng. Khách chuyển thêm cho đủ cả đơn thì được (mỗi lần phải đủ, chưa cộng dồn các lần). |
| Thông báo gửi lại (SePay thử lại) | Không làm gì hai lần. |
| Đơn đã trả mà lại chuyển nữa | Ghi "Chưa khớp" để người xử lý (hoàn tiền). |
| Không có mã, mã lạ, mã của cửa hàng khác, hoặc tiền đi ra | Chỉ ghi lại, không đụng tới đơn nào. |
| Lỗi giữa chừng (đã nhận tiền nhưng chưa cập nhật xong đơn) | Trả lỗi để SePay gửi lại (tối đa 7 lần theo tài liệu của SePay); lần gửi lại làm nốt. |
Ngân hàng đổi chữ hoa, chữ thường, bỏ dấu và dấu cách trong nội dung đều không sao: MARZI so sánh sau khi bỏ hết ký tự không phải chữ và số.
8. Bảo mật
- Địa chỉ webhook ai cũng đoán được, nên cái chứng minh cuộc gọi là của SePay là chữ ký HMAC-SHA256 (hoặc khóa API) và thời gian không lệch quá 5 phút (chống gửi lại cuộc gọi cũ).
- Chữ ký sai, hết hạn, cửa hàng chưa bật SePay hoặc mã cửa hàng không có đều nhận cùng một câu trả lời từ chối, nên kẻ lạ không biết mình sai ở đâu.
- Khóa bí mật được mã hóa trong cơ sở dữ liệu và không bao giờ hiện lại, cả trong bảng điều khiển lẫn app.
- Đổi tài khoản phải quét thử và xác nhận, được ghi lại người làm; nhân viên chỉ xem thì không đổi được.
9. Giới hạn hiện tại
- Chưa tự ghi cờ "đã thanh toán" về website của cửa hàng (xem mục 6).
- Mới có SePay. Casso và payOS để làm sau.
- Chưa cộng dồn nhiều lần chuyển cho một đơn, chưa hoàn tiền.
- Nút "Thanh toán bằng mã QR" nằm ở chi tiết đơn (ngay sau khi đặt); chưa là một lựa chọn lúc đặt hàng ở app người mua.
- Mã QR đọc đúng theo chuẩn VietQR (đã kiểm bằng bộ đọc độc lập) nhưng luôn quét thử bằng app ngân hàng thật trước khi dùng cho khách.
10. Xử lý sự cố
| Tình huống | Cách xử lý |
|---|---|
| Không thấy nút "Thanh toán bằng mã QR" | Cửa hàng chưa nhập tài khoản hoặc đã tắt "Nhận chuyển khoản bằng mã QR"; hoặc đơn đã thanh toán, đã hủy. |
| Quét mã mà app ngân hàng báo lỗi, hoặc hiện sai tên | Kiểm tra lại ngân hàng và số tài khoản; nhập lại và quét thử. Ngân hàng không có trong danh sách: dùng "Ngân hàng khác" với đúng mã 6 số. |
| Khách đã chuyển mà đơn chưa đổi | Xem bảng "Tiền vào gần đây". Có dòng "Chưa khớp": khách quên hoặc sửa nội dung; tìm theo số tiền và giờ rồi bấm Đánh dấu đã thanh toán. Không có dòng nào: SePay chưa báo (kiểm tra webhook ở SePay, khóa và kiểu xác thực). |
| Dòng "Thiếu tiền" | Khách chuyển chưa đủ. Báo khách chuyển thêm cho đủ cả đơn, hoặc bấm Đánh dấu đã thanh toán nếu hai bên đã thỏa thuận. |
| SePay báo webhook lỗi 401 | Sai khóa hoặc sai kiểu xác thực, hoặc đồng hồ máy chủ lệch quá 5 phút, hoặc cửa hàng chưa chọn SePay trong thẻ. |
| Lưu tài khoản báo "Hãy quét thử mã QR..." | Chưa tích ô xác nhận đã quét thử. |