DEV Community

Tack k
Tack k

Posted on

I Merged 5 FiveM Scripts Into One Unified Billing System

After running my QBCore server for over two years, one thing became painfully clear: my police-related scripts had quietly turned into a mess.

Not because any single one of them was bad. Each script did its job. The problem was that they had no idea the others existed.

This is the story of how I merged 5 separate scripts into a single unified system called tack_billing — and what it taught me about building for the server, not just shipping features.


The Problem: 5 Scripts, 5 UIs, 5 Mental Models

Over the life of my server, I had built or adopted these scripts independently:

Script Role
gg_billing2 Issuing bills to players
gg_oushu Seizing (confiscating) items or cash
gg_police_notice Sending alerts between officers
gg_tack_billing A second billing flow (wanted-list related)
lokat_sharing_money Distributing rewards between officers

Individually, they all worked. Together, they created chaos.

A police officer catching a suspect had to:

  1. Open one UI to issue a bill
  2. Switch to another command to seize items
  3. Manually notify other officers in yet another script
  4. Remember a separate /command to split the reward

Four different interactions, four different mental models, and zero awareness between them. If you were new to the police role, you were basically learning four mini-games just to do your job.

As the server operator, I felt it too. Bug reports came in from one script but the root cause was in another. Database tables had overlapping concepts. Every new feature meant touching code in multiple places.

The scripts weren't a system. They were a pile.


The Vision: One UI, One Flow

I wanted the police officer's experience to feel like one tool, not five. That meant:

  • A single, clean UI that covered billing, seizure, reward distribution, and notifications
  • Consistent design language — not five visual styles slammed together
  • Features that knew about each other (issue a bill → split the reward → notify the right officers, all in one action)
  • An external hook: integration with our in-game phone so players could check their wanted status without guessing

That last point mattered more than it sounds. On an RP server, a wanted timer is part of the game loop. Hiding it behind a /command breaks immersion. It belongs on the phone.


Folding 5 Scripts Into 1

The merge wasn't a copy-paste job. Each script had its own database schema, its own event names, its own config patterns. I had to decide what to keep, what to rename, and what to throw away.

What I kept:

  • The core billing logic from gg_billing2 — it was battle-tested
  • The seizure flow from gg_oushu — unique and well-scoped
  • The distribution math from lokat_sharing_money — worked cleanly

What I rebuilt:

  • The UI. Completely. The old one had everything crammed onto a single screen with no hierarchy — buttons next to buttons next to more buttons. The new UI is tab-based, so each function has its own space and the officer isn't overwhelmed.
  • The notification layer. I wrote a new notification system from scratch and made it the connective tissue between every action. Issue a bill? All relevant officers get notified. Seize an item? Same. Wanted timer expires? Phone updates.

What I threw away:

  • Redundant DB tables (three scripts had their own "transactions" concept)
  • Duplicate helper functions scattered across all five scripts
  • The gg_tack_billing flow — its functionality got absorbed into the main billing tab

The final resource lives at tack_billing — one folder, one UI, one coherent system.


The Phone Integration

This is the feature I'm proudest of.

We use LB-phone on our server, and I wrote a custom app that talks to tack_billing directly. When a player has an active wanted status, the app shows them a live countdown — how many minutes until the wanted timer expires.

No more asking "am I still wanted?" in OOC chat. No more guessing. Just open the phone, check the app, plan your next move.

It's a small feature in terms of code. But it changed how players play the role. Suspects actually lay low until the timer runs out, because now they can see it. Cops feel the pressure lift at the right moment. The entire cat-and-mouse loop got tighter because information became legible.

That's the kind of change you only notice after you make it.


The Reward Distribution Flow

In the old world, splitting a police reward was a separate action entirely. An officer would issue a bill, then remember to run /split (or whatever the command was), then manually select who gets what.

In the new UI, the split happens on the same screen as the bill. You fill out the bill, check the officers who should share the reward, submit. One action, one UI, one record in the database. Done.

This is the kind of integration that's obvious in hindsight but only possible once the scripts stop being strangers to each other.


What Actually Mattered

Here's the part that might surprise you: the technical execution was the easy part.

Designing what the merged system should be — that was the hard work. Which features deserved to live? Which flows were essential vs. legacy? What should the UI hierarchy look like when you put all of this under one roof?

Those are judgment calls. They require knowing the server, the players, and the police role inside and out. No amount of clever code substitutes for that understanding.

Once the design was clear, building it was mostly execution. I barely hit any real technical blockers — a few minor UI bugs, nothing worth remembering.

That's the shift I keep noticing in 2026: the bottleneck has moved from implementation to integration design. Writing the code is cheap. Knowing what code to write — and how it should connect to everything else — is where the actual work lives now.


The Result

For the police role:

  • One UI instead of four
  • Faster workflow (bill + seize + split + notify in one place)
  • Less onboarding friction for new officers

For players:

  • Phone app shows wanted timer in real time
  • Cleaner, more readable notifications
  • The cat-and-mouse loop finally feels deliberate

For me as the operator:

  • One codebase to maintain instead of five
  • One database schema to reason about
  • When something breaks, I know where to look

The server's been running on tack_billing for a while now, and I can't imagine going back to the old scattered setup.


Takeaway

If you run a FiveM server and you've got more than a handful of custom scripts, ask yourself: do they know about each other?

If the answer is no, you might be sitting on a pile, not a system. Merging isn't always the answer — sometimes separate scripts is the right call. But when features share a role, share a user, and share a purpose, keeping them apart costs more than people realize.

One UI to rule them all, it turns out, is worth the refactor.


Tack k is a freelance full-stack engineer based in Tokyo, building web apps, PWAs, and FiveM scripts. Working alongside AI as a true team member.

Top comments (0)