NullSect
Systems nominal

Docs / Banking / Installation

Installation — ns-banking 1.0.0

1. Requirements

ComponentVersionNotes
FXServerrecent build with Lua 5.4lua54 'yes'
oxmysql2.xrequired (@oxmysql/lib/MySQL.lua)
DatabaseMariaDB 10.3+ or MySQL 8.0+, InnoDBtested: 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
FrameworkQbox (qbx_core), QBCore (qb-core), ESX legacy 1.9+ (es_extended)auto-detected; standalone hooks otherwise
Optionalox_lib, ox_target or qb-targetnotifications / text UI / targeting; a built-in prompt is used without a target resource

2. Install

  1. Copy the ns-banking folder into your resources (e.g. resources/[nullsect]/ns-banking).
  2. 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
  1. 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)
  1. Walk to an ATM or a branch (default coordinates in data/branches.lua are examples: check them with /bank branches and adjust).

3. Database

  • Automatic (default): Config.Database.autoMigrate = true creates and upgrades all ns_bank_* tables on start. The database user needs CREATE, ALTER, INDEX on first start (and on upgrades).
  • Manual: set autoMigrate = false and import sql/ns-banking.sql once. It creates exactly the same schema as the migrations (checked by tests/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_mode contains NO_UNSIGNED_SUBTRACTION, the resource refuses money operations (err_not_ready) and logs why. Remove that flag from sql_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), plus ns_banking_meta (schema version, heartbeat, persisted toggles).
  • Upgrading 1.0.0 → 1.1.0 adds ns_bank_pins automatically; every character chooses a PIN on their next ATM use.
  • Price history is pruned to Config.Markets.historyRetentionDays; notifications to keepPerCharacter. The audit log is kept forever unless Config.Audit.retentionDays > 0.

4. Framework notes

FrameworkCharacter identityPersonal balance
QboxPlayerData.citizenidbank money
QBCorePlayerData.citizenidbank money
ESX legacyxPlayer.identifier (char-prefixed with multicharacter)bank account
StandaloneRockstar licenseConfig.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 DontAllowMinus lets bank go 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):

CommandNodePurpose
/bank statusns-banking.admin.statusframework, DB, config errors, review counts, markets, jobs
/bank branchesns-banking.admin.statusloaded branches and their state
/bank interestns-banking.admin.statusinterest interval, current period, job times
/bank account <iban|serverId>ns-banking.admin.inspectaccount, last transactions, members
/bank audit [count] [iban|reference]ns-banking.admin.inspectaudit trail
/bank adjust <iban|serverId> <+n|-n> <reason>ns-banking.admin.adjustauthorised adjustment (≤ maxAdjustment, audited, journaled)
/bank freeze <iban> / unfreezens-banking.admin.freezefreeze: no money in or out through ns-banking
/bank market status | pause <SYM|crypto|stocks|all> | resume … | event <id> [SYM]ns-banking.admin.marketmarkets
/bank review / /bank resolve …ns-banking.admin.recoverinterrupted operations / payouts (ARCHITECTURE.md §10)
/bank branch <id> on|off, /bank maintenance on|offns-banking.admin.maintenancelive 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

  1. Back up the database.
  2. Replace every file except config.lua, data/*.lua and locales/*.lua (compare them with the new version and copy new options over).
  3. Start: migrations run automatically; a released migration is never edited, new versions are appended.
  4. 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

SymptomCause / 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_SUBTRACTIONremove the flag from the server sql_mode (my.cnf) and restart
config: feature X disabled until this is fixeda critical config value is invalid; the line above names it
No ATM promptConfig.Features.Atm, Config.Atm.models; with ox_target / qb-target check that it is started before ns-banking
Two ATM promptsanother banking resource still registers ATM targets
"You are too far away" at a branchdata/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 startthe 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).

Cart

Your cart is empty.