Installation — ns-banking 1.0.0
1. Requirements
| Component | Version | Notes |
|---|---|---|
| FXServer | recent build with Lua 5.4 | lua54 'yes' |
| oxmysql | 2.x | required (@oxmysql/lib/MySQL.lua) |
| Database | MariaDB 10.3+ or MySQL 8.0+, InnoDB | tested: MariaDB 10.11 (strict and non-strict sql_mode). MySQL is expected to behave the same; the start-up self test checks it on your server |
| Framework | Qbox (qbx_core), QBCore (qb-core), ESX legacy 1.9+ (es_extended) | auto-detected; standalone hooks otherwise |
| Optional | ox_lib, ox_target or qb-target | notifications / text UI / targeting; a built-in prompt is used without a target resource |
2. Install
- Copy the
ns-bankingfolder into your resources (e.g.resources/[nullsect]/ns-banking). server.cfg— load order matters (framework and oxmysql first):
ensure oxmysql
ensure ox_lib # optional
ensure qbx_core # or qb-core / es_extended
ensure ox_target # optional (or qb-target)
ensure ns-banking
# permissions (see §5)
add_ace group.admin ns-banking.admin allow
- Start the server. The console must show:
[ns-banking:SV] [INFO] Database migrated to schema v1.
[ns-banking:SV] [INFO] config ok (0 warning(s))
[ns-banking:SV] [INFO] ready (framework=qbox, schema=v1, core=1.0.0)
- Walk to an ATM or a branch (default coordinates in
data/branches.luaare examples: check them with/bank branchesand adjust).
3. Database
- Automatic (default):
Config.Database.autoMigrate = truecreates and upgrades allns_bank_*tables on start. The database user needsCREATE,ALTER,INDEXon first start (and on upgrades). - Manual: set
autoMigrate = falseand importsql/ns-banking.sqlonce. It creates exactly the same schema as the migrations (checked bytests/realdb.py) and never drops anything. - Self test: on every start ns-banking checks that (1) unsigned subtraction below zero raises an error and
(2) a failing statement rolls back its transaction. Every balance guard depends on both. If your
sql_modecontainsNO_UNSIGNED_SUBTRACTION, the resource refuses money operations (err_not_ready) and logs why. Remove that flag fromsql_mode. - Tables:
ns_bank_accounts,_account_members,_operations,_transactions,_payouts,_interest_runs,_term_deposits,_assets,_price_history,_holdings,_trades,_dividend_runs,_audit,_notifications,_profiles,_pins(schema v2, 1.1.0), plusns_banking_meta(schema version, heartbeat, persisted toggles). - Upgrading 1.0.0 → 1.1.0 adds
ns_bank_pinsautomatically; every character chooses a PIN on their next ATM use. - Price history is pruned to
Config.Markets.historyRetentionDays; notifications tokeepPerCharacter. The audit log is kept forever unlessConfig.Audit.retentionDays > 0.
4. Framework notes
| Framework | Character identity | Personal balance |
|---|---|---|
| Qbox | PlayerData.citizenid | bank money |
| QBCore | PlayerData.citizenid | bank money |
| ESX legacy | xPlayer.identifier (char-prefixed with multicharacter) | bank account |
| Standalone | Rockstar license | Config.Standalone hooks (addMoney, removeMoney, getMoney, isAdmin) |
- The personal (current) account is the framework bank money: paychecks, shops and other resources keep working,
and their changes show up in the balance. To show them in the ns-banking history too, call
exports['ns-banking']:LogExternal(src, amount, label)from those resources (optional). - QBCore: its default
DontAllowMinusletsbankgo negative. ns-banking re-checks the balance right before every debit (no yield in between), so it can never overdraw through the bank. - Other banking resources (qb-banking, okokBanking, Renewed-Banking …): stop their ATM / bank targets, otherwise players see two prompts. Their own society / shared tables are not imported automatically.
- Character switch: the character identifier is re-checked on every request. A switch closes the session even when the framework sends no unload event.
5. Permissions and admin commands
add_ace group.admin ns-banking.admin allow grants everything. Granular nodes (Config.Admin.permissions):
| Command | Node | Purpose |
|---|---|---|
/bank status | ns-banking.admin.status | framework, DB, config errors, review counts, markets, jobs |
/bank branches | ns-banking.admin.status | loaded branches and their state |
/bank interest | ns-banking.admin.status | interest interval, current period, job times |
/bank account <iban|serverId> | ns-banking.admin.inspect | account, last transactions, members |
/bank audit [count] [iban|reference] | ns-banking.admin.inspect | audit trail |
/bank adjust <iban|serverId> <+n|-n> <reason> | ns-banking.admin.adjust | authorised adjustment (≤ maxAdjustment, audited, journaled) |
/bank freeze <iban> / unfreeze | ns-banking.admin.freeze | freeze: no money in or out through ns-banking |
/bank market status | pause <SYM|crypto|stocks|all> | resume … | event <id> [SYM] | ns-banking.admin.market | markets |
/bank review / /bank resolve … | ns-banking.admin.recover | interrupted operations / payouts (ARCHITECTURE.md §10) |
/bank branch <id> on|off, /bank maintenance on|off | ns-banking.admin.maintenance | live toggles (persisted) |
add_ace group.moderator ns-banking.admin.status allow
add_ace group.moderator ns-banking.admin.inspect allow
Config.Admin.useFrameworkAdmin = true also accepts framework admins (QBCore admin/god, ESX admin/superadmin,
Qbox admin permission). The server console can run every command. Denied attempts are flagged and audited.
6. Integrating other resources
Money exports only work for resources listed in Config.Exports.trustedResources; every call is journaled and audited.
-- police: fine paid into the police account
local ok, ref = exports['ns-banking']:ChargePlayer(target, 500, 'Speeding', 'police')
-- business payroll / sales
exports['ns-banking']:AddBusinessMoney('mechanic', 1200, 'Repair invoice #42')
local ok, err = exports['ns-banking']:RemoveBusinessMoney('mechanic', 300, 'Parts')
-- phone banking app (player-initiated transfer with the same rules as the bank)
local ok, refOrErr = exports['ns-banking']:Transfer(src, 'NS42058311772', 1000, 'Rent')
-- read-only (any resource)
local iban = exports['ns-banking']:GetIban(src)
local info = exports['ns-banking']:GetAccount(iban) -- { iban, type, name, status }
Security hooks: AddEventHandler('ns-banking:hook:securityFlag', function(src, reason, detail) end) (server).
7. Upgrading
- Back up the database.
- Replace every file except
config.lua,data/*.luaandlocales/*.lua(compare them with the new version and copy new options over). - Start: migrations run automatically; a released migration is never edited, new versions are appended.
- Never import an older
sql/file over a newer schema.
8. Tests
Offline (no server, no database) — from the [nullsect] folder with pip install lupa:
python ns-core/tools/run-tests.py ns-banking # run_shared, run_server (FakeDB), run_client
python ns-core/tools/ns-check.py ns-banking # syntax, manifest, core, locales, rules, docs, tests
Against a real MariaDB / MySQL (pip install lupa pymysql; the database is dropped and re-created, its name must
start with ns_bank_test):
python ns-banking/tests/realdb.py --host 127.0.0.1 --port 3306 --user root --password secret --database ns_bank_test
This runs tests/real_db.lua (SQL guards, real concurrency, recovery), tests/run_server.lua on the real
server/database.lua, and checks that sql/ns-banking.sql creates the migrated schema.
9. Troubleshooting
| Symptom | Cause / fix |
|---|---|
| Every request answers "The bank is starting" | DB not ready: look for Database self test failed or Migration v1 failed in the console |
sql_mode contains NO_UNSIGNED_SUBTRACTION | remove the flag from the server sql_mode (my.cnf) and restart |
config: feature X disabled until this is fixed | a critical config value is invalid; the line above names it |
| No ATM prompt | Config.Features.Atm, Config.Atm.models; with ox_target / qb-target check that it is started before ns-banking |
| Two ATM prompts | another banking resource still registers ATM targets |
| "You are too far away" at a branch | data/branches.lua coordinates / radius do not match your map; /bank branches |
| Transfer to an offline player "not received" | it is credited when they load a character (/bank account <iban> shows the pending payout) |
N interrupted operation(s) ... /bank review on start | the server stopped between a framework debit and the commit: follow ARCHITECTURE.md §10 |
| Stocks always "closed" | Markets.stocks.hours uses the server clock (os.date) |
10. Verified vs not verified
Verified offline / on MariaDB 10.11 (see ARCHITECTURE.md §12): all server logic through the real router, SQL guards including concurrent transactions, recovery, the client NUI bridge, and the NUI in a Chromium browser (EN/TR, 1920×1080 and 1280×720).
Not verified yet (needs a live server): a real FXServer with OneSync, the Qbox / QBCore / ESX adapters against the real frameworks, ox_target / qb-target model targets on real ATM props, the in-game CEF rendering, and oxmysql's behaviour on your exact database version (the self test covers the critical part on start).