QB-Shops - General Shopping System
The qb-shops resource provides a comprehensive shopping system for QBCore servers, featuring configurable shops, dynamic pricing, stock management, and support for various shop types including 24/7 stores, hardware stores, and specialty retailers.
Overview
QB-Shops creates an immersive shopping experience with realistic store mechanics, inventory management, and economic systems. Players can purchase items from various shops throughout the map, each with their own product catalogs and pricing structures.
Key Features
- Multiple Shop Types: 24/7 stores, hardware shops, weapon stores, and custom retailers
- Dynamic Inventory: Stock management with restocking timers
- Economic Integration: Price fluctuations and regional pricing
- Job Restrictions: Shop access based on player jobs or licenses
- Blip Management: Automatic map markers for shop locations
- Receipt System: Transaction logging and purchase receipts
- Admin Tools: Shop management and inventory control
- Integration Ready: Compatible with qb-inventory and banking systems
Installation
Prerequisites
- QBCore Framework
- qb-inventory (for item management)
- qb-target (for shop interactions)
- qb-menu (for shop interfaces)
Installation Steps
- Download the Resource
cd resources/[qb]
git clone https://github.com/qbcore-framework/qb-shops.git
- Database Setup
-- Create shop tables
CREATE TABLE IF NOT EXISTS `shop_items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shop` varchar(255) NOT NULL,
`item` varchar(50) NOT NULL,
`price` int(11) NOT NULL,
`amount` int(11) NOT NULL,
`info` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '{}' CHECK (json_valid(`info`)),
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `shop_receipts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) NOT NULL,
`shop` varchar(255) NOT NULL,
`item` varchar(50) NOT NULL,
`amount` int(11) NOT NULL,
`price` int(11) NOT NULL,
`date` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
);
- Add to server.cfg
ensure qb-shops
- Restart Server
restart qb-shops
Ensure qb-shops loads after qb-core, qb-inventory, and qb-target for proper functionality
Configuration
Basic Configuration
The main configuration file is located at config.lua
:
Config = {}
-- General settings
Config.UseTarget = true
Config.MinimumShopDistance = 2.0
Config.ShowBlips = true
Config.RestockInterval = 60 -- minutes
-- Shop types and locations
Config.Shops = {
["24/7"] = {
label = "24/7 Store",
coords = vector4(372.66, 325.95, 103.57, 255.73),
ped = 'mp_m_shopkeep_01',
scenario = 'WORLD_HUMAN_STAND_MOBILE',
radius = 1.5,
targetIcon = 'fas fa-shopping-basket',
targetLabel = 'Open Shop',
products = {
[1] = {name = 'sandwich', price = 4, amount = 50, info = {}, type = 'item', slot = 1},
[2] = {name = 'water_bottle', price = 2, amount = 50, info = {}, type = 'item', slot = 2},
[3] = {name = 'coffee', price = 3, amount = 25, info = {}, type = 'item', slot = 3},
[4] = {name = 'cigarette', price = 8, amount = 20, info = {}, type = 'item', slot = 4},
[5] = {name = 'phone', price = 850, amount = 5, info = {}, type = 'item', slot = 5}
},
showblip = true,
blipsprite = 52,
blipcolor = 0
},
["hardware"] = {
label = "Hardware Store",
coords = vector4(45.67, -1749.89, 29.61, 53.5),
ped = 'ig_terry_01',
scenario = 'WORLD_HUMAN_STAND_MOBILE',
radius = 1.5,
targetIcon = 'fas fa-hammer',
targetLabel = 'Browse Tools',
products = {
[1] = {name = 'lockpick', price = 20, amount = 25, info = {}, type = 'item', slot = 1},
[2] = {name = 'repairkit', price = 35, amount = 15, info = {}, type = 'item', slot = 2},
[3] = {name = 'screwdriverset', price = 85, amount = 10, info = {}, type = 'item', slot = 3},
[4] = {name = 'phone', price = 850, amount = 5, info = {}, type = 'item', slot = 4},
[5] = {name = 'radio', price = 250, amount = 10, info = {}, type = 'item', slot = 5}
},
showblip = true,
blipsprite = 402,
blipcolor = 47
}
}
-- Job restricted shops
Config.JobShops = {
["police_armory"] = {
label = "Police Armory",
coords = vector4(461.45, -979.69, 30.69, 0.0),
job = "police",
mingrade = 0,
products = {
[1] = {name = 'weapon_flashlight', price = 0, amount = 10, info = {}, type = 'weapon', slot = 1},
[2] = {name = 'weapon_nightstick', price = 0, amount = 10, info = {}, type = 'weapon', slot = 2},
[3] = {name = 'weapon_pistol', price = 0, amount = 5, info = {}, type = 'weapon', slot = 3}
}
}
}
-- Shop settings
Config.ShopSettings = {
dynamicPricing = true,
priceFluctuation = 0.1, -- 10% price variation
restockEnabled = true,
restockAmount = {min = 10, max = 50},
receiptEnabled = true
}
Product Categories
Config.ProductCategories = {
['food'] = {
label = 'Food & Beverages',
icon = 'fas fa-hamburger',
items = {'sandwich', 'water_bottle', 'coffee', 'taco'}
},
['tools'] = {
label = 'Tools & Equipment',
icon = 'fas fa-wrench',
items = {'lockpick', 'repairkit', 'screwdriverset'}
},
['electronics'] = {
label = 'Electronics',
icon = 'fas fa-mobile-alt',
items = {'phone', 'radio', 'laptop'}
},
['clothing'] = {
label = 'Clothing',
icon = 'fas fa-tshirt',
items = {'mask', 'hat', 'shoes'}
}
}
Economic Settings
Config.Economy = {
taxRate = 0.08, -- 8% sales tax
shopkeeperCut = 0.02, -- 2% goes to shop owner
baseMarkup = 1.25, -- 25% markup from wholesale
demandMultiplier = {
high = 1.5, -- High demand items cost 50% more
normal = 1.0, -- Normal pricing
low = 0.8 -- Low demand items cost 20% less
}
}
API Reference
Client Exports
OpenShop
Opens a specific shop interface.
-- Open shop by name
exports['qb-shops']:OpenShop('24/7')
-- Open custom shop
exports['qb-shops']:OpenShop('custom_shop', customProducts)
GetShopData
Gets information about a shop.
-- Get shop data
local shopData = exports['qb-shops']:GetShopData('24/7')
print("Shop label: " .. shopData.label)
IsShopOpen
Checks if a shop is currently open.
-- Check if shop is open (for shops with hours)
local isOpen = exports['qb-shops']:IsShopOpen('24/7')
Server Exports
AddShop
Adds a new shop dynamically.
-- Add custom shop
exports['qb-shops']:AddShop('new_shop', {
label = 'New Shop',
coords = vector4(100.0, 200.0, 30.0, 0.0),
products = {
[1] = {name = 'water_bottle', price = 2, amount = 50, type = 'item', slot = 1}
}
})
UpdateShopStock
Updates the stock of shop items.
-- Update stock for specific item
exports['qb-shops']:UpdateShopStock('24/7', 'water_bottle', 25)
-- Restock entire shop
exports['qb-shops']:RestockShop('24/7')
GetShopStock
Gets current stock levels.
-- Get stock for specific item
local stock = exports['qb-shops']:GetShopStock('24/7', 'water_bottle')
-- Get all shop stock
local allStock = exports['qb-shops']:GetAllShopStock('24/7')
SetItemPrice
Sets the price of shop items.
-- Set item price
exports['qb-shops']:SetItemPrice('24/7', 'water_bottle', 3)
-- Set price with dynamic calculation
exports['qb-shops']:SetDynamicPrice('24/7', 'water_bottle', 'high') -- high demand
Events
Client Events
-- Shop interaction events
RegisterNetEvent('qb-shops:client:openShop', function(shopName)
-- Handle shop opening
end)
RegisterNetEvent('qb-shops:client:purchaseComplete', function(receipt)
-- Handle successful purchase
end)
RegisterNetEvent('qb-shops:client:restockAlert', function(shopName, item)
-- Handle restock notifications
end)
Server Events
-- Purchase handling
RegisterNetEvent('qb-shops:server:purchaseItem', function(shopName, item, amount))
RegisterNetEvent('qb-shops:server:sellItem', function(shopName, item, amount))
-- Shop management
RegisterNetEvent('qb-shops:server:updateStock', function(shopName, item, amount))
RegisterNetEvent('qb-shops:server:setPrice', function(shopName, item, price))
-- Receipt system
RegisterNetEvent('qb-shops:server:generateReceipt', function(shopName, items, total))
Usage Examples
Basic Shop Implementation
-- Create a simple food shop
local foodShop = {
label = "Joe's Diner",
coords = vector4(123.45, -234.56, 30.0, 180.0),
ped = 'a_m_m_farmer_01',
products = {
[1] = {name = 'burger', price = 12, amount = 25, type = 'item', slot = 1},
[2] = {name = 'cola', price = 5, amount = 30, type = 'item', slot = 2},
[3] = {name = 'fries', price = 8, amount = 20, type = 'item', slot = 3}
},
showblip = true,
blipsprite = 93,
blipcolor = 2
}
exports['qb-shops']:AddShop('joes_diner', foodShop)
Dynamic Pricing System
-- Implement demand-based pricing
RegisterNetEvent('qb-shops:server:calculateDynamicPrice', function(shopName, itemName)
local basePrice = GetItemBasePrice(itemName)
local demand = CalculateItemDemand(itemName)
local supply = exports['qb-shops']:GetShopStock(shopName, itemName)
local priceMultiplier = 1.0
-- High demand, low supply = higher prices
if demand > 80 and supply < 10 then
priceMultiplier = 1.5
elseif demand < 20 and supply > 50 then
priceMultiplier = 0.8
end
local newPrice = math.floor(basePrice * priceMultiplier)
exports['qb-shops']:SetItemPrice(shopName, itemName, newPrice)
end)
Job-Restricted Shop Access
-- Police equipment shop
RegisterNetEvent('qb-shops:client:checkPoliceAccess', function()
local PlayerData = QBCore.Functions.GetPlayerData()
if PlayerData.job.name == 'police' and PlayerData.job.grade.level >= 1 then
exports['qb-shops']:OpenShop('police_armory')
else
QBCore.Functions.Notify('Access denied - Police personnel only', 'error')
end
end)
Custom Shop Categories
-- Electronics shop with categories
local electronicsShop = {
label = "Tech World",
coords = vector4(400.0, -500.0, 35.0, 90.0),
categories = {
['phones'] = {
[1] = {name = 'phone', price = 850, amount = 10, type = 'item', slot = 1},
[2] = {name = 'phone_case', price = 25, amount = 20, type = 'item', slot = 2}
},
['computers'] = {
[1] = {name = 'laptop', price = 2500, amount = 5, type = 'item', slot = 1},
[2] = {name = 'tablet', price = 1200, amount = 8, type = 'item', slot = 2}
}
}
}
Receipt and Transaction Logging
-- Advanced purchase handling with receipts
RegisterNetEvent('qb-shops:server:advancedPurchase', function(shopName, items)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local totalCost = 0
local receipt = {
shop = shopName,
items = {},
timestamp = os.time(),
customer = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
}
-- Calculate total and validate stock
for _, item in pairs(items) do
local stock = exports['qb-shops']:GetShopStock(shopName, item.name)
if stock >= item.amount then
local itemCost = item.price * item.amount
totalCost = totalCost + itemCost
table.insert(receipt.items, {
name = item.name,
amount = item.amount,
price = item.price,
total = itemCost
})
end
end
-- Process payment
if Player.Functions.RemoveMoney('cash', totalCost) then
-- Give items and update stock
for _, item in pairs(items) do
exports['qb-inventory']:AddItem(src, item.name, item.amount)
exports['qb-shops']:UpdateShopStock(shopName, item.name, -item.amount)
end
-- Generate receipt
receipt.total = totalCost
TriggerClientEvent('qb-shops:client:showReceipt', src, receipt)
-- Log transaction
exports['qb-shops']:LogTransaction(Player.PlayerData.citizenid, receipt)
end
end)
Automatic Restocking System
-- Automated restocking with configurable intervals
CreateThread(function()
while true do
Wait(Config.RestockInterval * 60000) -- Convert minutes to milliseconds
for shopName, shopData in pairs(Config.Shops) do
for _, product in pairs(shopData.products) do
local currentStock = exports['qb-shops']:GetShopStock(shopName, product.name)
local maxStock = product.amount
-- Restock if below 25% capacity
if currentStock < (maxStock * 0.25) then
local restockAmount = maxStock - currentStock
exports['qb-shops']:UpdateShopStock(shopName, product.name, restockAmount)
print("Restocked " .. shopName .. " - " .. product.name .. " +" .. restockAmount)
end
end
end
end
end)
Administrative Commands
Player Commands
/shops
- List nearby shops/receipt
- View last purchase receipt/shopinfo [shop]
- Get information about a shop
Admin Commands
/addshop [name] [x] [y] [z]
- Create new shop at location/removeshop [name]
- Remove existing shop/restockshop [name]
- Manually restock shop/setshopprice [shop] [item] [price]
- Set item price/shopstock [shop] [item] [amount]
- Set item stock
Console Commands
# Restock all shops
restockall
# Set shop hours
setshophours 24/7 0 24
# Generate shop report
shopreport
Integration with Other Resources
qb-inventory Integration
Advanced item handling and metadata:
-- Purchase with item metadata
RegisterNetEvent('qb-shops:server:purchaseWeapon', function(shopName, weaponName)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local weaponData = {
durability = 100,
ammo = 0,
serial = GenerateSerial(),
registered = Player.PlayerData.citizenid
}
exports['qb-inventory']:AddItem(src, weaponName, 1, false, weaponData)
end)
qb-banking Integration
Shop payments through banking:
-- Credit card payments
RegisterNetEvent('qb-shops:server:cardPayment', function(shopName, amount)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if exports['qb-banking']:RemoveMoney(Player.PlayerData.citizenid, 'bank', amount, 'Shop purchase - ' .. shopName) then
TriggerClientEvent('qb-shops:client:paymentSuccess', src)
else
TriggerClientEvent('qb-shops:client:paymentFailed', src)
end
end)
qb-phone Integration
Shop delivery and ordering:
-- Phone app for shop orders
RegisterNetEvent('qb-phone:server:shopOrder', function(shopName, items, deliveryAddress)
local src = source
local deliveryFee = CalculateDeliveryFee(deliveryAddress)
local totalCost = CalculateOrderTotal(items) + deliveryFee
-- Process order and schedule delivery
if ProcessPayment(src, totalCost) then
ScheduleDelivery(src, shopName, items, deliveryAddress, 15) -- 15 minute delivery
end
end)
Troubleshooting
Common Issues
Shop Not Appearing
Problem: Shop doesn’t show up or can’t be interacted with.
Solutions:
- Check coordinates and radius settings
- Verify qb-target configuration
- Check for conflicting resources
-- Debug shop detection
RegisterCommand('debugshop', function()
local coords = GetEntityCoords(PlayerPedId())
local nearestShop = GetNearestShop(coords)
if nearestShop then
print("Nearest shop: " .. nearestShop.label)
print("Distance: " .. #(coords - nearestShop.coords))
else
print("No shops found nearby")
end
end, false)
Purchase Failures
Problem: Items can’t be purchased or transactions fail.
Solutions:
- Check player funds
- Verify item existence in qb-inventory
- Check shop stock levels
-- Debug purchase process
RegisterNetEvent('qb-shops:debug:purchase', function(shopName, itemName, amount)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local stock = exports['qb-shops']:GetShopStock(shopName, itemName)
local playerMoney = Player.PlayerData.money.cash
print("Player: " .. Player.PlayerData.charinfo.firstname)
print("Item: " .. itemName .. " Amount: " .. amount)
print("Stock available: " .. stock)
print("Player cash: $" .. playerMoney)
end)
Stock Issues
Problem: Shop stock doesn’t update or restocking fails.
Solution:
-- Manual stock reset
RegisterCommand('resetstock', function(source, args)
local shopName = args[1]
if shopName and Config.Shops[shopName] then
for _, product in pairs(Config.Shops[shopName].products) do
exports['qb-shops']:UpdateShopStock(shopName, product.name, product.amount, true) -- true = set absolute amount
end
print("Stock reset for " .. shopName)
end
end, true)
Performance Optimization
-- Optimize shop updates
Config.UpdateFrequency = 30000 -- Update every 30 seconds
Config.MaxShopDistance = 50.0 -- Only process shops within 50 units
-- Disable features if not needed
Config.DynamicPricing = false
Config.RestockSystem = false
Config.ReceiptSystem = false
Related Resources
- qb-core - Core framework functions
- qb-inventory - Item management system
- qb-banking - Payment processing
- qb-phone - Mobile shopping apps
- qb-target - Shop interaction system
Support
For issues and feature requests:
-
GitHub: qb-shops Repository
-
Documentation: QBCore Docs