-- Initialize QBCore local QBCore = exports['qb-core']:GetCoreObject() local isUiOpen = false local cooldown = false -- Command to open main billing menu RegisterCommand('bill', function() OpenMainBillingMenu() end, false) -- Keybind registration RegisterKeyMapping('bill', 'Open Billing Menu', 'keyboard', 'F7') -- Default key is F7, can be changed in settings -- Function to open the main billing menu function OpenMainBillingMenu() lib.registerContext({ id = 'main_billing_menu', title = 'Billing System', options = { { title = 'Rechnung erstellen', description = 'Sende eine Rechnung an einen Spieler in der Nähe', icon = 'file-invoice-dollar', onSelect = function() OpenCreateBillMenu() end }, { title = 'Meine Rechnungen', description = 'Sehe & bezahle deine erhaltenen Rechnungen', icon = 'money-check', onSelect = function() ViewBills() end } } }) lib.showContext('main_billing_menu') end -- Function to open the create bill menu function OpenCreateBillMenu() if cooldown then lib.notify({ title = 'Billing System', description = 'Bitte warte, bevor du das Abrechnungssystem erneut verwendest', type = 'error' }) return end -- Get nearby players local players = GetNearbyPlayers() if #players == 0 then lib.notify({ title = 'Billing System', description = 'Keine Spieler in der Nähe zum Abrechnen', type = 'error' }) return end -- Get player's accounts for receiving payment local accounts = lib.callback.await('ps-banking:server:getAccounts', false) or {} local accountOptions = {} -- Add default bank account table.insert(accountOptions, { value = 'personal', label = 'Persönliches Bankkonto' }) -- Add other accounts for _, account in ipairs(accounts) do table.insert(accountOptions, { value = account.id, label = account.holder .. ' - ' .. account.cardNumber }) end -- Show the list of nearby players local playerOptions = {} for _, player in ipairs(players) do table.insert(playerOptions, { title = player.name, description = 'ID: ' .. player.id, icon = 'user', onSelect = function() -- When a player is selected, show the billing form ShowBillingForm(player, accountOptions) end }) end -- Register and show the player selection menu lib.registerContext({ id = 'nearby_players_menu', title = 'Spieler zum Abrechnen auswählen', menu = 'main_billing_menu', -- Add back button to main menu options = playerOptions }) lib.showContext('nearby_players_menu') end -- Function to show the billing form after selecting a player function ShowBillingForm(selectedPlayer, accountOptions) local input = lib.inputDialog('Rechnung erstellen für ' .. selectedPlayer.name, { { type = 'number', label = 'Betrag ($)', description = 'Gib den Rechnungsbetrag ein', icon = 'dollar-sign', required = true, min = 1, max = 100000 }, { type = 'input', label = 'Grund', description = 'Gib den Grund für die Rechnung ein', placeholder = 'Erbrachte Dienstleistungen...', required = true }, { type = 'select', label = 'Empfangskonto', options = accountOptions, required = true } }) if not input then return end -- Play animation PlayBillingAnimation() -- Send the bill local success = lib.callback.await('billing:server:sendBill', false, { playerId = selectedPlayer.id, amount = input[1], reason = input[2], account = input[3] }) if success then lib.notify({ title = 'Billing System', description = 'Rechnung erfolgreich an ' .. selectedPlayer.name .. ' gesendet', type = 'success' }) -- Set cooldown cooldown = true SetTimeout(5000, function() cooldown = false end) -- Return to main menu OpenMainBillingMenu() else lib.notify({ title = 'Billing System', description = 'Fehler beim Senden der Rechnung', type = 'error' }) end end -- Function to show payment account selection function ShowPaymentAccountSelection(billId, amount, description) -- Get player's accounts for payment local accounts = lib.callback.await('ps-banking:server:getAccounts', false) or {} local accountOptions = {} -- Add default bank account table.insert(accountOptions, { value = 'personal', label = 'Persönliches Bankkonto', description = 'Bezahlen mit deinem Hauptkonto' }) -- Add other accounts for _, account in ipairs(accounts) do table.insert(accountOptions, { value = account.id, label = account.holder .. ' - ' .. account.cardNumber, description = 'Kontostand: $' .. account.balance }) end -- Register and show the account selection menu lib.registerContext({ id = 'payment_account_menu', title = 'Wähle Zahlungskonto', menu = 'billing_menu', -- Add back button to bills menu options = accountOptions, onExit = function() ViewBills() -- Return to bills menu end, onSelect = function(selected) local accountId = selected.value local confirm = lib.alertDialog({ header = 'Rechnung bezahlen', content = ('Möchtest du $%s für %s von diesem Konto bezahlen?'):format(amount, description), centered = true, cancel = true }) if confirm == 'confirm' then local success = lib.callback.await('billing:server:payBillFromAccount', false, { billId = billId, accountId = accountId }) if success then lib.notify({ title = 'Billing System', description = 'Rechnung erfolgreich bezahlt', type = 'success' }) ViewBills() -- Refresh the list else lib.notify({ title = 'Billing System', description = 'Fehler beim Bezahlen der Rechnung. Unzureichendes Guthaben.', type = 'error' }) end end end }) lib.showContext('payment_account_menu') end -- Function to view received bills function ViewBills() -- Get all bills from ps-banking local bills = lib.callback.await('ps-banking:server:getBills', false) if not bills or #bills == 0 then lib.notify({ title = 'Billing System', description = 'Du hast keine unbezahlten Rechnungen', type = 'info' }) -- Return to main menu after a short delay SetTimeout(1000, function() OpenMainBillingMenu() end) return end -- Get bill statuses from our custom table local billStatuses = lib.callback.await('billing:server:getAllBillStatuses', false) local statusMap = {} -- Create a map of bill ID to status for quick lookup if billStatuses then for _, status in ipairs(billStatuses) do statusMap[status.bill_id] = status end end local billOptions = {} for _, bill in ipairs(bills) do local timestamp = os.date('%Y-%m-%d %H:%M', bill.date) -- Check if bill is declined in our custom table local billStatus = statusMap[bill.id] local isDeclined = billStatus and billStatus.declined == 1 local status = 'Unbezahlt' if bill.isPaid then status = 'Bezahlt' elseif isDeclined then status = 'Abgelehnt' end table.insert(billOptions, { title = bill.description, description = ('Betrag: $%s | Datum: %s'):format(bill.amount, timestamp), icon = 'file-invoice', metadata = { {label = 'Rechnungs-ID', value = bill.id}, {label = 'Typ', value = bill.type}, {label = 'Status', value = status} }, onSelect = function() if not bill.isPaid and not isDeclined then -- Show account selection for payment ShowPaymentAccountSelection(bill.id, bill.amount, bill.description) end end }) end lib.registerContext({ id = 'billing_menu', title = 'Deine Rechnungen', menu = 'main_billing_menu', -- Add back button to main menu options = billOptions }) lib.showContext('billing_menu') end -- Helper function to get nearby players function GetNearbyPlayers() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local players = {} -- Get all players local allPlayers = GetActivePlayers() for _, player in ipairs(allPlayers) do local targetPed = GetPlayerPed(player) local targetCoords = GetEntityCoords(targetPed) local distance = #(playerCoords - targetCoords) if distance <= 5.0 and targetPed ~= playerPed then local targetId = GetPlayerServerId(player) local targetName = GetPlayerName(player) table.insert(players, {id = targetId, name = targetName}) end end return players end -- Animation function function PlayBillingAnimation() lib.requestAnimDict("cellphone@") -- Request the prop model local propModel = 'bzzz_prop_payment_terminal' lib.requestModel(propModel) -- Create prop local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) local prop = CreateObject(GetHashKey(propModel), coords.x, coords.y, coords.z, true, true, true) -- Attach prop to player AttachEntityToEntity(prop, playerPed, GetPedBoneIndex(playerPed, 57005), 0.18, 0.01, 0.0, -54.0, 220.0, 43.0, true, true, false, true, 1, true) -- Play animation TaskPlayAnim(playerPed, "cellphone@", "cellphone_text_read_base", 2.0, 3.0, -1, 49, 0, false, false, false) -- Wait for animation to complete Wait(5000) -- Clear animation and delete prop ClearPedTasks(playerPed) DeleteEntity(prop) end -- Event to show payment prompt when receiving a bill RegisterNetEvent('billing:client:showPaymentPrompt', function(data) -- Play a notification sound PlaySound(-1, "Event_Start_Text", "GTAO_FM_Events_Soundset", 0, 0, 1) -- Get player's accounts for payment local accounts = lib.callback.await('ps-banking:server:getAccounts', false) or {} local accountOptions = {} -- Add default bank account table.insert(accountOptions, { title = 'Persönliches Bankkonto', description = 'Bezahlen mit deinem Hauptkonto', icon = 'credit-card', onSelect = function() local success = lib.callback.await('billing:server:handleBillResponse', false, { action = 'pay', billId = data.billId, accountId = 'personal' }) if success then lib.notify({ title = 'Rechnung bezahlt', description = 'Du hast die Rechnung erfolgreich bezahlt', type = 'success' }) else lib.notify({ title = 'Zahlung fehlgeschlagen', description = 'Du hast nicht genug Geld auf deinem Konto', type = 'error' }) end end }) -- Add other accounts for _, account in ipairs(accounts) do table.insert(accountOptions, { title = account.holder .. ' - ' .. account.cardNumber, description = 'Kontostand: $' .. account.balance, icon = 'university', onSelect = function() local success = lib.callback.await('billing:server:handleBillResponse', false, { action = 'pay', billId = data.billId, accountId = account.id }) if success then lib.notify({ title = 'Rechnung bezahlt', description = 'Du hast die Rechnung erfolgreich bezahlt', type = 'success' }) else lib.notify({ title = 'Zahlung fehlgeschlagen', description = 'Nicht genug Geld auf diesem Konto', type = 'error' }) end end }) end -- Create the payment prompt lib.registerContext({ id = 'bill_payment_prompt', title = 'Neue Rechnung erhalten', options = { { title = 'Rechnung Details', description = 'Von: ' .. data.sender, metadata = { {label = 'Betrag', value = '$' .. data.amount}, {label = 'Grund', value = data.reason}, } }, { title = 'Bezahlen', description = 'Wähle ein Konto zum Bezahlen', icon = 'money-bill', menu = 'payment_account_selection' }, { title = 'Ablehnen', description = 'Rechnung ablehnen', icon = 'times-circle', onSelect = function() local confirm = lib.alertDialog({ header = 'Rechnung ablehnen', content = 'Bist du sicher, dass du diese Rechnung ablehnen möchtest?', centered = true, cancel = true }) if confirm == 'confirm' then local success = lib.callback.await('billing:server:handleBillResponse', false, { action = 'decline', billId = data.billId }) if success then lib.notify({ title = 'Rechnung abgelehnt', description = 'Du hast die Rechnung abgelehnt', type = 'info' }) end end end }, { title = 'Später bezahlen', description = 'Rechnung für später speichern', icon = 'clock', onSelect = function() lib.callback.await('billing:server:handleBillResponse', false, { action = 'later', billId = data.billId }) lib.notify({ title = 'Rechnung gespeichert', description = 'Die Rechnung wurde für später gespeichert', type = 'info' }) end } } }) -- Register the account selection submenu lib.registerContext({ id = 'payment_account_selection', title = 'Wähle Zahlungskonto', menu = 'bill_payment_prompt', options = accountOptions }) -- Show the payment prompt lib.showContext('bill_payment_prompt') end)