Build a working Roblox shop system from scratch. Players spend in-game cash to buy tools and items. Includes server-side validation to prevent exploits.
A shop system in Roblox has two parts: a GUI the player clicks, and a server script that handles the purchase securely. Never process purchases on the client โ exploiters can fire RemoteEvents with any values they want.
Everything that changes game state (cash, inventory, items) must be validated server-side. The client sends a request, the server checks it, the server acts on it. Never trust the client.
In ServerScriptService, create the RemoteEvent that the shop GUI will fire:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyEvent = Instance.new("RemoteEvent")
buyEvent.Name = "BuyItem"
buyEvent.Parent = ReplicatedStoragelocal ITEMS = {
Sword = { price = 50 },
Shield = { price = 100 },
Bow = { price = 75 },
}Keep your item definitions on the server. Never put prices in the client-side GUI code โ an exploiter can read and modify client-side Lua.
local ServerStorage = game:GetService("ServerStorage")
buyEvent.OnServerEvent:Connect(function(player, itemId)
-- Validate item exists
local item = ITEMS[itemId]
if not item then return end
-- Validate player has enough cash
local cash = player.leaderstats and player.leaderstats:FindFirstChild("Cash")
if not cash or cash.Value < item.price then return end
-- Validate tool exists in ServerStorage
local tool = ServerStorage:FindFirstChild(itemId)
if not tool then return end
-- Process purchase
cash.Value -= item.price
tool:Clone().Parent = player.Backpack
print(player.Name .. " bought " .. itemId)
end)Notice how every step validates before acting. If anything is wrong, we just return โ no error, no crash.
In a LocalScript inside your shop GUI:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyEvent = ReplicatedStorage:WaitForChild("BuyItem")
-- Wire up your buy buttons
local buyButton = script.Parent:WaitForChild("BuyButton")
buyButton.MouseButton1Click:Connect(function()
buyEvent:FireServer("Sword")
end)Use WaitForChild to get the RemoteEvent โ it might not exist yet when the LocalScript runs.
Your tools need to live in ServerStorage, not the workspace. Drag your Tool objects into ServerStorage in the Explorer. Name them exactly as they appear in your ITEMS table โ "Sword", "Shield", etc.
Add a check so players can't buy a tool they already have:
-- Inside the OnServerEvent handler, before cloning:
if player.Backpack:FindFirstChild(itemId) or
(player.Character and player.Character:FindFirstChild(itemId)) then
return -- already owns it
endDon't want to build this from scratch? Grab our complete Shop System script โ fully working, exploit-safe, with comments explaining every line.
Browse our free library of copy-paste Luau scripts โ no setup needed.
Browse Script Library โ