Si vous utilisez WHMCS, utilisez le module WHMCS à la place — cette page concerne tout le reste. Il existe deux options, toutes deux gratuites et toutes deux GPL-2.0-or-later.
HostBill
Un module d’hébergement avec les six mêmes actions que celui de WHMCS.
- Téléchargez-le et décompressez-le.
- Téléversez le répertoire
zinn dans includes/modules/Hosting/ afin que les fichiers se retrouvent à
includes/modules/Hosting/zinn/class.zinn.php et includes/modules/Hosting/zinn/ZinnHostBillClient.php.
- Paramètres → Modules → Hébergement, activez Zinn Digital®, et ajoutez une connexion :
- Adresse API :
https://api.zinndigital.com
- Clé API : votre clé API Zinn® (un champ
password, HostBill la stocke donc chiffrée)
- Sur le produit, définissez Ligne de produit (p. ex.
mainstream), Stack (wordpress par
défaut), et éventuellement Application et Version PHP.
- Cliquez sur Tester la connexion.
⛔ Ne renommez pas le répertoire zinn. Le chargeur de HostBill déduit la classe qu’il recherche du nom du répertoire, si bien qu’un renommage produit un module que le panneau liste et n’appelle jamais.
Tout le reste — le client PHP
Un seul fichier, aucune dépendance, aucune hypothèse sur le panneau. Placez ZinnProvisioning.php dans votre propre base de code et appelez six méthodes.
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));
Vous n’écrivez pas de PHP ? Les mêmes appels figurent dans la référence de l’API et vous pouvez les effectuer depuis n’importe quoi.
La clé API
Sept permissions, et pas davantage : org.create, org.read, sites.create, sites.view, sites.delete, reseller.view, reseller.provision.
⛔ Pas reseller.manage. Une clé que vous collez dans un panneau de facturation doit pouvoir bloquer un client pour impayé et le connecter ; elle ne doit pas pouvoir réécrire vos propres identifiants de passerelle de paiement.
Les deux règles qui coûtent de l’argent lorsqu’elles sont enfreintes
- Prenez votre CLIENT comme clé du compte, et votre SERVICE comme clé du site. La deuxième
commande d’un client doit aboutir dans le compte qu’il a déjà. Identifiez les deux par le service et un client se retrouve avec trois comptes sans rapport et trois panneaux de contrôle distincts.
- Envoyez une clé d’idempotence à chaque création, dérivée de votre propre identifiant pour l’élément. Tous les
systèmes de facturation réessayent — un callback de passerelle arrive deux fois, un administrateur relance un provisionnement échoué, un client double-clique. Sans elle, la deuxième tentative crée un second site et vous êtes facturé pour celui-ci. Les deux modules et le client PHP le font pour vous ; si vous appelez l’API directement, envoyez Idempotency-Key.
Trois champs faciles à omettre
Chacun fait échouer tout un verbe, et aucun n’apparaît autrement que comme une erreur de validation — sauf le premier, qui n’apparaît comme rien du tout :
- ⛔⛔ Un client doit être placé sur une offre AVANT que son premier site ne soit provisionné, avec
POST /v1/reseller/clients/{orgId}/plan, et son subscription_id passé à POST /v1/sites. C’est celui pour lequel il n’y a aucune erreur à lire. Un nouveau compte client ne contient aucun abonnement et POST /v1/sites n’a pas de champ d’offre, donc un site provisionné sans offre n’emporte aucune offre du tout : votre relevé de gros est construit à partir des abonnements actifs de vos clients, donc aucune ligne n’est émise et Zinn® ne vous facture rien tant que l’hébergement fonctionne ; votre client n’hérite d’aucune allocation, donc aucun quota n’est appliqué à son site ; et il n’y a rien qu’une mise à niveau puisse modifier. La commande renvoie 201, le site se provisionne, et il sert parfaitement. Rien, ni d’un côté ni de l’autre, n’en signale quoi que ce soit.
L’ordre des opérations compte : POST /v1/sites enregistre l’abonnement qui existe au moment où il s’exécute, donc une offre définie ensuite ne s’attache pas rétroactivement.
POST /v1/sites exige stack_type. Omettez-le et aucune commande ne peut aboutir.
- **
DELETE /v1/sites/{siteId} exige un corps `{"confirm_domain": "<the site's own
domain>"}** — une confirmation saisie, car c’est le verbe destructeur. Omettez-le et aucune résiliation ne peut aboutir, donc le service continue de fonctionner et vous continuez d’être facturé. Relisez le domaine depuis GET /v1/reseller/services/{siteId}` plutôt que d’utiliser votre propre enregistrement : il est comparé au domaine principal du site lui-même et jamais à un alias.
Lisez l’erreur en entier, pas seulement la phrase
Le message d’un refus de validation est délibérément générique — « The request was well-formed but failed validation. » — car la moitié exploitable se trouve dans details, qui nomme le champ et la raison. Les deux modules et le client PHP l’ajoutent au message qu’ils affichent à votre administrateur. Si vous appelez l’API vous-même, lisez error.details ; certaines entrées portent une valeur nue plutôt qu’une phrase, c’est ainsi que max_sites: 0 vous parvient lorsque votre offre revendeur n’a plus de place.
Avant votre première commande
Les sites que vos clients achètent s’imputent sur le quota de sites de votre offre revendeur, vous avez donc besoin d’une offre qui a de la place. Un test de connexion ne peut pas le détecter — il lit votre programme et ne provisionne rien — il signale donc un compte sain qui ne peut pas encore vendre. Une commande passée sans quota est refusée avec un message le disant exactement.
Vérifiez votre téléchargement
Les deux archives sont servies en TLS depuis notre propre nom d’hôte, jamais redirigées ailleurs, avec leurs sommes de contrôle SHA-256 indiquées à côté des liens de téléchargement sur la page des téléchargements.