Billing & Society
When a mechanic charges a customer (mod purchase, repair, custom invoice), money flows through two layers:
- Billing channel — how the bill reaches the player (tablet UI, ESX bill, qb-phone notification, instant deduction, etc.)
- Society deposit — where the paid money lands (mechanic's job/business account)
Both are auto-detected and configurable.
Billing Channels
Set Config.Tablet.invoices.billing.script to one of the values below.
| Script | Behavior | Best for |
|---|---|---|
internal | Built-in tablet flow. Player sees the invoice on their mechanic tablet and accepts/pays from there. | Self-contained setups, no external billing dependency |
esx_billing | Triggers esx_billing:sendBill. Pending bill appears in the player's ESX bill list. | ESX Legacy with esx_billing |
qs-billing | Calls exports['qs-billing']:CreateBilling. Pending bill UI from Quasar. | Quasar all-in-one stacks |
okok_billing | Modern okokBilling:CreateCustomInvoice (8 params), with legacy okokBilling:Bill fallback for older installs. | OKOK ecosystem |
qb-banking | Instant deduction from the player's bank account + bank statement log. Falls back to internal pending bill if the player has insufficient bank funds. | QBCore servers using qb-banking only (no phone billing) |
Renewed-Banking | Instant deduction + handleTransaction log. Same insufficient-funds fallback as qb-banking. | Renewed-Banking users |
qb-phone | Inserts directly into qb-phone's phone_invoices table and refreshes the player's phone. The player accepts/declines via phone. qb-phone handles payment + society deposit on its end. | QBCore servers using qb-phone for invoices |
custom | Triggers your own event (customEvent) or export (customExport). | Anything else (gksphone, lb-phone addon apps, etc.) |
Society Deposit (Auto-Detect)
When an invoice is paid, the money is automatically deposited into the mechanic's society / job account. The script scans for running banking resources and uses the first match in this priority order:
| # | Resource | Export called |
|---|---|---|
| 1 | Renewed-Banking | addAccountMoney(jobName, amount) |
| 2 | qb-banking | AddMoney(jobName, amount, reason) |
| 3 | qs-banking | AddMoney(jobName, amount, reason) |
| 4 | okokBanking | AddMoney(jobName, amount) |
| 5 | qb-management | AddMoney(jobName, amount) |
| 6 | esx_addonaccount | GetSharedAccount(society).addMoney(amount) (falls back to GetAccount for older forks) |
| 7 | tgiann-bank | AddJobMoney(jobName, amount) |
If none of the above are running, the deposit step is skipped silently and a debug line is logged. The bill itself still goes through normally — money just doesn't end up in a society account.
Society Naming
The society account name is always derived from the mechanic's job:
Mechanic's job (set in Mechanics Editor) | Resulting society |
|---|---|
mechanic | society_mechanic |
tuning | society_tuning |
lscustoms | society_lscustoms |
bennys | society_bennys |
This means:
- Two mechanic locations with the same
jobshare one society (correct ESX/QB pattern — society = job). - Two mechanics with different jobs get separate society accounts (each job = its own kasa).
There is no global override. If you want Mechanic A and Mechanic B to use separate kasalar, give them different jobs in the Mechanics Editor.
Common Setups
Recipes for typical server stacks. Set script and make sure the matching banking resource is running — the rest auto-wires.
QBCore + qb-phone (most common)
Config.Tablet.invoices.billing.script = 'qb-phone'
Player receives the invoice on their qb-phone, accepts/declines from the Bills app. qb-phone handles deposit into the mechanic job account.
QBCore + qb-banking only (no phone)
Config.Tablet.invoices.billing.script = 'qb-banking'
Money deducted instantly from the player's bank. If insufficient, falls back to internal tablet bill so the player can pay later. Society deposit goes into qb-banking job account.
Quasar all-in-one
Config.Tablet.invoices.billing.script = 'qs-billing'
qs-billing creates the pending bill, qs-banking auto-detected for society deposit.
ESX Legacy + esx_billing + esx_addonaccount
Config.Tablet.invoices.billing.script = 'esx_billing'
esx_billing:sendBill fires with the mechanic's society_<job>. esx_billing deposits to the matching addon account.
Renewed-Banking (any framework)
Config.Tablet.invoices.billing.script = 'Renewed-Banking'
Instant bank deduction + transaction log + society deposit to the job account.
Internal only (no external dependency)
Config.Tablet.invoices.billing.script = 'internal'
The mechanic tablet handles everything. Society deposit still attempts auto-detect — if a banking script is running, the money lands there; otherwise the deposit step is skipped (the bill still completes).
Migration from Older Versions
If you're updating from a version that used the old billing.esxSociety global override:
- The
esxSocietyandsocietyconfig fields are now deprecated no-ops. They are silently ignored. - The script now derives the society per-mechanic from each mechanic's
job. - If all your mechanics use the same
job(e.g.'mechanic') and your society is namedsociety_mechanic, no action needed — behavior is identical. - If you have mechanics with different jobs (e.g.
mechanic,tuning), each one now correctly deposits to its own society. Make sure those society accounts (society_tuning, etc.) exist in your banking system.
Troubleshooting
"Player paid but the money never showed up in the business account"
- Check server console for
[CODE9_MECHANIC] [INVOICE] No banking script detected— means none of the supported banking resources are running. - Check for
[INVOICE] <script> society deposit error: ...— your banking script's export errored, usually because thesociety_<job>account doesn't exist. Create it in your banking system. - Confirm your mechanic's
jobfield in the editor matches the actual job name registered in your framework.
"qb-phone bill never appears"
- Confirm
qb-phoneresource is running and thephone_invoicestable exists. - Confirm the target player has a registered qb-phone (PlayerData populated).
- Check for
[INVOICE] qb-phone error: ...lines in the console.
"okok_billing invoice has no author / wrong sender"
Resolved in the latest version — invoiceSource parameter is now passed as the server source ID (number) per okok's documented signature. Older builds passed the sender's display name (string), which some okok versions rejected.
"esx_addonaccount returns nil for my society"
The script now calls GetSharedAccount(name) first (the canonical ESX Legacy method) and falls back to GetAccount(name) for older forks. If both return nil, the society account simply doesn't exist — create it with:
INSERT INTO addon_account (name, label, shared) VALUES ('society_yourjob', 'Your Job', 1);
INSERT INTO addon_account_data (account_name, money) VALUES ('society_yourjob', 0);
