API REST — Integración de suscripciones
Endpoints para que la web comercial de Help U consulte planes, valide empresas y confirme pagos de suscripción en Habitta.
1. Introducción
Habitta expone una API REST para que la página web comercial (checkout de planes) sincronice pagos de suscripción con el panel multi-tenant. La pasarela de pago vive en la web; Habitta no aloja el formulario de cobro, solo recibe la confirmación del pago exitoso.
- Consulta de planes activos para mostrar precios en la web.
- Consulta de temas visuales.
- Alta de empresa + administrador sin pasar por demo.
- Validación de empresa por NIT o correo antes del checkout.
- Registro del pago, renovación de suscripción y activación opcional de la empresa.
Para operación del panel (empresas, demos, avisos) consulte el manual de SuperAdmin. Para usuarios de conjuntos, use el manual de usuario.
2. Autenticación
Todos los endpoints requieren un token compartido configurado en el servidor Habitta.
| Encabezado | Valor |
|---|---|
Authorization |
Bearer {HABITTA_API_TOKEN} recomendado |
X-Habitta-Token |
{HABITTA_API_TOKEN} (alternativa) |
Respuestas de autenticación:
| HTTP | error | Causa |
|---|---|---|
| 401 | no_autorizado | Token ausente o incorrecto. |
| 503 | api_no_configurada | HABITTA_API_TOKEN vacío en .env. |
3. URL base y convenciones
- Prefijo: todas las rutas están bajo
/api. - Formato: JSON en peticiones (
Content-Type: application/json) y respuestas. - Fechas: formato
Y-m-d(ej.2026-07-05). - Moneda: valores numéricos en COP sin separadores.
Ejemplo de URL base en producción (ajuste en config.php de esta carpeta):
https://habitta.su-dominio.com/api
4. Listar planes
GET /api/public/planes
Devuelve el catálogo activo para la web comercial. Use el query
tipo para distinguir planes de la app y packs de asamblea.
Parámetros (query string)
| Parámetro | Valores | Descripción |
|---|---|---|
tipo |
plataforma · app · asamblea |
Opcional. Default: plataforma (alias app).
asamblea trae packs 1/3/5.
|
Respuesta 200 (planes app)
{
"ok": true,
"tipo": "plataforma",
"planes": [
{
"plan_id": 2,
"codigo": "esencial",
"nombre": "Esencial",
"tipo": "plataforma",
"dias_aviso": 7,
"activo": true,
"es_demo": false,
"capacidades": ["Cartera", "Reservas", "PQRS"],
"valor_unidad": 4500,
"valor_unidad_fmt": "$4.500",
"formula": "unidades × valor_unidad × meses × (1 − descuento_pct/100)",
"tarifas": [
{
"rango_unidades_id": 1,
"etiqueta": "Hasta 50 unidades",
"unidades_desde": 1,
"unidades_hasta": 50,
"requiere_cotizacion": false,
"precio_mensual": 4500,
"precio_mensual_fmt": "$4.500",
"precio_mensual_hasta": 225000,
"precio_mensual_hasta_fmt": "$225.000"
}
],
"periodos": [
{
"periodo_facturacion_id": 1,
"codigo": "mensual",
"nombre": "Mensual",
"meses": 1,
"descuento_pct": 0
}
]
}
]
}
capacidades son nombres de submenú (legibles), no claves de permiso.
No incluyen módulos de asamblea (Convocatorias / portal asamblea):
esos se ofrecen aparte con ?tipo=asamblea.
El precio de app usa valor_unidad del .env
(VALOR_UNIDAD_ESENCIAL / VALOR_UNIDAD_INTELIGENTE; Demo usa Inteligente).
Las tarifas conservan la lógica de rangos (1–50, 51–100, …; +700 = cotización);
precio_mensual / precio_mensual_hasta son el total mensual de referencia
en el piso y techo del rango. El cobro exacto se obtiene con
/api/public/cotizar.
Ver tabla en el manual SuperAdmin.
Respuesta 200 (packs asamblea)
{
"ok": true,
"tipo": "asamblea",
"planes": [
{
"plan_id": 10,
"codigo": "asamblea_1",
"nombre": "1 asamblea",
"tipo": "asamblea",
"asambleas": 1,
"capacidades": [],
"tarifas": [ /* precio del pack por rango */ ],
"periodos": []
}
]
}
empresa_servicios_asamblea y se activa
empresas.asamblea sin reemplazar la suscripción del plan app.
Detalle de precios: manual SuperAsamblea.
4.1 Cotizar plan
GET /api/public/cotizar
Calcula el precio del periodo según plan, unidades y periodo.
Parámetros
| Parámetro | Tipo | Descripción |
|---|---|---|
plan | integer o string | Obligatorio. ID del plan o código (esencial, inteligente, demo, asamblea_1, …). |
unidades | integer (min 1) | Obligatorio. Unidades administradas: definen el rango tarifario y, en planes app, multiplican el valor unitario. |
periodo | integer o string | Opcional. ID o código del periodo (mensual, trimestral, …). |
Respuesta 200: { "ok": true, "cotizacion": { ... } }
4.2. Listar temas
GET /api/public/temas
Devuelve los temas visuales disponibles para registro o selección en la web comercial.
Respuesta 200
{
"ok": true,
"temas": {
"default": {
"id": 1,
"show_name": "Predeterminado",
"primary": "#1B4F8A",
"primary_dark": "#143862",
"primary_light": "#2563A8",
"accent": "#3B9AE8",
"accent_dark": "#2E8AD0",
"surface": "#F4F8FC",
"footer_bar": "#E8EEF5",
"tag": "baDZr8M69O"
},
"red": {
"id": 2,
"show_name": "Rojo",
"primary": "#991B1B",
"primary_dark": "#7F1D1D",
"primary_light": "#B91C1C",
"accent": "#F87171",
"accent_dark": "#EF4444",
"surface": "#FEF2F2",
"footer_bar": "#FEE2E2",
"tag": "8XmnVjnR1L"
}
}
}
4.3. Listar ciudades
GET /api/public/ciudades
Devuelve las ciudades disponibles para seleccionar municipio en el formulario de alta.
Respuesta 200
{
"ok": true,
"ciudades": [
{ "ciudad_id": 1, "nombre": "Cundinamarca" }
]
}
4.4. Listar municipios
GET /api/public/municipios?ciudad_id=1
Devuelve los municipios de una ciudad. El municipio_id se usa en POST /public/empresas.
Parámetros (query string)
| Parámetro | Tipo | Descripción |
|---|---|---|
ciudad_id | integer | Obligatorio. ID de ciudad. |
Respuesta 200
{
"ok": true,
"ciudad_id": 1,
"municipios": [
{
"municipio_id": 912,
"ciudad_id": 1,
"nombre": "Soacha",
"codigo_dane": "25754"
}
]
}
5. Consultar empresa
GET /api/public/empresas/consulta
Valida si existe una empresa cliente antes del pago. Indique al menos uno de los parámetros de consulta.
Parámetros (query string)
| Parámetro | Tipo | Descripción |
|---|---|---|
nit | string | NIT de la empresa sin dígito de verificación (ni guión). |
email | string | Correo registrado de la empresa. |
Respuesta 200
{
"ok": true,
"existe": true,
"activo": false,
"empresa": {
"empresa_id": 12,
"nombre": "Conjunto Residencial Ejemplo",
"nit": "900123456",
"email": "admin@ejemplo.com",
"activo": false,
"asamblea": false,
"plan_actual": "demo",
"plan_nombre": "Demo",
"plan_vence": "2026-07-01",
"es_demo": true,
"dias_restantes": 12,
"estado_suscripcion": "vencido",
"servicio_asamblea": null
}
}
Si el NIT no existe: existe: false, activo: false,
empresa: null (HTTP 200).
Valores de estado_suscripcion: al_dia,
proximo_vencer, vencido.
También se exponen plan_nombre, es_demo y dias_restantes junto a plan_actual y plan_vence.
Con pack contratado, servicio_asamblea incluye
plan_codigo, asambleas_contratadas y fechas.
5.1. Alta comercial (sin demo)
POST /api/public/empresas
Crea una empresa nueva con su usuario administrador sin plan demo.
La empresa queda activo: false hasta que el webhook de pago la active y asigne el plan.
Úselo cuando el cliente compra desde la web sin pasar por solicitud de demo.
Cuerpo JSON
| Campo | Obligatorio | Descripción |
|---|---|---|
nit | Sí | NIT del conjunto sin dígito de verificación (ni guión). |
nombre | Sí | Razón social o nombre del conjunto. |
direccion | Sí | Dirección. |
telefono | Sí | Teléfono de contacto. |
email | Sí | Correo (también será el usuario admin). |
municipio_id | Sí | ID de municipio en Habitta. |
tema_id | Sí | ID de tema visual (GET /public/temas). |
Ejemplo
{
"nit": "900999888",
"nombre": "Conjunto Los Alamos",
"direccion": "Calle 10 # 20-30",
"telefono": "3001234567",
"email": "admin@losalamos.co",
"municipio_id": 912,
"tema_id": 1
}
Respuesta 201
{
"ok": true,
"empresa_id": 12,
"nombre": "Conjunto Los Alamos",
"nit": "900999888",
"email": "admin@losalamos.co",
"activo": false,
"admin_email": "admin@losalamos.co",
"tema_id": 1,
"mensaje": "Empresa registrada. Confirme el pago para activarla y asignar el plan."
}
POST /webhooks/suscripcion/pago.
6. Confirmar pago de suscripción
POST /api/webhooks/suscripcion/pago
Llame este endpoint después de que la pasarela confirme el cobro. Habitta registra el pago, renueva la suscripción y, por defecto, activa la empresa si estaba inactiva.
Cuerpo JSON
| Campo | Obligatorio | Descripción |
|---|---|---|
referencia | Sí | ID único de la transacción en la pasarela (idempotencia). |
valor | Sí | Monto pagado (numérico ≥ 0). |
empresa_id | Uno de tres* | ID interno de la empresa en Habitta. |
nit | Uno de tres* | NIT de la empresa sin dígito de verificación. |
email | Uno de tres* | Correo de la empresa. |
plan_id | Uno de dos** | ID del plan en Habitta. |
plan_codigo | Uno de dos** | Código del plan app (demo, esencial, inteligente) o pack asamblea (asamblea_1, asamblea_3, asamblea_5). |
unidades | Sí | Unidades contratadas (define el rango de tarifa). |
periodo_codigo | Condicional | Obligatorio para plan app (mensual, trimestral, …). No aplica a packs asamblea. |
fecha_pago | No | Fecha del pago; por defecto hoy. |
pasarela | No | Nombre corto de la pasarela (ej. WOMPI, PAYU). |
activar_empresa | No | true (default) activa la empresa si estaba inactiva. |
reset_datos_demo | No | true borra datos operativos del demo (unidades, residentes, etc.) y conserva empresa + administrador. Solo aplica al pasar de plan demo a un plan de pago. Default: false (conservar datos). |
observaciones | No | Texto libre (máx. 500 caracteres). |
* Debe enviar empresa_id, nit o email.
** Debe enviar plan_id o plan_codigo.
- Demo vigente → plan pago: inicia hoy y suma los días restantes del demo al vencimiento.
- Demo vencido → plan pago: inicia hoy, sin días bonus.
- Renovación pago → pago (vigente): el nuevo periodo empieza el día siguiente al vencimiento actual.
valor debe coincidir con la tarifa publicada para el plan, rango y periodo (tolerancia ±1 COP); si no, Habitta responde 422.
Ejemplo de petición
POST /api/webhooks/suscripcion/pago
Authorization: Bearer su_token_secreto
Content-Type: application/json
{
"referencia": "TX-98437261",
"nit": "900123456",
"plan_codigo": "esencial",
"unidades": 50,
"periodo_codigo": "mensual",
"valor": 80000,
"fecha_pago": "2026-07-05",
"pasarela": "WOMPI",
"activar_empresa": true,
"reset_datos_demo": false
}
Respuesta 201 (pago nuevo — plan app)
{
"ok": true,
"idempotente": false,
"empresa_id": 12,
"empresa_nombre": "Conjunto Residencial Ejemplo",
"empresa_activa": true,
"pago_id": 45,
"referencia": "WEB-WOMPI-TX-98437261",
"valor": 80000,
"fecha_pago": "2026-07-05",
"suscripcion": {
"empresa_suscripcion_id": 18,
"plan_id": 2,
"plan_codigo": "esencial",
"plan_nombre": "Esencial",
"fecha_inicio": "2026-07-05",
"fecha_vencimiento": "2026-08-04",
"activa": true,
"estado": "al_dia"
},
"servicio_asamblea": null,
"correo_activacion": {
"enviado": true
}
}
Si el pago es de un pack asamblea, suscripcion viene null y
servicio_asamblea incluye el pack contratado (asamblea_1, etc.).
Respuesta 200 (reintento idempotente)
Si la misma referencia ya fue procesada, devuelve 200 con "idempotente": true y los mismos datos del pago original.
Respuesta 422 (validación)
{
"ok": false,
"error": "validacion",
"mensaje": "Los datos enviados no son válidos.",
"errores": {
"empresa_id": ["No se encontró la empresa con los datos enviados."]
}
}
7. Códigos de error
| HTTP | error | Descripción |
|---|---|---|
| 401 | no_autorizado | Token inválido. |
| 404 | no_encontrada | Empresa no encontrada (consulta). |
| 422 | validacion | Datos incompletos o reglas de negocio (ej. empresa gestora). |
| 500 | error_interno | Error inesperado; reintentar con la misma referencia. |
| 503 | api_no_configurada | Token no configurado en el servidor. |
8. Idempotencia y referencias
Habitta antepone un prefijo configurable a la referencia de la pasarela para evitar
colisiones entre orígenes o reintentos. El formato es WEB[-PASARELA]-referencia.
Variable de entorno: HABITTA_PAGO_PREFIJO (default WEB).
| Entrada | Referencia almacenada |
|---|---|
referencia: "TX-001" | WEB-TX-001 |
referencia: "TX-001", pasarela: "WOMPI" | WEB-WOMPI-TX-001 |
9. Flujo recomendado (web comercial)
Cliente nuevo que paga (sin demo)
- Mostrar planes y temas:
GET /api/public/planes,GET /api/public/temas. - Registrar empresa:
POST /api/public/empresas→ obtenerempresa_id. - Cobrar en la pasarela con el monto del plan.
- Confirmar:
POST /api/webhooks/suscripcion/pagoconempresa_id, plan yactivar_empresa: true. - Mostrar login; el admin usa el correo de la empresa y la clave = NIT (sin dígito de verificación).
Cliente existente / demo
GET /api/public/empresas/consulta(NIT o correo).- Checkout Help U «Ya soy cliente» con el NIT validado.
- Cobro Wompi → Help U recibe el evento en
webhooks/wompi-events.phpo consulta estado enregistro-pago.php(polling local). POST /api/webhooks/suscripcion/pagoa Habitta conreferencia= UUID de la transacción Wompi,pasarela: "WOMPI"y datos del plan (opcionalreset_datos_demo: trueal salir de demo).
La reference del checkout Wompi suele ser tipo HELP-…; la
referencia enviada a Habitta es el UUID de la transacción, no la referencia del checkout.
Las solicitudes de demo desde /registro crean empresas inactivas con plan demo que pueden pagar
por este mismo webhook o ser activadas desde el panel (ver bandeja de demos).
10. Configuración en Habitta
Variables en el archivo .env del servidor Habitta:
| Variable | Descripción |
|---|---|
HABITTA_API_TOKEN | Token secreto compartido con la web. Generar con php -r "echo bin2hex(random_bytes(32));" |
HABITTA_PAGO_PREFIJO | Prefijo de referencias externas (default WEB). |
En el servidor Habitta, la integración usa el token anterior y las rutas bajo el prefijo /api. El código fuente de la API vive en el despliegue de Habitta, no en esta carpeta de documentación.