Explorer
KNOW-PAT-087

FiveM - Society / Billing / Invoice System : validation metier et comptes partages

Domaine
framework
Type
pattern
Priorité
P1

Parent : [[INDEX-FIVEM-FRAMEWORK]]

FiveM - Society / Billing / Invoice System : validation métier et comptes partagés

Problème

Un joueur peut usurper l'identité d'une entreprise pour envoyer des factures frauduleuses, ou retirer de l'argent d'un compte société sans être membre du job.

Solution

Validation server-side stricte du job de l'émetteur, comptes partagés par nom de société, et split de transaction entre joueur et société.

-- esx_billing : envoi de facture avec validation job
RegisterNetEvent('esx_billing:sendBill')
AddEventHandler('esx_billing:sendBill', function(targetId, sharedAccountName, label, amount)
    local xPlayer = ESX.Player(source)
    local jobName = string.gsub(sharedAccountName, 'society_', '')
    
    if xPlayer.getJob().name ~= jobName then
        print(('[^2ERROR^7] Player %s Attempted to Send bill from a society without correct Job - Possibly Cheats'):format(source))
        return
    end
    
    -- Insertion base de données
    MySQL.insert('INSERT INTO billing (identifier, sender, target_type, target, label, amount) VALUES (?, ?, ?, ?, ?, ?)', {
        xPlayer.getIdentifier(),
        xPlayer.getIdentifier(),
        'society',
        sharedAccountName,
        label,
        amount
    })
end)

-- esx_society : enregistrement dynamique
local SocietiesByName = {}

function registerSociety(name, label, account, datastore, inventory, data)
    if SocietiesByName[name] then
        print(('[^3WARNING^7] society already registered: %s'):format(name))
        return
    end
    SocietiesByName[name] = {
        name = name,
        label = label,
        account = account,
        datastore = datastore,
        inventory = inventory,
        data = data
    }
end

function getSociety(name)
    return SocietiesByName[name]
end

Pourquoi

  • Le serveur vérifie que l'émetteur a bien le job correspondant à la société
  • Les comptes partagés permettent de stocker l'argent d'une entreprise indépendamment des joueurs
  • Le datastore/inventory lié à la société permet la gestion de stock commune

Quand l'utiliser

  • Facturation métiers (police, EMS, mécanicien)
  • Gestion d'entreprises avec trésorerie partagée
  • Systèmes de salaires et retraites de société

Quand NE PAS l'utiliser

  • Transferts P2P directs (pas besoin de compte société)
  • Scripts standalone sans système de jobs

Exemples

Correct

-- Retrait société avec validation grade
RegisterNetEvent('society:withdraw')
AddEventHandler('society:withdraw', function(societyName, amount)
    local xPlayer = ESX.Player(source)
    local society = getSociety(societyName)
    
    if not society then return end
    if xPlayer.getJob().name ~= societyName then return end
    if xPlayer.getJob().grade < Config.minGradeForWithdraw then return end
    
    society.account.removeMoney(amount)
    xPlayer.addMoney(amount)
end)

Incorrect

-- Le client envoie le nom de la société → spoof possible
RegisterNetEvent('billing:send')
AddEventHandler('billing:send', function(target, society, amount)
    -- Aucune validation du job de l'émetteur
    MySQL.insert('INSERT INTO billing ...', {target, society, amount})
end)

Références

  • esx_billing/server/main.lua
  • esx_society/server/main.lua

Liens connexes

  • [[KNOW-ANT-FIVEM-002-client-side-validation-only|KNOW-ANT-FIVEM-002]]