Why I built my own
My QBCore server had a very specific need: mechanics needed to bill players for multiple repair items in a single invoice, with each item having a variable quantity. Something like "Engine repair × 3, Tire replacement × 4" — calculated on the fly, not pre-defined in config.
I also wanted job-based revenue splitting baked in — so when a bill gets paid, the money automatically goes to both the employee and the company society account at the right ratio.
I couldn't find an existing solution that did exactly what I wanted, so I built gg_billing2 from scratch using Claude Opus as my coding partner.
What gg_billing2 does
Quantity-aware billing. Players select items from a job-specific list and input quantities. The total is calculated live. No need to pre-define every price combination.
Job-based revenue split. Each job has a configurable company/personal share ratio. Payment is automatically distributed at the moment the bill is settled.
Config.JobShares = {
police = 0.5, -- 50% company, 50% personal
mechanic = 0.7, -- 70% company, 30% personal
ambulance = 0.5,
}
Due dates and overdue handling. Bills expire after a configurable number of days per job. You can choose to allow late payment, block it, or charge a late fee percentage.
Config.OverdueBehavior = 'late_fee'
Config.LateFee = {
type = 'percent',
value = 0.10, -- 10% late fee
toCompanyOnly = true
}
Repeat overdue notifications. Debtors get notified on a configurable interval while their bill remains unpaid and overdue. Also triggers on login.
Mass invoicing. Send the same bill to multiple players at once — handy for group fines after large RP events.
Dual banking support. Works with both okokBanking and qb-banking via a single config flag.
Config.useOkokBanking = true -- false = qb-banking
The trickiest part: split calculation
Revenue splitting sounds simple, but there's a subtle edge case: what if the config changes between when a bill is created and when it's paid? To handle this, gg_billing2 saves the job share at invoice creation time and uses that saved value when calculating the split — regardless of what the current config says.
-- Uses the share saved at invoice creation time
local jobShare = tonumber(invoice.companyShareUsed) or GetJobShare(jobName)
local companyAmount = math.floor(payBase * jobShare)
local personalAmount = payBase - companyAmount
xOwner.Functions.AddMoney('bank', personalAmount, 'billing-receive-personal')
AddCompanyMoney(jobName, companyAmount, 'billing-receive-company')
Built with AI, designed by a non-coder
I designed the entire system — the data model, UX flow, and edge cases — and implemented it using Claude Opus as my coding partner. I don't write Lua myself; my job is systems thinking and design.
This is Vol.1 of my FiveM Dev Diaries series, documenting 2+ years of custom scripts built for my QBCore server.
Next up: Vol.2 — A Police Reward Distribution System with PED-based interaction UI.
Questions about the implementation? Drop a comment — happy to share config snippets or dig into specifics.
Top comments (0)