1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-08-05 11:11:48 +02:00
parent 02dd8bf93c
commit cd36cf6694
6 changed files with 451 additions and 8 deletions

Binary file not shown.

View file

@ -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
files {
'stream/*.ytyp',
}
dependency '/assetpacks'

View file

@ -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

View file

@ -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)

View file

@ -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'
}

View file

@ -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