Toàn bộ luồng tiền trong hệ thống đấu giá SIM. Cập nhật: 29/04/2026
④b Phiên kết thúc — Có bid hợp lệ
LIVE → WAITING_DEPOSIT
Cron job phát hiện phiên hết giờ có bid hợp lệ. Winner bị giữ cọc, non-top3 được hoàn ngay. Cọc seller vẫn giữ nguyên — chỉ hoàn khi phiên SUCCESS hoặc CANCELLED.
winnerDepositRequired = currentPrice × winnerDepositPercent
additionalToHold = winnerDepositRequired − bidderEntryDeposit
heldNow = min(winner.available, additionalToHold)
stillNeeded = additionalToHold − heldNow
| Sự kiện | Chủ thể | available | pending | Entry Type | Ghi chú |
| Giữ thêm cọc winner (phần chưa có) |
Winner |
− heldNow |
+ heldNow |
WINNER_DEPOSIT_HOLD |
| Winner nạp thêm cho đủ cọc (nếu thiếu) |
Winner |
− stillNeeded |
+ stillNeeded |
WINNER_DEPOSIT_HOLD |
| Hoàn cọc tham gia (ngoài top 3) |
Bidder (≥ #4) |
+ bidderEntryDeposit |
− bidderEntryDeposit |
BIDDER_ENTRY_DEPOSIT_RELEASE |
⚠ Cọc seller KHÔNG được hoàn ở bước này — vẫn nằm trong pending cho đến khi phiên kết thúc hoàn toàn (SUCCESS hoặc CANCELLED).
Top 3 bidder (kể cả winner) vẫn bị giữ cọc tham gia trong pending. Cọc winner được giải quyết qua winner deposit flow. Cọc Top 2 & 3 được giải quyết khi: bị tịch thu nếu họ lần lượt trở thành winner rồi bỏ cọc; hoặc hoàn lại khi phiên SUCCESS/CANCELLED (ai còn đủ điều kiện).
⑤ Winner bỏ cọc / Hết hạn nạp cọc
WAITING_DEPOSIT → (vòng lặp)
Winner không nạp cọc trong thời hạn → bị tịch thu cọc, tạo ForfeitedDepositClaim, chuyển winner sang người bid cao thứ 2.
sellerPayout = forfeitAmount × (100 − forfeitPlatformSharePercent) / 100
| Sự kiện | Chủ thể | available | pending | Entry Type | Ghi chú |
| Tịch thu cọc winner |
Winner cũ |
— |
− winnerDeposit |
WINNER_DEPOSIT_FORFEIT |
| Tịch thu cọc tham gia (winner cũ) |
Winner cũ |
— |
− bidderEntryDeposit |
BIDDER_ENTRY_DEPOSIT_FORFEIT |
| Seller nhận phần cọc bị tịch thu (admin duyệt ForfeitedClaim) |
Seller |
+ sellerPayout |
— |
FORFEITED_DEPOSIT_PAYOUT |
| Admin từ chối payout cho seller |
Sàn |
— |
— |
FORFEITED_CLAIM_RETAINED |
| Giữ cọc winner MỚI (top2 lên thay) |
Winner mới |
− heldNow |
+ heldNow |
WINNER_DEPOSIT_HOLD |
Vòng lặp kết thúc — không còn ai → phiên CANCELLED:
Lúc này mỗi người trong top 3 đã forfeit ít nhất 1 lần. Trạng thái các khoản tiền:
- Cọc seller — vẫn trong pending → SELLER_DEPOSIT_RELEASE hoàn về seller
- Entry deposit non-top-3 — đã hoàn ở bước ④b, không cần xử lý thêm
- Entry deposit top 2 & 3 — đã bị FORFEIT (hoặc đã hoàn nếu winner từng đủ cọc) → không hoàn thêm
- Winner deposit (heldNow các lần) — đã bị FORFEIT → mỗi lần tạo một
ForfeitedDepositClaim chờ admin duyệt riêng
⑥ Admin xác nhận giao SIM — Giao dịch thành công
WAITING_TRANSACTION → SUCCESS
Admin xác nhận seller đã giao SIM cho winner. Toàn bộ bước ①②③④ đều trích từ cọc winner đang PENDING — không động đến available của bất kỳ ai. Seller nhận phần còn lại sau khi trừ hết phí.
floorFee = floorPrice × platformFeePercent
aboveFloorFee = (currentPrice − floorPrice) × aboveFloorFeePercent
floorFeeCharged = min(floorFee, winnerDepositRequired)
rem1 = winnerDepositRequired − floorFeeCharged
aboveFloorFeeCharged = min(aboveFloorFee, rem1)
rem2 = rem1 − aboveFloorFeeCharged
additionalFeeCharged = min(txAdditionalFee, rem2)
netToSeller = rem2 − additionalFeeCharged
| Bước | Chủ thể | available | pending (cọc winner) | Entry Type | Ghi chú |
| ① Thu phí sàn (% × giá sàn) |
Winner |
— |
− floorFeeCharged |
PLATFORM_FEE |
| ② Thu phí vượt sàn (% × phần vượt) |
Winner |
— |
− aboveFloorFeeCharged |
ABOVE_FLOOR_FEE |
| ③ Thu phí phát sinh |
Winner |
— |
− additionalFeeCharged |
TRANSACTION_ADDITIONAL_FEE |
| ④ Chuyển phần còn lại cho seller |
Winner |
— |
− netToSeller |
WINNER_DEPOSIT_PAYOUT_TO_SELLER |
| ④ Seller nhận tiền (net) |
Seller |
+ netToSeller |
— |
WINNER_DEPOSIT_SELLER_INCOME |
| ⑤ Hoàn cọc tham gia (ai đủ điều kiện) |
Bidder (≠ winner) |
+ bidderEntryDeposit |
− bidderEntryDeposit |
BIDDER_ENTRY_DEPOSIT_RELEASE |
| ⑥ Hoàn cọc seller |
Seller |
+ sellerDeposit |
− sellerDeposit |
SELLER_DEPOSIT_RELEASE |
Entry deposit của winner đã được tính vào winnerDepositRequired ngay từ bước ④b — không cần release riêng ở đây.
Ví dụ số: Bid 1,200,000 xu · floorPrice 1,000,000 · winnerDepositPercent 100% · bidderEntryDeposit 200,000
winnerDepositRequired = 1,200,000 · additionalHeld ở ④b = 1,000,000 (trừ entry deposit 200,000)
floorFee 2% = 20,000 · aboveFloorFee 5% × 200,000 = 10,000 · phí phát sinh = 5,000
X (tổng phí) = 35,000 → netToSeller = 1,200,000 − 35,000 = 1,165,000 xu
Thông báo seller: "Bạn nhận 1,165,000 xu từ phiên SIM xxx. Đã khấu trừ: phí sàn 20,000 + phí vượt sàn 10,000 + phí phát sinh 5,000 = 35,000 xu"
Bug cần fix: adminConfirmTransaction thiếu bước ⑤ — cọc tham gia của các bidder đủ điều kiện đang bị kẹt pending sau SUCCESS.
Logic: query tất cả BIDDER_ENTRY_DEPOSIT_HOLD của phiên → bỏ qua winner (entry deposit đã tiêu vào cọc) + bỏ qua ai đã có FORFEIT/RELEASE tương ứng → release phần còn lại.
Quy tắc bất biến: mọi tiền cọc phải được giải quyết (release hoặc forfeit) trước khi phiên SUCCESS hoặc CANCELLED.
⑦a Seller / Admin hủy phiên đang LIVE
LIVE → CANCELLED
Phiên bị hủy khi đang chạy. Phí tạo phiên không hoàn. Phí phạt + bồi thường bidder đều trích từ cọc seller — phần còn lại trả lại seller.
cancelPenalty = policy.cancelPenaltyFixed
compensationPerBidder = policy.cancelCompensationPerBidder
maxCompensatedBidders = policy.cancelCompensationMaxBidders
compensatedCount = min(actualBidderCount, maxCompensatedBidders)
totalCompensation = compensationPerBidder × compensatedCount
penaltyCharged = min(cancelPenalty, sellerDeposit)
rem1 = sellerDeposit − penaltyCharged
compensationCharged = min(totalCompensation, rem1)
netReturnToSeller = rem1 − compensationCharged
| Bước | Chủ thể | available | pending | Entry Type | Ghi chú |
| ① Thu phí phạt hủy từ cọc seller |
Seller |
— |
− penaltyCharged |
CANCEL_PENALTY |
| ② Bồi thường cho mỗi bidder từ cọc seller |
Seller |
— |
− compensationCharged |
CANCEL_PENALTY |
| ② Bidder nhận bồi thường |
Bidder (tối đa N người) |
+ compensationPerBidder |
— |
CANCEL_PENALTY_INCOME |
| ③ Hoàn phần cọc còn lại cho seller |
Seller |
+ netReturnToSeller |
− netReturnToSeller |
SELLER_DEPOSIT_RELEASE |
| ④ Hoàn cọc tham gia tất cả bidder |
Bidder |
+ bidderEntryDeposit |
− bidderEntryDeposit |
BIDDER_ENTRY_DEPOSIT_RELEASE |
| ⑤ Hoàn phí tạo phiên (tuỳ chọn) |
Seller |
+ auctionFee |
— |
AUCTION_FEE_REFUND |
Ví dụ số: sellerDeposit = 500,000 xu · cancelPenalty = 200,000 · compensationPerBidder = 50,000 · 3 bidder (maxCompensated = 5)
→ penaltyCharged = 200,000 · compensationCharged = 150,000 · netReturnToSeller = 150,000 xu
ℹ Phí tạo phiên (AUCTION_FEE) — admin có lựa chọn hoàn hoặc không khi hủy phiên. UI hiển thị checkbox "Hoàn phí tạo phiên cho seller" khi admin thực hiện hủy.
⚠ SELLER_DEPOSIT_FORFEIT không còn dùng cho flow này — cọc không bị tịch thu toàn bộ, chỉ trích phần phí + bồi thường.
Policy fields cần thêm: cancelCompensationPerBidder (xu/người) và cancelCompensationMaxBidders (số lượng người tối đa được bồi thường).
⑦b Seller không giao SIM (admin hủy WAITING_TRANSACTION)
WAITING_TRANSACTION → CANCELLED
Seller đã nhận cọc nhưng không giao SIM → bị phạt bồi thường cho winner, winner lấy lại cọc.
compensationAmount = sellerDeposit
| Bước | Chủ thể | available | pending | Entry Type | Ghi chú |
| ① Tịch thu cọc sim seller |
Seller |
— |
− sellerDeposit |
SELLER_DEPOSIT_FORFEIT |
| ② Admin duyệt → bồi thường cho winner |
Winner |
+ sellerDeposit |
— |
FORFEITED_DEPOSIT_PAYOUT |
| ③ Hoàn cọc winner |
Winner |
+ winnerDeposit |
− winnerDeposit |
WINNER_DEPOSIT_RELEASE |
| ④ Hoàn cọc tham gia (ai đủ điều kiện) |
Winner + Top 2 & 3 |
+ bidderEntryDeposit |
− bidderEntryDeposit |
BIDDER_ENTRY_DEPOSIT_RELEASE |
∑ Bảng tổng hợp tất cả Entry Types
Mỗi dòng ledger (WalletLedger) thuộc một trong các loại sau. Dấu + = tăng, − = giảm.
| Entry Type | available | pending | inflow/outflow | Ý nghĩa |
| DEPOSIT | + | — | inflow + | Nạp tiền PayOS thành công |
| WITHDRAW_HOLD | − | + | — | Yêu cầu rút, đóng băng chờ duyệt |
| WITHDRAW_RELEASE | + | − | — | Admin từ chối → hoàn về |
| WITHDRAW_APPROVED | — | − | outflow + | Admin duyệt → tiền ra ngoài |
| AUCTION_FEE | − | — | outflow + | Phí tạo phiên (cố định, giảm theo tier) |
| AUCTION_FEE_REFUND | + | — | inflow + | Hoàn phí tạo phiên (phiên không bid) |
| SELLER_DEPOSIT_HOLD | − | + | — | Cọc seller khi tạo phiên |
| SELLER_DEPOSIT_RELEASE | + | − | — | Hoàn cọc seller (phiên kết thúc hợp lệ) |
| SELLER_DEPOSIT_FORFEIT | — | − | outflow + | Tịch thu cọc seller (vi phạm) |
| BIDDER_ENTRY_DEPOSIT_HOLD | − | + | — | Cọc tham gia đầu khi bid lần đầu |
| BIDDER_ENTRY_DEPOSIT_RELEASE | + | − | — | Hoàn cọc tham gia |
| BIDDER_ENTRY_DEPOSIT_FORFEIT | — | − | outflow + | Tịch thu cọc tham gia (winner bỏ) |
| WINNER_DEPOSIT_HOLD | − | + | — | Cọc winner sau khi trúng bid |
| WINNER_DEPOSIT_RELEASE | + | − | — | Hoàn cọc winner (seller hủy) |
| WINNER_DEPOSIT_FORFEIT | — | − | outflow + | Tịch thu cọc winner (bỏ cọc) |
| WINNER_DEPOSIT_PAYOUT_TO_SELLER | — | − | outflow + | Cọc winner → chuyển cho seller (net) |
| WINNER_DEPOSIT_SELLER_INCOME | + | — | inflow + | Seller nhận tiền từ cọc winner (net) |
| PLATFORM_FEE | — | − | outflow + | Phí sàn (% × floorPrice), trừ từ cọc winner |
| ABOVE_FLOOR_FEE | — | − | outflow + | Phí vượt sàn (% × phần bid > floor), trừ từ cọc winner |
| TRANSACTION_ADDITIONAL_FEE | — | − | outflow + | Phí phát sinh admin nhập, trừ từ cọc winner |
| CANCEL_PENALTY | − | — | outflow + | Phí phạt hủy phiên (từ available seller) |
| CANCEL_PENALTY_INCOME | + | — | inflow + | Bidder nhận bồi thường khi seller hủy LIVE |
| SELLER_CANCEL_COMPENSATION | − | — | outflow + | Seller bồi thường winner (hủy WAITING_TRANSACTION) |
| FORFEITED_DEPOSIT_PAYOUT | + | — | inflow + | Seller nhận cọc bị tịch thu (admin duyệt) |
| FORFEITED_CLAIM_RETAINED | — | — | — | Sàn giữ lại (admin từ chối payout) |
| UPTOP_FEE | − | — | outflow + | Phí up top phiên (seller trả) |
| ADJUSTMENT | ± | — | inflow/outflow | Điều chỉnh thủ công (admin) |
$ Doanh thu sàn (Platform Revenue)
Sàn thu tiền khi các khoản outflow từ user không tương ứng với inflow của user khác.
| Nguồn | Entry Type | Điều kiện |
| Phí tạo phiên | AUCTION_FEE | Mỗi phiên được duyệt. Hoàn nếu phiên không có bid. |
| Phí sàn (% × giá sàn) | PLATFORM_FEE | Khi giao dịch SUCCESS. Trừ từ cọc winner. |
| Phí vượt sàn (% × phần bid > floor) | ABOVE_FLOOR_FEE | Khi bid vượt floorPrice. Trừ từ cọc winner. |
| Phí phát sinh | TRANSACTION_ADDITIONAL_FEE | Admin nhập khi confirm giao SIM. Trừ từ cọc winner. |
| Phí up top | UPTOP_FEE | Seller mua up top bất kỳ lúc nào. |
| Phần cọc bị tịch thu (giữ lại) | WINNER_DEPOSIT_FORFEIT (50%) | Winner bỏ cọc — sàn giữ 50% (default). |
| Phí phạt hủy phiên LIVE | CANCEL_PENALTY | Trích từ cọc seller. Sàn giữ phần penaltyCharged. |
| Claim giữ lại | FORFEITED_CLAIM_RETAINED | Admin từ chối payout cho seller. |
⑧ Lịch sử hành động admin (Audit Trail)
Vì nhiều admin cùng quản trị, mọi hành động phải được ghi lại: ai làm, làm gì, lúc nào, trên đối tượng nào. Model AuditLog đã tồn tại — cần đảm bảo mọi action đều được bắt.
AuditLog { id, actorUserId, actorRole, action, resourceType, resourceId, payload(JSON), ipAddress, createdAt }
Đã log
| Action | Mô tả |
| APPROVE_AUCTION | Duyệt phiên đấu giá |
| REJECT_AUCTION | Từ chối phiên |
| ADMIN_FORCE_CANCEL_LIVE | Hủy phiên đang LIVE |
| ADMIN_CANCEL_SCHEDULED | Hủy phiên SCHEDULED |
| CONFIRM_DEPOSIT | Xác nhận winner đã nạp cọc |
| CONFIRM_TRANSACTION | Xác nhận giao SIM thành công |
| MANUAL_CONFIRM_DEPOSIT | Xác nhận nạp tiền PayOS thủ công |
| ADMIN_MESSAGE_REPLY | Trả lời tin nhắn user |
| CHAT_SESSION_OPEN/CLOSE | Mở/đóng phiên chat |
| APPROVE/REJECT_AUCTION_TG | Duyệt/từ chối qua Telegram |
| RESOLVE_FORFEITED_CLAIM | Xử lý claim cọc bị tịch thu |
| CANCEL_WAITING_TRANSACTION | Hủy khi seller không giao SIM |
Còn thiếu — cần bổ sung
| Action | Mô tả |
| ADJUST_USER_BALANCE | Admin cộng/trừ xu thủ công cho user |
| UPDATE_AUCTION_POLICY | Thay đổi policy đấu giá |
| LOCK_USER / UNLOCK_USER | Khóa/mở tài khoản user |
| CHANGE_USER_ROLE | Thay đổi role user |
| APPROVE_WITHDRAW | Duyệt yêu cầu rút tiền |
| REJECT_WITHDRAW | Từ chối yêu cầu rút tiền |
| PLATFORM_WALLET_ADJUST | Điều chỉnh thủ công ví sàn |
| ADMIN_LOGIN | Admin đăng nhập (kèm IP) |
ipAddress: Field đã có trong schema nhưng chưa được populate. Cần lấy từ request header (X-Forwarded-For hoặc req.ip) và ghi vào mọi audit log.
Admin UI: Trang /admin/activity hiện đã có — cần đảm bảo hiển thị đủ các action mới, có filter theo admin/action/thời gian.
⑨ Ví sàn (Platform Wallet)
Đang thiết kế — chưa implement
Ví riêng của sàn theo dõi toàn bộ tiền sàn đang giữ. Mọi khoản thu/chi của sàn đều tạo một dòng lịch sử rõ ràng. Admin quản lý qua trang riêng.
Tại sao cần? Hiện tại phí thu từ user chỉ ghi là outflow khỏi ví user — không có ví thực để biết sàn đang giữ bao nhiêu, không thể điều chỉnh thủ công, không có lịch sử chi tiêu của sàn.
Schema DB
PlatformWallet { id="singleton", balance, updatedAt }
PlatformLedger {
direction: IN | OUT
amount, balanceBefore, balanceAfter
entryType
referenceType, referenceId
description, createdByAdminId
}
API Admin
GET /admin/platform-wallet
GET /admin/platform-wallet/ledger
POST /admin/platform-wallet/adjust
Tiền VÀO ví sàn (IN)
| Sự kiện | Entry Type (user) | Phần sàn nhận | Nơi gọi |
| Seller nộp phí tạo phiên |
AUCTION_FEE |
Toàn bộ amount |
| Thu phí sàn từ cọc winner |
PLATFORM_FEE |
Toàn bộ floorFeeCharged |
| Thu phí vượt sàn từ cọc winner |
ABOVE_FLOOR_FEE |
Toàn bộ aboveFloorFeeCharged |
| Thu phí phát sinh từ cọc winner |
TRANSACTION_ADDITIONAL_FEE |
Toàn bộ additionalFeeCharged |
| Seller mua up top |
UPTOP_FEE |
Toàn bộ amount |
| Winner bỏ cọc (phần sàn) |
WINNER_DEPOSIT_FORFEIT |
amount × forfeitPlatformSharePercent% |
| Tịch thu cọc tham gia winner |
BIDDER_ENTRY_DEPOSIT_FORFEIT |
Toàn bộ (sàn giữ hết) |
| Tịch thu cọc seller vi phạm |
SELLER_DEPOSIT_FORFEIT |
Toàn bộ (sàn giữ hết) |
| Seller bị phạt hủy phiên LIVE |
CANCEL_PENALTY |
Toàn bộ (trả bidder sau) |
Tiền RA khỏi ví sàn (OUT)
| Sự kiện | Entry Type (user) | Sàn trả ra | Nơi gọi |
| Hoàn phí tạo phiên cho seller |
AUCTION_FEE_REFUND |
Toàn bộ amount |
| Bồi thường bidder khi seller hủy LIVE |
CANCEL_PENALTY_INCOME |
Phần chia cho từng bidder |
| Admin duyệt payout cọc bị tịch thu |
FORFEITED_DEPOSIT_PAYOUT |
Phần của seller/winner (sau trừ platform share) |
| Admin cộng tiền cho user (ADJUSTMENT +) |
ADJUSTMENT |
Toàn bộ amount cộng cho user |
Tiền VÀO ví sàn bổ sung (từ ADJUSTMENT)
| Sự kiện | Entry Type (user) | Sàn nhận | Nơi gọi |
| Admin trừ tiền của user (ADJUSTMENT −) |
ADJUSTMENT |
Toàn bộ amount trừ từ user |
⚠ Số dư âm không được phép. Mọi debit phải kiểm tra balance >= amount trước khi thực hiện — nếu không đủ sẽ throw lỗi để admin xử lý.
⚠ Dữ liệu cũ: Balance bắt đầu từ 0 khi deploy feature — không backfill lịch sử trước đó.
DauSim Finance Map · Sinh từ source code · 29/04/2026