Final project: your first ATM · Lesson 3/4 · 11 min
The server: deposit and withdraw (validated)
The heart of the ATM: callbacks that check the balance and move the money, with anti-spam.
Here's the important part: ALL the money is moved on the server, validating that the player has what they claim. The client only asks 'deposit 100' or 'withdraw 50'.
-- server.lua
local ultimo = {}
local function antiSpam(src)
local ahora = GetGameTimer()
if ultimo[src] and (ahora - ultimo[src]) < 500 then return false end
ultimo[src] = ahora
return true
end
-- Query balance
ESX.RegisterServerCallback('cajero:saldo', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer then return cb(0, 0) end
cb(xPlayer.getMoney(), xPlayer.getAccount('bank').money)
end)
-- Deposit (cash -> bank)
ESX.RegisterServerCallback('cajero:ingresar', function(source, cb, cantidad)
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer or not antiSpam(source) then return cb(false) end
cantidad = tonumber(cantidad)
if not cantidad or cantidad < 1 then return cb(false) end
if xPlayer.getMoney() < cantidad then return cb(false) end -- do they have the cash?
xPlayer.removeMoney(cantidad)
xPlayer.addAccountMoney('bank', cantidad)
cb(true)
end)
-- Withdraw (bank -> cash)
ESX.RegisterServerCallback('cajero:retirar', function(source, cb, cantidad)
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer or not antiSpam(source) then return cb(false) end
cantidad = tonumber(cantidad)
if not cantidad or cantidad < 1 then return cb(false) end
if xPlayer.getAccount('bank').money < cantidad then return cb(false) end -- do they have the balance?
xPlayer.removeAccountMoney('bank', cantidad)
xPlayer.addMoney(cantidad)
cb(true)
end)server.lua
Count the safeguards: player exists, anti-spam, amount is a number and positive, and they HAVE the money before moving it. Without one of those checks, a cheater empties your bank. cb() on all paths (callbacks module).
Practice what you learned
0/3Ordena las comprobaciones del servidor ANTES de mover el dinero en un retiro.
Coloca las líneas en el orden correcto con las flechas.
if not xPlayer or not antiSpam(source) then return cb(false) endif xPlayer.getAccount('bank').money < cantidad then return cb(false) endif not cantidad or cantidad < 1 then return cb(false) endlocal xPlayer = ESX.GetPlayerFromId(source)cantidad = tonumber(cantidad)Pista
Primero el jugador y el anti-spam, luego convertir la cantidad, validarla, y por último comprobar el saldo.
Completa el movimiento de un ingreso ya validado: efectivo → banco.
-- Ingresar: cash -> bank (ya validado)xPlayer.(cantidad)xPlayer.('bank', cantidad)Pista
Quitas efectivo con removeMoney y lo sumas a la cuenta 'bank' con addAccountMoney.
Este callback de retirar mueve el dinero SIN comprobar el saldo del banco. Un tramposo te vacía la cuenta. Añade la comprobación.
Este código tiene un fallo:
ESX.RegisterServerCallback('cajero:retirar', function(source, cb, cantidad) local xPlayer = ESX.GetPlayerFromId(source) cantidad = tonumber(cantidad) xPlayer.removeAccountMoney('bank', cantidad) xPlayer.addMoney(cantidad) cb(true)end)Reescríbelo corregido:
Pista
Antes de mover el dinero: if xPlayer.getAccount('bank').money < cantidad then return cb(false) end.
Challenge: code it yourself
Add a maximum withdrawal limit per operation (e.g. 5000€) read from Config.
Write it yourself in your editor (VS Code) and test it on your server. You learn here by doing it, not by copying.
See hint
After validating the amount: if cantidad > Config.MaxRetiro then return cb(false) end.
Escribe aquí tu solución:
