Лицензия на плагин?
Проверим за 60 мс.
Плагин при запуске шлёт запрос на flupydev.ru/api/v1/license/check — сервер проверяет ключ, подписывает ответ по HMAC и говорит: запускать или гасить. Всё.
Плагин при запуске шлёт запрос на flupydev.ru/api/v1/license/check — сервер проверяет ключ, подписывает ответ по HMAC и говорит: запускать или гасить. Всё.
Вбей ключ в формате FLPY-XXXX-XXXX-XXXX и получи ровно тот же ответ, который улетает плагину при старте сервера.
Полный цикл от onEnable() до запуска плагина — меньше ста миллисекунд.
Ядро грузит плагин, тот читает ключ из config.yml и считает HWID.
--?GET на flupydev.ru/api/v1/license/check с key, hwid и версией плагина.
--?Сервер ищет ключ: срок, статус, привязка к железу, лимит серверов.
--?Ответ подписывается SHA-256 — подделать его по дороге невозможно.
--?valid=true > плагин стартует. Иначе — лок и сообщение в консоль.
Каждый ответ подписан HMAC-SHA256. Плагин сверяет подпись локально — эмуляция ответа через hosts не прокатит.
Ключ опционально биндится к железу: утащил конфиг на другой сервер — 403.
Если API лежит (5xx), плагин работает кэш-режим до 6 часов — легалы не страдают.
Один эндпоинт, один метод, никаких токенов со стороны плагина — только сам ключ.
| Параметр | Описание |
|---|---|
| key req | Лицензионный ключ вида FLPY-XXXX-XXXX-XXXX |
| hwid opt | Отпечаток сервера. Если ключ привязан и не совпадёт — 403 |
| plugin opt | Имя плагина — для логов и статистики |
| build opt | Версия плагина — для контроля минимальной версии |
{
"status": "ok",
"valid": true,
"product": "FlupyCore Pro",
"owner": "Steve_2012",
"expires_at": "2027-12-31T23:59:59Z",
"max_servers": 1,
"hwid_bind": "A1B2-C3D4-E5F6",
"signature": "hmac:a3f9c2…e81d"
}
# проверка ключа
curl -s "https://flupydev.ru/api/v1/license/check?key=FLPY-1111-2222-3333&hwid=A1B2-C3D4-E5F6&plugin=FlupyCore" | jq .
# > 200 OK
{
"status": "ok",
"valid": true,
"product": "FlupyCore Pro",
"expires_at": "2027-12-31T23:59:59Z",
"signature": "hmac:a3f9c2…e81d"
}
# отозванный ключ > 403 { "valid": false, "reason": "KEY_REVOKED" }
// onEnable() твоего плагина
@Override
public void onEnable() {
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
try {
String key = getConfig().getString("license.key");
String hwid = LicenseUtil.fingerprint(); // HWID сервера
URL url = new URL("https://flupydev.ru/api/v1/license/check"
+ "?key=" + URLEncoder.encode(key, "UTF-8")
+ "&hwid=" + hwid + "&plugin=" + getName());
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setConnectTimeout(4000);
c.setReadTimeout(4000);
if (c.getResponseCode() != 200) { lock("HTTP " + c.getResponseCode()); return; }
JsonObject r = JsonParser.parseReader(new InputStreamReader(
c.getInputStream(), StandardCharsets.UTF_8)).getAsJsonObject();
if (!r.get("valid").getAsBoolean()) { lock(r.get("reason").getAsString()); return; }
if (!LicenseUtil.verifySign(r)) { lock("BAD_SIGNATURE"); return; }
Bukkit.getScheduler().runTask(this, () ->
getLogger().info("Лицензия активна — запуск."));
} catch (Exception e) { graceMode(); } // API лежит > кэш-режим
});
}
<?php // api/v1/license/check/index.php — минимальный шлюз
require 'db.php';
header('Content-Type: application/json');
$key = strtoupper($_GET['key'] ?? '');
$hwid = strtoupper($_GET['hwid'] ?? '');
if (!preg_match('/^FLPY-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/', $key))
die(fail(400, 'INVALID_FORMAT'));
$st = $pdo->prepare('SELECT * FROM licenses WHERE `key` = ?');
$st->execute([$key]);
if (!$row = $st->fetch()) die(fail(404, 'NOT_FOUND'));
if ($row['revoked']) die(fail(403, 'KEY_REVOKED'));
if (time() > strtotime($row['expires_at'])) die(fail(410, 'KEY_EXPIRED'));
if ($row['hwid'] && $hwid !== $row['hwid']) die(fail(403, 'HWID_MISMATCH'));
$data = ['status'=>'ok','valid'=>true,'product'=>$row['product'],'expires_at'=>$row['expires_at']];
$data['signature'] = 'hmac:'.hash_hmac('sha256', json_encode($data), $SECRET);
echo json_encode($data);
Хочешь свой гейтвей вместо общего — весь серверный слой укладывается в один файл и одну таблицу.
Nginx + PHP 8.3-fpm (или Node 20), обязательно TLS. Хватит самого дешёвого VPS — нагрузка копеечная.
licensesПоля: key, product, owner, hwid, expires_at, revoked, max_servers. Индекс по key.
HMAC_SECRET живёт только в .env на сервере. В плагин зашивается тот же секрет — им сверяется подпись ответа.
Раз в час помечать истёкшие, слать алерты в ТГ при всплеске 403 — кто-то гоняет отозванный ключ.
Таймаут 3–5 сек, при 5xx — кэш-режим до 6 часов. Легальные юзеры не должны лежать вместе с твоим сервером.