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'.

lua
-- 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/3
Ordena el código

Ordena 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) end
if xPlayer.getAccount('bank').money < cantidad then return cb(false) end
if not cantidad or cantidad < 1 then return cb(false) end
local 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.

Rellena los huecos

Completa el movimiento de un ingreso ya validado: efectivo → banco.

1-- Ingresar: cash -> bank (ya validado)
2xPlayer.(cantidad)
3xPlayer.('bank', cantidad)
Pista

Quitas efectivo con removeMoney y lo sumas a la cuenta 'bank' con addAccountMoney.

Corrige el error

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:

1ESX.RegisterServerCallback('cajero:retirar', function(source, cb, cantidad)
2 local xPlayer = ESX.GetPlayerFromId(source)
3 cantidad = tonumber(cantidad)
4 xPlayer.removeAccountMoney('bank', cantidad)
5 xPlayer.addMoney(cantidad)
6 cb(true)
7end)

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:

How was this lesson?