Продажа цифровых товаров на автопилоте: лицензии и выдача без ручной работы
Разбираю на своём кейсе с Prism Pro: как отдавать ключи покупателю за секунды даже там, где нет API, и как сделать лицензию, которая проверяется без обращения к серверу.
Когда я продаю цифровой товар, руками мне делать не хочется ничего. Оплатил — получил рабочий ключ. Точка. Между этими двумя событиями меня быть не должно: ни ночью, ни в отпуске, ни когда я забыл заглянуть в чат. Покажу, как это работает у меня на примере Prism Pro — мы продаём его под брендом QoLab. Всё проверено руками, теории тут нет.
Автопилот собран из двух отдельных механизмов, и путать их нельзя. Первый — доставка: как ключ доезжает до покупателя сразу после оплаты. Второй — лицензирование: как программа понимает, что ключ настоящий, и отсекает чужих. Пройдусь по обоим.
Автовыдача там, где нет API
Мы продаём на FunPay. У площадки нет ни официального API, ни вебхуков — некому дёрнуть мой сервер и сказать «пришла оплата». Классический совет «повесьте вебхук» тут просто не работает.
Способ остаётся один: бот, который сидит под cookie-сессией моего аккаунта и читает чат как обычный пользователь. Я использую FunPayCardinal. Он ловит событие оплаченного заказа, а дальше в дело вступает мой плагин qolab_delivery.py. Логика короткая:
- заказ оплачен → плагин определяет, что это Prism Pro;
- плагин идёт в мой сервер:
POST https://api.qolab.space/admin/keysи чеканит свежий ключ; - ключ уходит покупателю прямо в чат FunPay.
Ключ создаётся динамически под конкретную продажу. Никаких заранее заготовленных списков в текстовом файле, которые кончаются в самый неподходящий момент или случайно уходят двум людям сразу. Каждому покупателю — свой уникальный ключ, выданный в момент оплаты.
Если делаете что-то похожее, заложите три вещи в ядро с самого начала. Дедупликация — площадка может прислать событие о заказе дважды, и без защиты вы отдадите два ключа за одну оплату. Идемпотентность на стороне сервера — повторный запрос по тому же заказу должен вернуть тот же ключ. Новые плодиться не должны. Обработка обрыва — сервер ответил, а сообщение в чат не ушло; нужно понимать, где вы остановились. У меня ядро (поиск товара, чеканка, отправка, дедуп) закрыто тестами, потому что именно здесь всё ломается тихо и обидно: клиент заплатил, а ключа нет.
Лицензия, которая работает без интернета
Выдать ключ — половина дела. Дальше программа на компьютере покупателя должна убедиться, что ключ настоящий. Простой путь — на каждый запуск ходить на сервер и спрашивать «этот ключ ещё живой?». Но тогда сервер становится точкой отказа: лёг сервер — у всех клиентов перестала запускаться программа. Меня такой сценарий не устраивает.
Поэтому в QoLab License Server я выдаю подписанные токены, которые проверяются офлайн. Подпись — Ed25519. Работает так: сервер один раз выпускает токен с полезной нагрузкой (версия, ключ, срок действия, дата выпуска) и подписывает его своим приватным ключом. В программу зашит только публичный ключ. Клиент проверяет подпись локально, без сети. Подделать токен невозможно: приватного ключа нет ни у кого, кроме сервера.
Есть одна деталь, на которой легко споткнуться. Токен похож на JWT, но это не он. Ed25519 подписывает байты строки payload в base64url — именно исходные байты, а собранный заново из JSON объект тут ни при чём. Проверка идёт байт в байт. Распарсите на клиенте JSON и соберёте его обратно перед проверкой подписи — порядок полей или пробелы поедут, и валидная лицензия начнёт отклоняться. Подписывайте и проверяйте ровно те же байты.
Стек у меня максимально скучный, и это плюс: Hono на TypeScript через tsx, SQLite через better-sqlite3, Docker, nginx. Тот же сервер отдаёт лендинг и кабинеты — клиентский и владельца. Ничего лишнего.
Что забрать себе
Если собираете продажу цифры на автопилоте, держите в голове три вывода из моей практики:
- Нет API у площадки — это ещё не приговор. Бот на сессии аккаунта закрывает автовыдачу там, где вебхуков просто не существует.
- Разделяйте доставку и лицензирование. Это два разных механизма с разными точками отказа.
- Офлайн-проверка подписью снимает зависимость от вашего сервера. Программа запускается у клиента, даже когда у вас идут работы.
Автопилот — это не магия и не волшебная кнопка из рекламы. Это несколько простых кусков, каждый из которых честно закрывает свою задачу и покрыт тестами там, где цена ошибки — оплата без товара.