4.7 KiB
Licenciamiento por plan
Producto comercial: cada instancia white-label puede llevar una licencia firmada que decide qué módulos incluye y sus límites de uso. Sin licencia aplicada (el default) el sistema se comporta exactamente como hoy.
Bloque licencia de clinic-settings
{
"plan": "custom",
"modo": "aviso",
"cliente": "",
"vence": "",
"clave": "",
"modulos": [],
"limites": {
"usuarios_max": 0,
"sucursales_max": 0,
"citas_mes_max": 0,
"almacenamiento_mb": 0
}
}
plan:basico | profesional | enterprise | custom.modo:aviso(default; solo advierte) obloqueo(aplica de verdad).limites:0= sin límite (default → nada cambia).modulos: lista de módulos incluidos; vacío = todos.clave: la clave firmada por el vendedor. Vacía = sin licencia aplicada.
La licencia nunca bloquea lo indispensable: login, agenda, pacientes, POS, inventario, reportes y configuración siempre están disponibles. Solo aplica a los módulos gateables y a los límites indicados.
Tabla de planes
| Plan | Módulos incluidos |
|---|---|
basico |
agenda, pacientes, pos, inventario, reportes (sin whatsapp, notificaciones, reservas, recursos, membresías, compras, comisiones, formularios) |
profesional |
Básico + whatsapp, notificaciones, reservas_online, recursos |
enterprise |
Todos los módulos (los 5 base + los 8 gateables) |
custom |
Lo que diga la clave (si no lista módulos, todos) |
Módulos gateables (controlables por licencia): whatsapp, notificaciones,
reservas_online, recursos, membresias, compras, comisiones,
formularios.
Aplicación con modo=bloqueo
- Módulos no incluidos: sus secciones desaparecen del menú, sus endpoints
responden 403 con un mensaje claro (
Módulo no incluido en tu plan) y su bloqueactivode clinic-settings se considera apagado. - Límites: se impide crear más allá de
usuarios_max,sucursales_max,citas_mes_max(citas del mes en curso) yalmacenamiento_mb(suma de adjuntos). El error indica el límite y cómo ampliarlo. - Con
modo=avisonada se bloquea; los excesos solo quedan advertidos.
Las tareas internas (cron, migraciones) pueden saltarse los límites con el
contexto skeen_licencia_skip=True.
Formato de la clave de licencia
Texto en una línea:
SKN1.<base64url(payload_json)>.<base64url(hmac_sha256(payload_b64, vendor_key))>
payload_json= JSON con claves ordenadas y separadores mínimos:cliente,plan,vence,emitida,modulos,limites.- Firmado con HMAC-SHA256 y la clave del vendedor.
Clave del vendedor
Debe estar presente en la instancia para poder validar (HMAC es simétrico):
ir.config_parameter 'skeen.licencia.vendor_key'- Variable de entorno
SKEEN_LICENCIA_VENDOR_KEY - Archivo
/root/.skeen-licencias/vendor.key(permisos600)
Ruta del secreto:
/root/.skeen-licencias/vendor.key(fuera del repo, 600). Nota de seguridad: al ser HMAC simétrico, quien tenga la clave del vendedor puede forjar licencias; mantenerla fuera de las instancias de cliente en un despliegue real (aquí vive en un VPS de desarrollo compartido).
Herramienta del vendedor
/root/skeen-proyecto/infra/licencias/generar_licencia.py (CLI, Python stdlib):
generar_licencia.py generar --cliente "Spa Bella" --plan profesional --vence 2027-12-31
generar_licencia.py generar --cliente "Spa Bella" --plan custom --vence 2027-12-31 \
--modulos agenda,pacientes,pos,inventario,reportes,compras \
--usuarios-max 10 --sucursales-max 2 --citas-mes-max 500 --almacenamiento-mb 4096
generar_licencia.py verificar --clave "SKN1...."
Endpoints
| Método | Ruta | Acceso | Descripción |
|---|---|---|---|
| GET | /skeen/frontend/v1/licencia |
admin/gerencia | Estado completo + uso vs límites |
| PUT | /skeen/frontend/v1/licencia |
admin | Aplicar/actualizar/quitar licencia |
| POST | /skeen/frontend/v1/licencia/validar |
admin/gerencia | Probar una clave sin aplicarla |
PUT con {"clave": "SKN1..."} verifica firma y vigencia; si es inválida o
vencida responde 400 y no sobrescribe la licencia previa. {"clave": ""}
quita la licencia y vuelve a habilitarlo todo. {"modo": "bloqueo"} cambia el
modo sin re-firmar.
UI
Configuración → pestaña Licencia (admin; gerencia también ve el estado): plan y cliente, vencimiento con días restantes, campo para pegar la clave, tabla de uso vs límites con semáforo, tabla de módulos incluidos/no incluidos, avisos de vencimiento (≤ 15 días) y, si no hay licencia, el mensaje "sin licencia aplicada — todas las funciones habilitadas".