> For the complete documentation index, see [llms.txt](https://realtime-dev.gitbook.io/realtime-dev-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://realtime-dev.gitbook.io/realtime-dev-docs/realtime-scripts/rt-bank.md).

# 🏦 RT BANK

Premium banking system for QBX and QB-Core. RT Bank is the **source of truth** for balances; the player's primary account is mirrored to framework `money.bank` for compatibility with existing scripts.

> ESX is not supported. Future support is not guaranteed.

> **Phone app:** currently integrated with **lb-phone** only. Other phone resources are **not** supported and we do **not** guarantee adapters for them.

***

### 📋 Requirements

* FXServer build 17000+
* OneSync enabled
* MySQL + `oxmysql`
* `ox_lib`
* `ox_target`
* `ox_inventory`
* `qbx_core` or `qb-core`
* Optional: **lb-phone** (custom bank app, OTP mail, bank notifications)

***

### 📦 Installation

**Step 1 — Upload**

```
resources/[banking]/rt-bank
```

(or any folder you use for banking / RT scripts)

**Step 2 — Items (ox\_inventory)**

Merge the entries from `items/rt_bank_items.lua` into `ox_inventory/data/items.lua`.

Copy the PNGs into `ox_inventory/web/images/`:

| File                        | Item                     |
| --------------------------- | ------------------------ |
| `rt_bank_card.png`          | Bank card                |
| `rt_bank_receipt.png`       | ATM receipt              |
| `rt_bank_loan_contract.png` | Loan contract            |
| `rt_bank_deposit_cert.png`  | Term deposit certificate |

> See `items/IMAGES.md` if you need naming reminders. Without images, slots work but look empty.

**Step 3 — Security (required)**

Edit `config/config.lua`:

```lua
Config.Locale = 'en' -- en | es | fr | de | it | pt-BR

Config.Security = {
    --- MUST be unique on YOUR server (16+ characters).
    --- The shipped value blocks money ops until you change it.
    PinPepper = 'CHANGE_ME_UNIQUE_PEPPER',
    AllowDefaultPepper = false,
    -- ...
}
```

Replace `PinPepper` with a long random secret. Changing it later invalidates existing PIN hashes — players must reissue / change PIN.

QB-Core only — if the framework is not detected:

```lua
Config.Framework = 'qb-core'
```

**Step 4 — server.cfg**

Start RT Bank **after** oxmysql, ox\_lib, ox\_inventory, framework, and (if you use it) lb-phone.

QBX example:

```cfg
ensure ox_lib
ensure oxmysql
ensure qbx_core
ensure ox_inventory
ensure ox_target
ensure lb-phone
ensure rt-bank
```

QB-Core example:

```cfg
ensure ox_lib
ensure oxmysql
ensure qb-core
ensure ox_inventory
ensure ox_target
ensure lb-phone
ensure rt-bank
```

**Step 5 — Restart**

Database tables / migrations are applied automatically on start. No manual SQL is required for a normal install (`sql/` is included for reference).

***

### ✅ Quick check

1. Join with a loaded character — primary checking account + debit card should be created (PIN shown once).
2. `/bank` or use a branch ped / ATM — UI opens.
3. F8 console — look for `[rt-bank] RT Bank ready` (or a **BLOCKED** PinPepper message if you left `CHANGE_ME`).
4. If lb-phone is installed — open the phone → **RT Bank** app appears (Wallet can be hidden — see phone section).

UI not updating after a rebuild? Clear the NUI cache for this resource under\
`%localappdata%\FiveM\FiveM.app\data\cache\files\rt-bank`.

***

### 📱 Phone app (lb-phone)

RT Bank registers a **custom app** on **lb-phone** (`AddCustomApp`). That is the only phone we integrate today.

|               |                                                         |
| ------------- | ------------------------------------------------------- |
| Supported now | **lb-phone**                                            |
| Not supported | qs-smartphone, npwd, gksphone, high-phone, etc.         |
| Guarantee     | We do **not** promise ports or support for other phones |

Without lb-phone, branch / ATM / `/bank` still work. You lose: in-phone app, OTP **Mail**, and phone toast notifications tied to the bank app.

**What you need**

1. **lb-phone** started and working (player has an equipped phone number).
2. `phone-web/build/` present inside rt-bank (shipped in the release; rebuild only if you edit sources).
3. `phone/icon.png` present (app icon).
4. Matching config in `config/config.lua` → `Config.Phone`.

**Config**

```lua
Config.Phone = {
    Resource = 'lb-phone',           -- resource name of lb-phone
    --- Must match App.Identifier so notifications open / show under RT Bank
    NotifyApp = 'rt-bank',
    App = {
        Enabled = true,
        Identifier = 'rt-bank',      -- AddCustomApp id (keep in sync with NotifyApp)
        Name = 'RT Bank',
        Description = 'Your RT Bank accounts, transfers and activity',
        Developer = 'RealTime Dev',
        DefaultApp = true,           -- install by default on phones
        --- Hide stock Wallet via exports (do NOT delete Wallet from lb-phone config.json)
        HideWallet = true,
        -- Icon = 'https://cfx-nui-rt-bank/phone/icon.png',
    },
}
```

| Setting          | What it does                                                                |
| ---------------- | --------------------------------------------------------------------------- |
| `Resource`       | Phone resource name (`lb-phone` by default)                                 |
| `NotifyApp`      | App id used in `SendNotification` — **must match** `App.Identifier`         |
| `App.Enabled`    | `false` = do not register the custom app                                    |
| `App.Identifier` | Unique id for `AddCustomApp`                                                |
| `App.DefaultApp` | Auto-install on player phones                                               |
| `App.HideWallet` | Hides lb-phone's default **Wallet** with `SetAppHidden` / `SetAppInstalled` |
| `App.Icon`       | Optional override URL; default is `phone/icon.png` via cfx-nui              |

**Compatibility checklist (lb-phone)**

1. Ensure `lb-phone` starts before or with rt-bank (rt-bank **retries** registration for \~90s if the phone starts late).
2. Keep the default **Wallet** entry in lb-phone `config.json` — do **not** remove it. RT Bank hides it at runtime when `HideWallet = true` (safer than deleting the app definition).
3. Leave `NotifyApp` and `App.Identifier` as the same string (default `rt-bank`).
4. OTP for large transfers is sent with lb-phone **Mail** (`SendMail`) so the code stays in the inbox (toasts disappear too fast).
5. Bank push notifications use `SendNotification` with `app = NotifyApp`.
6. After `ensure lb-phone` or `ensure rt-bank`, watch F8 for:

```
[rt-bank] lb-phone app registered: rt-bank (ui=rt-bank/phone-web/build/index.html)
```

**Optional — rebuild the phone UI**

Only if you change `phone-web/src`:

```bash
cd phone-web
npm install
npm run build
```

Then restart rt-bank (and clear NUI cache if the iframe looks stale).

**Disable the phone app**

```lua
Config.Phone.App.Enabled = false
```

Banking at ATMs / branches / `/bank` continues to work. OTP mail will not send if lb-phone is missing or down.

**Other phones**

There is **no** built-in bridge for phones other than lb-phone. Custom work is on you / a paid adapter — RealTime Dev does **not** guarantee multi-phone support.

***

### ⚙️ Main settings

File: `config/config.lua`

| Setting                          | What it does                                                 |
| -------------------------------- | ------------------------------------------------------------ |
| `Config.Locale`                  | UI + server language (`en`, `es`, `fr`, `de`, `it`, `pt-BR`) |
| `Config.Framework`               | `auto`, `qbx_core`, or `qb-core`                             |
| `Config.BankName`                | Brand name on UI / papers / mail                             |
| `Config.Security.PinPepper`      | Server PIN secret (unique per server)                        |
| `Config.Security.OtpMinTransfer` | Transfers ≥ this amount need OTP                             |
| `Config.Mirror`                  | Sync primary ledger ↔ `money.bank`                           |
| `Config.Limits`                  | Daily / per-tx ATM & transfer caps                           |
| `Config.Features`                | Loans, investments, staff panel flags                        |
| `Config.Phone`                   | lb-phone app + notifications (see above)                     |
| `Config.AutoOpen`                | Create primary account + debit card on first join            |

Other configs:

| File                  | Purpose                                |
| --------------------- | -------------------------------------- |
| `config/accounts.lua` | Account types & holder roles           |
| `config/cards.lua`    | Card types / item                      |
| `config/atm.lua`      | ATM models & bank branches             |
| `config/credit.lua`   | Credit score, loans, term deposits     |
| `config/papers.lua`   | Receipt / contract / certificate items |

All options have comments inside each config file.

***

#### 🏦 Player features

* **Branch (ox\_target):** accounts, cards, PIN, open app
* **ATM:** card insert → PIN → withdraw / deposit / history / transfer / receipt
* **App (`/bank`):** dashboard, transfers, history, loans, investments, security, staff business accounts
* **Phone (lb-phone):** balances, quick transfer, recent history
* **Papers:** ATM receipt, loan contract, deposit certificate (ox\_inventory use → ticket NUI)

Loans use **income + credit score** underwriting (no vehicle / housing seizure).

***

### 🔌 Exports

```lua
-- Server
exports['rt-bank']:GetBalance(src)
exports['rt-bank']:GetBalance(citizenid, accountId)
exports['rt-bank']:GetPrimaryAccount(src)
exports['rt-bank']:GetAccounts(src)
local ok, tx = exports['rt-bank']:Charge(src, amount, 'Shop purchase')
local ok, tx = exports['rt-bank']:Deposit(src, amount, 'Payroll')
exports['rt-bank']:CreateAccount(src, 'savings', 'My savings')
exports['rt-bank']:BlockCard(src, cardUid, 'stolen')
exports['rt-bank']:GetTransactions(src, { page = 1, perPage = 20, type = 'transfer' })

-- Client
exports['rt-bank']:OpenBank()
```

Prefer `Charge` / `Deposit` so the purchase is blocked if the ledger cannot cover it. Framework `RemoveMoney('bank')` / `AddMoney('bank')` can still be absorbed when `Config.Mirror.AbsorbExternalBank(Debit)` is enabled.

***

### 👮 Admin & staff

```lua
Config.Admin = {
    Ace = 'rt.perm.bank',
    Groups = { 'admin', 'god', 'owner' },
}
```

Staff tab (when allowed): create **job-linked business accounts**. ACE `rt.perm.bank` and configured framework groups are recognized.

***

### 📁 Resource layout (publish / escrow)

| Include in zip                                          | Exclude from zip                                      |
| ------------------------------------------------------- | ----------------------------------------------------- |
| Lua, `config/`, `locales/`, `sql/`, `items/`, `bridge/` | `web/node_modules`, `phone-web/node_modules`          |
| `web/build/`, `phone-web/build/`, `phone/`              | Optional: `web/src`, `phone-web/src` if closed-source |
| `fxmanifest.lua`, `README.md`, `LICENSE`                |                                                       |

`escrow_ignore` keeps configs, locales, SQL, items, bridges, and built UI readable for buyers.

Never ship a live `PinPepper` — leave `CHANGE_ME_UNIQUE_PEPPER` so each server sets its own.

***

### 🛠️ Troubleshooting

| Symptom                         | Fix                                                                                                 |
| ------------------------------- | --------------------------------------------------------------------------------------------------- |
| `[rt-bank] BLOCKED: PinPepper…` | Set a unique `Config.Security.PinPepper` (16+ chars)                                                |
| No money ops                    | Same as above, or `AllowDefaultPepper` left false on purpose                                        |
| Phone app missing               | Start `lb-phone`, check F8 for `AddCustomApp` / `app registered`                                    |
| Wallet still visible            | `HideWallet = true`; do not delete Wallet from lb-phone config — wait for hide burst / reopen phone |
| OTP never arrives               | Player needs equipped phone number; check Mail app; lb-phone must be started                        |
| Notifications under wrong app   | `NotifyApp` must equal `App.Identifier`                                                             |
| Blank inventory icons           | Copy PNGs into `ox_inventory/web/images/`                                                           |
| UI stale after update           | Clear NUI cache for `rt-bank`                                                                       |
| Framework nil errors            | Start qbx\_core / qb-core first, or set `Config.Framework`                                          |

***

### ❤️ Support

Developed by RT Dev.

Support through your Tebex purchase channel.

© RealTime Dev — all rights reserved.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://realtime-dev.gitbook.io/realtime-dev-docs/realtime-scripts/rt-bank.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
