local cachedAccounts = {} local cachedPlayers = {} CreateThread(function() Wait(500) if not LoadResourceFile("Renewed-Banking", 'web/public/build/bundle.js') or GetCurrentResourceName() ~= "Renewed-Banking" then error(locale("ui_not_built")) return StopResource("Renewed-Banking") end local accounts = MySQL.query.await('SELECT * FROM bank_accounts_new', {}) if accounts then for _,v in pairs (accounts) do local job = v.id v.auth = json.decode(v.auth) cachedAccounts[job] = { -- cachedAccounts[#cachedAccounts+1] id = job, type = locale("org"), name = GetSocietyLabel(job), frozen = v.isFrozen == 1, amount = v.amount, transactions = json.decode(v.transactions), auth = {}, creator = v.creator } if #v.auth >= 1 then for k=1, #v.auth do cachedAccounts[job].auth[v.auth[k]] = true end end end end local jobs, gangs = GetFrameworkGroups() local query = {} local function addCachedAccount(group) cachedAccounts[group] = { id = group, type = locale('org'), name = GetSocietyLabel(group), frozen = 0, amount = 0, transactions = {}, auth = {}, creator = nil } query[#query + 1] = {"INSERT INTO bank_accounts_new (id, amount, transactions, auth, isFrozen, creator) VALUES (?, ?, ?, ?, ?, NULL) ", { group, cachedAccounts[group].amount, json.encode(cachedAccounts[group].transactions), json.encode({}), cachedAccounts[group].frozen }} end for job in pairs(jobs) do if not cachedAccounts[job] then addCachedAccount(job) end end for gang in pairs(gangs) do if not cachedAccounts[gang] then addCachedAccount(gang) end end if #query >= 1 then MySQL.transaction.await(query) end end) function UpdatePlayerAccount(cid) local p = promise.new() MySQL.query('SELECT * FROM player_transactions WHERE id = ?', {cid}, function(account) local query = '%' .. cid .. '%' MySQL.query("SELECT * FROM bank_accounts_new WHERE auth LIKE ? ", {query}, function(shared) cachedPlayers[cid] = { isFrozen = 0, transactions = #account > 0 and json.decode(account[1].transactions) or {}, accounts = {} } if #shared >= 1 then for k=1, #shared do cachedPlayers[cid].accounts[#cachedPlayers[cid].accounts+1] = shared[k].id end end p:resolve(true) end) end) return Citizen.Await(p) end local function getBankData(source) local Player = GetPlayerObject(source) local bankData = {} local cid = GetIdentifier(Player) if not cachedPlayers[cid] then UpdatePlayerAccount(cid) end local funds = GetFunds(Player) bankData[#bankData+1] = { id = cid, type = locale("personal"), name = GetCharacterName(Player), frozen = cachedPlayers[cid].isFrozen, amount = funds.bank, cash = funds.cash, transactions = cachedPlayers[cid].transactions, } local jobs = GetJobs(Player) if #jobs > 0 then for k=1, #jobs do if cachedAccounts[jobs[k].name] and IsJobAuth(jobs[k].name, jobs[k].grade) then bankData[#bankData+1] = cachedAccounts[jobs[k].name] end end else local job = cachedAccounts[jobs.name] if job and IsJobAuth(jobs.name, jobs.grade) then bankData[#bankData+1] = job end end local gang = GetGang(Player) if gang and gang ~= 'none' then local gangData = cachedAccounts[gang] if gangData and IsGangAuth(Player, gang) then bankData[#bankData+1] = gangData end end local sharedAccounts = cachedPlayers[cid].accounts for k=1, #sharedAccounts do local sAccount = cachedAccounts[sharedAccounts[k]] bankData[#bankData+1] = sAccount end return bankData end lib.callback.register('renewed-banking:server:initalizeBanking', function(source) local bankData = getBankData(source) return bankData end) -- Events local function genTransactionID() local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' return string.gsub(template, '[xy]', function (c) local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb) return string.format('%x', v) end) end local function sanitizeMessage(message) if type(message) ~= "string" then message = tostring(message) end message = message:gsub("'", "''"):gsub("\\", "\\\\") return message end local Type = type local function handleTransaction(account, title, amount, message, issuer, receiver, transType, transID) if not account or Type(account) ~= 'string' then return print(locale("err_trans_account", account)) end if not title or Type(title) ~= 'string' then return print(locale("err_trans_title", title)) end if not amount or Type(amount) ~= 'number' then return print(locale("err_trans_amount", amount)) end if not message or Type(message) ~= 'string' then return print(locale("err_trans_message", message)) end if not issuer or Type(issuer) ~= 'string' then return print(locale("err_trans_issuer", issuer)) end if not receiver or Type(receiver) ~= 'string' then return print(locale("err_trans_receiver", receiver)) end if not transType or Type(transType) ~= 'string' then return print(locale("err_trans_type", transType)) end if transID and Type(transID) ~= 'string' then return print(locale("err_trans_transID", transID)) end local transaction = { trans_id = transID or genTransactionID(), title = title, amount = amount, trans_type = transType, receiver = receiver, message = sanitizeMessage(message), issuer = issuer, time = os.time() } if cachedAccounts[account] then table.insert(cachedAccounts[account].transactions, 1, transaction) local transactions = json.encode(cachedAccounts[account].transactions) MySQL.prepare("INSERT INTO bank_accounts_new (id, transactions) VALUES (?, ?) ON DUPLICATE KEY UPDATE transactions = ?",{ account, transactions, transactions }) elseif cachedPlayers[account] then table.insert(cachedPlayers[account].transactions, 1, transaction) local transactions = json.encode(cachedPlayers[account].transactions) MySQL.prepare("INSERT INTO player_transactions (id, transactions) VALUES (?, ?) ON DUPLICATE KEY UPDATE transactions = ?", { account, transactions, transactions }) else print(locale("invalid_account", account)) end return transaction end exports("handleTransaction", handleTransaction) function GetAccountMoney(account) if not cachedAccounts[account] then locale("invalid_account", account) return false end return cachedAccounts[account].amount end exports('getAccountMoney', GetAccountMoney) local function updateBalance(account) MySQL.prepare("UPDATE bank_accounts_new SET amount = ? WHERE id = ?",{ cachedAccounts[account].amount, account }) end function AddAccountMoney(account, amount) if not cachedAccounts[account] then locale("invalid_account", account) return false end cachedAccounts[account].amount += amount updateBalance(account) return true end exports('addAccountMoney', AddAccountMoney) local function getPlayerData(source, id) local Player = source and GetPlayerObject(source) if not Player then Player = GetPlayerObjectFromID(id) end if not Player then local msg = ("Cannot Find Account(%s)"):format(id) print(locale("invalid_account", id)) if source then Notify(source, {title = locale("bank_name"), description = msg, type = "error"}) end end return Player end lib.callback.register('Renewed-Banking:server:deposit', function(source, data) local Player = GetPlayerObject(source) local amount = tonumber(data.amount) if not amount or amount < 1 then Notify(source, {title = locale("bank_name"), description = locale("invalid_amount", "deposit"), type = "error"}) return false end local name = GetCharacterName(Player) if not data.comment or data.comment == "" then data.comment = locale("comp_transaction", name, "deposited", amount) else sanitizeMessage(data.comment) end if RemoveMoney(Player, amount, 'cash', data.comment) then if cachedAccounts[data.fromAccount] then AddAccountMoney(data.fromAccount, amount) else AddMoney(Player, amount, 'bank', data.comment) end local Player2 = getPlayerData(source, data.fromAccount) Player2 = Player2 and GetCharacterName(Player2) or data.fromAccount handleTransaction(data.fromAccount, locale("personal_acc") .. data.fromAccount, amount, data.comment, name, Player2, "deposit") local bankData = getBankData(source) return bankData else TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("not_enough_money")) return false end end) function RemoveAccountMoney(account, amount) if not cachedAccounts[account] then print(locale("invalid_account", account)) return false end if cachedAccounts[account].amount < amount then print(locale("broke_account", account, amount)) return false end cachedAccounts[account].amount -= amount updateBalance(account) return true end exports('removeAccountMoney', RemoveAccountMoney) lib.callback.register('Renewed-Banking:server:withdraw', function(source, data) local Player = GetPlayerObject(source) local amount = tonumber(data.amount) if not amount or amount < 1 then Notify(source, {title = locale("bank_name"), description = locale("invalid_amount", "withdraw"), type = "error"}) return false end local name = GetCharacterName(Player) local funds = GetFunds(Player) if not data.comment or data.comment == "" then data.comment = locale("comp_transaction", name, "withdrawed", amount) else sanitizeMessage(data.comment) end local canWithdraw if cachedAccounts[data.fromAccount] then canWithdraw = RemoveAccountMoney(data.fromAccount, amount) else canWithdraw = funds.bank >= amount and RemoveMoney(Player, amount, 'bank', data.comment) or false end if canWithdraw then local Player2 = getPlayerData(source, data.fromAccount) Player2 = Player2 and GetCharacterName(Player2) or data.fromAccount AddMoney(Player, amount, 'cash', data.comment) handleTransaction(data.fromAccount,locale("personal_acc") .. data.fromAccount, amount, data.comment, Player2, name, "withdraw") local bankData = getBankData(source) return bankData else TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("not_enough_money")) return false end end) lib.callback.register('Renewed-Banking:server:transfer', function(source, data) local Player = GetPlayerObject(source) local amount = tonumber(data.amount) if not amount or amount < 1 then Notify(source, {title = locale("bank_name"), description = locale("invalid_amount", "transfer"), type = "error"}) return false end local name = GetCharacterName(Player) if not data.comment or data.comment == "" then data.comment = locale("comp_transaction", name, "transfered", amount) else sanitizeMessage(data.comment) end if cachedAccounts[data.fromAccount] then if cachedAccounts[data.stateid] then local canTransfer = RemoveAccountMoney(data.fromAccount, amount) if canTransfer then AddAccountMoney(data.stateid, amount) local title = ("%s / %s"):format(cachedAccounts[data.fromAccount].name, data.fromAccount) local transaction = handleTransaction(data.fromAccount, title, amount, data.comment, cachedAccounts[data.fromAccount].name, cachedAccounts[data.stateid].name, "withdraw") handleTransaction(data.stateid, title, amount, data.comment, cachedAccounts[data.fromAccount].name, cachedAccounts[data.stateid].name, "deposit", transaction.trans_id) else TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("not_enough_money")) return false end else local Player2 = getPlayerData(source, data.stateid) if not Player2 then TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("fail_transfer")) return false end local canTransfer = RemoveAccountMoney(data.fromAccount, amount) if canTransfer then AddMoney(Player2, amount, 'bank', data.comment) local plyName = GetCharacterName(Player2) local transaction = handleTransaction(data.fromAccount, ("%s / %s"):format(cachedAccounts[data.fromAccount].name, data.fromAccount), amount, data.comment, cachedAccounts[data.fromAccount].name, plyName, "withdraw") handleTransaction(data.stateid, ("%s / %s"):format(cachedAccounts[data.fromAccount].name, data.fromAccount), amount, data.comment, cachedAccounts[data.fromAccount].name, plyName, "deposit", transaction.trans_id) else TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("not_enough_money")) return false end end else local funds = GetFunds(Player) if cachedAccounts[data.stateid] then if funds.bank >= amount and RemoveMoney(Player, amount, 'bank', data.comment) then AddAccountMoney(data.stateid, amount) local transaction = handleTransaction(data.fromAccount, locale("personal_acc") .. data.fromAccount, amount, data.comment, name, cachedAccounts[data.stateid].name, "withdraw") handleTransaction(data.stateid, locale("personal_acc") .. data.fromAccount, amount, data.comment, name, cachedAccounts[data.stateid].name, "deposit", transaction.trans_id) else TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("not_enough_money")) return false end else local Player2 = getPlayerData(source, data.stateid) if not Player2 then TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("fail_transfer")) return false end if funds.bank >= amount and RemoveMoney(Player, amount, 'bank', data.comment) then AddMoney(Player2, amount, 'bank', data.comment) local name2 = GetCharacterName(Player2) local transaction = handleTransaction(data.fromAccount, locale("personal_acc") .. data.fromAccount, amount, data.comment, name, name2, "withdraw") handleTransaction(data.stateid, locale("personal_acc") .. data.fromAccount, amount, data.comment, name, name2, "deposit", transaction.trans_id) else TriggerClientEvent('Renewed-Banking:client:sendNotification', source, locale("not_enough_money")) return false end end end local bankData = getBankData(source) return bankData end) RegisterNetEvent('Renewed-Banking:server:createNewAccount', function(accountid) local Player = GetPlayerObject(source) if cachedAccounts[accountid] then return Notify(source, {title = locale("bank_name"), description = locale("account_taken"), type = "error"}) end local cid = GetIdentifier(Player) cachedAccounts[accountid] = { id = accountid, type = locale("org"), name = accountid, frozen = 0, amount = 0, transactions = {}, auth = { [cid] = true }, creator = cid } cachedPlayers[cid].accounts[#cachedPlayers[cid].accounts+1] = accountid MySQL.insert("INSERT INTO bank_accounts_new (id, amount, transactions, auth, isFrozen, creator) VALUES (?, ?, ?, ?, ?, ?) ",{ accountid, cachedAccounts[accountid].amount, json.encode(cachedAccounts[accountid].transactions), json.encode({cid}), cachedAccounts[accountid].frozen, cid }) end) RegisterNetEvent("Renewed-Banking:server:getPlayerAccounts", function() local Player = GetPlayerObject(source) local cid = GetIdentifier(Player) local accounts = cachedPlayers[cid].accounts local data = {} if #accounts >= 1 then for k=1, #accounts do if cachedAccounts[accounts[k]].creator == cid then data[#data+1] = accounts[k] end end end TriggerClientEvent("Renewed-Banking:client:accountsMenu", source, data) end) RegisterNetEvent("Renewed-Banking:server:viewMemberManagement", function(data) local Player = GetPlayerObject(source) local account = data.account local retData = { account = account, members = {} } local cid = GetIdentifier(Player) for k,_ in pairs(cachedAccounts[account].auth) do local Player2 = getPlayerData(source, k) if cid ~= GetIdentifier(Player2) then retData.members[k] = GetCharacterName(Player2) end end TriggerClientEvent("Renewed-Banking:client:viewMemberManagement", source, retData) end) RegisterNetEvent('Renewed-Banking:server:addAccountMember', function(account, member) local Player = GetPlayerObject(source) if GetIdentifier(Player) ~= cachedAccounts[account].creator then print(locale("illegal_action", GetPlayerName(source))) return end local Player2 = getPlayerData(source, member) if not Player2 then return end local targetCID = GetIdentifier(Player2) if cachedPlayers[targetCID] then cachedPlayers[targetCID].accounts[#cachedPlayers[targetCID].accounts+1] = account end local auth = {} for k in pairs(cachedAccounts[account].auth) do auth[#auth+1] = k end auth[#auth+1] = targetCID cachedAccounts[account].auth[targetCID] = true MySQL.update('UPDATE bank_accounts_new SET auth = ? WHERE id = ?',{json.encode(auth), account}) end) RegisterNetEvent('Renewed-Banking:server:removeAccountMember', function(data) local Player = GetPlayerObject(source) if GetIdentifier(Player) ~= cachedAccounts[data.account].creator then print(locale("illegal_action", GetPlayerName(source))) return end local Player2 = getPlayerData(source, data.cid) if not Player2 then return end local targetCID = GetIdentifier(Player2) local tmp = {} for k in pairs(cachedAccounts[data.account].auth) do if targetCID ~= k then tmp[#tmp+1] = k end end if cachedPlayers[targetCID] then local newAccount = {} if #cachedPlayers[targetCID].accounts >= 1 then for k=1, #cachedPlayers[targetCID].accounts do if cachedPlayers[targetCID].accounts[k] ~= data.account then newAccount[#newAccount+1] = cachedPlayers[targetCID].accounts[k] end end end cachedPlayers[targetCID].accounts = newAccount end cachedAccounts[data.account].auth[targetCID] = nil MySQL.update('UPDATE bank_accounts_new SET auth = ? WHERE id = ?',{json.encode(tmp), data.account}) end) RegisterNetEvent('Renewed-Banking:server:deleteAccount', function(data) local account = data.account local Player = GetPlayerObject(source) local cid = GetIdentifier(Player) cachedAccounts[account] = nil for k=1, #cachedPlayers[cid].accounts do if cachedPlayers[cid].accounts[k] == account then cachedPlayers[cid].accounts[k] = nil end end MySQL.update("DELETE FROM `bank_accounts_new` WHERE id=:id", { id = account }) end) local find = string.find local sub = string.sub local function split(str, delimiter) local result = {} local from = 1 local delim_from, delim_to = find(str, delimiter, from) while delim_from do result[#result + 1] = sub(str, from, delim_from - 1) from = delim_to + 1 delim_from, delim_to = find(str, delimiter, from) end result[#result + 1] = sub(str, from) return result end local function updateAccountName(account, newName, src) if not account or not newName then return false end if not cachedAccounts[account] then local getTranslation = locale("invalid_account", account) print(getTranslation) if src then Notify(src, {title = locale("bank_name"), description = split(getTranslation, '0')[2], type = "error"}) end return false end if cachedAccounts[newName] then local getTranslation = locale("existing_account", account) print(getTranslation) if src then Notify(src, {title = locale("bank_name"), description = split(getTranslation, '0')[2], type = "error"}) end return false end if src then local Player = GetPlayerObject(src) if GetIdentifier(Player) ~= cachedAccounts[account].creator then local getTranslation = locale("illegal_action", GetPlayerName(src)) print(getTranslation) Notify(src, {title = locale("bank_name"), description = split(getTranslation, '0')[2], type = "error"}) return false end end cachedAccounts[newName] = json.decode(json.encode(cachedAccounts[account])) cachedAccounts[newName].id = newName cachedAccounts[newName].name = newName cachedAccounts[account] = nil for _, id in ipairs(GetPlayers()) do local Player2 = GetPlayerObject(id) if not Player2 then goto Skip end local cid = GetIdentifier(Player2) if #cachedPlayers[cid].accounts >= 1 then for k=1, #cachedPlayers[cid].accounts do if cachedPlayers[cid].accounts[k] == account then table.remove(cachedPlayers[cid].accounts, k) cachedPlayers[cid].accounts[#cachedPlayers[cid].accounts+1] = newName end end end ::Skip:: end MySQL.update('UPDATE bank_accounts_new SET id = ? WHERE id = ?',{newName, account}) return true end RegisterNetEvent('Renewed-Banking:server:changeAccountName', function(account, newName) updateAccountName(account, newName, source) end) exports("changeAccountName", updateAccountName)-- Should only use this on very secure backends to avoid anyone using this as this is a server side ONLY export -- --- Retrieves a cached job account if it exists. ---@param jobName string The name of the job whose account is being retrieved. ---@return table|nil account Returns the job account if it exists, otherwise `nil`. function GetJobAccount(jobName) if type(jobName) ~= "string" or jobName == "" then error(("^5[%s]^7-^1[ERROR]^7 %s"):format(GetInvokingResource(), "Invalid job name: expected a non-empty string")) end return cachedAccounts[jobName] or nil -- Returns account if found, otherwise nil end exports('GetJobAccount', GetJobAccount) --- Creates a shared job account for an organization/society. --- @param job table A table containing job account details: --- job.name string - The unique identifier for the job (e.g., "mechanic", "police"). --- job.label string - The display name/label for the job (e.g., "Mechanic", "Police Department"). --- @param initialBalance number? The starting balance of the account. Default is 0. --- @return table Returns the account table if found or successfully created. This function may raise an error if validation or database insertion fails. local function CreateJobAccount(job, initialBalance) local currentResourceName = GetInvokingResource() -- Validate input parameters if type(job) ~= "table" then error(("^5[%s]^7-^1[ERROR]^7 %s"):format(currentResourceName, "Invalid parameter: expected a table (job)")) end if type(job.name) ~= "string" or job.name == "" then error(("^5[%s]^7-^1[ERROR]^7 %s"):format(currentResourceName, "Invalid job name: expected a non-empty string")) end if type(job.label) ~= "string" or job.label == "" then error(("^5[%s]^7-^1[ERROR]^7 %s"):format(currentResourceName, "Invalid job label: expected a non-empty string")) end -- Check if account already exists if cachedAccounts[job.name] then return cachedAccounts[job.name] end -- Create the job account in cache cachedAccounts[job.name] = { id = job.name, type = locale("org"), name = job.label, frozen = 0, amount = tonumber(initialBalance) or 0, transactions = {}, auth = {}, creator = nil } local success, errorMsg = MySQL.insert("INSERT INTO bank_accounts_new (id, amount, transactions, auth, isFrozen, creator) VALUES (?, ?, ?, ?, ?, NULL)", { job.name, cachedAccounts[job.name].amount, json.encode(cachedAccounts[job.name].transactions), -- Convert transactions to JSON json.encode(cachedAccounts[job.name].auth), -- Convert auth list to JSON cachedAccounts[job.name].frozen }) -- Handle potential database errors if not success then cachedAccounts[job.name] = nil error(("^5[%s]^7-^1[ERROR]^7 %s"):format(currentResourceName, "Database error: " .. tostring(errorMsg))) end return cachedAccounts[job.name] end exports("CreateJobAccount", CreateJobAccount) local function addAccountMember(account, member) if not account or not member then return end if not cachedAccounts[account] then print(locale("invalid_account", account)) return end local Player2 = getPlayerData(false, member) if not Player2 then return end local targetCID = GetIdentifier(Player2) if cachedPlayers[targetCID] then cachedPlayers[targetCID].accounts[#cachedPlayers[targetCID].accounts+1] = account end local auth = {} for k, _ in pairs(cachedAccounts[account].auth) do auth[#auth+1] = k end auth[#auth+1] = targetCID cachedAccounts[account].auth[targetCID] = true MySQL.update('UPDATE bank_accounts_new SET auth = ? WHERE id = ?',{json.encode(auth), account}) end exports("addAccountMember", addAccountMember) local function removeAccountMember(account, member) local Player2 = getPlayerData(false, member) if not Player2 then return end if not cachedAccounts[account] then print(locale("invalid_account", account)) return end local targetCID = GetIdentifier(Player2) local tmp = {} for k in pairs(cachedAccounts[account].auth) do if targetCID ~= k then tmp[#tmp+1] = k end end if cachedPlayers[targetCID] then local newAccount = {} if #cachedPlayers[targetCID].accounts >= 1 then for k=1, #cachedPlayers[targetCID].accounts do if cachedPlayers[targetCID].accounts[k] ~= account then newAccount[#newAccount+1] = cachedPlayers[targetCID].accounts[k] end end end cachedPlayers[targetCID].accounts = newAccount end cachedAccounts[account].auth[targetCID] = nil MySQL.update('UPDATE bank_accounts_new SET auth = ? WHERE id = ?',{json.encode(tmp), account}) end exports("removeAccountMember", removeAccountMember) local function getAccountTransactions(account) if cachedAccounts[account] then return cachedAccounts[account].transactions elseif cachedPlayers[account] then return cachedPlayers[account].transactions end print(locale("invalid_account", account)) return false end exports("getAccountTransactions", getAccountTransactions) lib.addCommand('givecash', { help = 'Gives an item to a player', params = { { name = 'target', type = 'playerId', help = locale("cmd_plyr_id"), }, { name = 'amount', type = 'number', help = locale("cmd_amount"), } } }, function(source, args) local Player = GetPlayerObject(source) if not Player then return end local iPlayer = GetPlayerObject(args.target) if not iPlayer then return Notify(source, {title = locale("bank_name"), description = locale('unknown_player', args.target), type = "error"}) end if IsDead(Player) then return Notify(source, {title = locale("bank_name"), description = locale('dead'), type = "error"}) end if #(GetEntityCoords(GetPlayerPed(source)) - GetEntityCoords(GetPlayerPed(args.target))) > 10.0 then return Notify(source, {title = locale("bank_name"), description = locale('too_far_away'), type = "error"}) end if args.amount < 0 then return Notify(source, {title = locale("bank_name"), description = locale('invalid_amount', "give"), type = "error"}) end if RemoveMoney(Player, args.amount, 'cash') then AddMoney(iPlayer, args.amount, 'cash') local nameA = GetCharacterName(Player) local nameB = GetCharacterName(iPlayer) Notify(source, {title = locale("bank_name"), description = locale('give_cash', nameB, tostring(args.amount)), type = "error"}) Notify(args.target, {title = locale("bank_name"), description = locale('received_cash', nameA, tostring(args.amount)), type = "success"}) else Notify(args.target, {title = locale("bank_name"), description = locale('not_enough_money'), type = "error"}) end end) function ExportHandler(resource, name, cb) AddEventHandler(('__cfx_export_%s_%s'):format(resource, name), function(setCB) setCB(cb) end) end local createTables = { { query = "CREATE TABLE IF NOT EXISTS `bank_accounts_new` (`id` varchar(50) NOT NULL, `amount` int(11) DEFAULT 0, `transactions` longtext DEFAULT '[]', `auth` longtext DEFAULT '[]', `isFrozen` int(11) DEFAULT 0, `creator` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`));", values = nil }, { query = "CREATE TABLE IF NOT EXISTS `player_transactions` (`id` varchar(50) NOT NULL, `isFrozen` int(11) DEFAULT 0, `transactions` longtext DEFAULT '[]', PRIMARY KEY (`id`));", values = nil } } assert(MySQL.transaction.await(createTables), "Failed to create tables")