Назад к блогу
Tutorial9 мин чтения

Скрипты банков и банкоматов FiveM: реалистичные денежные системы

Создайте правдоподобную экономику с помощью скриптов банков и банкоматов для FiveM. Логика переводов, идеи интерфейса, лучшие бесплатные и премиум ресурсы, а также интеграция с фреймворками.

Agency Scripts

Основатель и ведущий разработчик Agency Scripts

Архитектура банковской системы

Банковская система , финансовый фундамент любого ролевого сервера FiveM, обрабатывающая всё от простых внесений наличных до сложных межигроковых переводов и общих счетов организаций. Большинство фреймворков, таких как QBCore и ESX, включают базовое управление деньгами, но выделенная банковская система расширяет это полноценным управлением счетами, логированием транзакций и продуманным интерфейсом банкоматов, погружающим игроков в финансовую сторону ролевой игры. Архитектура делится на три слоя: база данных хранит балансы счетов и записи транзакций, сервер проверяет каждую финансовую операцию и соблюдает бизнес-правила, клиент предоставляет интерфейсы банкомата и банковской кассы через NUI. Все операции с деньгами должны проходить через сервер, так как манипуляции с деньгами на клиенте , главный вектор эксплойтов на серверах FiveM. Даже отображение баланса должно идти через серверный callback, а не из кэшированных клиентских данных, которые могут быть изменены.

Схема базы данных для банковских операций

Ваша банковская база данных должна поддерживать личные счета, совместные счета для организаций и бизнесов, а также полный журнал транзакций. Журнал транзакций обязателен, так как выполняет двойную функцию: это и функция для игроков, и инструмент для администраторов для расследования мошенничества с деньгами. Проектируйте схему так, чтобы она справлялась с высокими нагрузками, так как на загруженных серверах может обрабатываться сотни транзакций в минуту в часы пик:

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

Параметр bank_account_access таблица позволяет создавать общие аккаунты, где несколько игроков могут иметь разные уровни доступа. Лидер банды может иметь полный доступ к казне банды, в то время как обычные участники могут только просматривать баланс. Используя BIGINT для полей баланса предотвращает проблемы переполнения на серверах с раздутой экономикой, где балансы игроков могут достигать миллиардов.

Логика транзакций на стороне сервера

Каждая финансовая транзакция должна быть атомарной и проверяться на сервере. Используйте транзакции базы данных, чтобы гарантировать, что деньги не создаются и не уничтожаются во время переводов. Когда игрок A отправляет деньги игроку B, и списание с A, и добавление к B должны пройти вместе, или ни одно не применяется. Реализуйте проверки на достаточный баланс, статус замороженного аккаунта, дневные лимиты переводов и минимальные суммы транзакций. Вот безопасная реализация перевода:

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)

Обратите внимание на WHERE balance >= ? условие в запросе на списание, которое служит последней защитой от гонок, когда два одновременных перевода могут привести к перерасходу счёта. Эта проверка на уровне базы данных необходима, даже если вы уже проверяете баланс в Lua, потому что между проверкой и обновлением могут прийти несколько запросов.

Пользовательский интерфейс банкомата

Интерфейс банкомата , это компактная панель NUI, которая обеспечивает быстрый доступ к основным банковским функциям: проверка баланса, внесение наличных, снятие наличных и перевод денег. Держите дизайн чистым и привычным, потому что игроки инстинктивно ожидают, что банкомат работает как реальный аналог. Отображайте текущий баланс заметно вверху, а под ним кнопки для каждой операции. В видах внесения и снятия должны быть кнопки с предустановленными суммами для распространённых значений, таких как $100, $500, $1000 и $5000, а также поле для ввода произвольной суммы. Для переводов предоставьте поля для номера счёта получателя и суммы, а также необязательное поле для заметки. Показывайте шаг подтверждения перед выполнением любой транзакции, чтобы предотвратить случайные клики, которые могут стоить игрокам денег. Включите список последних транзакций, отображающий последние 10 записей, чтобы игроки могли проверить свою финансовую активность без необходимости посещать полноценное отделение банка. Интерфейс банкомата должен быть отзывчивым, поэтому получайте данные баланса и транзакций одним вызовом при открытии меню, а не делайте отдельные запросы для каждой информации.

Настройка взаимодействия с банкоматом

Размещайте точки взаимодействия с банкоматами на существующих объектах банкоматов по всей карте GTA. FiveM предоставляет список хэшей моделей банкоматов, по которым можно пройтись, чтобы найти все объекты банкоматов в игровом мире. Используйте систему таргетинга, например ox_target, для удобного взаимодействия или используйте проверку близости к каждому объекту банкомата. При взаимодействии игрока с банкоматом проигрывайте анимацию использования банкомата, затем открывайте 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

История транзакций и выписки

История транзакций превращает вашу банковскую систему из простой машины для внесения и снятия средств в полноценный инструмент финансового управления. Игроки должны иметь возможность просматривать полную историю транзакций в отделениях банка с фильтрацией по диапазону дат, типу транзакции или сумме. Каждая запись транзакции должна отображать дату, тип, сумму, итоговый баланс, описание и другую сторону для переводов. Реализуйте постраничную загрузку на стороне сервера, так как загрузка тысяч транзакций одновременно заморозит NUI-фрейм и вызовет резкий рост использования памяти сервера. Возвращайте по 20-30 транзакций на страницу и позволяйте игроку загружать больше по необходимости. Для отделений банка предложите дополнительные функции, выходящие за рамки возможностей банкоматов, такие как открытие новых счетов, управление разрешениями на совместное использование счетов, генерация выписок за определенный период и оформление кредитов, если ваш сервер поддерживает эту механику. Храните описания транзакций в виде читаемых строк, чтобы автоматические транзакции от работ, покупок в магазинах и налогов отображались понятными записями, которые игроки могут понять без контекста.

Механика ограбления банков

Ограбления банков , одни из самых захватывающих событий на любом ролевом сервере, создающие напряжённые сценарии с участием преступников, полиции, переговорщиков и свидетелей. Хорошо продуманная система ограбления включает несколько этапов: разведка банка, начало ограбления, взлом или сверление систем безопасности через мини-игры, загрузка добычи и побег с преследованием полиции. Начните с определения банков, которые можно ограбить, их уровня сложности, таймеров перезарядки и необходимых предметов. Ограбление должно требовать специальных инструментов, таких как термит для дверей сейфа, устройства для взлома панелей безопасности и сумки для переноски добычи. Реализуйте прогрессивные мини-игры сложности для каждого уровня безопасности, где неудачный взлом вызывает дополнительные тревоги или ещё больше блокирует сейф:

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 },
        },
    },
}

Используйте помеченные банкноты в качестве награды за ограбление вместо чистых денег, заставляя преступников отмывать деньги через дополнительные игровые циклы, такие как места для отмывания денег или обмены грязных денег. Это расширяет ролевую игру ограбления за пределы самого ограбления и создаёт возможности для полицейских расследований. Уведомляйте систему диспетчеризации полиции при начале ограбления и отслеживайте прогресс ограбления на стороне сервера, чтобы офицеры могли тактически реагировать в зависимости от того, насколько далеко продвинулись преступники.

Меры безопасности и защиты от эксплойтов

Финансовые системы являются основной целью для злоумышленников, поскольку деньги напрямую переводятся в игровую силу. Помимо уже обсуждаемой серверной проверки, реализуйте несколько дополнительных уровней безопасности. Добавьте ограничение скорости для всех банковских операций, чтобы один игрок не мог отправлять сотни запросов на депозиты или переводы в секунду. Логируйте каждую финансовую транзакцию с идентификаторами источника игрока и отметкой времени, чтобы администраторы могли отслеживать движение денег и выявлять эксплойты дублирования. Реализуйте систему отмены транзакций, которую админы могут использовать для отмены мошеннических операций при обнаружении эксплойтов. Установите максимальные лимиты на одну транзакцию и суточные кумулятивные лимиты, которые масштабируются с возрастом аккаунта и общим временем игры, делая недавно созданные аккаунты менее полезными для отмывания денег. Следите за подозрительными паттернами, такими как быстрые круговые переводы между двумя аккаунтами, депозиты, точно совпадающие с суммой снятия с другого игрока в течение нескольких секунд, или увеличение баланса без соответствующих записей транзакций. Отправляйте оповещения в Discord webhook при обнаружении подозрительной активности, чтобы ваша команда модерации могла расследовать в реальном времени без ожидания жалоб игроков. Рассмотрите возможность внедрения системы заморозки аккаунтов, когда админ может блокировать аккаунты во время расследований, предотвращая расходование или перевод эксплуатируемых денег до решения проблемы.

Интеграция с экономикой сервера

Ваша банковская система должна служить центральным узлом для всех денежных потоков на сервере. Пропускайте зарплаты через банковскую систему, чтобы игроки получали зарплату в виде банковского депозита с чёткой записью транзакции, показывающей, какая работа и сколько заплатила. Связывайте покупки в магазине с автоматическим списанием с банковского счёта при оплате картой вместо наличных, создавая бумажный след, который добавляет реализма и даёт игрокам повод использовать банковскую систему не только для хранения денег. Реализуйте автоматическое выставление счетов за регулярные расходы, такие как налоги на имущество, страхование автомобилей и операционные расходы бизнеса, которые списываются с банковского счёта игрока с регулярными интервалами. Если на счёте игрока недостаточно средств для автоматического платежа, система должна фиксировать неудавшийся платёж и запускать последствия, такие как предупреждения о конфискации имущества или прекращении страховки. Свяжите банковскую систему с вашим телефонным ресурсом, чтобы игроки могли проверять баланс, просматривать последние транзакции и быстро переводить деньги без необходимости посещать банкомат или отделение банка. Эта интеграция превращает банковскую систему из изолированной функции в финансовую нервную систему, которая связывает все экономические действия на вашем сервере.

Готовы начать?

Возьмите скрипты в нашем магазине или заходите в Discord за поддержкой, обновлениями и анонсами.