Billing
Send bills (fines, service fees, player invoices), list and pay a player's open bills, through whichever billing script runs or ai-core's own bills.
Bills go through ai-core, never through a billing script's own exports or events. ai-core sends each bill to the active billing script: the one the panel's Billing script setting picks, or the first one running. Without one, ai-core's own bills take it: the player sees a list of open bills on screen and the money moves when they pay. A bill never goes nowhere.
A bill comes from a job (the job's bank account gets the payment: police fines, a mechanic's fee) or from a player (that player gets the payment).
ai-core's own bills in short: open bills are stored per character (offline characters too), at most 20 open bills per character, and paying takes the money from bank, then cash, in one transaction with the bill's removal.
GetBillingSystem
Where bills go.
Side: Server · Waits: no · Lib: billing.provider()
GetBillingSystem(): string | nulllocal system = exports['ai-core']:GetBillingSystem() -- 'ai-core' or a script nameParameters
None.
Returns
string | null: the billing script's resource name, or "ai-core" for ai-core's own bills. Since ai-core's own bills are always available, this is not null in practice.
Example
import { resourceExport } from "@aicore-fivem/lib/server";
const core = resourceExport("ai-core");
/** Bills to offline characters only work with ai-core's own bills. */
export function canBillOffline(): boolean {
return core.GetBillingSystem() === "ai-core";
}SendBill
Sends a bill to a player.
Side: Server · Waits: no · Lib: billing.send
SendBill(bill: BillRequest): booleanlocal sent = exports['ai-core']:SendBill({ to = target, amount = 250, label = 'Speeding', from = source, job = 'police' })Parameters
Prop
Type
BillRequest
Prop
Type
See BillRequest.
Returns
boolean: true when the bill was sent. false when:
- the bill is invalid: both or neither of
to/toCitizenid, nolabel,amountnot a whole number from 1 to 10,000,000, neitherjobnorfrom, orfromequal to the recipient; fromis given but has no loaded character, orjobdoes not exist;- the recipient is offline and bills go to a billing script (only ai-core's own bills reach offline characters);
- the billing script refused it, or the recipient already has 20 open bills with ai-core's own bills.
An offline bill with ai-core's own bills answers true once it is valid; the database may still refuse it (unknown citizen id, 20 open bills), which is logged.
Example
import { onClientCallback, coordsOf, isNear, resourceExport } from "@aicore-fivem/lib/server";
import { fail, readId, readInt } from "@aicore-fivem/lib/shared";
import { Locale } from "common";
const core = resourceExport("ai-core");
// An officer fines the player in front of them; the police account gets the money.
onClientCallback(
"myscript:fine",
{
args: ([target, amount]) => {
const t = readId(target);
const a = readInt(amount, 1, 50_000);
return t === null || a === null ? null : [t, a];
},
},
(source, target, amount) => {
if (core.GetDutyJob(source) !== "police") return fail("NOT_POLICE");
if (!isNear(target, coordsOf(source), 3)) return fail("TOO_FAR");
const sent = core.SendBill({ to: target, amount, label: Locale("BILL_FINE"), from: source, job: "police" });
return sent ? { ok: true } : fail("BILL_FAILED");
},
);Never take money from the target yourself as a "forced bill" (TransferMoney from their account): a bill is the player's choice to pay.
GetUnpaidBills
A player's open (unpaid) bills.
Side: Server · Waits: no (answers through done) · Lib: billing.unpaid(source) returns a Promise
GetUnpaidBills(source: number, done: (bills: UnpaidBill[] | null) => void): voidexports['ai-core']:GetUnpaidBills(source, function(bills) end) -- list or nilParameters
Prop
Type
Returns
Nothing. done gets one entry per open bill with id (the billing script's id), label, amount (what is still owed) and sender (job label or player name) (UnpaidBill), or null when the player has no loaded character or the billing script cannot list bills. Without a function as done the call does nothing.
Example
import { onClientCallback, resourceExport } from "@aicore-fivem/lib/server";
import { fail } from "@aicore-fivem/lib/shared";
import type { UnpaidBill } from "@aicore-fivem/shared";
const core = resourceExport("ai-core");
// A city hall counter shows what the player owes.
onClientCallback("myscript:myBills", async (source) => {
const bills = await new Promise<UnpaidBill[] | null>((resolve) => core.GetUnpaidBills(source, resolve));
if (!bills) return fail("BILLS_UNAVAILABLE");
return { ok: true, bills, total: bills.reduce((sum, b) => sum + b.amount, 0) };
});PayBill
The player pays one of their open bills.
Side: Server · Waits: no · Lib: billing.pay
PayBill(source: number, id: string): booleanlocal paid = exports['ai-core']:PayBill(source, billId)Parameters
Prop
Type
Returns
boolean: true when the bill was paid. false when the player has no loaded character, the bill is not theirs or gone, they cannot pay it, the billing script refused or only takes payments in its own screen. With ai-core's own bills a personal bill also needs the issuer online (the money goes to their bank).
Example
import { onClientCallback, resourceExport } from "@aicore-fivem/lib/server";
import { fail, readText } from "@aicore-fivem/lib/shared";
const core = resourceExport("ai-core");
onClientCallback(
"myscript:payBill",
{ args: ([id]) => { const v = readText(id, 64); return v ? [v] : null; }, lock: true },
(source, id) => (core.PayBill(source, id) ? { ok: true } : fail("BILL_NOT_PAID")),
);