Skip to content

esx_shops

A modern, performance-optimized shop system for ESX Legacy with a sleek NUI interface.


Github Link


Key Features

  • Modern Svelte 5 UI with smooth animations
  • Dynamic tax system with job-based exemptions
  • Performance-optimized client
  • Server-side validation prevents all common exploits
  • Supports both ESX and ox_inventory
  • Fully configurable categories and items
  • Theme integration via ESX convars

Requirements

Installation

  1. Download and place esx_shops in your resources folder
  2. Build the UI:
cd esx_shops/web
npm install
npm run build
  1. Add to server.cfg:
 ensure esx_shops
  1. Configure in shared/config/ (see below)

File Structure

  esx_shops/
  ├── fxmanifest.lua           # Resource manifest
  ├── README.md                # This file
  ├── types.lua                # Lua type definitions
  │
  ├── shared/
  │   ├── locale.lua           # Self-contained locale system
  │   ├── config/
  │   │   ├── main.lua         # Core configuration (tax, inventory, markers, security)
  │   │   └── shops.lua        # Shop definitions (zones, items, categories)
  │   ├── functions.lua        # Shared utilities (ProcessItemImages, DebugPrint, etc.)
  │   └── compat.lua           # Backward compatibility exports
  │
  ├── client/
  │   ├── main.lua             # Entry point 
  │   ├── functions.lua        # Client utilities (GetESXThemeColors)
  │   ├── compat.lua           # Client backward compatibility
  │   └── modules/
  │       ├── blips.lua        # Blip creation & management
  │       ├── markers.lua      # Marker drawing & proximity cache
  │       ├── nui.lua          # NUI open/close/purchase callbacks
  │       └── interactions.lua # ESX interaction registration
  │
  ├── server/
  │   ├── main.lua             # Entry point (callback registration)
  │   ├── functions.lua        # Server utilities (ValidatePlayer, CheckMoney, etc.)
  │   ├── compat.lua           # Server backward compatibility
  │   └── modules/
  │       ├── tax.lua          # Tax calculation & collection
  │       ├── inventory.lua    # Inventory validation (ESX/ox_inventory)
  │       ├── validation.lua   # Rate limiting, price validation, item checks
  │       └── transactions.lua # Purchase processing orchestration
  │
  └── web/
      ├── src/
      │   ├── components/      # Svelte components
      │   ├── stores/          # State management
      │   ├── utils/           # NUI helpers
      │   └── App.svelte       # Root component
      └── dist/                # Built files (created by npm run build)

Configuration

Main Config (shared/config/main.lua)

---@type table
Config = Config or {}
 
-- Enable debug prints in console (set to true for troubleshooting)
Config.Debug = false
 
-- ════════════════════════════════════════════════════════════════
-- CORE CONFIGURATION
-- ════════════════════════════════════════════════════════════════
 
-- Inventory system ('esx' or 'ox_inventory')
Config.Inventory = 'ox_inventory'
 
-- ════════════════════════════════════════════════════════════════
-- IMAGE CONFIGURATION
-- ════════════════════════════════════════════════════════════════
 
-- Automatic image path generation for items
-- If an item doesn't have an 'image' field, it will auto-generate as:
-- {DefaultImagePath}/{itemName}.{DefaultImageFormat}
-- Set to nil or empty string to disable auto-generation
Config.DefaultImagePath = "nui://esx_shops/web/images"
Config.DefaultImageFormat = "png" -- png, webp, jpg, etc.
 
-- ════════════════════════════════════════════════════════════════
-- TAX SYSTEM CONFIGURATION
-- ════════════════════════════════════════════════════════════════
 
-- Default tax rate (19% VAT)
Config.TaxRate = 0.19
 
-- Enable/Disable tax collection to society account
-- false = Tax is only displayed, not collected
-- true = Tax is collected and deposited to society account
Config.EnableTaxCollection = true
 
-- Society account for tax collection (requires esx_addonaccount)
-- Only used if Config.EnableTaxCollection = true
-- Make sure this society exists in your database
Config.TaxSocietyAccount = 'society_banker'
 
-- Enable/Disable job-based tax exemptions
-- false = Everyone pays full tax
-- true = Jobs in TaxExemptJobs list pay 0% tax
Config.EnableTaxExemptions = true
 
-- Jobs that are exempt from paying tax
-- Only used if Config.EnableTaxExemptions = true
-- These jobs see 0% tax rate with special message in UI
Config.TaxExemptJobs = {
	'police',      -- Law enforcement
	'ambulance',   -- Emergency medical services
}
 
-- ════════════════════════════════════════════════════════════════
-- MARKER CONFIGURATION
-- ════════════════════════════════════════════════════════════════
 
Config.DrawDistance = 7.5
Config.MarkerSize = {x = 1.1, y = 0.7, z = 1.1}
Config.MarkerType = 29
Config.MarkerColor = {r = 50, g = 200, b = 50, a = 200}
 
-- ════════════════════════════════════════════════════════════════
-- PERFORMANCE CONFIGURATION
-- ════════════════════════════════════════════════════════════════
 
-- Movement threshold for recalculating nearby shops (meters)
Config.MovementThreshold = 5.0
 
-- Sleep times for marker thread (ms)
Config.SleepNear = 0        -- Within 50m
Config.SleepMedium = 500    -- 50-100m away
Config.SleepFar = 1500      -- Far away
 
-- ════════════════════════════════════════════════════════════════
-- SECURITY CONFIGURATION
-- ════════════════════════════════════════════════════════════════
 
-- Rate limiting: cooldown between purchases (ms)
Config.PurchaseCooldownMs = 500
 
-- Auto-expire rate limit entries (ms)
Config.CooldownExpiryMs = 10000
 
-- Maximum quantity per item per transaction
Config.MaxQuantityPerItem = 999
 
-- Price validation tolerance
Config.PriceTolerance = 0.001

Shop Config (shared/config/shops.lua)

---@type table<string, ShopZone>
Config.Zones = Config.Zones or {}
 
-- ════════════════════════════════════════════════════════════════
-- SHOP DEFINITIONS
-- ════════════════════════════════════════════════════════════════
 
-- Categories with FontAwesome icons (Find more at: https://fontawesome.com/icons)
-- Icon format: "fa-solid fa-icon-name" or "fa-regular fa-icon-name"
 
Config.Zones.TwentyFourSeven = {
	Items = {
		{name = "burger", label = "Burger", price = 15, category = "food"},
		{name = "water", label = "Water", price = 10, category = "drinks"},
    ...
	},
	Categories = {
		{id = "food", label = "Food", icon = "fa-solid fa-burger"},
		{id = "drinks", label = "Drinks", icon = "fa-solid fa-bottle-water"},
    ...
	},
	Pos = {
		vector3(373.8, 325.8, 103.5),
		vector3(2557.4, 382.2, 108.6),
		vector3(-3038.9, 585.9, 7.9),
    ...
	},
	Size = 0.8,
	Type = 59,
	Color = 25,
	ShowBlip = true,
	ShowMarker = true
}
--ADD MORE SHOPS

Zone Format Reference

FieldTypeDescription
ItemstableArray of items for sale
Items[].namestringItem identifier (must match your inventory)
Items[].labelstringDisplay name shown in UI
Items[].pricenumberPrice per unit
Items[].categorystringCategory ID for grouping (optional)
Items[].imagestringCustom image URL/path (optional, overrides auto-generation)
CategoriestableArray of category definitions
Categories[].idstringCategory identifier (must match item category)
Categories[].labelstringDisplay name shown in UI
Categories[].iconstringFontAwesome 6 icon class (e.g. fa-solid fa-burger)
PostableArray of vector3 locations for this shop
SizenumberBlip size on map
TypenumberBlip type ID (see FiveM docs)
ColornumberBlip color ID
ShowBlipbooleanShow blip on minimap/map
ShowMarkerbooleanShow 3D world marker

Tax System Explained

Tax Collection (Config.EnableTaxCollection = true):

  • Requires esx_addonaccount resource
  • Tax deposits to Config.TaxSocietyAccount (society_banker by default)
  • Logs warning if society account doesn’t exist

Tax Exemptions (Config.EnableTaxExemptions = true):

  • Jobs in Config.TaxExemptJobs pay 0% tax
  • Exempt players see ”⭐ Thanks for your service!” message in UI
  • Tax is calculated from gross prices (price includes tax)

Example:

  Item: $100 (gross price with 19% tax)
  Net:  $84.03
  Tax:  $15.97
  ---
  Police (exempt): Pays $84.03
  Civilian:        Pays $100.00

Development

cd web
 
# Development (auto-rebuild)
npm run dev:game
 
# Production build
npm run build
 
# Type checking
npm run check:strict