# 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](https://docs.cxgstudios.com/docs/en/01-cxg-tattoos/01-introduccion.md), [Installation](https://docs.cxgstudios.com/docs/en/01-cxg-tattoos/02-instalacion.md), [Configuration](https://docs.cxgstudios.com/docs/en/01-cxg-tattoos/03-configuracion.md), [Catalog](https://docs.cxgstudios.com/docs/en/01-cxg-tattoos/04-catalogo.md), and [Interface](https://docs.cxgstudios.com/docs/en/01-cxg-tattoos/06-interfaz.md).

## 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:

```lua
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.

```lua
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:

```lua
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:

```cfg
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:

```lua
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`:

```lua
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:

```lua
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:

```lua
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`.

