-- 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 { success = 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 if not billId then return { success = false } end -- 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 }) -- Store sender information for notifications MySQL.insert.await('INSERT INTO billing_senders (bill_id, sender_id, sender_source) VALUES (?, ?, ?)', { billId, player.PlayerData.citizenid, src }) -- Send payment prompt to target player TriggerClientEvent('billing:client:showPaymentPrompt', data.playerId, { billId = billId, amount = data.amount, reason = data.reason, sender = senderName }) -- Notify the sender TriggerClientEvent('QBCore:Notify', src, 'Rechnung über $' .. data.amount .. ' an ' .. target.PlayerData.charinfo.firstname .. ' gesendet', 'success') -- Return success with bill ID return { success = true, billId = billId } 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 callback to get all bill statuses for a player lib.callback.register('billing:server:getAllBillStatuses', function(source) local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return nil end local citizenId = player.PlayerData.citizenid -- Get all bill statuses for this player local result = MySQL.query.await('SELECT * FROM billing_accounts WHERE receiver_id = ?', {citizenId}) if result and #result > 0 then return result end return {} end) -- Add a 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 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") -- IMPORTANT: Delete the bill from ps_banking_bills table MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId}) -- Process the payment to the recipient's account ProcessBillPayment(data.billId) -- Notify the player TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' bezahlt', 'success') 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}) -- IMPORTANT: Delete the bill from ps_banking_bills table when declined MySQL.query.await('DELETE FROM ps_banking_bills WHERE 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 -- Notify the player TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung abgelehnt', 'info') return true elseif data.action == 'later' then -- Simply close the prompt without any action -- The bill will remain in the system as unpaid -- Notify the player TriggerClientEvent('QBCore:Notify', src, 'Die Rechnung wurde für später gespeichert', 'info') 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 TriggerClientEvent('QBCore:Notify', src, 'Du hast keinen Zugriff auf dieses Konto', 'error') return false end -- Check if account has enough balance if tonumber(account.balance) < amount then TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Geld auf diesem Konto', 'error') return false end -- Process payment MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance - ? WHERE id = ?', {amount, accountId}) -- IMPORTANT: Delete the bill from ps_banking_bills table MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {billId}) -- Process the payment to the recipient's account ProcessBillPayment(billId) -- Notify the player TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' vom Konto ' .. account.holder .. ' bezahlt', 'success') 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 TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Geld auf deinem Konto', 'error') return false end -- Process payment manually player.Functions.RemoveMoney("bank", amount, "bill-payment") -- IMPORTANT: Delete the bill from ps_banking_bills table MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId}) -- Process the payment to the recipient's account ProcessBillPayment(data.billId) -- Notify the player TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' bezahlt', 'success') 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 }) -- Try to notify the account owner if online local accountResult = MySQL.query.await('SELECT * FROM ps_banking_accounts WHERE id = ?', {accountId}) if accountResult and #accountResult > 0 then local account = accountResult[1] local owner = json.decode(account.owner) local ownerPlayer = QBCore.Functions.GetPlayerByCitizenId(owner.identifier) if ownerPlayer then TriggerClientEvent('QBCore:Notify', ownerPlayer.PlayerData.source, 'Das Konto ' .. account.holder .. ' hat $' .. amount .. ' von einer Rechnungszahlung erhalten', 'success') end end 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() -- First check if the billing_accounts table exists local tableExists = MySQL.query.await("SHOW TABLES LIKE 'billing_accounts'") if #tableExists > 0 then -- Table exists, check if the declined column exists local columnExists = MySQL.query.await("SHOW COLUMNS FROM billing_accounts LIKE 'declined'") if #columnExists == 0 then -- Add the declined column if it doesn't exist MySQL.query.await("ALTER TABLE billing_accounts ADD COLUMN declined TINYINT DEFAULT 0") print("^2[nordi_billing] Added 'declined' column to billing_accounts table^7") end else -- Create the table with all required columns 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 ) ]]) print("^2[nordi_billing] Created billing_accounts table^7") end -- Create billing_senders table for tracking bill senders MySQL.query.await([[ CREATE TABLE IF NOT EXISTS billing_senders ( id INT AUTO_INCREMENT PRIMARY KEY, bill_id INT, sender_id VARCHAR(50), sender_source INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ]]) -- Create offline_payments table if it doesn't exist 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 ) ]]) print("^2[nordi_billing] Database tables initialized^7") end) -- Event to notify bill sender RegisterNetEvent('billing:server:notifyBillSender', function(billId, action) local src = source local player = QBCore.Functions.GetPlayer(src) if not player then return end -- Get sender information local senderInfo = MySQL.query.await('SELECT * FROM billing_senders WHERE bill_id = ?', {billId}) if not senderInfo or #senderInfo == 0 then return end local senderSource = senderInfo[1].sender_source -- Get bill information local billInfo = MySQL.query.await('SELECT * FROM billing_accounts WHERE bill_id = ?', {billId}) if not billInfo or #billInfo == 0 then return end local amount = billInfo[1].amount local playerName = player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname -- Notify the sender TriggerClientEvent('billing:client:billResponse', senderSource, billId, action, playerName) 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 -- Check for unpaid bills local unpaidBills = MySQL.query.await('SELECT COUNT(*) as count FROM ps_banking_bills WHERE identifier = ? AND isPaid = 0', {citizenId}) if unpaidBills and unpaidBills[1].count > 0 then TriggerClientEvent('QBCore:Notify', src, 'Du hast ' .. unpaidBills[1].count .. ' unbezahlte Rechnungen. Tippe /bills um sie anzuzeigen.', 'info') end end) -- Hook into ps-banking bill payments -- This is crucial for handling payments made through ps-banking interface 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) -- Register a command to check bills QBCore.Commands.Add('bills', 'Zeige deine unbezahlten Rechnungen an', {}, false, function(source) TriggerClientEvent('QBCore:Notify', source, 'Öffne Rechnungsübersicht...', 'info') -- This will trigger the client to open the bills menu TriggerClientEvent('billing:client:openBillsMenu', source) end)