Architecture du système bancaire
Un système bancaire est l'épine dorsale financière de tout serveur de jeu de rôle FiveM, gérant tout, des simples dépôts en espèces aux transferts complexes entre joueurs et aux comptes d'organisation partagés. La plupart des frameworks comme QBCore et ESX incluent une gestion financière de base, mais un système bancaire dédié étend cela avec une gestion de compte appropriée, une journalisation des transactions et une interface ATM raffinée qui plonge les joueurs dans l'aspect financier du jeu de rôle. L'architecture est divisée en trois couches : la base de données stocke les soldes des comptes et les enregistrements de transactions, le serveur valide chaque opération financière et applique les règles commerciales, et le client fournit les interfaces ATM et guichet bancaire via NUI. Chaque opération financière doit passer par le côté serveur, car la manipulation financière côté client est le vecteur d'exploitation numéro un sur les serveurs FiveM. Même l'affichage d'un solde doit provenir d'un rappel du serveur, jamais de données mises en cache côté client qui pourraient être falsifiées.
Schéma de base de données pour le secteur bancaire
Ton base de données bancaire doit prendre en charge les comptes personnels, les comptes partagés pour les organisations et les entreprises, ainsi qu'un journal des transactions complet. Le journal des transactions n'est pas facultatif car il sert à la fois de fonctionnalité destinée aux joueurs et d'outil d'administration pour enquêter sur les exploits financiers. Concevez ton schéma pour gérer les opérations à haut débit, car les serveurs très occupés peuvent traiter des centaines de transactions par minute pendant les heures de pointe :
CREATE TABLE IF NOT EXISTS bank_accounts (
id INT AUTO_INCREMENT PRIMARY KEY,
account_number VARCHAR(20) UNIQUE NOT NULL,
owner_citizenid VARCHAR(50) NOT NULL,
account_type ENUM('personal', 'business', 'gang', 'shared') DEFAULT 'personal',
balance BIGINT DEFAULT 0,
account_name VARCHAR(100) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_frozen BOOLEAN DEFAULT FALSE,
INDEX idx_owner (owner_citizenid),
INDEX idx_type (account_type)
);
CREATE TABLE IF NOT EXISTS bank_transactions (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
account_id INT NOT NULL,
type ENUM('deposit', 'withdraw', 'transfer_in', 'transfer_out', 'paycheck', 'purchase') NOT NULL,
amount BIGINT NOT NULL,
balance_after BIGINT NOT NULL,
description VARCHAR(255) DEFAULT NULL,
other_account VARCHAR(20) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_account (account_id),
INDEX idx_created (created_at),
FOREIGN KEY (account_id) REFERENCES bank_accounts(id)
);
CREATE TABLE IF NOT EXISTS bank_account_access (
account_id INT NOT NULL,
citizenid VARCHAR(50) NOT NULL,
permission ENUM('view', 'withdraw', 'full') DEFAULT 'view',
PRIMARY KEY (account_id, citizenid),
FOREIGN KEY (account_id) REFERENCES bank_accounts(id)
);
Le bank_account_access Le tableau permet des comptes partagés où plusieurs joueurs peuvent avoir différents niveaux d'autorisation. Un chef de gang peut avoir un accès complet à la trésorerie du gang tandis que les membres réguliers ne peuvent consulter que le solde. En utilisant BIGINT pour les champs de solde évite les problèmes de débordement sur les serveurs avec des économies gonflées où les soldes des joueurs peuvent atteindre des milliards.
Logique de transaction côté serveur
Chaque transaction financière doit être atomique et validée sur le serveur. Utilisez les transactions de base de données pour garantir que l’argent n’est jamais créé ou détruit lors des transferts. Lorsque le joueur A envoie de l'argent au joueur B, la déduction de A et l'ajout à B doivent réussir ensemble, sinon aucune ne doit s'appliquer. Mettez en œuvre des contrôles de validation pour vérifier le solde suffisant, le statut du compte gelé, les limites de transfert quotidiennes et les montants minimum des transactions. Voici une implémentation de transfert sécurisé :
RegisterNetEvent('banking:server:transfer', function(targetAccount, amount, description)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
amount = math.floor(tonumber(amount) or 0)
if amount <= 0 then
TriggerClientEvent('QBCore:Notify', src, 'Invalid amount', 'error')
return
end
local citizenid = Player.PlayerData.citizenid
local senderAccount = GetPlayerPrimaryAccount(citizenid)
if not senderAccount or senderAccount.is_frozen then
TriggerClientEvent('QBCore:Notify', src, 'Account unavailable', 'error')
return
end
if senderAccount.balance < amount then
TriggerClientEvent('QBCore:Notify', src, 'Insufficient funds', 'error')
return
end
-- Check daily transfer limit
local todayTransfers = GetDailyTransferTotal(senderAccount.id)
if todayTransfers + amount > Config.DailyTransferLimit then
TriggerClientEvent('QBCore:Notify', src, 'Daily limit exceeded', 'error')
return
end
-- Atomic transfer using database transaction
local success = MySQL.transaction.await({
{
query = 'UPDATE bank_accounts SET balance = balance - ? WHERE id = ? AND balance >= ?',
values = {amount, senderAccount.id, amount}
},
{
query = 'UPDATE bank_accounts SET balance = balance + ? WHERE account_number = ?',
values = {amount, targetAccount}
},
{
query = 'INSERT INTO bank_transactions (account_id, type, amount, balance_after, description, other_account) VALUES (?, "transfer_out", ?, (SELECT balance FROM bank_accounts WHERE id = ?), ?, ?)',
values = {senderAccount.id, amount, senderAccount.id, description or 'Transfer', targetAccount}
},
})
if success then
TriggerClientEvent('QBCore:Notify', src, 'Transfer complete: $' .. amount, 'success')
TriggerClientEvent('banking:client:refreshBalance', src)
else
TriggerClientEvent('QBCore:Notify', src, 'Transfer failed', 'error')
end
end)
Remarquez le WHERE balance >= ? clause dans la requête de déduction, qui agit comme une dernière protection contre les conditions de concurrence où deux transferts simultanés pourraient mettre le compte à découvert. Cette vérification au niveau de la base de données est essentielle même si tu vérifies déjà le solde dans Lua, car plusieurs demandes peuvent arriver entre ton vérification et la mise à jour réelle.
Interface utilisateur ATM
L'interface ATM est un panneau NUI compact qui offre un accès rapide aux fonctions bancaires de base : vérifier le solde, déposer de l'argent, retirer de l'argent et transférer de l'argent. Gardez le design épuré et familier, car les joueurs s’attendent instinctivement à ce qu’un guichet automatique fonctionne comme son équivalent dans le monde réel. Affichez le solde actuel bien en évidence en haut, avec des boutons d'action en dessous pour chaque opération. Les vues de dépôt et de retrait doivent inclure des boutons de montant prédéfinis pour les valeurs courantes telles que 100 $, 500 $, 1 000 $ et 5 000 $, ainsi qu'un champ de saisie de montant personnalisé. Pour les virements, fournissez des champs pour le numéro de compte et le montant du destinataire, ainsi qu'un champ mémo facultatif. Affichez une étape de confirmation avant d'exécuter toute transaction pour éviter que des clics accidentels ne coûtent de l'argent aux joueurs. Incluez une liste de transactions récentes qui affiche les 10 dernières entrées afin que les joueurs puissent vérifier leur activité financière sans avoir besoin de se rendre dans une agence bancaire complète. L'interface utilisateur du guichet automatique devrait être rapide, alors récupérez le solde et les données de transaction en un seul rappel lorsque le menu s'ouvre plutôt que de faire des demandes distinctes pour chaque élément d'information.
Configuration de l'interaction ATM
Placez des points d'interaction ATM aux emplacements des accessoires ATM existants sur la carte GTA. FiveM fournit une liste de hachages de modèles ATM sur lesquels tu peux parcourir pour trouver tous les accessoires ATM dans le monde du jeu. Utilisez un système cible tel que ox_target pour une interaction propre, ou revenez aux contrôles de proximité à proximité de chaque accessoire ATM. Lorsque le joueur interagit avec un guichet automatique, lancez une animation du joueur à l'aide du guichet automatique, puis ouvrez le panneau NUI :
local atmModels = {
'prop_atm_01', 'prop_atm_02', 'prop_atm_03',
'prop_fleeca_atm', 'v_5_b_atm1'
}
-- Using ox_target for ATM interaction
for _, model in ipairs(atmModels) do
exports.ox_target:addModel(GetHashKey(model), {
{
name = 'use_atm',
icon = 'fas fa-credit-card',
label = 'Use ATM',
onSelect = function(data)
local ped = PlayerPedId()
local atmCoords = GetEntityCoords(data.entity)
-- Face the ATM
TaskTurnPedToFaceCoord(ped, atmCoords.x, atmCoords.y, atmCoords.z, 1000)
Wait(1000)
-- Play ATM animation
RequestAnimDict('mini@atmenter')
while not HasAnimDictLoaded('mini@atmenter') do Wait(10) end
TaskPlayAnim(ped, 'mini@atmenter', 'enter', 8.0, -8.0, -1, 0, 0, false, false, false)
-- Open ATM UI
QBCore.Functions.TriggerCallback('banking:server:getAccountData', function(data)
SetNuiFocus(true, true)
SendNUIMessage({
action = 'openATM',
balance = data.balance,
transactions = data.recentTransactions,
accountNumber = data.accountNumber
})
end)
end
}
})
end
Historique des transactions et relevés
L’historique des transactions transforme ton système bancaire d’une simple machine de dépôt et de retrait en un outil complet de gestion financière. Les joueurs devraient pouvoir consulter l’historique complet de leurs transactions dans les succursales bancaires, filtré par plage de dates, type de transaction ou montant. Chaque entrée de transaction doit afficher la date, le type, le montant, le solde résultant, la description et l'autre partie impliquée dans les transferts. Implémentez la pagination côté serveur, car le chargement de milliers de transactions à la fois gèlera la trame NUI et augmentera l'utilisation de la mémoire du serveur. Renvoyez 20 à 30 transactions par page et laissez le lecteur en charger davantage si nécessaire. Pour les succursales bancaires, proposez des fonctionnalités supplémentaires au-delà de ce que proposent les guichets automatiques, telles que l'ouverture de nouveaux comptes, la gestion des autorisations de compte partagé, la génération de relevés de compte pour une période spécifique et la demande de prêts si ton serveur prend en charge ce mécanisme. Stockez les descriptions des transactions sous forme de chaînes lisibles par l'homme afin que les transactions automatisées provenant des tâches, des achats en magasin et des taxes gouvernementales affichent toutes des entrées claires que les joueurs peuvent comprendre sans contexte.
Mécanique du vol de banque
Les vols de banque sont l'un des événements les plus excitants sur n'importe quel serveur de jeu de rôle, créant des scénarios aux enjeux élevés impliquant des criminels, des policiers, des négociateurs d'otages et des passants. Un système de vol bien conçu comprend plusieurs phases : enquêter sur la banque, lancer le braquage, pirater ou percer les systèmes de sécurité via des mini-jeux, charger le butin et s'échapper avec la police à sa poursuite. Commencez par définir quelles banques peuvent être cambriolées, leur niveau de difficulté, leurs temps de recharge et les éléments requis. Le vol devrait nécessiter des outils spécifiques comme de la thermite pour les portes des coffres-forts, des dispositifs de piratage pour les panneaux de sécurité et des sacs polochons pour transporter le butin. Implémentez des mini-jeux à difficulté progressive pour chaque couche de sécurité, où l'échec d'un piratage déclenche des alarmes supplémentaires ou verrouille davantage le coffre-fort :
Config.BankRobberies = {
['fleeca_1'] = {
label = 'Fleeca Bank - Legion Square',
coords = vector3(149.73, -1042.65, 29.37),
vault = vector3(144.87, -1044.16, 29.37),
tier = 1, -- 1=Fleeca, 2=Paleto, 3=Pacific Standard
cooldown = 7200, -- 2 hours
minPolice = 3,
requiredItems = {'electronickit', 'thermite'},
reward = { min = 40000, max = 80000, markedBills = true },
securityLayers = {
{ type = 'hack', difficulty = 'easy', time = 30 },
{ type = 'thermite', time = 10 },
{ type = 'drill', time = 45 },
},
},
['pacific_standard'] = {
label = 'Pacific Standard Bank',
coords = vector3(255.85, 225.60, 101.88),
vault = vector3(262.20, 222.10, 101.68),
tier = 3,
cooldown = 14400, -- 4 hours
minPolice = 6,
requiredItems = {'electronickit', 'thermite', 'advancedlaptop'},
reward = { min = 200000, max = 400000, markedBills = true },
securityLayers = {
{ type = 'hack', difficulty = 'hard', time = 20 },
{ type = 'hack', difficulty = 'hard', time = 20 },
{ type = 'thermite', time = 8 },
{ type = 'drill', time = 60 },
{ type = 'hack', difficulty = 'expert', time = 15 },
},
},
}
Utilisez des billets marqués comme récompense de vol au lieu d'argent propre, obligeant les criminels à blanchir l'argent via des boucles de jeu supplémentaires comme des lieux de blanchiment d'argent ou des échanges d'argent sale. Cela étend le jeu de rôle au-delà du braquage lui-même et crée des opportunités d'enquêtes policières. Avertissez le système de répartition de la police lorsqu'un vol commence et suivez la progression du vol côté serveur afin que les agents puissent réagir tactiquement en fonction de la progression des criminels.
Mesures de sécurité et anti-exploit
Les systèmes financiers sont la principale cible des exploiteurs, car l’argent se traduit directement en pouvoir dans le jeu. Au-delà de la validation côté serveur déjà évoquée, implémentez plusieurs couches de sécurité supplémentaires. Ajoutez une limitation de taux à tous les événements bancaires afin qu'un seul joueur ne puisse pas lancer des centaines de demandes de dépôt ou de transfert par seconde. Enregistrez chaque transaction financière avec les identifiants et l'horodatage du joueur source afin que les administrateurs puissent retracer les flux financiers et identifier les exploits de duplication. Implémentez un système d'annulation des transactions que les administrateurs peuvent utiliser pour annuler les transactions frauduleuses lorsque des exploits sont découverts. Fixez des limites maximales pour une transaction unique et des limites cumulatives quotidiennes qui s'adaptent à l'âge du compte du joueur et à la durée totale de jeu, rendant les comptes fraîchement créés moins utiles pour le blanchiment d'argent. Surveillez les modèles suspects tels que les transferts aller-retour rapides entre deux comptes, les dépôts qui correspondent exactement au montant du retrait d'un autre joueur en quelques secondes ou les augmentations de solde sans enregistrements de transactions correspondants. Envoyez des alertes webhook Discord lorsqu'une activité suspecte est détectée afin que ton équipe de modération puisse enquêter en temps réel sans attendre les rapports des joueurs. Envisagez de mettre en place un système de comptes gelés dans lequel l'administrateur peut verrouiller les comptes pendant les enquêtes, empêchant ainsi la dépense ou le transfert de l'argent exploité pendant que le problème est résolu.
Intégration à l'économie des serveurs
Ton système bancaire doit servir de plaque tournante centrale pour tous les flux monétaires sur le serveur. Acheminez les chèques de paie via le système bancaire afin que les joueurs reçoivent leur salaire sous forme de dépôt bancaire avec un relevé de transaction clair indiquant quel travail les a payés et combien. Connectez les achats en magasin pour déclencher des retraits bancaires lorsque les joueurs paient avec leur carte plutôt qu'en espèces, créant ainsi une trace écrite qui ajoute du réalisme et donne aux joueurs une raison d'utiliser le système bancaire au-delà du simple stockage. Mettez en œuvre une facturation automatique pour les coûts récurrents tels que les taxes foncières, l'assurance automobile et les dépenses d'exploitation de l'entreprise, qui sont déduites du compte bancaire du joueur à intervalles réguliers. Si le compte d'un joueur ne dispose pas de fonds suffisants pour un paiement automatique, le système devrait enregistrer un échec de paiement et déclencher des conséquences telles que des avertissements de saisie de propriété ou des déchéances d'assurance. Reliez le système bancaire à ton ressource téléphonique afin que les joueurs puissent vérifier leur solde, consulter les transactions récentes et effectuer des virements rapides sans se rendre physiquement à un guichet automatique ou à une agence bancaire. Cette intégration transforme le système bancaire d'une fonctionnalité isolée en un système nerveux financier qui relie chaque activité économique sur ton serveur.
