Skip to main content

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 → PlayerPhase 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)
  • 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
PlayerYêu cầu rút; có mặt tại quầy
Shop ManagerNhập credit redeem + tiền mặt trả Player; xác nhận redeem từ Shop portal
CashierNhập credit redeem + tiền mặt trả Player; trả cash sau completed (hoặc sau Manager approve)
Hệ thốngDebit player wallet, credit shop wallet, ghi cash_transactions, cập nhật shift Cash Out

Hai kênh thực hiện

KênhVai tròĐiều kiệnAPI
Cashier portalCashierActive shift; cash onlyPOST /cashier/players/:id/redeem
Shop portalShop ManagerKhông bắt buộc shiftPOST /shop/players/:id/credits (action: deduct)

Thông tin bắt buộc

TrườngBắt buộcMô tả
PlayerThuộc shop
Số credit redeem≤ player balance; credit trừ khỏi ví Player
Số tiền mặt trả PlayerCash 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ụ

  1. Player balance ≥ số credit redeem.
  2. Cashier: active shift; trả tiền mặt sau completed / approve.
  3. cashDelta = −cash_paid (số tiền mặt trả Player) → Cash Out trong Expected Closing Cash.
  4. Không OTP tại quầy (player có mặt) — khác online withdrawal.
  5. Sau complete: không sửa / hủy / reverse (escalate Manager).
  6. Shop Manager approve/reject qua Shop portal khi pending.

Công thức tiền

Chỉ sốCông thức
credit_amountSố credit redeem (wallet transfer)
expected_cash / cash_receivedSố tiền mặt Shop trả Player (cash_paid)
Gross marginKhô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)

FieldValue
flowshop_player_redemption
sellerplayer / player uuid
buyershop / shop uuid
credit_amountSố credit redeem
expected_cash / cash_receivedSố tiền mặt trả Player
statuspaid_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ó

MethodEndpointBody
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

MethodEndpointMô tả
GET/api/shop/cash-transactions?flow=shop_player_redemptionList redemptions
GET/api/shop/cash-transactions/:idDetail + approval context
GET/api/shop/cash-transactions/exportCSV (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 DepositPhase 3 Redemption
Flow enumshop_player_depositshop_player_redemption
Cash chiềuPlayer → Shop (Cash In)Shop → Player (Cash Out)
Credit chiềuShop → PlayerPlayer → Shop
MarginCó (shop.costRate)Không
ApprovalKhôngCó thể (threshold)
Payment methodsCash (+ card/bank shop portal)Cash only
OTPKhôngKhông (counter)

Hiện trạng

Thành phầnTrạ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