1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_billing/billing_server.lua

517 lines
20 KiB
Lua
Raw Normal View History

2025-08-05 14:07:51 +02:00
-- Initialize QBCore
local QBCore = exports['qb-core']:GetCoreObject()
2025-08-05 11:11:48 +02:00
-- 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
2025-08-05 14:07:51 +02:00
local description = data.reason .. ' (Von: ' .. senderName .. ')'
2025-08-05 11:11:48 +02:00
-- Create the bill
exports["ps-banking"]:createBill({
identifier = target.PlayerData.citizenid,
description = description,
type = "Invoice",
amount = data.amount,
})
2025-08-05 14:07:51 +02:00
-- 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
-- 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,
2025-08-05 11:11:48 +02:00
description,
player.PlayerData.citizenid,
target.PlayerData.citizenid,
data.account,
data.amount
})
2025-08-05 14:07:51 +02:00
-- Send payment prompt to target player
TriggerClientEvent('billing:client:showPaymentPrompt', data.playerId, {
billId = billId,
amount = data.amount,
reason = data.reason,
sender = senderName
})
2025-08-05 11:11:48 +02:00
2025-08-05 15:06:44 +02:00
-- Notify the sender
TriggerClientEvent('QBCore:Notify', src, 'Rechnung über $' .. data.amount .. ' an ' .. target.PlayerData.charinfo.firstname .. ' gesendet', 'success')
2025-08-05 11:11:48 +02:00
return true
end)
2025-08-05 14:07:51 +02:00
-- Add a callback to get bill status
lib.callback.register('billing:server:getBillStatus', function(source, billId)
2025-08-05 11:11:48 +02:00
local src = source
local player = QBCore.Functions.GetPlayer(src)
2025-08-05 14:07:51 +02:00
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)
2025-08-05 15:02:32 +02:00
-- 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)
2025-08-05 14:07:51 +02:00
-- Add a new 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
2025-08-05 14:27:19 +02:00
-- Process payment based on selected account
if data.accountId == 'personal' then
2025-08-05 15:08:53 +02:00
-- Pay from personal bank account
2025-08-05 14:43:36 +02:00
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")
2025-08-05 15:08:53 +02:00
-- IMPORTANT: Delete the bill from ps_banking_bills table
2025-08-05 14:43:36 +02:00
MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId})
-- Process the payment to the recipient's account
ProcessBillPayment(data.billId)
2025-08-05 15:06:44 +02:00
-- Notify the player
TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' bezahlt', 'success')
2025-08-05 14:43:36 +02:00
return true
2025-08-05 14:07:51 +02:00
else
2025-08-05 14:27:19 +02:00
-- Pay from shared account
local success = PayBillFromSharedAccount(src, data.billId, data.accountId)
return success
2025-08-05 14:07:51 +02:00
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})
2025-08-05 15:08:53 +02:00
-- IMPORTANT: Delete the bill from ps_banking_bills table when declined
MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId})
2025-08-05 14:07:51 +02:00
-- 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
2025-08-05 15:08:53 +02:00
end
2025-08-05 15:06:44 +02:00
-- Notify the player
TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung abgelehnt', 'info')
2025-08-05 14:07:51 +02:00
return true
elseif data.action == 'later' then
2025-08-05 15:02:32 +02:00
-- Simply close the prompt without any action
-- The bill will remain in the system as unpaid
2025-08-05 15:06:44 +02:00
-- Notify the player
TriggerClientEvent('QBCore:Notify', src, 'Die Rechnung wurde für später gespeichert', 'info')
2025-08-05 14:07:51 +02:00
return true
end
return false
end)
2025-08-05 14:27:19 +02:00
-- Function to pay bill from a shared account
function PayBillFromSharedAccount(source, billId, accountId)
2025-08-05 14:07:51 +02:00
local src = source
2025-08-05 14:27:19 +02:00
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]
2025-08-05 14:43:36 +02:00
local amount = tonumber(bill.amount)
2025-08-05 14:27:19 +02:00
-- 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
2025-08-05 11:11:48 +02:00
2025-08-05 15:06:44 +02:00
if not hasAccess then
TriggerClientEvent('QBCore:Notify', src, 'Du hast keinen Zugriff auf dieses Konto', 'error')
return false
end
2025-08-05 14:27:19 +02:00
-- Check if account has enough balance
2025-08-05 15:06:44 +02:00
if tonumber(account.balance) < amount then
TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Geld auf diesem Konto', 'error')
return false
end
2025-08-05 14:27:19 +02:00
-- Process payment
MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance - ? WHERE id = ?', {amount, accountId})
2025-08-05 15:08:53 +02:00
-- IMPORTANT: Delete the bill from ps_banking_bills table
2025-08-05 14:27:19 +02:00
MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {billId})
-- Process the payment to the recipient's account
ProcessBillPayment(billId)
2025-08-05 15:06:44 +02:00
-- Notify the player
TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' vom Konto ' .. account.holder .. ' bezahlt', 'success')
2025-08-05 14:27:19 +02:00
return true
end
-- Add a callback for paying bill from selected account
lib.callback.register('billing:server:payBillFromAccount', function(source, data)
local src = source
2025-08-05 14:43:36 +02:00
local player = QBCore.Functions.GetPlayer(src)
if not player then return false end
2025-08-05 14:27:19 +02:00
if data.accountId == 'personal' then
-- Pay from personal bank account
2025-08-05 14:43:36 +02:00
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
2025-08-05 15:06:44 +02:00
TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Geld auf deinem Konto', 'error')
2025-08-05 14:43:36 +02:00
return false
end
-- Process payment manually
player.Functions.RemoveMoney("bank", amount, "bill-payment")
2025-08-05 15:08:53 +02:00
-- IMPORTANT: Delete the bill from ps_banking_bills table
2025-08-05 14:43:36 +02:00
MySQL.query.await('DELETE FROM ps_banking_bills WHERE id = ?', {data.billId})
-- Process the payment to the recipient's account
ProcessBillPayment(data.billId)
2025-08-05 15:06:44 +02:00
-- Notify the player
TriggerClientEvent('QBCore:Notify', src, 'Du hast die Rechnung über $' .. amount .. ' bezahlt', 'success')
2025-08-05 14:43:36 +02:00
return true
2025-08-05 14:27:19 +02:00
else
-- Pay from shared account
return PayBillFromSharedAccount(src, data.billId, data.accountId)
end
end)
-- Function to process bill payment to recipient
function ProcessBillPayment(billId)
2025-08-05 11:11:48 +02:00
-- 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
2025-08-05 14:43:36 +02:00
local amount = tonumber(bill.amount)
2025-08-05 11:11:48 +02:00
-- 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')
2025-08-05 14:07:51 +02:00
TriggerClientEvent('QBCore:Notify', receiver.PlayerData.source, 'Du hast $' .. amount .. ' von einer Rechnungszahlung erhalten', 'success')
2025-08-05 11:11:48 +02:00
else
2025-08-05 14:07:51 +02:00
-- Handle offline player
2025-08-05 11:11:48 +02:00
MySQL.insert.await('INSERT INTO offline_payments (citizen_id, amount, reason) VALUES (?, ?, ?)', {
receiverId,
amount,
2025-08-05 14:07:51 +02:00
'Rechnungszahlung'
2025-08-05 11:11:48 +02:00
})
end
else
-- Add money to the specified account
MySQL.update.await('UPDATE ps_banking_accounts SET balance = balance + ? WHERE id = ?', {
amount,
accountId
})
2025-08-05 15:06:44 +02:00
-- 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
2025-08-05 11:11:48 +02:00
end
-- Update the bill status
MySQL.update.await('UPDATE billing_accounts SET paid = 1, paid_at = NOW() WHERE bill_id = ?', {billId})
end
2025-08-05 14:27:19 +02:00
end
2025-08-05 11:11:48 +02:00
-- Create the necessary database tables if they don't exist
MySQL.ready(function()
2025-08-05 15:24:24 +02:00
-- First check if the billing_accounts table exists
local tableExists = MySQL.query.await("SHOW TABLES LIKE 'billing_accounts'")
2025-08-05 11:11:48 +02:00
2025-08-05 15:24:24 +02:00
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 offline_payments table if it doesn't exist
2025-08-05 11:11:48 +02:00
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
)
]])
2025-08-05 15:24:24 +02:00
print("^2[nordi_billing] Database tables initialized^7")
end) -- Added closing parenthesis here
2025-08-05 11:11:48 +02:00
-- 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')
2025-08-05 14:07:51 +02:00
TriggerClientEvent('QBCore:Notify', src, 'Du hast $' .. totalAmount .. ' von Rechnungszahlungen erhalten, während du offline warst', 'success')
2025-08-05 11:11:48 +02:00
-- Mark payments as processed
MySQL.update.await('UPDATE offline_payments SET processed = 1 WHERE citizen_id = ? AND processed = 0', {citizenId})
end
end
2025-08-05 15:06:44 +02:00
-- 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
2025-08-05 11:11:48 +02:00
end)
2025-08-05 14:07:51 +02:00
-- Alternative hook method if you can't modify ps-banking
-- This listens for the MySQL query that deletes a bill (which happens when a bill is paid)
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()
2025-08-05 14:27:19 +02:00
ProcessBillPayment(billId)
2025-08-05 14:07:51 +02:00
end)
end
end
2025-08-05 11:11:48 +02:00
end)
2025-08-05 15:06:44 +02:00
-- 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)
-- Event handler for opening bills menu from command
RegisterNetEvent('billing:client:openBillsMenu', function()
-- This is handled on the client side
end)
-- Add a server event to notify bill sender when a bill is paid
RegisterServerEvent('billing:server:notifyBillPaid', function(senderId, amount, receiverName)
local sender = QBCore.Functions.GetPlayerByCitizenId(senderId)
if sender then
TriggerClientEvent('QBCore:Notify', sender.PlayerData.source, receiverName .. ' hat deine Rechnung über $' .. amount .. ' bezahlt', 'success')
end
end)
-- Add a server event to notify bill sender when a bill is declined
RegisterServerEvent('billing:server:notifyBillDeclined', function(senderId, amount, receiverName)
local sender = QBCore.Functions.GetPlayerByCitizenId(senderId)
if sender then
TriggerClientEvent('QBCore:Notify', sender.PlayerData.source, receiverName .. ' hat deine Rechnung über $' .. amount .. ' abgelehnt', 'error')
end
end)
2025-08-05 15:35:51 +02:00
-- Add a new 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)