Player → Shop/Cashier (Counter Redemption)
Phase 3 — tách riêng khỏi Phase 2 (deposit). Player đổi credit lấy tiền mặt tại quầy; Shop trả lại Player khoản cash tương ứng.
Player yêu cầu rút
→ Shop/Cashier nhập số credit redeem + số tiền mặt trả Player
→ credit trừ Player, cộng Shop CDN wallet
→ Shop trả tiền mặt cho Player
→ ghi sổ cash (Cash Out) + báo cáo
Luồng deposit (Player nộp tiền nhận credit): Shop/Cashier → Player — Phase 2.
Mockup: Phase 2 — Redemptions tab · Shift: phase3-shift · Cashier spec: Cashier Management spec
Vị trí trong hệ thống
flowchart LR
P2["Phase 2\nDeposit"] --> P3["Phase 3\nRedemption"]
P3 --> SHIFT["Shift Cash Out\n+ reconciliation"]
Triển khai sau Phase 2 — dùng chung schema cash_transactions và pattern hook biz, nhưng nghiệp vụ và UI tách biệt vì:
- Chiều cash ngược (Shop trả tiền)
- Có approval threshold (Cashier)
- Liên kết chặt shift Cash Out và blind closing
- Không có margin shop — redemption 1:1 face value
Ai làm gì
| Vai trò | Hành động |
|---|---|
| Player | Yêu cầu rút; có mặt tại quầy |
| Shop Manager | Nhập credit redeem + tiền mặt trả Player; xác nhận redeem từ Shop portal |
| Cashier | Nhập credit redeem + tiền mặt trả Player; trả cash sau completed (hoặc sau Manager approve) |
| Hệ thống | Debit player wallet, credit shop wallet, ghi cash_transactions, cập nhật shift Cash Out |
Hai kênh thực hiện
| Kênh | Vai trò | Điều kiện | API |
|---|---|---|---|
| Cashier portal | Cashier | Active shift; cash only | POST /cashier/players/:id/redeem |
| Shop portal | Shop Manager | Không bắt buộc shift | POST /shop/players/:id/credits (action: deduct) |
Thông tin bắt buộc
| Trường | Bắt buộc | Mô tả |
|---|---|---|
| Player | ✅ | Thuộc shop |
| Số credit redeem | ✅ | ≤ player balance; credit trừ khỏi ví Player |
| Số tiền mặt trả Player | ✅ | Cash Shop đưa trực tiếp cho Player; ghi vào ledger cash_received |
| Hình thức trả | ✅ | Counter: cash only |
V1 thường 1:1 (30 credits → $30.00) — UI có thể pre-fill cash = credit, nhưng Shop nhập và xác nhận cả hai để báo cáo ghi nhận chính xác số tiền mặt đã chi.
Không áp shop.costRate cho redemption — không có gross margin như deposit.
Approval (Cashier)
Redemption có thể pending approval nếu vượt ngưỡng (per-transaction hoặc daily limit — env):
Cashier nhập amount
→ needsApproval?
YES → CashierTransaction (pending_approval) + approval record
→ chưa debit player, chưa trả tiền, cashDelta = 0
NO → transfer ngay → CashierTransaction (completed), cashDelta = −amount
→ Manager approve → transfer + completed
Quan trọng cho ledger: chỉ tạo cash_transactions khi wallet transfer thực sự chạy (completed), không tạo ở pending_approval.
Quy tắc nghiệp vụ
- Player balance ≥ số credit redeem.
- Cashier: active shift; trả tiền mặt sau completed / approve.
cashDelta = −cash_paid(số tiền mặt trả Player) → Cash Out trong Expected Closing Cash.- Không OTP tại quầy (player có mặt) — khác online withdrawal.
- Sau complete: không sửa / hủy / reverse (escalate Manager).
- Shop Manager approve/reject qua Shop portal khi pending.
Công thức tiền
| Chỉ số | Công thức |
|---|---|
| credit_amount | Số credit redeem (wallet transfer) |
| expected_cash / cash_received | Số tiền mặt Shop trả Player (cash_paid) |
| Gross margin | Không áp dụng |
Ví dụ: redeem 30 credits, trả $30.00 cash → Player −30, Shop CDN +30, ledger Cash Out $30.00.
Báo cáo và shift reconciliation dùng cash_received (tiền mặt đã chi), không suy từ credit.
Ledger đề xuất (cash_transactions)
| Field | Value |
|---|---|
| flow | shop_player_redemption |
| seller | player / player uuid |
| buyer | shop / shop uuid |
| credit_amount | Số credit redeem |
| expected_cash / cash_received | Số tiền mặt trả Player |
| status | paid_full (auto khi completed) |
Links: payment_transaction_id, wallet txs, cashier_transaction_id, shift_id, metadata.approvalId (nếu có).
Backend hiện tại
Cashier: cashierTransactionBizService.redeemCredits
→ (optional) cashierRedemptionApprovalBizService
→ playerCreditPort.transferCreditsAtCounter (redeem)
→ shopPlayerBizService._adjustCreditsDeduct
→ walletTransferService (player → shop)
→ PaymentTransaction (type: withdrawal, provider: shop_player_cash)
→ CashierTransaction (type: redeem_credits)
Shop: adjustPlayerCredits (action: deduct)
→ cùng _adjustCreditsDeduct
API
Đã có
| Method | Endpoint | Body |
|---|---|---|
POST | /api/cashier/players/:id/redeem | { "amount": number, "cash_paid": number } |
POST | /api/shop/players/:id/credits | { "action": "deduct", "amount": N, "cash_paid": number } |
amount= credit redeem;cash_paid= tiền mặt trả Player. V1 thường bằng nhau; cả hai bắt buộc.
Target Phase 3
| Method | Endpoint | Mô tả |
|---|---|---|
GET | /api/shop/cash-transactions?flow=shop_player_redemption | List redemptions |
GET | /api/shop/cash-transactions/:id | Detail + approval context |
GET | /api/shop/cash-transactions/export | CSV (filter flow redemption) |
Shop Manager approve API: xem Cashier spec § Redemption.
UI đề xuất (Shop portal)
Tab Payable — Redemptions trên mockup phase2-counter:
- Summary: Counter Cash Out (today) — SUM
cash_received, pending approval count - Modal redeem: 2 ô — credit redeem + cash trả Player
- Bảng: player, credits redeemed, cash paid, cashier, status (Settled / Pending approval)
- Detail: link shift, approval timeline
Tích hợp shift: phase3-shift mockup — Cash Out trong Expected Closing Cash.
So sánh Phase 2 vs Phase 3
| Tiêu chí | Phase 2 Deposit | Phase 3 Redemption |
|---|---|---|
| Flow enum | shop_player_deposit | shop_player_redemption |
| Cash chiều | Player → Shop (Cash In) | Shop → Player (Cash Out) |
| Credit chiều | Shop → Player | Player → Shop |
| Margin | Có (shop.costRate) | Không |
| Approval | Không | Có thể (threshold) |
| Payment methods | Cash (+ card/bank shop portal) | Cash only |
| OTP | Không | Không (counter) |
Hiện trạng
| Thành phần | Trạng thái |
|---|---|
| Wallet transfer player → shop | ✅ |
Cashier redeem + approval + cashDelta | ✅ |
| Shop portal manual deduct | ✅ |
cash_transactions flow shop_player_redemption | ❌ |
| Shop portal redemption ledger | ❌ |
Tasks: tasks-player-to-shop-cashier
Phụ thuộc: Nên ship sau Phase 2 deposit ledger — schema player owner type và cột cashier/shift dùng chung.
Liên quan
- Phase 2: Shop/Cashier → Player
- Schema: schema-cash-transactions
- Shift reconciliation: Cashier spec § Expected Closing Cash