Tips history¶
Purpose¶
The Tips screen has two faces:
- Player → player tips — peer-to-peer in-chat micro-transfers. The admin view here is read-only audit (
/admin/tips). - Admin → player tips — operator-funded grants (refunds, goodwill). These are SuperAdmin-only and require OTP. They show up under the same
/tipsUI but live behind separate endpoints.
Both share an audit log and both pass through Transaction rows for accounting.
Audience¶
Customer support (admin tips for compensation), risk (P2P abuse review), finance (admin-tip pool reconciliation).
Path in admin-fe¶
| Screen | URL | Page |
|---|---|---|
| Tips list | /tips |
ebit-admin-fe/src/app/(dashboard)/tips/page.tsx |
| Tip detail | /tips/[id] |
ebit-admin-fe/src/app/(dashboard)/tips/[id]/page.tsx |
The "Tip user" / "Deduct balance" buttons live in user-profile.md (drawer action, not on the Tips list itself).
Backing API endpoints¶
| Endpoint | Source |
|---|---|
GET /admin/tips (P2P tips audit) |
apps/api/src/tips/admin.tips.controller.ts:15 |
GET /admin/admin-tips (admin-funded tips list) |
apps/api/src/admin-tips/admin.admin-tips.controller.ts:48 |
POST /admin/admin-tips (tip a user, SuperAdmin) |
apps/api/src/admin-tips/admin.admin-tips.controller.ts:28 |
POST /admin/admin-tips/deduct-balance (negative tip, SuperAdmin) |
apps/api/src/admin-tips/admin.admin-tips.controller.ts:38 |
Frontend wiring: ebit-admin-fe/src/queries/tips/.
Key actions¶
| Action | Required permission / role | API call | DB tables touched | Audit-logged? |
|---|---|---|---|---|
| List P2P tips | tip.view |
GET /admin/tips |
Tip, User |
yes |
| List admin tips | admin-tips.view |
GET /admin/admin-tips |
AdminTip, User |
yes |
| Tip a user (admin-funded) | admin-tips.tip |
POST /admin/admin-tips |
AdminTip, Account, Transaction |
yes |
| Deduct from a user (admin-funded) | admin-tips.deduct-balance |
POST /admin/admin-tips/deduct-balance |
AdminTip (negative), Account, Transaction |
yes |
Filters and views¶
- Type — P2P (player→player) or Admin (operator→player).
- From / To — userId of the sender or receiver.
- Date range.
- Amount range (USD-equiv).
- Reason — free text on admin tips; filterable as substring match.
Common workflows¶
- Compensate a player after a known incident. From user-profile.md, click "Tip user". Modal opens. Fill amount + reason ("post-incident credit per ticket #1234"). OTP. Backend writes
AdminTip, increments player balance, writesTransaction. Cross-link the Zendesk ticket via the reason field. - Reverse an erroneous tip / deposit. Compliance discovers a stuck deposit was credited twice. They use
POST /admin/admin-tips/deduct-balancewith negative amount (semantically a deduct). Player's balance decreases. Both rows visible on this screen withTransactionchain. - Investigate suspicious P2P pattern. Risk filters the Tips list by sender/receiver, looks for circular flows (bonus-arbitrage). Cross-references with bets-history.md and withdrawals.md.
- Monthly admin-tip pool reconciliation. Finance filters admin tips by date range, sums by currency, cross-checks against the configured
Vaultsource for the admin-tip pool. Discrepancy → escalate. - Player asks "who sent me this tip"? Open
/tips/[id], see sender User reference. P2P tips show sender + receiver; admin tips show admin operator + receiver.
Edge cases / gotchas¶
- P2P tips have a chat-rate-limit. Spam-detection is on the chat side, not here. Anomalies on this screen often mean the chat limiter failed.
- Admin tips bypass deposit limits. Granting via admin-tips lets you exceed a player's gambling-limit-style caps; finance must approve. SuperAdmin-only enforcement is the safety net.
deduct-balancecan drive a balance negative. Backend allows it (operations sometimes need to). The player can't bet from a negative balance, but they can deposit to recover.- Reason text is stored verbatim. Don't put PII or external IDs that could leak — admin logs are reviewed by ops, not all admins.
- Tip transactions are NOT undoable. There is no "cancel admin tip" button. To reverse, issue an opposing admin tip or deduct.
- Batch tips are not supported. Each
POST /admin/admin-tipsis one user. For broad makegoods (thousands of users), engineering runs a script.
Cross-links¶
- Action launcher: user-profile.md → Tip user / Deduct balance buttons
- Permission catalog:
libs/auth/src/permissions/const.ts:156-167(admin-tips.* keys) - Reconciliation context: dashboard.md → finance tab
- Audit: admin-logs.md
- Incident response (mass-tip after outage):
handover/oncall-runbook.md
Sequence — admin-funded tip¶
sequenceDiagram
actor sa as SuperAdmin
participant admin-fe
participant api
participant pg as Postgres
sa->>admin-fe: open /users/[id], click "Tip user", amount=50 USD, reason="..."
admin-fe->>api: POST /admin/admin-tips { userId, amount, currency, reason }, x-otp
api->>api: PermissionGuard('admin-tips.tip') + OtpGuard
api->>pg: BEGIN
api->>pg: INSERT Transaction (FROM admin-tip-pool TO user, type=ADMIN_TIP)
api->>pg: UPDATE Account SET balance += amount WHERE userId
api->>pg: INSERT AdminTip (adminUserId, userId, amount, reason)
api->>pg: INSERT AdminActionLog
api->>pg: COMMIT
api-->>admin-fe: 200 OK with AdminTip DTO
admin-fe-->>sa: confirmation toast