forked from Simnation/Main
ed
This commit is contained in:
parent
02dd8bf93c
commit
cd36cf6694
6 changed files with 451 additions and 8 deletions
BIN
resources/[props]/bzzz_terminal/.fxap
Normal file
BIN
resources/[props]/bzzz_terminal/.fxap
Normal file
Binary file not shown.
|
@ -1,12 +1,42 @@
|
||||||
|
--[[ FX Information ]]--
|
||||||
fx_version 'cerulean'
|
fx_version 'cerulean'
|
||||||
game { 'gta5' }
|
use_experimental_fxv2_oal 'yes'
|
||||||
author 'BzZz'
|
lua54 'yes'
|
||||||
description 'Bzzz - Payment terminal'
|
game 'gta5'
|
||||||
|
|
||||||
|
--[[ Resource Information ]]--
|
||||||
|
name 'bzzz_payment_terminal'
|
||||||
version '2.1.0'
|
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
|
files {
|
||||||
-- 22/10/2024 - removed logos
|
'stream/*.ytyp',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dependency '/assetpacks'
|
242
resources/[tools]/nordi_billing/billing_client.lua
Normal file
242
resources/[tools]/nordi_billing/billing_client.lua
Normal 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
|
141
resources/[tools]/nordi_billing/billing_server.lua
Normal file
141
resources/[tools]/nordi_billing/billing_server.lua
Normal 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)
|
24
resources/[tools]/nordi_billing/fxmanifest.lua
Normal file
24
resources/[tools]/nordi_billing/fxmanifest.lua
Normal 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'
|
||||||
|
}
|
|
@ -432,12 +432,18 @@ lib.callback.register("ps-banking:server:payBill", function(source, billId)
|
||||||
xPlayer.Functions.RemoveMoney("bank", tonumber(amount))
|
xPlayer.Functions.RemoveMoney("bank", tonumber(amount))
|
||||||
end
|
end
|
||||||
MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', { billId })
|
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
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function createBill(data)
|
function createBill(data)
|
||||||
local identifier = data.identifier
|
local identifier = data.identifier
|
||||||
local description = data.description
|
local description = data.description
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue