Tasks — Player → Shop/Cashier (Phase 3)
Spec: player-to-shop-cashier · Mockup: Redemptions tab
Dependencies: Phase 2 deposit (schema + shop cash-transactions API foundation); Cashier redeem + approval (Cashier spec).
Business summary
When Shop redeems Player credits, Shop pays Player cash at the counter.
Player requests credit withdrawal
→ Shop/Cashier confirms
→ Debits Player credits, credits Shop (CDN wallet)
→ Shop pays cash to Player
→ Cash ledger (Cash Out) + reporting
Two required values when operating:
| Field | Meaning | Example |
|---|
| Credits to redeem | Credits debited from Player wallet | 30 credits |
| Cash paid to Player | Cash Shop pays directly to Player | $30.00 |
V1: usually 1:1 (30 credits → $30.00), but Shop enters both so the system clearly records credits recovered and cash paid. Ledger and reporting use cash paid to Player for Cash Out — not inferred from credits.
V1 scope: Redemption only. Auto-settled when completed; pending approval does not create cash tx.
Who does what
| Role | Action |
|---|
| Player | Requests withdrawal; present at counter |
| Shop Manager | Enters credits to redeem + cash paid to Player; confirms redeem from Shop portal |
| Cashier | Enters credits to redeem + cash paid to Player; pays cash after completed (or after Manager approval) |
| System | Debits player wallet, credits shop wallet, writes cash_transactions, updates shift Cash Out |
Two execution channels
| Channel | API | Notes |
|---|
| Shop portal | POST /shop/players/:id/credits (action: deduct) | Shift not required |
| Cashier portal | POST /cashier/players/:id/redeem | Active shift; cash only |
Both channels must collect credits to redeem and cash paid; after successful wallet transfer → create cash_transactions flow shop_player_redemption.
Phase 1 — Schema
| # | Task | Notes |
|---|
| 1.1 | Flow shop_player_redemption | If not shipped with Phase 2 migration |
| 1.2 | Optional metadata.approvalId | Link approval record (Cashier path) |
| 1.3 | Doc schema-cash-transactions | credit_amount ≠ cash_received when needed |
Phase 2 — Backend biz (redemption only)
| # | Task | Notes |
|---|
| 2.1 | createCounterRedemptionCashTransaction | seller=player, buyer=shop |
| 2.2 | Payload: credit_amount + cash_paid (cash_received) | Both required, > 0 |
| 2.3 | expected_cash = cash_paid | Cash Shop pays out |
| 2.4 | Hook after _adjustCreditsDeduct | Only when wallet transfer completed |
| 2.5 | Do not hook from pending_approval | Only after approve + transfer |
| 2.6 | Shop portal action: deduct | Validate + pass cash_paid to ledger |
| 2.7 | Cashier redeemCredits | Body adds cash_paid; cashDelta = −cash_paid |
| 2.8 | Wire after cashierRedemptionApprovalBizService approve | Approval uses saved cash_paid |
| 2.9 | Idempotency by reqId | Avoid duplicate cash tx |
| 2.10 | Unit tests | Happy path; insufficient balance; pending→approve; cash ≠ credit (if allowed) |
Main files: cashTransactionBizService.js, shopPlayerBizService.js, cashierTransactionBizService.js, cashierRedemptionApprovalBizService.js
Ledger (cash_transactions):
| Field | Value |
|---|
| flow | shop_player_redemption |
| seller | player / player uuid |
| buyer | shop / shop uuid |
| credit_amount | Credits to redeem |
| expected_cash / cash_received | Cash paid to Player |
| status | paid_full (auto on completed) |
Links: payment_transaction_id, wallet txs, cashier_transaction_id, shift_id, metadata.approvalId.
Phase 3 — Shop portal API (redemption filter)
| # | Task | Notes |
|---|
| 3.1 | GET /api/shop/cash-transactions?flow=shop_player_redemption | List redemptions |
| 3.2 | Detail shows credit + cash paid + approval context | |
| 3.3 | Summary: Cash Out (SUM cash_received), pending approval count | Aggregate full filter, not page only |
| 3.4 | Export CSV | Columns credit redeem + cash paid |
Extend shopCashSettlementController — currently only filters shop_player_deposit.
Phase 4 — Shop portal UI
| # | Task | Notes |
|---|
| 4.1 | Tab Payable — Redemptions | Mockup phase2 redeem tab |
| 4.2 | Player redeem modal: 2 input fields — credits to redeem + cash paid to Player | Pre-fill cash = credit (1:1); Shop may edit |
| 4.3 | Review step shows both credit and cash before confirm | |
| 4.4 | Summary card Cash Out (today / filter) | From ledger cash_received |
| 4.5 | Table: player, credits redeemed, cash paid, cashier/actor, status | Settled / Pending approval |
| 4.6 | Badge Pending approval vs Settled | |
| 4.7 | Combined dashboard: Cash In (P2) + Cash Out (P3) = Net | Optional after both tabs |
Phase 5 — Cashier / Shift
| # | Task | Notes |
|---|
| 5.1 | Cashier redeem form: credit + cash paid | Same as Shop portal |
| 5.2 | Redemption completed → shift Cash Out = cash_paid | Do not use credit amount for cashDelta |
| 5.3 | Pending approval: cashDelta = 0 | Cash not yet paid |
| 5.4 | Cashier tx list shows CTX-* after settle | Read-only; show cash paid |
| 5.5 | Integration test Expected Closing Cash | Cash Out matches total redemptions |
Phase 6 — Reporting
| # | Task | Notes |
|---|
| 6.1 | Shop portal Redemptions tab summary | Cash Out aggregate |
| 6.2 | Export CSV redemption | credit_amount, cash_received, player, date |
| 6.3 | Shift reconciliation report | Cash Out from redemption uses cash_paid |
| 6.4 | Admin / Shop cash-transaction detail | View-only; link payment + wallet txs |
Phase 7 — QA (redemption)
| # | Scenario | Expected |
|---|
| 7.1 | Shop redeem 30 credits, pay $30 cash | cash tx: credit=30, cash_received=30; wallet −30/+30 |
| 7.2 | Shop redeem 30 credits, pay $28 cash (if allowed) | cash tx cash_received=28; shift cashDelta −28 |
| 7.3 | Cashier redeem pending approval | No cash tx; cashDelta = 0 |
| 7.4 | Approve → complete | cash tx created once with saved cash_paid |
| 7.5 | Insufficient player balance | 400; no cash tx |
| 7.6 | Shop manual deduct missing cash_paid | 400 validation |
| 7.7 | Idempotent retry same reqId | Single cash tx only |
Implementation order
Phase 2 deposit done
→ Schema redemption (1)
→ Biz hook + cash_paid payload (2)
→ Shop API redemption filter (3)
→ UI: 2-field modal + Redemptions tab (4)
→ Cashier cash_paid + shift (5)
→ Reporting (6)
→ QA (7)
Current status (baseline)
| Component | Status |
|---|
| Wallet transfer player → shop | ✅ |
Cashier redeem + approval + cashDelta (1 field) | ✅ — needs cash_paid added |
| Shop portal manual deduct (1 field) | ✅ — needs cash_paid added |
cash_transactions flow shop_player_redemption | ❌ |
| Shop portal redemption ledger + Cash Out reporting | ❌ |