Features
Multi-Framework
The resource detects the active framework at startup — no manual config needed.
| Framework | Detection |
|---|---|
| ESX | es_extended resource started |
| QBCore | qb-core resource started |
| QBox | qbx_core resource started |
| Standalone | Fallback if none detected |
This affects job lookup, money handling, and item delivery during checkout.
Shop Access
Shops can be public or restricted by job.
JobRestriction = 'police'
JobRestriction = { 'police', 'sheriff' }Restricted shops are inaccessible for players without the matching job. Blip visibility updates automatically on job change — a background poll checks the player’s job every 5 seconds to catch changes from admin commands or external scripts that don’t fire framework events.
Grade Restrictions
Individual items can require a minimum job grade.
{ name = 'weapon_carbinerifle', label = 'Carbine Rifle', price = 0, image = 'rifle.png', maxQty = 1, grade = 2 }Players below the required grade won’t see the item in the UI. The server also blocks direct checkout attempts.
Themes
Five built-in UI color presets. Set via Config.Theme.
Locales
The resource ships with 8 locales. Set Config.Locale to choose. All UI labels and system messages use the selected locale table.
| Code | Language |
|---|---|
en | English |
de | German |
es | Spanish |
fr | French |
it | Italian |
pl | Polish |
pt | Portuguese |
tr | Turkish |
Target Systems
| Mode | Description |
|---|---|
'none' | Built-in 3D markers with E-key interaction |
'qb-target' | qb-target box zones per shop |
'ox-target' | ox_target sphere zones per shop |
No external dependency needed for marker mode.
Logging
Checkout logs include: player name, items, payment type, shop location, and player identifiers (license, Steam, Discord ID, FiveM).
| Backend | Config |
|---|---|
| Discord | Config.LogType = 'discord' + Config.WebhookURL |
| FiveManage | Config.LogType = 'fivemanage' + Config.FivemanageToken |
Discord IDs are logged as raw numeric IDs, not as mention pings. No one gets pinged by checkout logs.
Exports
Other resources can open or close the shop UI through client exports. See Exports for usage and examples.
Item Categories
Shops can optionally organize items into categories. When any item in a shop has a category field, the UI renders filter tabs above the item grid.
items = {
{ name = 'water', label = 'Water', price = 5, image = 'water.png', maxQty = 99, category = 'Food & Snacks' },
{ name = 'repairkit', label = 'Repair Kit', price = 250, image = 'lockpick.png', maxQty = 10, category = 'Equipment' },
}An “All” tab is always shown first. Items without a category field appear in every tab. Categories are fully backward-compatible — shops without any category fields behave exactly as before.
Shop Peds
Each shop can optionally spawn an NPC ped instead of (or alongside) a marker. The ped is invincible, frozen, and passive — players interact with it via target system or E-key.
PedModel = 'mp_m_shopkeep_01', -- ped model name
PedHeading = 146.37, -- facing direction (0-360)
PedScenario = 'WORLD_HUMAN_STAND_IMPATIENT', -- optional idle animation- Peds replace markers when configured — the marker is hidden automatically
- Works with qb-target, ox_target, and E-key fallback
- Job-restricted peds are only visible to authorized players
- Peds refresh automatically on job change
Ped Admin Tools
The admin panel includes a built-in ped model picker with ~500 searchable models. Per shop you can:
- Toggle peds on/off
- Search and select any ped model from a visual grid
- Set heading or use “my current direction”
- Assign idle scenario animations
Item Picker
The admin panel’s item editor includes a framework item picker that lists all registered items directly.
- QBCore: reads from
QBCore.Shared.Items - ESX: queries items from the database
- QBox: reads from
exports.qbx_core:GetItems()
Search and filter by item name or label. Selecting an item auto-fills both the internal name and the display label.
Version Checker
On resource start, the script checks GitHub Releases for a newer version.
- Single HTTP request — no performance impact
- Console notification with download link if an update is available
- Silent if already up to date
Dynamic Pricing
Prices can fluctuate automatically within a configurable range. Enable per shop via config or admin panel.
['247_supermarket'] = {
DynamicPricing = true, -- enable for this shop
DynamicPriceRange = 30, -- ±30% price fluctuation
DynamicPriceInterval = 30, -- refresh interval in minutes
items = {
{ name = 'vanbottle', label = 'Gin', price = 500, minPrice = 555, maxPrice = 666 }, -- per-item override
}
}- Per-shop toggle —
DynamicPricing = true/false - Global interval —
Config.DynamicPriceInterval(default: 30 minutes) - Per-shop interval —
DynamicPriceIntervaloverrides the global setting - Price range —
DynamicPriceRangeas percentage (±%), or use per-itemminPrice/maxPricefor exact bounds - UI indicator — Dynamic prices are displayed in orange with an animated chart icon
- Persistence — Settings persist across restarts via
saved_shops.json
Admin Commands
/smartshopcreate <shopId> [newId]
Copies an existing shop to the player’s current position. Requires the command.smartshopcreate ACE permission.
/smartshopcreate 247_supermarket my_new_shop- Copies all items, job restrictions, blip/marker settings from the source shop
- Uses the player’s current coordinates
- Saves the new shop to
saved_shops.jsonso it persists across restarts - Registers blips and target zones for all connected players instantly
Add add_ace group.admin command.smartshopcreate allow to your server.cfg to grant admin access. Without this, the command is blocked.
/smartshoplist
Prints all registered shops and their positions to the F8 console, including both config-defined and dynamically created shops.
========== [mizu_smartshop] All Shops ==========
24/7 Supermarket (247_supermarket) — 25.68, -1346.58, 29.49
LSPD Armory (police_armory) — 452.19, -980.00, 30.68 [job: police, sheriff]
My Custom Shop (my_new_shop) — 100.00, 200.00, 30.00 [dynamic]
================================================This command is available to all players by default. Dynamic shops are marked with [dynamic].
Admin Panel
The built-in admin panel lets you create, edit, and delete shops directly in-game — no config file editing required. Open via /smartshopedit (requires ACE permission command.smartshopedit).
Add both commands to your server.cfg:
add_ace group.admin command.smartshopcreate allow
add_ace group.admin command.smartshopedit allow
Shop Overview
The main screen shows all shops as cards in a grid. Each card displays: name, ID, item count, job restrictions, and a type badge.
| Badge | Meaning |
|---|---|
| config | Defined in config.lua (original) |
| override | Config shop with in-game edits applied |
| dynamic | Created entirely in-game |
Use the ”+ New Shop” card to create a fresh empty shop at your current position.
Shop Editor
Click Edit on any shop card. The split-layout editor shows:
- Left pane — Shop settings: name, coordinates, job restrictions (multi-select dropdown), blip config, marker config
- Right pane — Item list with add/edit/remove per item
Job restrictions use a searchable multi-select dropdown populated from your framework’s job list (QBCore Shared.Jobs, QBox GetJobs(), or ESX MySQL query). Select or deselect jobs with checkboxes — no manual typing required.
Config shops show a “Reset to Config” button that removes all overrides. Dynamic shops show a “Delete” button.
Use the crosshair button next to coordinates to set the shop position to your current in-game location.
Item Editor
Click Edit on any item or ”+ Add Item”. The modal provides fields for: name, label, price, max quantity, grade, category, and image.
Image Picker
Click the image browse button in the item editor. The picker scans html/images/ and common inventory folders (qb-inventory, ox_inventory, etc.) at resource start, displaying all available images as a searchable thumbnail grid.
Data Storage
The admin panel uses Option 3 (Override) storage:
config.luais never modified — it remains the baseline- Edits to config shops are stored as overrides in
saved_shops.json - New shops are stored as dynamic entries in the same file
- On resource start, the JSON is loaded and merged over
config.lua - Resetting a config shop simply removes its override entry
Security
All admin panel server events are protected:
- ACE permission check — every event verifies
command.smartshopeditviaIsPlayerAceAllowed. Unauthorized requests are silently dropped. - Input sanitization — shop IDs are stripped to alphanumeric characters, underscores, and hyphens (max 64 chars). Invalid input is rejected.
- No client trust — all data validation happens server-side. The NUI is a display layer only.