CYBERSOFT
Đăng nhập

Thực chiến: kiểm thử khuyến mãi/coupon & Order-to-Cash trong TMĐT

Thực chiến doanh nghiệpTMĐTPlaywrightAPIThực tế
🗓 1 tháng trước22 phút đọc·👁 1,134 lượt xem👤 218 người đọc

Bài sâu: bối cảnh, kiến trúc order-to-cash, bất biến tiền/coupon, test plan, ma trận ca, automation, race condition, đối soát, CI, AI, phỏng vấn.

1. Bối cảnh doanh nghiệp & phạm vi

Một sàn TMĐT thời trang đa danh mục xử lý trung bình 180.000 đơn hàng/ngày, cao điểm sale 9.9 hoặc 11.11 có thể chạm 1,2 triệu đơn trong 24 giờ. Đội ngũ marketing phát hành liên tục các chương trình khuyến mãi: mã giảm giá phần trăm, mã freeship, mã giảm giá theo ngưỡng đơn hàng, và các chương trình có thể stack (cộng dồn) với nhau theo quy tắc ưu tiên. Sai sót trong tính coupon từng khiến một sàn tương tự lỗ hơn 4 tỷ đồng trong một đợt flash sale do lỗi cho phép cộng dồn hai mã loại trừ lẫn nhau.

Phạm vi bài viết bao trùm toàn bộ vòng đời order-to-cash: khách chọn sản phẩm, áp mã khuyến mãi, đặt hàng, thanh toán qua cổng (VNPay/Momo/thẻ), giao vận, và cuối cùng ghi nhận doanh thu vào sổ cái kế toán. Đây là chuỗi nghiệp vụ xuyên nhiều service (pricing, order, payment, inventory, ledger) nên lỗi ở một khâu có thể lan sang khâu sau, gây lệch số liệu tài chính hoặc bán vượt tồn kho. Ràng buộc tuân thủ bao gồm: không được ghi nhận doanh thu trước khi hàng giao thành công (theo nguyên tắc kế toán dồn tích có điều kiện), và log áp dụng coupon phải lưu tối thiểu 5 năm để phục vụ kiểm toán thuế.

Luồng Order-to-Cash · Order-to-Cash flow Giỏ hàng + Coupon Pricing Engine Order Service Payment Gateway Ledger/Revenue Coupon Ledger Inventory Reserve Fulfillment/WMS Bất biến: Tổng tiền = Σ item − giảm giá hợp lệ
Kiến trúc luồng Order-to-Cash với các service pricing/order/payment/ledger

Phạm vi tự động hoá

  • Kiểm thử API cho pricing engine: tính giảm giá, stacking, ngưỡng, giới hạn dùng
  • Kiểm thử E2E Playwright cho luồng đặt hàng có áp coupon
  • Kiểm thử đối soát cuối ngày giữa order, payment, ledger
Bài này giả định hệ thống có test-only endpoint để seed coupon và reset ví ngân sách khuyến mãi giữa các lần chạy.

2. Kiến trúc & luồng nghiệp vụ

Kiến trúc gồm 5 service chính: Pricing Engine (tính giá và giảm giá), Order Service (quản lý vòng đời đơn hàng), Payment Gateway (tích hợp cổng thanh toán bên thứ ba), Inventory Reserve (giữ tồn kho tạm thời trong 15 phút chờ thanh toán), và Ledger/Revenue (ghi nhận bút toán kế toán). Luồng đồng bộ diễn ra khi khách bấm 'Áp dụng mã' — pricing engine phải phản hồi trong dưới 300ms để không ảnh hưởng trải nghiệm; luồng bất đồng bộ diễn ra sau khi thanh toán thành công, hệ thống phát sự kiện OrderPaid để Inventory chốt trừ kho vĩnh viễn và Ledger ghi nhận doanh thu tạm hoãn (deferred revenue) chờ giao hàng.

Điểm khó khi kiểm thử

Điểm khó nhất nằm ở việc pricing engine phải tính đúng thứ tự áp dụng khi có nhiều coupon: giảm giá theo % áp dụng trước, sau đó trừ ngưỡng cố định, cuối cùng mới áp freeship — nếu đổi thứ tự sẽ ra số tiền khác. Ngoài ra, giới hạn dùng coupon (ví dụ 'chỉ 500 lượt đầu') là một tài nguyên chia sẻ (shared counter) dễ bị race condition khi nhiều người dùng bấm cùng lúc trong khung giờ vàng — cần kiểm thử đồng thời (concurrency testing) chứ không chỉ kiểm thử tuần tự. Một khó khăn khác là việc hoàn tiền coupon khi huỷ đơn: ngân sách coupon đã trừ phải được hoàn lại đúng, nếu không sẽ làm cạn quỹ khuyến mãi giả tạo trước hạn.

🎯 Sự cố thực tế

Trong đợt sale 11.11 năm trước, đội vận hành phát hiện quỹ coupon 'giảm 50k' báo hết ngân sách chỉ sau 20 phút dù giới hạn là 10.000 lượt. Điều tra cho thấy một lỗi retry ở client (người dùng bấm nút 'Áp dụng' nhiều lần do UI phản hồi chậm) đã gửi trùng request, và backend không có idempotency key nên đã trừ ngân sách 3 lần cho cùng một hành động của một người dùng.

3. Mô hình dữ liệu & bất biến (oracle)

Mô hình dữ liệu cốt lõi gồm bảng Order (id, userId, status, subtotal, discountTotal, shippingFee, grandTotal), CouponRedemption (couponId, userId, orderId, amountApplied, redeemedAt), CouponBudget (couponId, totalBudget, usedBudget, usageLimitPerUser), và LedgerEntry (orderId, debitAccount, creditAccount, amount, postedAt). Trạng thái đơn hàng đi theo máy trạng thái hữu hạn: DRAFT → PLACED → PAID → FULFILLED → REVENUE_RECOGNIZED, hoặc bất kỳ trạng thái nào trước FULFILLED có thể chuyển sang CANCELLED kèm theo bù trừ (compensation) tương ứng.

Bất biến nghiệp vụ bắt buộc (oracle)

  • Bất biến 1: grandTotal = subtotal − discountTotal + shippingFee, sai lệch phải bằng 0 (không làm tròn quá 1 đồng)
  • Bất biến 2: Σ amountApplied của một coupon (usedBudget) không bao giờ vượt totalBudget đã cấu hình
  • Bất biến 3: số lượt redeem của 1 user cho 1 coupon ≤ usageLimitPerUser tại mọi thời điểm
  • Bất biến 4: mỗi orderId chỉ có đúng 1 trạng thái hiệu lực tại một thời điểm (không tồn tại PAID và CANCELLED đồng thời)
  • Bất biến 5: doanh thu chỉ được ghi Ledger khi status = FULFILLED, không sớm hơn
🔒

Nội dung bị khoá

Bạn đang xem bản xem trước. Đăng nhập để mở khoá toàn bộ bài viết và tất cả tài liệu Tester/QA.

Đã xem 24% nội dung
  • Đọc trọn vẹn mọi bài viết
  • Lưu bài & ghi chú cá nhân
  • Theo dõi tiến độ đã đọc
  • Luyện phỏng vấn, ISTQB, Mock
Đăng nhập để đọc tiếp

Chưa có mã? Lấy mã qua Fanpage / Zalo CyberSoft.

0

💬 Bình luận (0)

Bạn có thể đọc mọi bình luận. Đăng nhập để bình luận

Chưa có bình luận nào. Hãy là người đầu tiên!