From cd36cf6694d82aee142f24ac876432c2bf1656f6 Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Tue, 5 Aug 2025 11:11:48 +0200 Subject: [PATCH] ed --- resources/[props]/bzzz_terminal/.fxap | Bin 0 -> 178 bytes .../[props]/bzzz_terminal/fxmanifest.lua | 46 +++- .../[tools]/nordi_billing/billing_client.lua | 242 ++++++++++++++++++ .../[tools]/nordi_billing/billing_server.lua | 141 ++++++++++ .../[tools]/nordi_billing/fxmanifest.lua | 24 ++ resources/[tools]/ps-banking/server/main.lua | 6 + 6 files changed, 451 insertions(+), 8 deletions(-) create mode 100644 resources/[props]/bzzz_terminal/.fxap create mode 100644 resources/[tools]/nordi_billing/billing_client.lua create mode 100644 resources/[tools]/nordi_billing/billing_server.lua create mode 100644 resources/[tools]/nordi_billing/fxmanifest.lua diff --git a/resources/[props]/bzzz_terminal/.fxap b/resources/[props]/bzzz_terminal/.fxap new file mode 100644 index 0000000000000000000000000000000000000000..a734660751d594a546b5230dd0ade3ef137f30f6 GIT binary patch literal 178 zcmV;j08Rf!SV2$$00000079PPlm1Oq^K0(r9&?YoE)DJ(colzon?H+huoB({K^cOfvoemW3ks469j6TgWP9<$Ea5gH>!8MxL=R6$8cd0X>i%+fwR gecnnA_>;Q3RV$l#nj9f!!&!8^I@$bg-O}nBwxTgp^Z)<= literal 0 HcmV?d00001 diff --git a/resources/[props]/bzzz_terminal/fxmanifest.lua b/resources/[props]/bzzz_terminal/fxmanifest.lua index ecd62e9b2..49de766ba 100644 --- a/resources/[props]/bzzz_terminal/fxmanifest.lua +++ b/resources/[props]/bzzz_terminal/fxmanifest.lua @@ -1,12 +1,42 @@ -fx_version 'cerulean' -game { 'gta5' } -author 'BzZz' -description 'Bzzz - Payment terminal' -version '2.1.0' +--[[ FX Information ]]-- +fx_version 'cerulean' +use_experimental_fxv2_oal 'yes' +lua54 'yes' +game 'gta5' + +--[[ Resource Information ]]-- +name 'bzzz_payment_terminal' +version '2.1.0' +description 'Payment terminal' +author 'BzZz' + +--[[ Manifest ]]-- +shared_scripts { +} + +client_scripts { +} + +server_scripts { +} + +escrow_ignore { + 'stream/*.ydr', + 'data/*.lua' +} + +dependencies { + +} -data_file 'DLC_ITYP_REQUEST' 'stream/bzzz_prop_payment_terminal.ytyp' +data_file 'DLC_ITYP_REQUEST' 'stream/*.ytyp' --- update --- 22/10/2024 - removed logos \ No newline at end of file +files { + 'stream/*.ytyp', +} + + + +dependency '/assetpacks' \ No newline at end of file diff --git a/resources/[tools]/nordi_billing/billing_client.lua b/resources/[tools]/nordi_billing/billing_client.lua new file mode 100644 index 000000000..82493bd9a --- /dev/null +++ b/resources/[tools]/nordi_billing/billing_client.lua @@ -0,0 +1,242 @@ +local isUiOpen = false +local cooldown = false + +-- Command to open billing menu +RegisterCommand('bill', function() + OpenBillingMenu() +end, false) + +-- Keybind registration +RegisterKeyMapping('bill', 'Open Billing Menu', 'keyboard', 'F7') -- Default key is F7, can be changed in settings + +-- Function to open the billing menu +function OpenBillingMenu() + if cooldown then + lib.notify({ + title = 'Billing System', + description = 'Please wait before using the billing system again', + type = 'error' + }) + return + end + + -- Get nearby players + local players = GetNearbyPlayers() + if #players == 0 then + lib.notify({ + title = 'Billing System', + description = 'No players nearby to bill', + 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 = 'Personal Bank Account' + }) + + -- Add other accounts + for _, account in ipairs(accounts) do + table.insert(accountOptions, { + value = account.id, + label = account.holder .. ' - ' .. account.cardNumber + }) + end + + -- Create player options for selection + local playerOptions = {} + for _, player in ipairs(players) do + table.insert(playerOptions, { + value = player.id, + label = player.name .. ' (ID: ' .. player.id .. ')' + }) + end + + -- Create the input dialog + local input = lib.inputDialog('Create Bill', { + { + type = 'select', + label = 'Select Player', + options = playerOptions, + required = true + }, + { + type = 'number', + label = 'Amount ($)', + description = 'Enter the bill amount', + icon = 'dollar-sign', + required = true, + min = 1, + max = 100000 + }, + { + type = 'input', + label = 'Reason', + description = 'Enter the reason for the bill', + placeholder = 'Services provided...', + required = true + }, + { + type = 'select', + label = 'Receiving Account', + 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 = input[1], + amount = input[2], + reason = input[3], + account = input[4] + }) + + if success then + lib.notify({ + title = 'Billing System', + description = 'Bill sent successfully', + type = 'success' + }) + + -- Set cooldown + cooldown = true + SetTimeout(5000, function() + cooldown = false + end) + else + lib.notify({ + title = 'Billing System', + description = 'Failed to send bill', + type = 'error' + }) + end +end + +-- Function to view received bills +function ViewBills() + local bills = lib.callback.await('ps-banking:server:getBills', false) + + if not bills or #bills == 0 then + lib.notify({ + title = 'Billing System', + description = 'You have no unpaid bills', + type = 'info' + }) + return + end + + local billOptions = {} + for _, bill in ipairs(bills) do + local timestamp = os.date('%Y-%m-%d %H:%M', bill.date) + table.insert(billOptions, { + title = bill.description, + description = ('Amount: $%s | Date: %s'):format(bill.amount, timestamp), + metadata = { + {label = 'Bill ID', value = bill.id}, + {label = 'Type', value = bill.type}, + {label = 'Status', value = bill.isPaid and 'Paid' or 'Unpaid'} + }, + onSelect = function() + local confirm = lib.alertDialog({ + header = 'Pay Bill', + content = ('Do you want to pay $%s for %s?'):format(bill.amount, bill.description), + centered = true, + cancel = true + }) + + if confirm == 'confirm' then + local success = lib.callback.await('ps-banking:server:payBill', false, bill.id) + if success then + lib.notify({ + title = 'Billing System', + description = 'Bill paid successfully', + type = 'success' + }) + ViewBills() -- Refresh the list + else + lib.notify({ + title = 'Billing System', + description = 'Failed to pay bill. Insufficient funds.', + type = 'error' + }) + end + end + end + }) + end + + lib.registerContext({ + id = 'billing_menu', + title = 'Your Bills', + options = billOptions + }) + + lib.showContext('billing_menu') +end + +-- Command to view bills +RegisterCommand('bills', function() + ViewBills() +end, false) + +-- Helper function to get nearby players +function GetNearbyPlayers() + local playerPed = PlayerPedId() + local players = QBCore.Functions.GetPlayers() + local nearbyPlayers = {} + + for _, player in ipairs(players) do + local targetPed = GetPlayerPed(player) + local targetCoords = GetEntityCoords(targetPed) + local distance = #(GetEntityCoords(playerPed) - targetCoords) + + if distance <= 5.0 and targetPed ~= playerPed then + local targetId = GetPlayerServerId(player) + local targetName = GetPlayerName(player) + table.insert(nearbyPlayers, {id = targetId, name = targetName}) + end + end + + return nearbyPlayers +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 diff --git a/resources/[tools]/nordi_billing/billing_server.lua b/resources/[tools]/nordi_billing/billing_server.lua new file mode 100644 index 000000000..3f0da91c9 --- /dev/null +++ b/resources/[tools]/nordi_billing/billing_server.lua @@ -0,0 +1,141 @@ +-- 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 .. ' (From: ' .. senderName .. ')' + + -- Create the bill + exports["ps-banking"]:createBill({ + identifier = target.PlayerData.citizenid, + description = description, + type = "Invoice", + amount = data.amount, + }) + + -- Store additional data about the bill if needed + -- You could create a separate table to track which account the payment should go to + MySQL.insert.await('INSERT INTO billing_accounts (bill_description, sender_id, receiver_id, account_id, amount, created_at) VALUES (?, ?, ?, ?, ?, NOW())', { + description, + player.PlayerData.citizenid, + target.PlayerData.citizenid, + data.account, + data.amount + }) + + -- Notify the target player + TriggerClientEvent('QBCore:Notify', data.playerId, 'You received a bill for $' .. data.amount .. ' - ' .. data.reason, 'primary', 7500) + + return true +end) + +-- Event handler for when a bill is paid +RegisterNetEvent('billing:server:billPaid', function(billId) + local src = source + local player = QBCore.Functions.GetPlayer(src) + + if not player then return end + + -- 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 = 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, 'You received $' .. amount .. ' from a bill payment', 'success') + else + -- Handle offline player (could store in a separate table for when they log in) + MySQL.insert.await('INSERT INTO offline_payments (citizen_id, amount, reason) VALUES (?, ?, ?)', { + receiverId, + amount, + 'Bill payment' + }) + 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, + 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, 'You received $' .. totalAmount .. ' from bill payments while offline', 'success') + + -- Mark payments as processed + MySQL.update.await('UPDATE offline_payments SET processed = 1 WHERE citizen_id = ? AND processed = 0', {citizenId}) + end + end +end) + +-- Listen for bill payment events from ps-banking +RegisterNetEvent('ps-banking:server:billPaid', function(billId) + TriggerEvent('billing:server:billPaid', billId) +end) diff --git a/resources/[tools]/nordi_billing/fxmanifest.lua b/resources/[tools]/nordi_billing/fxmanifest.lua new file mode 100644 index 000000000..97d67b43b --- /dev/null +++ b/resources/[tools]/nordi_billing/fxmanifest.lua @@ -0,0 +1,24 @@ +fx_version 'cerulean' +game 'gta5' + +description 'QBCore Billing System with ox_lib' +version '1.0.0' + +shared_scripts { + '@ox_lib/init.lua', +} + +client_scripts { + 'billing_client.lua', +} + +server_scripts { + '@oxmysql/lib/MySQL.lua', + 'billing_server.lua', +} + +dependencies { + 'ox_lib', + 'ps-banking', + 'qb-core' +} diff --git a/resources/[tools]/ps-banking/server/main.lua b/resources/[tools]/ps-banking/server/main.lua index faea5b7ae..265777cd0 100644 --- a/resources/[tools]/ps-banking/server/main.lua +++ b/resources/[tools]/ps-banking/server/main.lua @@ -432,12 +432,18 @@ lib.callback.register("ps-banking:server:payBill", function(source, billId) 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