# Integraciones

cxg-tattoos puede conectarse a distintos frameworks para cobrar compras y comprobar permisos de administración. La integración de apariencia vuelve a aplicar los tatuajes guardados cuando el personaje carga o cambia de skin. Cada parte se configura por separado.

Consulta también [Introducción](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/01-introduccion.md), [Instalación](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/02-instalacion.md), [Configuración](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/03-configuracion.md), [Catálogo](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/04-catalogo.md) e [Interfaz](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/06-interfaz.md).

## Frameworks disponibles

| Sistema | Identificador de configuración | Recurso predeterminado | Pagos | Permisos |
| --- | --- | --- | --- | --- |
| Qbox | `qbx` | `qbx_core` | Sí | Sí |
| QBCore | `qbcore` | `qb-core` | Sí | Sí |
| ESX | `esx` | `es_extended` | Sí | Sí |
| Sin framework | `standalone` | No aplica | Callback propio o compras gratis | ACE o comprobación propia |

Con `auto`, el recurso detecta Qbox, QBCore o ESX si están iniciados; si no encuentra ninguno, usa `standalone`. Puedes fijar el framework cuando el servidor tenga varios frameworks activos o use un nombre de recurso distinto:

```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
```

Las cuentas se prueban en el orden indicado. El cobro se hace desde una sola cuenta; si su saldo no alcanza, se prueba la siguiente. En ESX, `esxAccountMap` traduce `cash` al nombre de cuenta `money`.

`Config.Purchases.reason` y `refundReason` identifican el motivo de los movimientos para los proveedores que lo admiten. Sus valores iniciales son `tattoo-purchase` y `tattoo-purchase-refund`. La opción `allowFreePurchases` permite omitir el cobro en el adaptador standalone; no transforma en gratuitas las compras de otros frameworks.

## Pagos sin framework

En modo `standalone`, conecta `tryCharge` y `refund` al recurso de economía de tu servidor. Ambos callbacks son necesarios para aceptar compras: el reembolso permite devolver el cobro si el guardado del tatuaje falla.

```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` representa la API de tu servidor; cámbiala por el nombre y las llamadas reales. Para permitir compras sin coste de forma intencionada, configura `allowFreePurchases = true`.

Los adaptadores de pago exponen `isAvailable(settings)`, `charge(source, amount, context, settings)` y `refund(source, amount, context, settings)`. Un pago aprobado debe conservar el recibo en `context.paymentReceipt`; cxg-tattoos lo usa para enviar cualquier devolución al mismo sistema de pago. Para una economía propia, los callbacks `tryCharge` y `refund` de `standalone` son la vía de configuración directa.

## Permisos de administración

Los permisos se verifican en el servidor para abrir y operar el panel administrativo. Ocultar o modificar la interfaz del cliente no concede acceso.

La configuración inicial usa el framework detectado y ACE como respaldo:

```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' },
        },
    },
}
```

El modo `framework` consulta los roles del framework. `ace` usa las entradas ACE del binding. `hybrid` permite cualquiera de las dos rutas. En `standalone`, la autorización de framework no está disponible; configura ACE o una comprobación propia.

Por ejemplo, para permitir a los miembros del principal ACE `group.admin`:

```cfg
add_ace group.admin cxg-tattoos.admin allow
```

Para delegar una decisión a tu propio sistema, usa `mode = 'custom'`. El callback recibe el id del jugador, el permiso, el contexto de la operación y el binding configurado; solo el valor booleano `true` autoriza:

```lua
Config.Permissions.mode = 'custom'
Config.Permissions.custom = {
    hasPermission = function(source, permission, context, binding)
        return exports.my_permissions:hasPermission(source, permission) == true
    end,
}
```

Adapta `my_permissions` a la API instalada en tu servidor. Si el callback falta, falla o devuelve otro valor, el acceso se rechaza.

El comando que abre el panel también se configura. `permission` debe coincidir con una clave de `Config.Permissions.bindings`:

```lua
Config.AdminCommand.enabled = true
Config.AdminCommand.name = 'tattooadmin'
Config.AdminCommand.permission = 'admin'
```

Con esos valores, el permiso `admin` protege el comando `/tattooadmin` y las acciones administrativas. Pon `enabled = false` para desactivar el comando; el permiso sigue verificándose en las demás rutas de administración.

### Archivos de integración editables

Los bridges editables agrupan las conexiones a los sistemas externos. Para un permiso o pago personalizado, es preferible configurar los callbacks explicados arriba. Si vas a adaptar un bridge existente, conserva su contrato:

| Archivo o grupo | Uso | Contrato del adaptador |
| --- | --- | --- |
| `bridge/permissions/server.lua` | Selección del framework y aplicación del modo `framework`, `ace`, `hybrid` o `custom` | `TattooPermissions.Has(source, permission, context)` devuelve `true` solo si autoriza |
| `bridge/permissions/registry.lua` | Registro y utilidades comunes de permisos | `TattooPermissionBridge.register(name, adapter)` |
| `bridge/permissions/qbx.lua`, `qbcore.lua`, `esx.lua`, `standalone.lua` | Comprobaciones para cada framework o modo sin framework | `isAvailable(settings)` y `hasPermission(source, binding, settings, permission)`; acepta `context` adicional si se adapta |
| `bridge/payments/server.lua` | Selección del proveedor, cobro y devolución | `Config.TryChargePlayer(source, totalAmount, context)` y `Config.RefundPlayer(source, totalAmount, context)` |
| `bridge/payments/registry.lua` | Registro y utilidades comunes de pagos | `TattooPaymentBridge.register(name, adapter)` y utilidades de recibos |
| `bridge/payments/qbx.lua`, `qbcore.lua`, `esx.lua`, `standalone.lua` | Operaciones de pago por framework o por callbacks propios | `isAvailable(settings)`, `charge(...)` y `refund(...)` |
| `bridge/appearance.lua` | Eventos de carga de skin y solicitud de tatuajes guardados | Solicita la lista del personaje y la aplica cuando termina la carga de apariencia |

El callback de permiso también recibe `context` como quinto argumento cuando lo invoca el bridge. El registro contiene los proveedores incluidos (`qbx`, `qbcore`, `esx`, `standalone`); las opciones personalizadas no requieren añadir otro proveedor.

En un adaptador de pagos, conserva el registro del recibo con `TattooPaymentBridge.setReceipt(context, provider, account, amount, identifier, data)`. La devolución debe comprobar el mismo proveedor y cantidad con `getReceipt(context, provider, amount)` y marcar el recibo como devuelto después de una devolución confirmada. Adapta el archivo del proveedor que ya utilices; para una integración propia sin cambiar los adaptadores, usa los callbacks standalone.

## Apariencia y carga del personaje

El recurso escucha eventos habituales de `illenium-appearance`, `fivem-appearance`, QBCore, ESX y `spawnmanager`; después recupera y vuelve a aplicar los tatuajes guardados. En Qbox, la configuración de vista previa multicharacter usa `playerskins.skin` para mostrar los tatuajes guardados en la selección de personaje.

Si tu sistema de apariencia usa un evento distinto, solicita la aplicación después de que termine de cargar la skin:

```lua
TriggerServerEvent('cxg-tattoos:fetchMyTattoos')
```

El evento devuelve al cliente los tatuajes del personaje actual. No envíes la lista desde una fuente elegida por el jugador: la propiedad se resuelve y valida en el servidor.

La vista previa multicharacter de Qbox se ajusta con estos campos:

```lua
Config.QbxMulticharacterPreview.enabled = true
Config.QbxMulticharacterPreview.resource = 'qbx_core'
Config.QbxMulticharacterPreview.playerskinsTable = 'playerskins'
Config.QbxMulticharacterPreview.playersTable = 'players'
Config.QbxMulticharacterPreview.syncExistingOnStart = true
```

`enabled` activa la integración; `resource` indica el nombre instalado de Qbox. `playerskinsTable` y `playersTable` identifican las tablas de apariencia y personajes que usa esa instalación. `syncExistingOnStart` permite sincronizar tatuajes ya guardados al iniciar cxg-tattoos, para que aparezcan también en la selección multicharacter. Si tu servidor renombró las tablas, actualiza esos dos campos. No hace falta modificar `qbx_core`.
