FiveM refund payouts with Discord webhooks and in-game bank logging
Build a refund workflow that’s fast for staff, clear for players, and auditable for admins.
Refund payouts are one of the fastest ways for a community to lose trust if they’re handled inconsistently. The fix isn’t “more rules”—it’s a workflow that produces evidence automatically: a Discord ticket trail, an approval step, a webhook log, and an in-game bank transaction record tied to a character and staff member. This post outlines a concrete setup you can implement on ESX or QBCore, using Discord webhooks for staff visibility and in-game bank logging for an immutable audit trail. If you already use a refund tool like LD Refund System, the same principles apply: treat every payout as a logged transaction with an approval chain.
Define a refund payout workflow (tickets, approvals, and evidence)
Start by defining what “done” looks like for a refund. A payout is complete only when (1) the request exists in a Discord ticket, (2) a staff member approves it, (3) the payout executes in-game, and (4) both Discord and the server store logs that can be reviewed later. This prevents “I paid them” disputes and stops staff from paying outside policy.
- Player opens a Discord ticket in #support (e.g., via Ticket Tool, Discord Tickets, or a custom bot).
- Bot requests required fields: in-game name, character ID/citizenid, amount, reason, and proof (clip, screenshot, crash log).
- Support role triages; Finance role (or Senior Staff) approves or denies in the ticket.
- If approved, staff triggers the payout in-game (command or UI) using the player’s identifier, not a nickname.
- Server writes a bank transaction entry (from “Refunds” account to player account) and stores metadata: ticket ID, approver, executor, timestamp.
- Discord webhook posts a structured log message to #refund-logs and optionally pings @Finance for high amounts.
- Ticket is closed automatically after the webhook confirms the payout log was sent (or after a staff checklist is completed).
Two Discord roles keep this clean: a Support role that can collect info and a Finance role that can approve payouts. In Discord, limit who can see refund logs and who can manage webhooks. In FiveM, limit who can execute payout commands via ACE permissions (or framework permissions) so you don’t rely on “trust me.”
Practical tip: enforce “two-person integrity” for larger refunds
Set an amount threshold (for example, $50,000 in-game) that requires a Finance approval reaction in the ticket before the payout command works. Your script can check a stored approval flag keyed to the Discord ticket ID. This reduces impulsive payouts and protects staff from pressure.
Discord webhooks: structure your refund logs for fast review
A webhook log is only useful if it’s searchable and consistent. Use a dedicated channel like #refund-logs, restrict it to Finance/Admin roles, and post one message per payout with the same fields every time. Avoid free-form paragraphs; staff should be able to scan amounts, identifiers, and who approved.
- Channel permissions: deny @everyone, allow View Channel for Finance/Admin, allow Send Messages only for the webhook (and optionally bots).
- Webhook security: only server owners/admins can Manage Webhooks; rotate the webhook URL if it leaks.
- Message format: include ticket ID, citizenid/char identifier, amount, reason, approver, executor, and a transaction reference ID.
- Mentions: use role mentions sparingly (e.g., ping @Finance only when amount exceeds a threshold).
- Retention: keep #refund-logs read-only and avoid staff chatter in it; use a separate #finance-chat for discussion.
Example webhook payload fields you want to see in the Discord message: Ticket: #1249, Player: citizenid=ABCD1234, Character: “M. Reyes”, Amount: 25000, Reason: “Vehicle deleted on restart”, Approved by: @FinanceLead, Executed by: @StaffName, TxnRef: REF-2026-01-23-00041. If you later need to audit, you can match TxnRef to your in-game bank log entry.
“”
In-game bank logging: make refunds traceable inside FiveM
Discord logs show intent and staff actions; in-game bank logs prove the money moved. On ESX and QBCore, you can implement a simple transaction ledger that records every credit/debit with metadata. Don’t rely on “addMoney” alone—wrap it in a function that always writes a transaction row and emits a server event for webhook logging.
For QBCore, you typically have citizenid and charinfo; for ESX you may have identifier and character data depending on your setup. Your refund function should accept a stable identifier (citizenid/identifier), not a player name. If the player is offline, use your framework’s offline money handling and still write the same transaction record.
- Transaction ID (server-generated, unique)
- Timestamp (server time)
- From account (e.g., "refund_pool" or "state_treasury")
- To account (player citizenid/identifier)
- Amount and currency type (cash/bank/crypto if applicable)
- Reason code (e.g., "vehicle_loss", "double_charge", "admin_error")
- Discord ticket ID and message link (if you store it)
- ApprovedBy (Discord user ID) and ExecutedBy (FiveM license/Discord ID)
If you run a shared “refund pool” account, treat it like a ledger-backed account: every refund debits the pool and credits the player. This makes it obvious if refunds exceed expected levels and helps you detect abuse (for example, repeated refunds to the same citizenid).
Practical tip: log both the framework event and the final balance
When you write a bank transaction row, also store the player’s resulting bank balance (or at least the delta and previous balance). If a staff member runs multiple payouts quickly, you can still reconstruct the exact sequence and catch accidental double payments.
Permissions and safeguards: reduce mistakes and prevent abuse
Refund automation fails when permissions are loose. Lock down who can approve, who can execute, and who can edit logs. In Discord, a webhook URL is effectively a password to your log channel—protect it. In FiveM, treat payout commands like admin money commands: whitelist roles and record who used them.
- Discord: Finance role can approve in tickets; only Admin can Manage Webhooks; Support can’t see #refund-logs if you want separation.
- FiveM ACE: create an ace like "refunds.execute" and grant it only to a staff group; deny it to everyone else by default.
- Rate limits: block more than N refunds per staff member per hour unless Finance overrides.
- Amount caps: require secondary approval above a threshold; auto-deny above a hard cap unless an Admin flag is present.
- Anti-tamper: write logs server-side only; don’t accept client-sent reason/amount without server validation.
Also decide how you’ll handle edge cases: player offline, character deleted, or refunds that must be item-based (e.g., returning a weapon or vehicle) instead of money. Your logging should support these by recording “refund type” and linking to the ticket evidence.
Implementation example: ticket ID to webhook to transaction reference
A reliable pattern is to generate a transaction reference first, then use it everywhere: in the in-game ledger row, in the Discord webhook message, and optionally in the ticket. That way, any staff member can search “REF-…” in Discord and immediately find the matching database entry.
Example flow with common tools: a player opens a ticket in #support; your bot tags it with a numeric ticket ID and stores the channel ID. Finance approves by clicking an “Approve” button (interaction) or reacting with ✅, and the bot stores the approver’s Discord user ID. The staff member runs /refund citizenid amount reason ticketId in-game. The server validates the staff ACE permission, checks approval status for that ticket ID, generates TxnRef, writes the bank transaction, applies the money change, then posts a webhook embed to #refund-logs including TxnRef and a link back to the ticket channel.
If you use an off-the-shelf workflow like LD Refund System, confirm it supports (or can be extended to support) the same three anchors: a stable player identifier, a transaction reference, and dual logging (Discord + in-game ledger). Those anchors matter more than any specific UI.
Audit and reporting: catch patterns early and keep players informed
Once logs exist, use them. A weekly audit doesn’t need to be complicated: export refund transactions, group by reason code, and review outliers. You’re looking for repeated refunds to the same citizenid, unusually high totals tied to one executor, or vague reasons like “bug” without evidence links.
- Pull the last 7 days of refund ledger rows from your database.
- Group by reason code and total amount; flag the top 5 categories.
- Group by executor (staff) and count payouts; flag unusual spikes.
- Sample 10 transactions and verify: ticket exists, approval present, webhook log present, TxnRef matches.
- Post a short staff-only summary in #finance-chat and update your refund policy if a pattern repeats.
For player communication, keep it simple: when a refund is approved, the ticket bot should reply with the TxnRef and the amount. If it’s denied, provide the reason and what evidence is missing. Consistent messaging reduces repeat tickets and prevents staff from negotiating payouts in DMs.
Need a smarter refund flow?
LD Refund System automates Discord approvals, in-game claims, and audit logging so your staff stay focused on players.