-- Initialize QBCore local QBCore = exports['qb-core']:GetCoreObject() -- Register callback for sending bills lib.callback.register('billing:server:sendBill', function(source, data) local src = source local player = QBCore.Functions.GetPlayer(src) local target = QBCore.Functions.GetPlayer(data.playerId) if not player or not target then return false end local senderName = player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname local description = data.reason .. ' (Von: ' .. senderName .. ')' -- Create the bill exports["ps-banking"]:createBill({ identifier = target.PlayerData.citizenid, description = description, type = "Invoice", amount = data.amount, }) -- Get the latest bill ID for this user (assuming it's the one we just created) local result = MySQL.query.await('SELECT id FROM ps_banking_bills WHERE identifier = ? ORDER BY id DESC LIMIT 1', { target.PlayerData.citizenid }) local billId = result[1] and result[1].id or nil -- Store additional data about the bill MySQL.insert.await('INSERT INTO billing_accounts (bill_id, bill_description, sender_id, receiver_id, account_id, amount, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())', { billId, description, player.PlayerData.citizenid, target.PlayerData.citizenid, data.account, data.amount }) -- Send payment prompt to target player TriggerClientEvent('billing:client:showPaymentPrompt', data.playerId, { billId = billId, amount = data.amount, reason = data.reason, sender = senderName }) return true end) -- Add a callback to get bill status lib.callback.register('billing:server:getBillStatus', function(source, billId) local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return nil end local result = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId}) if result and #result > 0 then return result[1] end return nil end) -- Add a new callback for handling bill responses lib.callback.register('billing:server:handleBillResponse', function(source, data) local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return false end if data.action == 'pay' then -- Process payment based on selected account if data.accountId == 'personal' then -- Pay from personal bank account using ps-banking's payBill function local billResult = MySQL.query.await('SELECT * FROM ps_banking_bills WHERE id = ?', {data.billId}) if not billResult or #billResult == 0 then return false end local bill = billResult[1] local amount = tonumber(bill.amount) -- Check if player has enough money in their bank account if player.PlayerData.money["bank"] < amount then return false end -- Process payment manually instead of using callback to avoid issues player.Functions.RemoveMoney("bank", amount, "bill-payment") MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId}) -- Process the payment to the recipient's account ProcessBillPayment(data.billId) return true else -- Pay from shared account local success = PayBillFromSharedAccount(src, data.billId, data.accountId) return success end elseif data.action == 'decline' then -- Mark as declined in our system MySQL.update.await('UPDATE billing_accounts SET declined = 1 WHERE bill_id = ?', {data.billId}) -- Find the sender to notify them local billInfo = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {data.billId}) if billInfo and #billInfo > 0 then local senderId = billInfo[1].sender_id local sender = QBCore.Functions.GetPlayerByCitizenId(senderId) if sender then TriggerClientEvent('QBCore:Notify', sender.PlayerData.source, 'Deine Rechnung wurde abgelehnt', 'error') end end return true elseif data.action == 'later' then -- No action needed, bill remains in system return true end return false end) -- Function to pay bill from a shared account function PayBillFromSharedAccount(source, billId, accountId) local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return false end -- Get bill details local billResult = MySQL.query.await('SELECT * FROM ps_banking_bills WHERE id = ?', {billId}) if not billResult or #billResult == 0 then return false end local bill = billResult[1] local amount = tonumber(bill.amount) -- Get account details local accountResult = MySQL.query.await('SELECT * FROM ps_banking_accounts WHERE id = ?', {accountId}) if not accountResult or #accountResult == 0 then return false end local account = accountResult[1] -- Check if player has access to this account local hasAccess = false local accountOwner = json.decode(account.owner) local accountUsers = json.decode(account.users) if accountOwner.identifier == player.PlayerData.citizenid then hasAccess = true else for _, user in ipairs(accountUsers) do if user.identifier == player.PlayerData.citizenid then hasAccess = true break end end end if not hasAccess then return false end -- Check if account has enough balance if tonumber(account.balance) < amount then return false end -- Process payment MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance - ? WHERE id = ?', {amount, accountId}) MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {billId}) -- Process the payment to the recipient's account ProcessBillPayment(billId) return true end -- Add a callback for paying bill from selected account lib.callback.register('billing:server:payBillFromAccount', function(source, data) local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return false end if data.accountId == 'personal' then -- Pay from personal bank account local billResult = MySQL.query.await('SELECT * FROM ps_banking_bills WHERE id = ?', {data.billId}) if not billResult or #billResult == 0 then return false end local bill = billResult[1] local amount = tonumber(bill.amount) -- Check if player has enough money in their bank account if player.PlayerData.money["bank"] < amount then return false end -- Process payment manually player.Functions.RemoveMoney("bank", amount, "bill-payment") MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId}) -- Process the payment to the recipient's account ProcessBillPayment(data.billId) return true else -- Pay from shared account return PayBillFromSharedAccount(src, data.billId, data.accountId) end end) -- Function to process bill payment to recipient function ProcessBillPayment(billId) -- Find the bill in our custom table local billData = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId}) if billData and #billData > 0 then local bill = billData[1] local receiverId = bill.sender_id local accountId = bill.account_id local amount = tonumber(bill.amount) -- If it's a personal account if accountId == 'personal' then local receiver = QBCore.Functions.GetPlayerByCitizenId(receiverId) if receiver then -- Add money directly to the receiver's bank account receiver.Functions.AddMoney('bank', amount, 'bill-payment') TriggerClientEvent('QBCore:Notify', receiver.PlayerData.source, 'Du hast $' .. amount .. ' von einer Rechnungszahlung erhalten', 'success') else -- Handle offline player MySQL.insert.await('INSERT INTO offline_payments (citizen_id, amount, reason) VALUES (?, ?, ?)', { receiverId, amount, 'Rechnungszahlung' }) end else -- Add money to the specified account MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance + ? WHERE id = ?', { amount, accountId }) end -- Update the bill status MySQL.update.await('UPDATE billing_accounts SET paid = 1, paid_at = NOW() WHERE bill_id = ?', {billId}) end end -- Create the necessary database tables if they don't exist MySQL.ready(function() MySQL.query.await([[ CREATE TABLE IF NOT EXISTS billing_accounts ( id INT AUTO_INCREMENT PRIMARY KEY, bill_id INT, bill_description VARCHAR(255), sender_id VARCHAR(50), receiver_id VARCHAR(50), account_id VARCHAR(50), amount DECIMAL(10,2), created_at DATETIME, paid TINYINT DEFAULT 0, declined TINYINT DEFAULT 0, paid_at DATETIME ) ]]) MySQL.query.await([[ CREATE TABLE IF NOT EXISTS offline_payments ( id INT AUTO_INCREMENT PRIMARY KEY, citizen_id VARCHAR(50), amount DECIMAL(10,2), reason VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, processed TINYINT DEFAULT 0 ) ]]) end) -- Handle offline payments when a player logs in RegisterNetEvent('QBCore:Server:PlayerLoaded', function() local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return end local citizenId = player.PlayerData.citizenid local offlinePayments = MySQL.query.await('SELECT * FROM offline_payments WHERE citizen_id = ? AND processed = 0', {citizenId}) if offlinePayments and #offlinePayments > 0 then local totalAmount = 0 for _, payment in ipairs(offlinePayments) do totalAmount = totalAmount + payment.amount end if totalAmount > 0 then player.Functions.AddMoney('bank', totalAmount, 'offline-bill-payments') TriggerClientEvent('QBCore:Notify', src, 'Du hast $' .. totalAmount .. ' von Rechnungszahlungen erhalten, während du offline warst', 'success') -- Mark payments as processed MySQL.update.await('UPDATE offline_payments SET processed = 1 WHERE citizen_id = ? AND processed = 0', {citizenId}) end end end) -- Alternative hook method if you can't modify ps-banking -- This listens for the MySQL query that deletes a bill (which happens when a bill is paid) AddEventHandler('oxmysql:query', function(query, params) if string.find(query, "DELETE FROM ps_banking_bills WHERE id = ?") then -- This is likely a bill payment local billId = params[1] if billId then -- Small delay to ensure the deletion completes SetTimeout(100, function() ProcessBillPayment(billId) end) end end end)