Ако използвате WHMCS, използвайте модула за WHMCS вместо това — тази страница е за всичко останало. Има две опции, и двете безплатни и под лиценз GPL-2.0-or-later.
HostBill
Хостинг модул със същите шест действия като този за WHMCS.
- Изтеглете го и го разархивирайте.
- Качете директорията
zinn в includes/modules/Hosting/, така че файловете да попаднат в includes/modules/Hosting/zinn/class.zinn.php и includes/modules/Hosting/zinn/ZinnHostBillClient.php.
- Settings → Modules → Hosting, активирайте Zinn Digital® и добавете връзка:
- API address:
https://api.zinndigital.com
- API key: Вашият API ключ за Zinn® (поле тип
password, така че HostBill го съхранява криптиран)
- При продукта задайте Product line (напр.
mainstream), Stack (wordpress по подразбиране) и по избор Application и PHP version.
- Натиснете Test connection.
⛔ Не преименувайте директорията zinn. Зареждащият механизъм на HostBill извежда класа, който търси, от името на директорията, така че преименуването създава модул, който панелът показва в списъка, но никога не извиква.
Всичко останало — PHP клиентът
Един файл, без зависимости, без предположения за контролния панел. Поставете ZinnProvisioning.php във Вашата кодова база и извикайте шест метода.
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));
Не пишете на PHP? Същите извиквания са налични в справочника за API и можете да ги правите от всякаква среда.
Ключът за API
Седем разрешения, и не повече: org.create, org.read, sites.create, sites.view, sites.delete, reseller.view, reseller.provision.
⛔ Не reseller.manage. Ключ, който поставяте в панел за фактуриране, трябва да може да спре клиент поради неплащане и да го впише; той не бива да може да презаписва Вашите собствени идентификационни данни за разплащателни шлюзове.
Двете правила, които струват пари, когато бъдат нарушени
- Свържете акаунта с Вашия КЛИЕНТ, а сайта с Вашата УСЛУГА. Втората поръчка на клиента трябва да постъпи в акаунта, с който вече разполага. Свържете и двете с услугата и един клиент ще се окаже с три несвързани акаунта и три отделни контролни панела.
- Изпращайте ключ за идемпотентност (idempotency key) при всяко създаване, изведен от Вашия собствен идентификатор за съответния обект. Всяка система за фактуриране прави повторни опити — обратно извикване от шлюз пристига два пъти, администратор стартира отново неуспешно обезпечаване, клиент кликва два пъти. Без него вторият опит създава втори сайт и той Ви се таксува. И двата модула, както и PHP клиентът, правят това вместо Вас; ако извиквате API директно, изпратете
Idempotency-Key.
Три полета, които лесно се пропускат
Всяко от тях нарушава функционирането на цяло действие и никое от тях не се показва като нещо различно от грешка при валидация — с изключение на първото, което се показва като абсолютно нищо:
- ⛔⛔ Клиентът трябва да бъде поставен на план, ПРЕДИ да бъде предоставен първият му сайт, чрез
POST /v1/reseller/clients/{orgId}/plan, като неговият subscription_id се предава на POST /v1/sites. Това е този случай, при който няма грешка за четене. Новият клиентски акаунт не съдържа абонамент, а POST /v1/sites няма поле за план, така че сайт, предоставен без такъв, изобщо няма план: Вашето извлечение за продажба на едро се изгражда от активните абонаменти на Вашите клиенти, така че не се начислява ред и Zinn® не Ви таксува с нищо, докато хостингът работи; Вашият клиент не наследява квоти, така че към неговия сайт не се прилага лимит; и няма нищо, което надстройката да промени. Поръчката връща 201, сайтът се предоставя и функционира перфектно. Нито една от страните не съобщава нищо за това.
Последователността е от значение: POST /v1/sites записва абонамента, който съществува в момента на изпълнението му, така че план, зададен след това, не се прикачва със задна дата.
POST /v1/sites изисква stack_type. Пропуснете го и нито една поръчка няма да може да бъде успешна.
DELETE /v1/sites/{siteId} изисква тяло {"confirm_domain": "<the site's own domain>"} — ръчно въведено потвърждение, тъй като това е разрушителният глагол. Пропуснете го и нито едно анулиране няма да успее, така че услугата продължава да работи и Вие продължавате да бъдете таксувани за нея. Прочетете обратно домейна от GET /v1/reseller/services/{siteId}, вместо да използвате Вашия собствен запис: той се сравнява с основния домейн на самия сайт и никога с псевдоним.
Прочетете цялата грешка, а не само изречението
Съобщението при отказ от валидация е умишлено общо — „Заявката беше правилно оформена, но не премина валидацията.“ — тъй като приложимата част се намира в details, където са посочени полето и причината. И двата модула, както и PHP клиентът, го добавят към съобщението, което показват на Вашия администратор. Ако сами извиквате API, прочетете error.details; някои записи съдържат само стойност, а не изречение, чрез което max_sites: 0 достига до Вас, когато във Вашия план за риселър няма свободно място.
Преди първата Ви поръчка
Сайтовете, които Вашите клиенти купуват, се отчитат към лимита за сайтове на Вашия план за риселър, така че се нуждаете от план със свободно място в него. Тестът на връзката не може да открие това — той прочита Вашата програма и не предоставя нищо — затова отчита изправен акаунт, който все още не може да продава. Поръчка, направена без наличен лимит, се отхвърля със съобщение, което гласи точно това.
Проверете Вашето изтегляне
И двата архива се предоставят през TLS от нашето собствено име на хост, никога не се пренасочват другаде, а техните контролни суми по SHA-256 са отпечатани до връзките за изтегляне на страницата за изтегляния.