Examples
Each example is one job. Copy the block, swap the ids, and wire it into your resource.
Related: API · Statebags · Events · Hooks
1. Register a society for your shop
Scenario: You run a shop script. Valentine General Store should be its own organization with its own account and members.
Goal: Create that organization on server boot and look it up later — without saving a society id in your database.
-- server — run once when bln_society is ready
AddEventHandler('bln_society:ready', function()
exports.bln_society:EnsureSociety({
name = 'Valentine General Store',
preset = 'business',
fundingSource = Const.FUNDING.ACCOUNT,
ownerResource = 'my_store', -- your resource name
externalKey = 'valentine_general_store', -- your config id for this shop
})
end)
-- server — call this anywhere you need the shop's society
local function getStoreSociety(storeId)
return exports.bln_society:SocietyByKey('my_store', storeId)
endResult: First boot creates the society. Every boot after that finds the same one. getStoreSociety('valentine_general_store') gives you a handle — or nil if the society was deleted.
2. Send customer payment to the society account
Scenario: A player buys items at your shop. The money should go into the shop's society till, not disappear.
Goal: Credit the society balance when a sale completes. No permission check — the buyer is a customer, not a member.
-- server — inside your purchase handler, after the player paid
RegisterNetEvent('my_store:purchaseComplete', function(storeId, transactionId, price)
local society = exports.bln_society:SocietyByKey('my_store', storeId)
if not society then return end
society:AddMoney(price, {
currency = 0,
type = 'sale',
reason = 'Cart sold',
ref = ('my_store:sale:%s'):format(transactionId), -- unique — safe to retry
})
end)Result: The society balance goes up. The ledger records the sale. Calling again with the same ref does not double-credit.
Member restocking from the till is different — use
Withdraw/Depositso permissions and player pockets are handled. See API — Finance.
3. Block staff actions for players without permission
Scenario: A player tries to open your restock menu. Only store staff with the right permission should get in.
Goal: Check permission before your action runs. Reject everyone else.
-- server — before opening restock or running any staff-only logic
RegisterNetEvent('my_store:tryRestock', function(storeId)
local society = exports.bln_society:SocietyByKey('my_store', storeId)
if not society then return end
local charId = exports.bln_society:GetCharacterId(source)
if not charId then return end
if not society:HasPermission(charId, 'stores.restock') then
-- notify player: no permission
return
end
-- player passed — run your restock logic here
TriggerClientEvent('my_store:openRestockUi', source, storeId)
end)Result: Only members whose rank (or overrides) include stores.restock reach the restock UI. Everyone else is stopped here.
Register stores.restock in config/permissions.cfg.lua if it is not in the built-in list. Check permission names, not rank names like "Deputy".
4. Open the management menu from your own NPC
Scenario: Your shop already has a clerk NPC. You do not want a second BLN Society prompt on the same counter — you want your interaction to open the society menu.
Goal: Open the BLN Society management UI when the player talks to your NPC.
-- server — triggered by your existing clerk / target / prompt
RegisterNetEvent('my_store:clerkTalk', function(storeId)
local society = exports.bln_society:SocietyByKey('my_store', storeId)
if not society then return end
society:Open(source) -- opens the management menu for this player
end)Result: The player sees the full society menu (members, finances, duty, etc.) without BLN Society placing its own world point.
Hide tabs and actions your resource owns
When your script manages lifecycle or money outside the society menu, lock down what members can change:
AddEventHandler('bln_society:ready', function()
exports.bln_society:EnsureSociety({
name = 'Valentine General Store',
preset = 'business',
ownerResource = 'my_store',
externalKey = 'valentine_general_store',
uiPolicy = {
tabs = { settings = false, payroll = false },
actions = {
society_delete = false,
society_rename = false,
society_status = false,
},
},
})
end)Members still open the menu from your clerk or the society point, but they cannot dissolve the store or rename it from BLN Society. Your resource keeps delete/rename in its own admin flow.
To hide more for one open only (server export — cannot loosen stored policy):
society:Open(source, {
ui = { tabs = { finances = false } },
})Alternative — BLN Society draws the blip and prompt
Use this only when you have no clerk or interaction of your own:
local society = exports.bln_society:SocietyByKey('my_store', storeId)
if not society then return end
society:SetPoint({
coords = { x = -324.1, y = 776.5, z = 117.8, h = 90.0 },
blip = { sprite = 'blip_shop_store', name = 'Valentine Store' },
})Members see the blip and prompt at the coords. Strangers do not.
5. On the client: show a staff door only when on duty
Scenario: Your shop MLO has a back room. Only employees who are clocked in should see the enter prompt.
Goal: Read the local player's duty statebag and show or hide your prompt. No server callback.
-- client
local STORE_SOCIETY_ID = 3 -- send this from your server payload when the player enters the shop
CreateThread(function()
while not GlobalState['society:ready'] do Wait(100) end
while true do
local duty = LocalPlayer.state['society:duty']
local onDutyForThisStore = duty and duty.id == STORE_SOCIETY_ID
if onDutyForThisStore then
ShowBackRoomPrompt() -- your prompt / target / text UI
else
HideBackRoomPrompt()
end
Wait(500)
end
end)Result: The prompt appears only while the player is on duty for that society. Clock out and it disappears.
Your server already knows the society id — include it when the client loads the shop:
-- server — in whatever payload you send when the player enters the store
TriggerClientEvent('my_store:enter', source, {
storeId = storeId,
societyId = society and society.id or nil,
})For permission checks on the client, read LocalPlayer.state['society:memberships']. Bag shapes: Statebags. To refresh UI when duty changes without polling, use Events.
