Money System
Complete guide to the RSG Framework money system with multiple bank types and physical money items
Introduction
The RSG Framework features a comprehensive money system designed for RedM that includes multiple bank types, physical money items, and bloodmoney for illegal activities. Unlike traditional frameworks with a single bank account, RSG provides location-specific banks throughout the map.
Version: 2.3.6+
The legacy bank money type is DEPRECATED and set to 0 by default. Use location-specific banks instead!
Core Features
Money Types
The framework supports these money types:
- cash - Physical money in hand (can be dropped/stolen)
- gold - Gold currency (works exactly like cash — see below)
- valbank - Valentine Bank account
- rhobank - Rhodes Bank account
- blkbank - Blackwater Bank account
- armbank - Armadillo Bank account
- bloodmoney - Illegal money from criminal activities
- bank - DEPRECATED (legacy, set to 0)
New: Gold currency
The latest update adds a gold currency. It behaves just like any other money type
(such as cash or bloodmoney) and uses the same API — AddMoney, RemoveMoney,
SetMoney, and GetMoney. Like cash, gold can also be represented as a physical inventory
item. See Gold currency below.
Physical Money Items
When EnableMoneyItems is true (default):
- cash becomes a physical inventory item (
dollar) - bloodmoney becomes a physical inventory item (
blood_dollar) - gold becomes a physical inventory item when
EnableGoldItemsistrue - Players can see, drop, and trade physical money
- Money can be stolen from players when downed/robbed
Money Logging
All money transactions are automatically logged:
- Transactions over $100,000 trigger special high-value logging
- All AddMoney/RemoveMoney/SetMoney calls include reason tracking
- Integration with
rsg-logfor audit trails
How It Works
Money Storage
Money is stored in the player's data as a JSON object:
PlayerData.money = {
cash = 50, -- Starting cash
gold = 0, -- Gold currency
valbank = 0, -- Valentine Bank
rhobank = 0, -- Rhodes Bank
blkbank = 0, -- Blackwater Bank
armbank = 0, -- Armadillo Bank
bloodmoney = 0, -- Bloodstained money
bank = 0 -- DEPRECATED
}Default Starting Money
Configured in rsg-core/config.lua:
RSGConfig.Money.MoneyTypes = {
cash = 50, -- $50 starting cash
gold = 0, -- Gold currency
bank = 0, -- Deprecated
valbank = 0,
rhobank = 0,
blkbank = 0,
armbank = 0,
bloodmoney = 0
}Physical Money Items
Enable/disable in rsg-core/config.lua:
RSGConfig.Money.EnableMoneyItems = true -- Default: trueWhen enabled:
cash→dollaritem (weight: 1g each)bloodmoney→blood_dollaritem (weight: 1g each)
Item definitions in rsg-core/shared/items.lua:
['dollar'] = {
name = 'dollar',
label = 'Dollars',
weight = 1,
type = 'item',
image = 'dollar.png',
unique = false,
useable = false,
shouldClose = false,
combinable = nil,
description = 'US Dollar'
},
['blood_dollar'] = {
name = 'blood_dollar',
label = 'Bloodstained Dollars',
weight = 1,
type = 'item',
image = 'bloodmoney.png',
unique = false,
useable = false,
shouldClose = false,
combinable = nil,
description = 'Blood-stained money from illegal activities'
}Gold currency
Gold is a first-class money type introduced in the latest update. It behaves exactly like
cash — you add, remove, set, and read it with the same
player money functions — and, like cash, it can optionally be
represented as a physical inventory item.
Configuration
Gold is configured in rsg-core/config.lua:
-- The inventory item used to represent gold when EnableGoldItems is true
RSGConfig.Money.GoldItem = 'gold'
RSGConfig.Gold = {}
RSGConfig.Gold.EnableGoldItems = false -- Default: falseRSGConfig.Money.GoldItem— the item name (fromshared/items.lua) used when gold is a physical item.RSGConfig.Gold.EnableGoldItems— whentrue, gold becomes a physical inventory item (likedollarfor cash). Whenfalse(default), gold is a virtual balance only.
Add gold = 0 to RSGConfig.Money.MoneyTypes so new characters start with a gold balance,
and make sure the gold item exists in shared/items.lua if you enable
EnableGoldItems.
Using gold
Because gold shares the standard money API, all the usual functions work — just pass
'gold' as the money type:
-- SERVER SIDE
local Player = RSGCore.Functions.GetPlayer(source)
if not Player then return end
-- Reward the player with gold
Player.Functions.AddMoney('gold', 5, 'quest-reward')
-- Charge gold for a premium purchase
if Player.Functions.GetMoney('gold') >= 10 then
Player.Functions.RemoveMoney('gold', 10, 'premium-shop')
end
-- Read the current gold balance
local gold = Player.Functions.GetMoney('gold')
print('Player has '.. gold ..' gold')Everything else in this guide — logging, DontAllowMinus, the OnMoneyChange events —
applies to gold exactly as it does to cash and bloodmoney.
Player Money Functions
Add Money
Adds money to a player's account.
Player.Functions.AddMoney(moneytype, amount, reason)Parameters:
moneytype(string) - Type of money ('cash', 'valbank', etc.)amount(number) - Amount to addreason(string) - Reason for logging (optional)
Returns: boolean - True if successful, false if failed
Example:
-- SERVER SIDE
RegisterNetEvent('job:server:payPlayer', function(amount)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Add cash
if Player.Functions.AddMoney('cash', amount, 'job-payment') then
TriggerClientEvent('ox_lib:notify', src, {
title = 'Payment Received',
description = 'You received $'.. amount,
type = 'success',
duration = 5000
})
end
end)
-- Add to Valentine Bank
Player.Functions.AddMoney('valbank', 500, 'bank-deposit')
-- Add bloodmoney from robbery
Player.Functions.AddMoney('bloodmoney', 250, 'store-robbery')Money over $100,000 triggers special high-value logging for admin monitoring
Remove Money
Removes money from a player's account.
Player.Functions.RemoveMoney(moneytype, amount, reason)Parameters:
moneytype(string) - Type of moneyamount(number) - Amount to removereason(string) - Reason for logging (optional)
Returns: boolean - True if successful, false if insufficient funds
Example:
-- SERVER SIDE
RegisterNetEvent('shop:server:purchase', function(itemPrice)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Check if player has enough cash
if Player.Functions.GetMoney('cash') < itemPrice then
TriggerClientEvent('ox_lib:notify', src, {
title = 'Not Enough Money',
description = 'You need $'.. itemPrice,
type = 'error',
duration = 5000
})
return
end
-- Remove money
if Player.Functions.RemoveMoney('cash', itemPrice, 'shop-purchase') then
-- Give item
exports['rsg-inventory']:AddItem(src, 'bread', 1, nil, nil, 'purchased')
TriggerClientEvent('ox_lib:notify', src, {
title = 'Purchase Successful',
type = 'success',
duration = 5000
})
end
end)Money types in DontAllowMinus config cannot go below 0. Transaction will fail instead.
Set Money
Sets a specific money amount (overwrites current value).
Player.Functions.SetMoney(moneytype, amount, reason)Parameters:
moneytype(string) - Type of moneyamount(number) - New amountreason(string) - Reason for logging (optional)
Returns: boolean - True if successful, false if failed
Example:
-- SERVER SIDE
-- Admin command to set player money
RegisterCommand('setmoney', function(source, args)
local src = source
if not RSGCore.Functions.HasPermission(src, 'admin') then return end
local targetId = tonumber(args[1])
local moneyType = args[2] or 'cash'
local amount = tonumber(args[3]) or 0
local Target = RSGCore.Functions.GetPlayer(targetId)
if not Target then return end
if Target.Functions.SetMoney(moneyType, amount, 'admin-set') then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Set '.. moneyType ..' to $'.. amount,
type = 'success'
})
end
end, false)SetMoney completely overwrites the current amount. Use AddMoney/RemoveMoney for relative changes.
Get Money
Gets the current amount of a specific money type.
Player.Functions.GetMoney(moneytype)Parameters:
moneytype(string) - Type of money to check
Returns: number|boolean - Amount or false if invalid type
Example:
-- SERVER SIDE
local Player = RSGCore.Functions.GetPlayer(source)
-- Check cash
local cashAmount = Player.Functions.GetMoney('cash')
print('Player has $'.. cashAmount ..' cash')
-- Check Valentine Bank balance
local valbankAmount = Player.Functions.GetMoney('valbank')
print('Valentine Bank: $'.. valbankAmount)
-- Check total money across all accounts
local totalMoney = 0
for moneyType, _ in pairs(Player.PlayerData.money) do
if moneyType ~= 'bank' then -- Skip deprecated bank
totalMoney = totalMoney + Player.Functions.GetMoney(moneyType)
end
end
print('Total wealth: $'.. totalMoney)Money Configuration
Config Settings
Located in rsg-core/config.lua:
RSGConfig.Money = {
-- Starting amounts for new players
MoneyTypes = {
cash = 50,
gold = 0, -- Gold currency
bank = 0, -- DEPRECATED
valbank = 0,
rhobank = 0,
blkbank = 0,
armbank = 0,
bloodmoney = 0
},
-- Enable physical money items
EnableMoneyItems = true,
-- Inventory item used to represent gold when EnableGoldItems is true
GoldItem = 'gold',
-- Money types that cannot go below 0
DontAllowMinus = { 'cash', 'gold', 'valbank', 'rhobank', 'blkbank', 'armbank', 'bloodmoney' },
-- Minimum balance allowed (for types not in DontAllowMinus)
MinusLimit = -5000,
-- Paycheck settings
PayCheckTimeOut = 30, -- Minutes between paychecks
PayCheckSociety = false -- Deduct paychecks from society accounts
}
-- Gold currency settings
RSGConfig.Gold = {}
RSGConfig.Gold.EnableGoldItems = false -- true = gold becomes a physical inventory itemMoney Item Configuration
Physical money behavior:
- When player receives cash → automatically converted to
dollaritem - When player spends cash →
dollaritems are removed - Can drop/trade dollar items like any other inventory item
- Death/robbery can result in cash loss
Bank System
Location-Specific Banks
Each major town has its own bank:
| Bank Type | Location | Config Key |
|---|---|---|
| Valentine Bank | Valentine | valbank |
| Rhodes Bank | Rhodes | rhobank |
| Blackwater Bank | Blackwater | blkbank |
| Armadillo Bank | Armadillo | armbank |
Why Multiple Banks?
- Realism: Players must travel to specific towns to access their accounts
- Gameplay: Creates opportunities for bank robberies and escorts
- Economy: Different regions can have different banking features
- Risk: Traveling with cash adds danger and roleplay opportunities
Bank Account Management
-- SERVER SIDE
-- Deposit cash into Valentine Bank
RegisterNetEvent('bank:server:deposit', function(bankType, amount)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate bank type
local validBanks = { 'valbank', 'rhobank', 'blkbank', 'armbank' }
if not lib.table.contains(validBanks, bankType) then
return
end
-- Check player has enough cash
if Player.Functions.GetMoney('cash') < amount then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Not enough cash',
type = 'error'
})
return
end
-- Process deposit
if Player.Functions.RemoveMoney('cash', amount, 'bank-deposit') then
if Player.Functions.AddMoney(bankType, amount, 'bank-deposit') then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Deposited $'.. amount,
type = 'success'
})
end
end
end)
-- Withdraw from Rhodes Bank
RegisterNetEvent('bank:server:withdraw', function(bankType, amount)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Check bank balance
if Player.Functions.GetMoney(bankType) < amount then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Insufficient funds',
type = 'error'
})
return
end
-- Process withdrawal
if Player.Functions.RemoveMoney(bankType, amount, 'bank-withdrawal') then
if Player.Functions.AddMoney('cash', amount, 'bank-withdrawal') then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Withdrew $'.. amount,
type = 'success'
})
end
end
end)Bloodmoney System
What is Bloodmoney?
Bloodmoney is illegal currency obtained through:
- Store/bank robberies
- Drug sales
- Gang activities
- Stolen goods fencing
Bloodmoney Features
- Separate Currency: Cannot be deposited in legitimate banks
- Physical Item: Appears as
blood_dollarwhen money items enabled - Illegal Use: Used for black market purchases
- Risk Factor: Can be confiscated by law enforcement
Using Bloodmoney
-- SERVER SIDE
-- Award bloodmoney from robbery
RegisterNetEvent('robbery:server:complete', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
local bloodReward = math.random(100, 500)
if Player.Functions.AddMoney('bloodmoney', bloodReward, 'store-robbery') then
TriggerClientEvent('ox_lib:notify', src, {
description = 'You got $'.. bloodReward ..' in bloodmoney',
type = 'error',
duration = 5000
})
end
end)
-- Black market shop accepting bloodmoney
RegisterNetEvent('blackmarket:server:purchase', function(itemName, price)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Check bloodmoney balance
if Player.Functions.GetMoney('bloodmoney') < price then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Not enough bloodmoney',
type = 'error'
})
return
end
-- Process purchase
if Player.Functions.RemoveMoney('bloodmoney', price, 'blackmarket-purchase') then
exports['rsg-inventory']:AddItem(src, itemName, 1, nil, nil, 'blackmarket')
end
end)
-- Law enforcement confiscation
RegisterNetEvent('police:server:confiscateBloodmoney', function(targetId)
local src = source
local Officer = RSGCore.Functions.GetPlayer(src)
local Target = RSGCore.Functions.GetPlayer(targetId)
if not Officer or not Target then return end
-- Check officer is law enforcement
if Officer.PlayerData.job.type ~= 'leo' then return end
local bloodAmount = Target.Functions.GetMoney('bloodmoney')
if bloodAmount > 0 then
Target.Functions.SetMoney('bloodmoney', 0, 'police-confiscation')
TriggerClientEvent('ox_lib:notify', targetId, {
description = 'Police confiscated $'.. bloodAmount ..' bloodmoney',
type = 'error'
})
TriggerClientEvent('ox_lib:notify', src, {
description = 'Confiscated $'.. bloodAmount ..' bloodmoney',
type = 'success'
})
end
end)Money Events
Server Events
Listen for money changes in your resources:
-- Triggered when money is added/removed/set
AddEventHandler('RSGCore:Server:OnMoneyChange', function(source, moneyType, amount, changeType, reason)
print('Player '.. source ..' '.. changeType ..' $'.. amount ..' '.. moneyType)
print('Reason: '.. reason)
-- changeType can be: 'add', 'remove', 'set'
end)Client Events
-- CLIENT SIDE
RegisterNetEvent('RSGCore:Client:OnMoneyChange', function(moneyType, amount, changeType, reason)
print('Your '.. moneyType ..' was '.. changeType ..'ed by $'.. amount)
-- Optional: Update custom UI
if moneyType == 'cash' then
-- Update cash display
end
end)Complete Examples
Example 1: Paycheck System
-- SERVER SIDE
-- Automatic paycheck (called by rsg-core every X minutes)
function PaycheckInterval()
if next(RSGCore.Players) then
for _, Player in pairs(RSGCore.Players) do
if Player then
local job = Player.PlayerData.job
local payment = RSGShared.Jobs[job.name]['grades'][tostring(job.grade.level)].payment
-- Only pay if on duty (or job allows off-duty pay)
if payment > 0 and (RSGShared.Jobs[job.name].offDutyPay or job.onduty) then
Player.Functions.AddMoney('cash', payment, 'paycheck')
TriggerClientEvent('ox_lib:notify', Player.PlayerData.source, {
title = 'Paycheck',
description = 'Received $'.. payment,
type = 'info',
duration = 5000
})
end
end
end
end
SetTimeout(RSGCore.Config.Money.PayCheckTimeOut * 60000, PaycheckInterval)
endExample 2: ATM/Bank UI
-- SERVER SIDE
RegisterNetEvent('bank:server:getBalance', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
local bankData = {
cash = Player.Functions.GetMoney('cash'),
valbank = Player.Functions.GetMoney('valbank'),
rhobank = Player.Functions.GetMoney('rhobank'),
blkbank = Player.Functions.GetMoney('blkbank'),
armbank = Player.Functions.GetMoney('armbank'),
bloodmoney = Player.Functions.GetMoney('bloodmoney')
}
TriggerClientEvent('bank:client:displayBalance', src, bankData)
end)
-- Transfer between accounts
RegisterNetEvent('bank:server:transfer', function(fromBank, toBank, amount)
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate
if Player.Functions.GetMoney(fromBank) < amount then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Insufficient funds',
type = 'error'
})
return
end
-- Process transfer
if Player.Functions.RemoveMoney(fromBank, amount, 'bank-transfer') then
if Player.Functions.AddMoney(toBank, amount, 'bank-transfer') then
TriggerClientEvent('ox_lib:notify', src, {
description = 'Transfer successful',
type = 'success'
})
end
end
end)Example 3: Money Drop on Death
-- SERVER SIDE
RegisterNetEvent('player:server:onDeath', function()
local src = source
local Player = RSGCore.Functions.GetPlayer(src)
if not Player then return end
-- Drop random percentage of cash (10-30%)
local cashAmount = Player.Functions.GetMoney('cash')
if cashAmount > 0 then
local dropPercent = math.random(10, 30) / 100
local dropAmount = math.floor(cashAmount * dropPercent)
if Player.Functions.RemoveMoney('cash', dropAmount, 'death-penalty') then
-- Create ground drop
local playerPed = GetPlayerPed(src)
local coords = GetEntityCoords(playerPed)
exports['rsg-inventory']:ForceDropItem(src, 'dollar', dropAmount, {}, 'death')
TriggerClientEvent('ox_lib:notify', src, {
description = 'You dropped $'.. dropAmount ..' when you died',
type = 'error',
duration = 5000
})
end
end
end)Best Practices
Tip
Always validate before transactions: Check if player has sufficient funds before attempting to remove money
Use the deprecated 'bank' type carefully: It's set to 0 by default. Migrate to location-specific banks
Include descriptive reasons: Always provide a reason parameter for audit logging and debugging
Common Patterns
- Check before removing:
if Player.Functions.GetMoney('cash') >= price then
Player.Functions.RemoveMoney('cash', price, 'purchase')
end- Handle failures:
if not Player.Functions.AddMoney('cash', reward, 'quest-reward') then
-- Player inventory might be full if money items enabled
-- Money will be dropped on ground automatically
end- Use appropriate money type:
-- Legal job payment
Player.Functions.AddMoney('cash', payment, 'job-salary')
-- Illegal activity
Player.Functions.AddMoney('bloodmoney', reward, 'drug-sale')
-- Bank deposit
Player.Functions.AddMoney('valbank', amount, 'deposit')Troubleshooting
Check that player data is being saved correctly. Money is saved as JSON in the money column of the players table.
Verify RSGConfig.Money.EnableMoneyItems = true in config.lua and ensure dollar and blood_dollar items exist in shared/items.lua
The 'bank' type is deprecated. Use location-specific banks: valbank, rhobank, blkbank, or armbank
Check DontAllowMinus config array. Money types in this list cannot go below 0.
Migration from Old Bank System
If migrating from an older framework with single 'bank' type:
-- SERVER SIDE - Migration script
RegisterCommand('migratebanks', function(source)
if not RSGCore.Functions.HasPermission(source, 'admin') then return end
MySQL.query('SELECT citizenid, money FROM players', {}, function(result)
if result then
for i = 1, #result do
local money = json.decode(result[i].money)
if money.bank and money.bank > 0 then
-- Transfer old 'bank' to 'valbank' (or chosen default)
money.valbank = money.valbank + money.bank
money.bank = 0
MySQL.update('UPDATE players SET money = ? WHERE citizenid = ?', {
json.encode(money),
result[i].citizenid
})
end
end
print('Bank migration complete!')
end
end)
end, false)Next Steps
- Player Data Reference - Learn about PlayerData structure
- Configuration Guide - Configure money settings
- Server Functions - All money-related functions
Need help? Join the RSG Framework Discord!

