lib.versionCheck('Project-Sloth/ps-banking') assert(lib.checkDependency('ox_lib', '3.20.0', true)) local framework = nil if GetResourceState("es_extended") == "started" then framework = "ESX" ESX = exports["es_extended"]:getSharedObject() elseif GetResourceState("qb-core") == "started" then framework = "QBCore" QBCore = exports["qb-core"]:GetCoreObject() else return error(locale("no_framework_found")) end local function getPlayerIdentifier(player) if framework == "ESX" then return player.getIdentifier() elseif framework == "QBCore" then return player.PlayerData.citizenid end end local function getPlayerFromId(source) if framework == "ESX" then return ESX.GetPlayerFromId(source) elseif framework == "QBCore" then return QBCore.Functions.GetPlayer(source) end end local function getPlayerAccounts(player) if framework == "ESX" then return player.getAccount("bank").money elseif framework == "QBCore" then return player.PlayerData.money["bank"] end end local function getName(player) if framework == "ESX" then return player.getName() elseif framework == "QBCore" then return player.PlayerData.charinfo.firstname .. " " .. player.PlayerData.charinfo.lastname end end lib.callback.register("ps-banking:server:getHistory", function(source) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) local result = MySQL.query.await('SELECT * FROM ps_banking_transactions WHERE identifier = ?', { identifier }) return result end) lib.callback.register("ps-banking:server:deleteHistory", function(source) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) MySQL.query.await('DELETE FROM ps_banking_transactions WHERE identifier = ?', { identifier }) return true end) lib.callback.register("ps-banking:server:payAllBills", function(source) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) local result = MySQL.query.await('SELECT SUM(amount) as total FROM ps_banking_bills WHERE identifier = ? AND isPaid = 0', { identifier }) local totalAmount = result[1].total or 0 local bankBalance = getPlayerAccounts(xPlayer) if tonumber(bankBalance) >= tonumber(totalAmount) then if framework == "ESX" then xPlayer.removeAccountMoney("bank", tonumber(totalAmount)) elseif framework == "QBCore" then xPlayer.Functions.RemoveMoney("bank", tonumber(totalAmount)) end MySQL.query.await('DELETE FROM ps_banking_bills WHERE identifier = ?', { identifier }) return true else return false end end) lib.callback.register("ps-banking:server:getWeeklySummary", function(source) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) local receivedResult = MySQL.query.await('SELECT SUM(amount) as totalReceived FROM ps_banking_transactions WHERE identifier = ? AND isIncome = ? AND DATE(date) >= DATE(NOW() - INTERVAL 7 DAY)', { identifier, true }) local totalReceived = receivedResult[1].totalReceived or 0 local usedResult = MySQL.query.await('SELECT SUM(amount) as totalUsed FROM ps_banking_transactions WHERE identifier = ? AND isIncome = ? AND DATE(date) >= DATE(NOW() - INTERVAL 7 DAY)', { identifier, false }) local totalUsed = usedResult[1].totalUsed or 0 return { totalReceived = totalReceived, totalUsed = totalUsed, } end) lib.callback.register("ps-banking:server:transferMoney", function(source, data) local xPlayer = getPlayerFromId(source) local targetPlayer = getPlayerFromId(data.id) local amount = tonumber(data.amount) if data.id == source and data.method == "id" then return false, locale("cannot_send_self_money") end if xPlayer and targetPlayer and amount > 0 then local xPlayerBalance = getPlayerAccounts(xPlayer) if xPlayerBalance >= amount then if data.method == "id" then if framework == "ESX" then xPlayer.removeAccountMoney("bank", amount) targetPlayer.addAccountMoney("bank", amount) elseif framework == "QBCore" then xPlayer.Functions.RemoveMoney("bank", amount) targetPlayer.Functions.AddMoney("bank", amount) end return true, locale("money_sent", amount, getName(targetPlayer)) elseif data.method == "phone" then exports["lb-phone"]:AddTransaction(targetPlayer.identifier, amount, locale("received_money", getName(xPlayer), amount)) return true, locale("money_sent", amount, getName(targetPlayer)) end else return false, locale("no_money") end else return false, locale("user_not_in_city") end end) local function logTransaction(identifier, description, accountName, amount, isIncome) MySQL.insert.await('INSERT INTO ps_banking_transactions (identifier, description, type, amount, date, isIncome) VALUES (?, ?, ?, ?, NOW(), ?)', { identifier, description, accountName, amount, isIncome }) end RegisterNetEvent("ps-banking:server:logClient", function(account, moneyData) if account.name ~= "bank" then return end local src = source local xPlayer = getPlayerFromId(src) local identifier = getPlayerIdentifier(xPlayer) local previousBankBalance = 0 if moneyData then for _, data in ipairs(moneyData) do if data.name == "bank" then previousBankBalance = data.amount break end end end local currentBankBalance = getPlayerAccounts(xPlayer) local amountChange = currentBankBalance - previousBankBalance if amountChange ~= 0 then local isIncome = currentBankBalance >= previousBankBalance and true or false local description = locale("transaction_description") logTransaction(identifier, description, account.name, math.abs(amountChange), isIncome) end end) lib.callback.register("ps-banking:server:getTransactionStats", function(source) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) local result = MySQL.query.await('SELECT COUNT(*) as totalCount, SUM(amount) as totalAmount FROM ps_banking_transactions WHERE identifier = ?', { identifier }) local transactionData = MySQL.query.await('SELECT amount, date FROM ps_banking_transactions WHERE identifier = ? ORDER BY date DESC LIMIT 50', { identifier }) return { totalCount = result[1].totalCount, totalAmount = result[1].totalAmount, transactionData = transactionData, } end) lib.callback.register("ps-banking:server:createNewAccount", function(source, newAccount) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end MySQL.insert.await('INSERT INTO ps_banking_accounts (balance, holder, cardNumber, users, owner) VALUES (?, ?, ?, ?, ?)', { newAccount.balance, newAccount.holder, newAccount.cardNumber, json.encode(newAccount.users), json.encode(newAccount.owner) }) return true end) lib.callback.register("ps-banking:server:getUser", function(source) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end return { name = getName(xPlayer), identifier = getPlayerIdentifier(xPlayer), } end) lib.callback.register("ps-banking:server:getAccounts", function(source) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end local playerIdentifier = getPlayerIdentifier(xPlayer) local accounts = MySQL.query.await('SELECT * FROM ps_banking_accounts') local result = {} for _, account in ipairs(accounts) do local accountData = { id = account.id, balance = account.balance, holder = account.holder, cardNumber = account.cardNumber, users = json.decode(account.users), owner = json.decode(account.owner), } if accountData.owner.identifier == playerIdentifier then accountData.owner.state = true table.insert(result, accountData) else for _, user in ipairs(accountData.users) do if user.identifier == playerIdentifier then accountData.owner.state = false table.insert(result, accountData) break end end end end return result end) lib.callback.register("ps-banking:server:deleteAccount", function(source, accountId) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end MySQL.query.await('DELETE FROM ps_banking_accounts WHERE id = ?', { accountId }) return true end) lib.callback.register("ps-banking:server:withdrawFromAccount", function(source, accountId, amount) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end local account = MySQL.query.await('SELECT * FROM ps_banking_accounts WHERE id = ?', { accountId }) if #account > 0 then local balance = account[1].balance if balance >= amount then local affectedRows = MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance - ? WHERE id = ?', { amount, accountId }) if affectedRows > 0 then if framework == "ESX" then xPlayer.addAccountMoney("bank", amount) elseif framework == "QBCore" then xPlayer.Functions.AddMoney("bank", amount) end return true else return false end else return false end else return false end end) lib.callback.register("ps-banking:server:depositToAccount", function(source, accountId, amount) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end if getPlayerAccounts(xPlayer) >= amount then local affectedRows = MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance + ? WHERE id = ?', { amount, accountId }) if affectedRows > 0 then if framework == "ESX" then xPlayer.removeAccountMoney("bank", amount) elseif framework == "QBCore" then xPlayer.Functions.RemoveMoney("bank", amount) end return true else return false end else return false end end) lib.callback.register("ps-banking:server:addUserToAccount", function(source, accountId, userId) local xPlayer = getPlayerFromId(source) local targetPlayer = getPlayerFromId(userId) local promise = promise.new() if source == userId then return { success = false, message = locale("cannot_add_self"), } end if not xPlayer then return { success = false, message = locale("player_not_found"), } end if not targetPlayer then return { success = false, message = locale("target_player_not_found"), } end local accounts = MySQL.query.await('SELECT * FROM ps_banking_accounts WHERE id = ?', { accountId }) if #accounts > 0 then local account = accounts[1] local users = json.decode(account.users) for _, user in ipairs(users) do if user.identifier == userId then return { success = false, message = locale("user_already_in_account"), } end end table.insert(users, { name = getName(targetPlayer), identifier = getPlayerIdentifier(targetPlayer), }) local affectedRows = MySQL.update.await('UPDATE ps_banking_accounts SET users = ? WHERE id = ?', { json.encode(users), accountId }) return { success = affectedRows > 0, userName = getName(targetPlayer), } else return { success = false, message = locale("account_not_found"), } end end) lib.callback.register("ps-banking:server:removeUserFromAccount", function(source, accountId, userId) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end local accounts = MySQL.query.await('SELECT * FROM ps_banking_accounts WHERE id = ?', { accountId }) if #accounts > 0 then local account = accounts[1] local users = json.decode(account.users) local updatedUsers = {} for _, user in ipairs(users) do if user.identifier ~= userId then table.insert(updatedUsers, user) end end local affectedRows = MySQL.update.await('UPDATE ps_banking_accounts SET users = ? WHERE id = ?', { json.encode(updatedUsers), accountId }) return affectedRows > 0 else return false end end) lib.callback.register("ps-banking:server:renameAccount", function(source, id, newName) local xPlayer = getPlayerFromId(source) if not xPlayer then return false end local affectedRows = MySQL.update.await('UPDATE ps_banking_accounts SET holder = ? WHERE id = ?', { newName, id }) return affectedRows > 0 end) lib.callback.register("ps-banking:server:ATMwithdraw", function(source, amount) local xPlayer = getPlayerFromId(source) local bankBalance = getPlayerAccounts(xPlayer) if bankBalance >= amount then if framework == "ESX" then xPlayer.removeAccountMoney("bank", amount) xPlayer.addMoney(amount) elseif framework == "QBCore" then xPlayer.Functions.RemoveMoney("bank", amount) xPlayer.Functions.AddMoney("cash", amount) end return true else return false end end) lib.callback.register("ps-banking:server:ATMdeposit", function(source, amount) local xPlayer = getPlayerFromId(source) local cashBalance = nil if framework == "ESX" then cashBalance = xPlayer.getMoney() elseif framework == "QBCore" then cashBalance = xPlayer.PlayerData.money["cash"] end if cashBalance >= amount then if framework == "ESX" then xPlayer.removeMoney(amount) xPlayer.addAccountMoney("bank", amount) elseif framework == "QBCore" then xPlayer.Functions.RemoveMoney("cash", amount) xPlayer.Functions.AddMoney("bank", amount) end return true else return false end end) lib.callback.register("ps-banking:server:getBills", function(source) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) local result = MySQL.query.await('SELECT * FROM ps_banking_bills WHERE identifier = ?', { identifier }) return result end) lib.callback.register("ps-banking:server:payBill", function(source, billId) local xPlayer = getPlayerFromId(source) local identifier = getPlayerIdentifier(xPlayer) local result = MySQL.query.await('SELECT * FROM ps_banking_bills WHERE id = ? AND identifier = ? AND isPaid = 0', { billId, identifier }) if #result == 0 then return false end local bill = result[1] local amount = bill.amount if tonumber(getPlayerAccounts(xPlayer)) >= tonumber(amount) then if framework == "ESX" then xPlayer.removeAccountMoney("bank", tonumber(amount)) elseif framework == "QBCore" then xPlayer.Functions.RemoveMoney("bank", tonumber(amount)) end MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', { billId }) -- Add this line to trigger our custom event TriggerEvent('ps-banking:server:billPaid', billId) return true else return false end end) function createBill(data) local identifier = data.identifier local description = data.description local type = data.type local amount = data.amount MySQL.insert.await('INSERT INTO ps_banking_bills (identifier, description, type, amount, date, isPaid) VALUES (?, ?, ?, ?, NOW(), ?)', { identifier, description, type, amount, false }) end exports("createBill", createBill) --[[ EXAMPLE exports["ps-banking"]:createBill({ identifier = "char1:df6c12c50e2712c57b1386e7103d5a372fb960a0", description = "Utility Bill", type = "Expense", amount = 150.00, }) ]]