Perfil comercial/1.0
Versión 1.0 · 12 de octubre de 2026. La definición para máquinas es perfil-comercial-1.0.json; el SDK PHP (JesgIA\Conector\Perfil) la trae incorporada.
Este perfil es el contrato entre JesgIA y el sistema de gestión de una empresa cliente. JesgIA le pide datos al sistema con herramientas de solo lectura y redacta la respuesta para el personal de la empresa.
JesgIA nunca escribe SQL ni ve la base del cliente. Recibe solo los campos de este perfil, y el cliente decide qué puede ver cada rol.
1. Cómo se conectan las partes
Navegador del personal ──(widget, token de 15 min)──▶ JesgIA (servidor A)
│ petición firmada Ed25519
▼
Sistema del cliente: jesgia.php (SDK) ──▶ su MariaDB (solo lectura)
El cliente expone un solo archivo PHP con dos acciones:
| Acción | Método | URL | Para qué |
|---|---|---|---|
manifest | GET | https://SISTEMA/jesgia.php/manifest (o ?accion=manifest) | Qué herramientas implementa |
ejecutar | POST | https://SISTEMA/jesgia.php/ejecutar (o ?accion=ejecutar) | Ejecuta una herramienta |
Reglas comunes a todas las peticiones:
- Solo lectura. Ninguna herramienta modifica datos.
- Se devuelven como máximo 10 filas.
- JesgIA espera 3 segundos por respuesta; después la descarta.
- Se usa HTTPS en producción.
2. Firma de las peticiones
JesgIA firma cada petición con su clave privada Ed25519. El sistema la verifica con la clave pública, que viene con el SDK y está publicada en el portal. Una petición sin firma válida no ejecuta nada y recibe 401 FIRMA_INVALIDA.
Cabeceras de la petición:
| Cabecera | Contenido |
|---|---|
X-Jesgia-Tenant | Id del tenant del cliente |
X-Jesgia-Fecha | Marca de tiempo Unix en segundos. Se aceptan ±300 s: sincroniza el reloj con NTP. |
X-Jesgia-Nonce | Identificador único de la petición, igual al request_id del cuerpo (UUID) |
X-Jesgia-Firma | Firma Ed25519 en base64 estándar del mensaje de abajo |
Mensaje firmado: siete líneas unidas por \n, sin salto final.
JESGIA-FIRMA-1
<MÉTODO> GET | POST
<acción> manifest | ejecutar
<tenant>
<fecha>
<nonce>
<sha256 hex del cuerpo> (del cuerpo vacío en GET: e3b0c442…b855)
Se firma la acción y no la ruta, para que una reescritura de URL en el servidor del cliente no invalide la firma.
Anti-replay:
- El sistema rechaza un nonce ya usado.
- Solo lo registra después de verificar la firma, así nadie puede «gastar» nonces ajenos.
Rotación de la clave:
- El archivo de claves públicas admite varias líneas
id base64, y vale cualquiera de ellas. - La clave nueva se publica antes de empezar a usarla.
3. Manifest
GET manifest responde así:
{
"ok": true,
"perfil": "comercial/1.0",
"sdk": "jesgia-conector-php/1.0.0",
"empresa": "Mercaderías Demo S.A.C.",
"moneda": "PEN",
"zona_horaria": "America/Lima",
"herramientas": ["buscar_producto", "detalle_producto", "buscar_cliente"]
}
El cliente implementa las herramientas que pueda. Las que no declara responden NO_IMPLEMENTADO.
4. Ejecutar
Petición
{
"perfil": "comercial/1.0",
"herramienta": "buscar_producto",
"parametros": { "texto": "arroz" },
"usuario": { "id": "u-123", "rol": "vendedor" },
"request_id": "9b2f…-…",
"contexto": { "pagina": "/clientes/ficha.php?id=2" }
}
usuarioes la persona que pregunta, tal como la identificó el propio sistema al pedir su token. **El sistema decide los permisos conusuario.rol.** Por ejemplo, si un vendedor no debe ver costos, no se devuelven. Si un rol no puede usar una herramienta, respondeNO_AUTORIZADO.contextoes opcional e informativo.
Respuesta correcta
{
"ok": true, "perfil": "comercial/1.0", "request_id": "9b2f…", "herramienta": "buscar_producto",
"datos": [ { "sku": "AR-005", "nombre": "Arroz Doña Luz 5 kg", "unidad": "saco", "stock": 42, "precio": 18.5, "moneda": "PEN" } ],
"total": 3,
"truncado": false
}
- **
datostrae como máximo 10 filas, con exactamente** los campos de la herramienta. Los campos de más se descartan. - **
total** es el número de filas encontradas. Si es mayor que las devueltas,truncadoestrueytotales un mínimo.
Respuesta con error
{ "ok": false, "perfil": "comercial/1.0", "request_id": "9b2f…",
"error": { "codigo": "AMBIGUO", "mensaje": "Hay varias coincidencias; hace falta precisar." },
"candidatos": [ … ] }
| Código | HTTP | Cuándo |
|---|---|---|
NO_ENCONTRADO | 200 | La búsqueda no encontró nada. El SDK lo responde solo si la herramienta devuelve 0 filas. |
AMBIGUO | 200 | Una herramienta de registro único encontró varios. Trae candidatos (≤ 10). |
NO_AUTORIZADO | 200 | El rol del usuario no puede ver eso |
NO_IMPLEMENTADO | 200 | La herramienta no está en el manifest |
PARAMETRO_INVALIDO | 400 | Falta un parámetro obligatorio o no cumple su tipo o largo |
FIRMA_INVALIDA | 401 | Sin firma, firma no válida, fecha fuera de la ventana, nonce repetido u otro tenant |
ERROR_INTERNO | 500 | Falló el sistema. El mensaje es genérico: nunca se exponen SQL ni rutas. |
Cómo actúa JesgIA con cada caso:
- **
NO_ENCONTRADO:** dice que no encontró, sin inventar. - **
AMBIGUOo varias filas:** pregunta cuál de ellas. - **
NO_AUTORIZADO:** se lo dice al usuario.
5. Herramientas
Tipos de los campos
| Tipo | Valor |
|---|---|
texto | Cadena, recortada a 300 caracteres |
numero | Entero o decimal. El texto numérico que devuelve PDO se convierte. |
entero | Número entero |
fecha | AAAA-MM-DD. Un AAAA-MM-DD HH:MM:SS se recorta a la fecha. |
moneda | Código ISO 4217 de 3 letras |
enum:a\|b | Uno de los valores indicados |
lista:X | Lista (≤ 10) de objetos del grupo X |
Un tipo terminado en ? admite null.
buscar_producto
| Parámetro | texto (2–100 caracteres): parte del nombre o el código exacto |
| Devuelve (lista) | sku, nombre, unidad, stock (numero), precio (numero?), moneda |
detalle_producto
| Parámetro | sku (1–60 caracteres) |
| Devuelve (un registro) | los campos de buscar_producto + almacen (texto?), categoria (texto?), ultimo_ingreso (fecha?) |
buscar_cliente
| Parámetro | texto (2–100 caracteres): parte del nombre, o RUC o DNI exactos |
| Devuelve (lista) | id (texto), nombre, documento (texto?), telefono (texto?) |
estado_cuenta
| Parámetro | id_cliente (el id de buscar_cliente) |
| Devuelve (un registro) | deuda_total, vencido (numero), moneda, documentos: lista de {numero, fecha_emision (fecha), fecha_vencimiento (fecha?), saldo (numero)} |
Si el cliente existe pero no debe nada, la respuesta es deuda_total 0 y documentos vacío. No es NO_ENCONTRADO.
pedidos_cliente
| Parámetros | id_cliente; estado (opcional, texto libre del sistema) |
| Devuelve (lista, del más reciente al más antiguo) | numero, fecha (fecha), total (numero), moneda, estado |
movimientos_producto
| Parámetros | sku; dias (opcional, entero 1–365, 30 por defecto) | |
| Devuelve (lista, del más reciente al más antiguo) | fecha (fecha), tipo (entrada \ | salida), cantidad (numero), documento (texto?) |
6. Token de usuario del widget
El servidor del cliente pide el token para su usuario ya logueado:
POST https://JESGIA/conector/token
Authorization: Bearer jgk_live_… ← API key del tenant; nunca en el navegador
{"usuario": {"id": "u-123", "rol": "vendedor", "nombre": "Ana"}}
200 {"token": "…", "expira_en": 1760000000}
El token dura 15 minutos y está firmado por JesgIA con el tenant, el usuario y su rol. El navegador solo recibe el token. En el SDK: TokenUsuario::pedir().
7. Prueba de conformidad y activación
php bin/conformidad.php arranque.php casos.php [--url=https://SISTEMA/jesgia.php] repite las peticiones firmadas de JesgIA con una clave de prueba y comprueba:
- la firma, el anti-replay y el tenant;
- el manifest;
- cada herramienta: con datos, sin datos y sin parámetros;
- los campos exactos y el tiempo de respuesta;
- opcionalmente, que el usuario de la base sea de solo lectura y que el endpoint publicado use HTTPS y rechace las peticiones sin firma.
Un tenant pasa de pendiente a activo solo si su sistema es conforme. Con --json, la prueba produce el informe que se adjunta al pedido de activación.
8. Compatibilidad
- Los perfiles nuevos (por ejemplo,
logistica/1.0ocomercial/2.0) no cambian este. Un sistema conforme con comercial/1.0 lo sigue siendo. - Agregar una herramienta opcional a este perfil sería
comercial/1.1. Cambiar o quitar campos seríacomercial/2.0.