Spec — Admin debit / correction for shop, agent, super agent (system errors & adjustments)
1. Purpose (correct business intent)
When the system books incorrectly (bug, duplicate job, duplicate callback), reconciliation finds a mismatch, or an approved ops / legal decision requires clawing back / debiting funds already credited to a shop, agent, or super agent, you need a controlled process: who may act, on which wallet type, with reason and audit trail.
This document does not describe “admin creates an external USD payout on behalf of the user” (normal partner withdrawal). That flow is portal-initiated + approval; see the hierarchy approval guide (admin-hierarchy-usd-and-credit-withdrawal — doc pending).
2. Two balance types to keep separate
| Type | Meaning | Where “debit for a system error” usually lands |
|---|---|---|
| USD wallet | USD online balance (usd_wallets / UsdWalletTransaction) | Needs an adjustment ledger or controlled withdrawal / reversal with reason metadata — today there is no dedicated admin “USD adjustment” API for shop/agent/super_agent in usdWalletManagementController (only approve/reject pending withdrawals). |
| CDN credits | Game credit wallet (wallets by ownerType) | Partially supported today; gaps to fill (section 4). |
Every correction must state explicitly whether you are fixing USD or credits so the wrong bucket is never debited.
3. General principles (before shipping a feature)
- Ticket / incident: Ticket id (Jira/Linear…), bug description, monetary impact, internal approver.
- Four-eyes (recommended): Proposer of the debit ≠ approver (especially for large amounts).
- Idempotency: Each debit carries an
idempotencyKeyso retries do not double-post. - Audit: Admin action logs (who, owner, amount, reason, key, ledger transaction ids).
- No raw DB edits on production except emergencies with their own runbook — the goal is to move work into a controlled API + UI.
- Partners: If a debit affects contracts, get legal / CS sign-off first.
4. Current codebase baseline (for implementation)
4.1 Shop — debit CDN credits (exists)
- API:
POST /api/admin/agents/:agentId/shops/:shop_id/credit-adjust - Body:
direction: "debit",amount(positive) → debits shop credits (walletTransferService.transfershop →null, debit ledger),referenceType: admin_shop_adjustment. - Permission:
agentManagement(routecdnAgents).
Fit: Shop credit corrections for over-credits or controlled internal adjustments.
4.2 Shop — debit USD wallet (no standard admin button yet)
UsdWalletTransaction.typeincludesadjustment, butusdWalletServicecurrently has no publicrecordAdjustmentparallel to deposit/withdrawal — needs implementation (or a controlled reversal+deposit pair — engineering must pick one reporting story).
4.3 Agent — debit CDN credits (not symmetric with shop)
cash-fundingexists (adds credits); no agentcredit-adjustroute was found in admin routes reviewed.
4.4 Super agent — debit CDN credits (exists but wrong semantics for “bug”)
POST /api/admin/super-agents/:id/cash-withdrawal: debits credits + writesPaymentTransactionwithdrawaladmin_cash— designed for physical cash paid out, not an “INC-ticket over-credit bug”. Using it for pure system corrections is suboptimal for books (“cash withdrawn” vs “system correction”).
4.5 Player — debit CDN credits (clawback) (implemented)
POST /api/admin/finance-correctionswithownerType: "player",walletKind: "cdn_credit",direction: "debit": debits the player wallet viawalletTransferService.transfer(playerWallet, null, …)— credits are recalled to the platform, not credited to the player’s shop (unlike shop redeem).- Available balance guard: debit amount must be ≤
balance - reservedBalance(reserved funds cannot be clawed back through this path). - USD: rejected — players do not have an
usd_wallet. - Terminated players: blocked (same as shop/agent).
- Admin UI: “Debit correction” on Player details (
UserDetailPage); modal shows Available (CDN credits) before apply. - Legacy:
POST /payment/admin/users/:userId/adjust-balanceremains for flagged-withdrawal workflows; prefer finance-corrections for player clawback (ticket + idempotency + unified audit).
5. Proposed product direction (Admin UI)
A single “Balance correction (debit)” screen or modal on Shop / Agent / Super agent / Player detail:
- Wallet kind:
USD|CDN credits - Direction: debit only initially (later: optional credit top-up)
- Amount (USD or credits)
- Reason (required, minimum length)
- Ticket id (required)
- Idempotency key (client-generated)
- (Optional) Password / OTP second step for super admin or system admin
Display: Balance before/after, ledger preview, warning if owner is terminated.
6. Proposed technical direction (API)
6.1 Single family endpoint (implemented)
POST /api/admin/finance-corrections (mounted in backend bootstrap; route module routes/admin/financeCorrections.js).
Example body:
{
"ownerType": "shop | agent | super_agent | player",
"ownerId": "...",
"walletKind": "usd | cdn_credit",
"direction": "debit",
"amount": "123.45",
"reason": "INC-12345: duplicate deposit callback",
"ticketId": "INC-12345",
"idempotencyKey": "uuid-v4"
}
- Shop + cdn_credit + debit:
financeCorrectionBizServicedelegates toadminAgentManagementBizService.applyShopWalletAdjust(same transfer rules as legacy shop adjust). - Agent / super_agent + cdn_credit:
walletTransferService.transfertonullwithreferenceTypeadmin_agent_adjustment/admin_super_agent_adjustment. - Player + cdn_credit:
walletTransferService.transfertonullwithreferenceTypeadmin_finance_correction; available-balance check before transfer; audittargetType: user. - usd:
usdWalletService.recordAdjustment—type: adjustment,status: completed, grossUsd = netUsd for a flat correction debit; metadata carries ticket/reason/idempotency key.
6.2 Permissions
Only super admin and system admin may call the API / use the correction-debit UI. Middleware: requireSuperAdmin or the project’s equivalent that distinguishes these roles; do not grant this to day-to-day ops admins or fold it into agentManagement.
6.3 Admin UI (implemented)
- kioskgaming_admin: “Debit correction” / “Balance correction” modal on shop, agent, super agent, and player detail pages — only when the logged-in admin role is
super_adminorsystem_admin(matches APIrequireSuperAdmin). Player modal: CDN credits only, displays available balance, burn semantics (not shop redeem).
7. Minimum QA
- Correct owner / wallet kind.
- Balance does not go negative (or document policy if negatives are allowed).
- Idempotent behavior.
- Audit log fields complete.
- Shop debits via legacy API vs new API (if any) never double-apply the same key.
- Player debit ≤ available; shop wallet unchanged after player clawback.
8. Related docs
- Hierarchy — Who approves withdrawals (
admin-hierarchy-usd-and-credit-withdrawal— doc pending) - Admin USD Wallet Transaction Detail View
Status: Implemented — unified API POST /api/admin/finance-corrections (shop, agent, super_agent, player), usdWalletService.recordAdjustment, admin UI modal (super/system admin only), and AdminActionLog value finance_balance_correction (targetType includes super_agent and user for players). Legacy POST /api/admin/agents/:agentId/shops/:shop_id/credit-adjust remains; optional idempotencyKey on that body maps to a stable ledger id when present.