Integrations
cxg-tattoos can connect to different frameworks to charge for purchases and check administrative permissions. The appearance integration reapplies saved tattoos when the character loads or changes skin. Configure each part separately.
Also see Introduction, Installation, Configuration, Catalog, and Interface.
Available frameworks
| System | Configuration identifier | Default resource | Payments | Permissions |
|---|---|---|---|---|
| Qbox | qbx | qbx_core | Yes | Yes |
| QBCore | qbcore | qb-core | Yes | Yes |
| ESX | esx | es_extended | Yes | Yes |
| No framework | standalone | N/A | Custom callback or free purchases | ACE or custom check |
With auto, the resource detects Qbox, QBCore, or ESX if they are started; if it finds none, it uses standalone. You can pin the framework when the server has multiple active frameworks or uses a different resource name:
Config.Purchases.framework = 'qbx' -- auto | qbx | qbcore | esx | standalone
Config.Purchases.resources.qbx = 'qbx_core'
Config.Purchases.accounts = { 'cash', 'bank' }
Config.Purchases.esxAccountMap.cash = 'money'
Config.Purchases.esxAccountMap.bank = 'bank'
Config.Purchases.allowFreePurchases = false
Accounts are checked in the listed order. The charge is taken from a single account; if its balance is insufficient, the next one is tried. In ESX, esxAccountMap translates cash to the account name money.
Config.Purchases.reason and refundReason identify the reason for transactions for providers that support it. Their initial values are tattoo-purchase and tattoo-purchase-refund. The allowFreePurchases option allows charges to be skipped in the standalone adapter; it does not make purchases free in other frameworks.
Payments without a framework
In standalone mode, connect tryCharge and refund to your server's economy resource. Both callbacks are required to accept purchases: the refund callback returns the charge if saving the tattoo fails.
Config.Purchases.framework = 'standalone'
Config.Purchases.standalone.tryCharge = function(source, totalAmount, context)
-- Sustituye esta llamada por la API de economía de tu servidor.
local charged, receipt = MyEconomy.charge(source, totalAmount, context)
if charged then
return true, receipt
end
return false, 'No tienes saldo suficiente.'
end
Config.Purchases.standalone.refund = function(source, totalAmount, context, receiptData)
-- Usa receiptData para devolver el mismo cobro aprobado.
return MyEconomy.refund(source, totalAmount, receiptData) == true
end
MyEconomy represents your server's API; replace it with the actual name and calls. To intentionally allow free purchases, set allowFreePurchases = true.
Payment adapters expose isAvailable(settings), charge(source, amount, context, settings), and refund(source, amount, context, settings). An approved payment must retain the receipt in context.paymentReceipt; cxg-tattoos uses it to send any refund to the same payment system. For a custom economy, the standalone tryCharge and refund callbacks are the direct configuration path.
Administrative permissions
Permissions are checked on the server to open and operate the admin panel. Hiding or modifying the client interface does not grant access.
The initial configuration uses the detected framework and ACE as a fallback:
Config.Permissions = {
framework = 'auto', -- auto | qbx | qbcore | esx | standalone
mode = 'hybrid', -- framework | ace | hybrid | custom
acePrefix = 'cxg-tattoos.',
resources = {
qbx = 'qbx_core',
qbcore = 'qb-core',
esx = 'es_extended',
},
bindings = {
admin = {
ace = { 'cxg-tattoos.admin', 'group.admin', 'admin' },
qbx = { 'god', 'admin', 'group.admin' },
qbcore = { 'god', 'admin', 'group.admin' },
esx = { 'superadmin', 'admin' },
},
},
}
framework mode checks framework roles. ace uses the binding's ACE entries. hybrid allows either path. In standalone, framework authorization is unavailable; configure ACE or a custom check.
For example, to allow members of the group.admin ACE principal:
add_ace group.admin cxg-tattoos.admin allow
To delegate a decision to your own system, use mode = 'custom'. The callback receives the player ID, permission, operation context, and configured binding; only the boolean value true authorizes access:
Config.Permissions.mode = 'custom'
Config.Permissions.custom = {
hasPermission = function(source, permission, context, binding)
return exports.my_permissions:hasPermission(source, permission) == true
end,
}
Adapt my_permissions to the API installed on your server. If the callback is missing, fails, or returns another value, access is denied.
The command that opens the panel is also configurable. permission must match a key in Config.Permissions.bindings:
Config.AdminCommand.enabled = true
Config.AdminCommand.name = 'tattooadmin'
Config.AdminCommand.permission = 'admin'
With these values, the admin permission protects the /tattooadmin command and administrative actions. Set enabled = false to disable the command; permission checks still apply through other administration paths.
Editable integration files
Editable bridges group connections to external systems. For custom permissions or payments, it is preferable to configure the callbacks described above. If you adapt an existing bridge, preserve its contract:
| File or group | Purpose | Adapter contract |
|---|---|---|
bridge/permissions/server.lua | Framework selection and application of framework, ace, hybrid, or custom mode | TattooPermissions.Has(source, permission, context) returns true only when authorized |
bridge/permissions/registry.lua | Registration and shared permission utilities | TattooPermissionBridge.register(name, adapter) |
bridge/permissions/qbx.lua, qbcore.lua, esx.lua, standalone.lua | Checks for each framework or framework-free mode | isAvailable(settings) and hasPermission(source, binding, settings, permission); accepts additional context when adapted |
bridge/payments/server.lua | Provider selection, charging, and refunds | Config.TryChargePlayer(source, totalAmount, context) and Config.RefundPlayer(source, totalAmount, context) |
bridge/payments/registry.lua | Registration and shared payment utilities | TattooPaymentBridge.register(name, adapter) and receipt utilities |
bridge/payments/qbx.lua, qbcore.lua, esx.lua, standalone.lua | Payment operations for each framework or custom callbacks | isAvailable(settings), charge(...), and refund(...) |
bridge/appearance.lua | Skin-load events and requests for saved tattoos | Requests the character's list and applies it after appearance loading completes |
The permission callback also receives context as its fifth argument when invoked by the bridge. The registry contains the included providers (qbx, qbcore, esx, standalone); custom options do not require adding another provider.
In a payment adapter, preserve receipt registration with TattooPaymentBridge.setReceipt(context, provider, account, amount, identifier, data). The refund must check the same provider and amount with getReceipt(context, provider, amount) and mark the receipt as refunded after a confirmed refund. Adapt the file for the provider you already use; for a custom integration without changing adapters, use the standalone callbacks.
Appearance and character loading
The resource listens for common events from illenium-appearance, fivem-appearance, QBCore, ESX, and spawnmanager; it then retrieves and reapplies saved tattoos. In Qbox, the multicharacter preview configuration uses playerskins.skin to show saved tattoos on the character selection screen.
If your appearance system uses a different event, request application after the skin has finished loading:
TriggerServerEvent('cxg-tattoos:fetchMyTattoos')
The event returns the current character's tattoos to the client. Do not send the list from a source chosen by the player: ownership is resolved and validated on the server.
Qbox multicharacter preview is configured with these fields:
Config.QbxMulticharacterPreview.enabled = true
Config.QbxMulticharacterPreview.resource = 'qbx_core'
Config.QbxMulticharacterPreview.playerskinsTable = 'playerskins'
Config.QbxMulticharacterPreview.playersTable = 'players'
Config.QbxMulticharacterPreview.syncExistingOnStart = true
enabled activates the integration; resource specifies the installed Qbox resource name. playerskinsTable and playersTable identify the appearance and character tables used by that installation. syncExistingOnStart lets saved tattoos be synchronized when cxg-tattoos starts so they also appear in multicharacter selection. If your server renamed the tables, update those two fields. You do not need to modify qbx_core.