Skip to main content

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

TypeMeaningWhere “debit for a system error” usually lands
USD walletUSD 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 creditsGame 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)

  1. Ticket / incident: Ticket id (Jira/Linear…), bug description, monetary impact, internal approver.
  2. Four-eyes (recommended): Proposer of the debit ≠ approver (especially for large amounts).
  3. Idempotency: Each debit carries an idempotencyKey so retries do not double-post.
  4. Audit: Admin action logs (who, owner, amount, reason, key, ledger transaction ids).
  5. No raw DB edits on production except emergencies with their own runbook — the goal is to move work into a controlled API + UI.
  6. 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.transfer shop → null, debit ledger), referenceType: admin_shop_adjustment.
  • Permission: agentManagement (route cdnAgents).

Fit: Shop credit corrections for over-credits or controlled internal adjustments.

4.2 Shop — debit USD wallet (no standard admin button yet)

  • UsdWalletTransaction.type includes adjustment, but usdWalletService currently has no public recordAdjustment parallel 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-funding exists (adds credits); no agent credit-adjust route 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 + writes PaymentTransaction withdrawal admin_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-corrections with ownerType: "player", walletKind: "cdn_credit", direction: "debit": debits the player wallet via walletTransferService.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-balance remains 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: financeCorrectionBizService delegates to adminAgentManagementBizService.applyShopWalletAdjust (same transfer rules as legacy shop adjust).
  • Agent / super_agent + cdn_credit: walletTransferService.transfer to null with referenceType admin_agent_adjustment / admin_super_agent_adjustment.
  • Player + cdn_credit: walletTransferService.transfer to null with referenceType admin_finance_correction; available-balance check before transfer; audit targetType: user.
  • usd: usdWalletService.recordAdjustmenttype: 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_admin or system_admin (matches API requireSuperAdmin). 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.


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.