Si utiliza WHMCS, use el módulo de WHMCS en su lugar; esta página es para todo lo demás. Existen dos opciones, ambas gratuitas y ambas bajo licencia GPL-2.0-or-later.
HostBill
Un módulo de hosting con las mismas seis acciones que el de WHMCS.
- Descárguelo y descomprímalo.
- Suba el directorio
zinn en includes/modules/Hosting/ para que los archivos se ubiquen en
includes/modules/Hosting/zinn/class.zinn.php y includes/modules/Hosting/zinn/ZinnHostBillClient.php.
- Ajustes → Módulos → Hosting, active Zinn Digital® y añada una conexión:
- Dirección de la API:
https://api.zinndigital.com
- Clave de API: su clave de API de Zinn® (un campo
password, por lo que HostBill la almacena cifrada)
- En el producto, configure Línea de productos (p. ej.,
mainstream), Stack (wordpress de
forma predeterminada) y, opcionalmente, Aplicación y Versión de PHP.
- Pulse Probar conexión.
⛔ No cambie el nombre del directorio zinn. El cargador de HostBill deriva la clase que busca a partir del nombre del directorio, por lo que un cambio de nombre produce un módulo que el panel enumera pero nunca llama.
Cualquier otra cosa: el cliente PHP
Un solo archivo, sin dependencias, sin suposiciones sobre el panel. Coloque ZinnProvisioning.php en su propia base de código y llame a seis métodos.
require_once 'ZinnProvisioning.php';
$zinn = new ZinnProvisioning(getenv('ZINN_API_KEY'));
// When an order is paid — once per CUSTOMER, then once per SERVICE:
$orgId = $zinn->account('customer-4211', 'Acme Ltd');
siteId = $zinn->provision('service-9915', $orgId, 'acme.com', 'mainstream', 'wordpress');
// Store $orgId against your customer and $siteId against your service.
$zinn->suspend($siteId, 'INV-2026-114 unpaid'); // an invoice goes unpaid
$zinn->unsuspend($siteId); // they pay
$zinn->terminate($siteId); // they cancel — schedules a deletion
// When they click "log in to my hosting" — mint it on the CLICK, never on page render:
header('Location: ' . $zinn->signInLink($siteId));
¿No programa en PHP? Las mismas llamadas se encuentran en la referencia de la API y puede realizarlas desde cualquier entorno.
La clave de API
Siete permisos, y nada más: org.create, org.read, sites.create, sites.view, sites.delete, reseller.view, reseller.provision.
⛔ No reseller.manage. Una clave que pegue en un panel de facturación debería poder suspender a un cliente por falta de pago e iniciar su sesión; no debería poder reescribir las credenciales de su propia pasarela de pago.
Las dos reglas que cuestan dinero si se incumplen
- Asocie la cuenta a su CLIENTE y el sitio a su SERVICIO. El segundo pedido de un cliente
debe recaer en la cuenta que ya posee. Si asocia ambos al servicio, un mismo cliente terminará con tres cuentas no relacionadas y tres paneles de control independientes.
- Envíe una clave de idempotencia en cada creación, derivada de su propio ID para el elemento. Todo
sistema de facturación reintenta: una devolución de llamada de pasarela llega dos veces, un administrador vuelve a ejecutar un aprovisionamiento fallido, un cliente hace doble clic. Sin ella, el segundo intento crea un segundo sitio y se le facturará por él. Tanto los módulos como el cliente PHP hacen esto por usted; si está llamando a la API directamente, envíe Idempotency-Key.
Tres campos que son fáciles de omitir
Cada uno anula una operación completa, y ninguno se muestra como otra cosa que no sea un error de validación, excepto el primero, que se muestra como absolutamente nada:
- ⛔⛔ Se debe asignar un plan a un cliente ANTES de aprovisionar su primer sitio, con
POST /v1/reseller/clients/{orgId}/plan, y pasar su subscription_id a POST /v1/sites. Este es el caso en el que no hay ningún error para leer. Una cuenta de cliente nueva no contiene ninguna suscripción y POST /v1/sites no tiene un campo de plan, por lo que un sitio aprovisionado sin él no tiene ningún plan en absoluto: su estado de cuenta mayorista se genera a partir de las suscripciones activas de sus clientes, por lo que no se genera ninguna línea y Zinn® no le factura nada mientras el alojamiento esté en funcionamiento; su cliente no hereda ninguna asignación, por lo que no se aplica ninguna cuota a su sitio; y no hay nada que una ampliación pueda modificar. El pedido devuelve 201, el sitio se aprovisiona y funciona a la perfección. Ninguna de las partes informa nada de esto.
El orden importa: POST /v1/sites registra cualquier suscripción que exista en el momento en que se ejecuta, por lo que un plan configurado con posterioridad no se vincula de forma retrospectiva.
POST /v1/sites requiere stack_type. Si lo omite, ningún pedido podrá procesarse correctamente.
- **
DELETE /v1/sites/{siteId} requiere un cuerpo `{"confirm_domain": "<the site's own
domain>"}** — una confirmación escrita, ya que este es el verbo destructivo. Si lo omite, ninguna cancelación podrá procesarse correctamente, por lo que el servicio seguirá ejecutándose y a usted se le seguirá facturando por él. Vuelva a leer el dominio desde GET /v1/reseller/services/{siteId}` en lugar de utilizar su propio registro: este se compara con el dominio principal del propio sitio y nunca con un alias.
Lea el error completo, no solo la frase
El mensaje de rechazo por validación es deliberadamente genérico — «The request was well-formed but failed validation.» — porque la parte procesable se encuentra en details, que indica el campo y el motivo. Tanto los módulos como el cliente PHP lo añaden al mensaje que muestran a su administrador. Si llama a la API usted mismo, lea error.details; algunas entradas contienen un valor simple en lugar de una frase, que es la forma en que max_sites: 0 le llega cuando su plan de revendedor no tiene espacio.
Antes de su primer pedido
Los sitios que compran sus clientes cuentan para el límite de sitios de su plan de revendedor, por lo que necesita un plan con espacio disponible. Una prueba de conexión no puede detectar esto — lee su programa y no aprovisiona nada —, por lo que informa de una cuenta en buen estado que aún no puede vender. Un pedido realizado sin disponibilidad se rechaza con un mensaje que indica exactamente eso.
Verifique su descarga
Ambos archivos se sirven mediante TLS desde nuestro propio nombre de host, nunca se redirigen a otra parte, con sus sumas de comprobación SHA-256 impresas junto a los enlaces de descarga en la página de descargas.