esx_barbershop
Install and configure esx_barbershop for ESX Legacy. Hair, beard and makeup changes via esx_skin, all config keys, the pay callback, refund logic and common fixes.
esx_barbershop places barber shops on the map where players pay to change their hairstyle, beard, eyebrows, makeup, lipstick and ear accessories. It reuses the esx_skin menu in restricted mode and saves the result server-side, with an automatic refund if saving fails.
Overview
| Resource | esx_barbershop |
| Side | Client + server |
| Dependencies | esx_lib, es_extended, esx_skin |
| Database | None (uses the skin column via esx_skin) |
| Payment | Cash (xPlayer.removeMoney) |
In-game flow:
- The player walks into one of the 7 default shop markers and presses
E. - The esx_skin menu opens, limited to the fields listed below.
- After confirming with "Yes", the server checks distance and money, charges
Config.Priceand saves the skin. - "No", leaving the marker or a failed payment reloads the previous look.
Editable fields: hair_1, hair_2, hair_color_1, hair_color_2, beard_1 to beard_4, eyebrows_1 to eyebrows_4, makeup_1 to makeup_4, lipstick_1 to lipstick_4, ears_1, ears_2.
Installation
There is no SQL file. Make sure esx_skin (which exports SaveSkin) starts first:
ensure es_extended
ensure esx_lib
ensure skinchanger
ensure esx_skin
ensure esx_barbershopConfiguration
All keys live in config.lua.
| Key | Default | Description |
|---|---|---|
Config.Price | 100 | Cash price per visit. |
Config.ShopDistance | 3.0 | Server-side maximum distance (meters) between the player and a shop for paying. |
Config.DrawDistance | 10.0 | Distance at which markers are drawn. |
Config.MarkerSize | vector3(1.5, 1.5, 1.0) | Marker size. The interaction radius is fixed to 1.5 in the client. |
Config.MarkerColor | {r = 102, g = 102, b = 204} | Marker color (alpha fixed to 100). |
Config.MarkerType | 1 | Marker type. |
Config.Locale | GetConvar('esx:locale', 'en') | Locale for help text and notifications. |
Config.Shops | 7 vector3 positions | Shop locations. Each gets a marker and a blip (sprite 71, color 51). |
Default shop positions:
Config.Shops = {
vector3(-814.3, -183.8, 36.6),
vector3(136.8, -1708.4, 28.3),
vector3(-1282.6, -1116.8, 6.0),
vector3(1931.5, 3729.7, 31.8),
vector3(1212.8, -472.9, 65.2),
vector3(-32.9, -152.3, 56.1),
vector3(-278.1, 6228.5, 30.7)
}Callbacks
Both callbacks are registered with xLib.callback.registerCompat, so they work with xLib.callback and ESX.TriggerServerCallback.
esx_barbershop:pay
xLib.callback('esx_barbershop:pay', false, function(paid, reason) end, skin)| Name | Type | Description |
|---|---|---|
skin | table | Full skin table from skinchanger:getSkin. |
Returns paid (boolean) and, on failure, a reason string. Reasons set by this resource: player_unavailable, rate_limited, not_near_shop, invalid_appearance:payload:expected_table, payment_pending, insufficient_funds, payment_failed, appearance_save_failed, appearance_save_error, appearance_save_rejected, resource_stopped. esx_skin can pass its own reasons through as well (for example appearance_scope_mismatch:sex).
The server:
- rate limits each player (2 requests, refilled 1 per 2 seconds),
- requires the player within
Config.ShopDistanceof a shop, - locks the account with
xPlayer.beginAccountOperation()and allows only one pending payment per identifier, - calls
exports.esx_skin:SaveSkin(source, skin, finish, 'barber')and refunds the price with reasonHaircut Refundif the save does not succeed.
esx_barbershop:checkMoney
xLib.callback('esx_barbershop:checkMoney', false, function(canPay) end)Returns true when the player is near a shop and has at least Config.Price cash.
Server events
esx_barbershop:pay
TriggerServerEvent('esx_barbershop:pay', skin)Legacy net event that runs the same logic as the callback without returning a result. The bundled client uses the callback.
Client-triggerable
Any client can send this event with an arbitrary skin table. The server still enforces the distance check, rate limit and full price, and esx_skin's SaveSkin with scope 'barber' only copies the fields listed in its Config.SkinEditFields.barber onto the stored skin and rejects a changed sex. A cheater cannot get a free haircut or swap clothes this way, as long as esx_skin is current.
Client events
Local events fired by the marker zones:
| Event | Argument | Fired when |
|---|---|---|
esx_barbershop:hasEnteredMarker | zone (shop index) | Player enters a shop marker. Shows the help text with ESX.TextUI. |
esx_barbershop:hasExitedMarker | zone | Player leaves. Closes the menu and restores the old skin if nothing was paid. |
Common issues
- Purchase failed notification, money returned. esx_skin rejected the save. Check the server console for
[esx_barbershop] Appearance save failedand the reason; usually esx_skin is outdated and has noSaveSkinexport. - "Not enough money" although the bank is full. Only cash (
moneyaccount) is used. - Look resets after relog. The save failed or another skin script overwrote the skin. Make sure only one appearance resource writes to
users.skin.
Related
Frequently asked questions
Independent documentation
Last updated on
esx_banking
esx_banking adds banks, ATMs, PIN codes, transfers and transaction history to ESX Legacy. Config defaults, the logTransaction export and security model.
esx_basicneeds
esx_basicneeds adds hunger and thirst to ESX Legacy. Learn how to add food and drink items, the /heal command, events, config keys and how statuses drain.