Configurations
config.lua at the root of the resource. Changes take effect on resource restart.General
Config.Locale = 'en' -- 'en', 'de', 'es', 'fr', 'it', 'pl', 'pt', 'tr'
Config.Theme = 'default' -- 'default', 'green', 'yellow', 'silver', 'red'
Config.TargetSystem = 'none' -- 'none', 'qb-target', 'ox-target'
Config.LogType = 'discord' -- 'discord' or 'fivemanage'
Config.DynamicPriceInterval = 30 -- Minutes between price updates (global default)
Config.WebhookURL = ''
Config.FivemanageToken = ''Config.Locale
Controls which locale file is loaded. Ships with 'en' (English), 'de' (German), 'es' (Spanish), 'fr' (French), 'it' (Italian), 'pl' (Polish), 'pt' (Portuguese), and 'tr' (Turkish).
Config.Theme
Controls the UI color preset: 'default', 'green', 'yellow', 'silver', 'red'.
Config.TargetSystem
Controls how players interact with shops.
| Value | Behavior |
|---|---|
'none' | Built-in 3D markers + E key |
'qb-target' | qb-target box zones |
'ox-target' | ox_target sphere zones |
Config.LogType
Controls where checkout logs are sent: 'discord' or 'fivemanage'.
Config.WebhookURL
Discord webhook URL. Only used when LogType = 'discord'. Leave empty to disable.
Config.FivemanageToken
FiveManage API token. Only used when LogType = 'fivemanage'. Leave empty to disable.
Config.DynamicPriceInterval
Global interval (in minutes) between dynamic price refreshes. Default: 30. Individual shops can override this with their own DynamicPriceInterval field.
Do not commit real webhook URLs or API tokens to public repositories. Keep credentials out of version control.
Shops
Each entry inside Config.Shops defines one shop.
Config.Shops = {
['247_supermarket'] = {
name = '24/7 Supermarket',
coords = vector3(25.68, -1346.58, 29.49),
DynamicPricing = true, -- enable dynamic pricing
DynamicPriceRange = 30, -- ±30% price fluctuation
--DynamicPriceInterval = 30, -- override global interval (minutes)
Blipname = 'Supermarket',
BlipSprite = 52,
BlipColor = 2,
BlipMinimapOnly = true,
MarkerType = 1,
MarkerSize = vector3(1.0, 1.0, 0.5),
MarkerColor = { r = 30, g = 150, b = 30, a = 100 },
PedModel = 'mp_m_shopkeep_01', -- spawn a ped instead of marker
PedHeading = 146.37, -- facing direction (0-360)
PedScenario = '', -- idle animation
items = {
{ name = 'water_bottle', label = 'Juice', price = 5, image = 'berriesjuice.png', maxQty = 999, category = 'Food & Snacks' }
}
}
}Shop fields
| Field | Description |
|---|---|
name | Display name shown in the UI and logs |
coords | Main shop position (target zones, markers, logs) |
JobRestriction | Optional. String or table of job names. Remove to make public |
DynamicPricing | Optional. true to enable fluctuating prices for this shop |
DynamicPriceRange | Optional. ± percentage for price fluctuation (default: 30) |
DynamicPriceInterval | Optional. Override global refresh interval in minutes |
-- Single job
JobRestriction = 'police'
-- Multiple jobs
JobRestriction = { 'police', 'sheriff' }Blip settings
Remove Blipname from a shop to disable its blip entirely.
| Field | Description |
|---|---|
Blipname | Map label. Remove to disable the blip |
BlipSprite | GTA blip sprite ID |
BlipColor | GTA blip color ID |
BlipMinimapOnly | true = minimap only, false = full map blip |
Blips for job-restricted shops are only visible to players with the matching job. Blips refresh automatically on job change.
Marker settings
These are only used when Config.TargetSystem = 'none'.
| Field | Description |
|---|---|
MarkerType | GTA marker type ID. Remove to disable the marker |
MarkerSize | vector3 scale |
MarkerPos | Optional custom position. Falls back to coords |
MarkerColor | RGBA table: { r = 30, g = 150, b = 30, a = 100 } |
Ped settings
Shops can spawn an NPC ped instead of a marker. When PedModel is set, the marker is hidden and the ped becomes the interaction point.
| Field | Description |
|---|---|
PedModel | Ped model name (e.g. 'mp_m_shopkeep_01'). Remove to disable |
PedHeading | Facing direction 0–360 |
PedScenario | Optional idle animation (e.g. 'WORLD_HUMAN_STAND_IMPATIENT'). Leave empty for none |
PedModel = 'mp_m_shopkeep_01',
PedHeading = 146.37,
PedScenario = 'WORLD_HUMAN_STAND_IMPATIENT',Peds are invincible, frozen, and passive. They work with qb-target, ox_target, and E-key fallback. Job-restricted peds are only visible to authorized players.
Items
Each shop has an items table.
items = {
{ name = 'weapon_pistol', label = 'Combat Pistol', price = 0, image = 'pistol.png', maxQty = 1, grade = 0 }
}| Field | Description |
|---|---|
name | Internal item name used by your framework / inventory |
label | Display name in the UI |
price | Price per unit |
image | Image filename. Must match your inventory image path |
maxQty | Max amount per checkout |
grade | Optional. Minimum job grade required. Hidden + blocked if player grade is lower |
category | Optional. Category name for tab filtering. Items without a category show under “All” |
minPrice | Optional. Minimum price for dynamic pricing (overrides DynamicPriceRange) |
maxPrice | Optional. Maximum price for dynamic pricing (overrides DynamicPriceRange) |
When at least one item in a shop has a category, the UI shows category tabs above the item grid. Items without a category always appear in every tab.
items = {
{ name = 'water_bottle', label = 'Juice', price = 5, image = 'juice.png', maxQty = 999, category = 'Food & Snacks' },
{ name = 'repairkit', label = 'Repair Kit', price = 250, image = 'lockpick.png', maxQty = 10, category = 'Equipment' },
{ name = 'vanbottle', label = 'Gin', price = 500, image = 'vanbottle.png', maxQty = 5, minPrice = 555, maxPrice = 666 }, -- dynamic pricing override
}Notifications
The default implementation auto-detects QBCore, ox_lib, ESX, or falls back to native GTA notifications. Replace it to use your own notification system.
Config.Notify = function(msg, type, title)
local notifyTitle = title or 'SmartShop'
if GetResourceState('qb-core') == 'started' then
local QBCore = exports['qb-core']:GetCoreObject()
QBCore.Functions.Notify(msg, type)
elseif GetResourceState('ox_lib') == 'started' then
exports.ox_lib:notify({ title = notifyTitle, description = msg, type = type })
elseif GetResourceState('es_extended') == 'started' then
local ESX = exports['es_extended']:getSharedObject()
ESX.ShowNotification(msg)
else
SetNotificationTextEntry('STRING')
AddTextComponentString(msg)
DrawNotification(0, 1)
end
endQBox is covered by the ox_lib check since QBox uses ox_lib for notifications.
| Parameter | Description |
|---|---|
msg | The message text |
type | 'success', 'error', or your system’s equivalent |
title | Optional shop or system title |
saved_shops.json
This file is managed automatically by the admin panel and /smartshopcreate command. Do not edit it manually unless you know what you’re doing.
The file stores two types of entries:
| Type | Flag | Description |
|---|---|---|
| Override | _override: true | Field-level changes to a config.lua shop |
| Dynamic | _dynamic: true | A shop created entirely in-game |
On resource start, config.lua shops load first, then saved_shops.json is merged on top. Override entries only replace the fields they contain — all other fields keep their config.lua values. Dynamic entries are added as new shops.
Deleting saved_shops.json resets everything back to the pure config.lua state.