1. Bối cảnh doanh nghiệp & phạm vi
Bạn là QA Lead tại MoMoPay-like Wallet — ví điện tử phục vụ khoảng 15 triệu người dùng hoạt động hàng tháng, xử lý trung bình 900.000 lệnh rút tiền (payout) từ ví ra tài khoản ngân hàng mỗi ngày, với tổng giá trị khoảng 1.100 tỷ đồng/ngày. Các lệnh rút được định tuyến qua 6 đối tác cổng thanh toán khác nhau tuỳ ngân hàng đích, và SLA cam kết với người dùng là 95% lệnh hoàn tất trong vòng 2 phút. Quý này, đội kỹ thuật mở rộng thêm 2 đối tác cổng thanh toán mới để tăng tỉ lệ thành công cho các ngân hàng nhỏ, kéo theo yêu cầu kiểm thử lại toàn bộ luồng đối soát cuối ngày.
Ràng buộc nghiệp vụ quan trọng nhất là: số dư ví của người dùng KHÔNG BAO GIỜ được âm dưới bất kỳ tình huống nào, kể cả khi có lỗi hệ thống hay callback trùng lặp từ đối tác. Ngân hàng Nhà nước yêu cầu ví điện tử phải đối soát khớp 100% với các đối tác thanh toán vào cuối mỗi ngày làm việc, và mọi khoản tiền 'treo' (không rõ trạng thái) quá 24 giờ phải được báo cáo lên hệ thống giám sát rủi ro. Một lỗi đặc biệt nguy hiểm trong domain ví là 'lệnh treo vĩnh viễn' — khi callback từ đối tác không bao giờ đến, khiến tiền bị khoá trong ví nhưng không rút được cũng không hoàn lại.
Phạm vi tự động hoá của tài liệu này
- Luồng rút tiền đầu-cuối: kiểm tra số dư → khoá tiền → gọi cổng đối tác → xử lý callback bất đồng bộ → cập nhật số dư cuối.
- Xử lý lệnh treo/timeout: job quét chủ động, truy vấn trạng thái từ đối tác, tự động hoàn tiền khi vượt SLA.
- Đối soát cuối ngày với đối tác: so khớp file settlement, phát hiện lệch 2 chiều, mở case điều tra.
- Các ca đồng thời (concurrency): nhiều lệnh rút cùng lúc, chống race condition làm âm số dư.
- Tích hợp AI Agent hỗ trợ phân tích log lệnh treo và đề xuất ca kiểm thử cho đối tác mới.
2. Kiến trúc & luồng nghiệp vụ
Luồng rút tiền từ ví ra ngân hàng là một trong những luồng bất đồng bộ điển hình nhất trong fintech: hành động của người dùng kết thúc gần như ngay lập tức trên UI, nhưng kết quả thực sự có thể đến sau vài giây, vài phút, hoặc đôi khi không bao giờ đến. QA phải thiết kế test để xử lý được toàn bộ dải thời gian phản hồi này, không chỉ trường hợp callback đến ngay.
Khi người dùng bấm rút tiền, Payout Service ngay lập tức kiểm tra số dư khả dụng và KHOÁ (lock) số tiền tương ứng — số tiền này vẫn thuộc về ví người dùng nhưng không thể dùng cho giao dịch khác cho tới khi lệnh hoàn tất. Đây là bước đồng bộ, phải hoàn thành trong vài trăm mili-giây. Sau đó, Payout Service gửi yêu cầu tới Payment Partner Gateway phù hợp với ngân hàng đích, và trả về màn hình 'Đang xử lý' cho người dùng ngay lập tức.
Phần bất đồng bộ diễn ra khi đối tác xử lý lệnh chuyển tiền thực sự tới Bank B, và gửi callback (webhook) về Callback Handler khi có kết quả. Nếu SUCCESS, số tiền bị khoá được giải phóng khỏi ví (đã thực sự chuyển đi), trạng thái chuyển sang SUCCESS. Nếu FAILED, số tiền được hoàn lại vào số dư khả dụng, trạng thái chuyển sang FAILED. Vấn đề khó nhất là khi callback KHÔNG BAO GIỜ đến — một job quét nền phải chủ động truy vấn trạng thái từ đối tác hoặc tự động hoàn tiền sau khi vượt SLA 30 phút.
3. Mô hình dữ liệu & bất biến nghiệp vụ
Mô hình dữ liệu gồm WalletAccount (số dư khả dụng, số dư bị khoá), PayoutOrder (lệnh rút, có requestId duy nhất, amount, partnerId, trạng thái), và PartnerCallback (log mọi callback nhận được, kể cả callback trùng lặp, để phục vụ điều tra sau này). Hiểu đúng quan hệ giữa số dư khả dụng và số dư bị khoá là chìa khoá để định nghĩa oracle chính xác cho domain ví.
Bốn bất biến phải đúng ở MỌI thời điểm
- Số dư không âm: số dư khả dụng của ví KHÔNG BAO GIỜ được nhỏ hơn 0, dù ở bất kỳ bước trung gian nào của lệnh rút.
- Bảo toàn tổng số dư: số dư khả dụng + số dư bị khoá = tổng số dư ví, không đổi trừ phi có phí giao dịch được trừ đúng quy định.
- Idempotency: cùng một requestId dù gửi lại bao nhiêu lần cũng chỉ tạo ĐÚNG MỘT lệnh rút và khoá tiền đúng một lần.
- Trạng thái cuối duy nhất: mỗi lệnh rút cuối cùng phải ở đúng một trong SUCCESS/FAILED/REFUNDED — không được 'treo' ở PROCESSING vô thời hạn.
💬 Bình luận (0)
Chưa có bình luận nào. Hãy là người đầu tiên!