Запуск продаж VPN через API: от ключа до первой подписки
Путь от ключа до первой подписки, которая открывается в VPN-приложении: какие запросы отправить, в каком порядке и что сохранить у себя.
Содержание 8 разделов
Чтобы продавать VPN со своего сайта, из бота или биллинга, от платформы нужны ключ и депозит, а с вашей стороны хранилище пользователей и один обработчик HTTP. Прототип с первой подпиской собирается за вечер, рабочая интеграция с продлениями и разбором отказов занимает несколько дней.
#Что сохранить на своей стороне
external_user_id: ваш идентификатор человека. Один человек это один id, подписок у него может быть несколько.custom_id: ваш идентификатор операции, новый на каждую покупку и каждое продление. Сохраните его к заказу до отправки запроса.subscription_idиsubscription_urlиз ответа: по первому идут все дальнейшие вызовы, второй вы отдаёте пользователю.
#Шаг первый: ключ и депозит
Ключ выпускается в кабинете VPN API, раздел «Ключ и вебхук». Он показывается один раз, потом виден только префикс. Ключ тратит деньги депозита, поэтому храните его в переменной окружения на сервере: в браузер и приложение он попадать не должен.

Депозит пополняется в том же кабинете или запросом POST /deposit. Все суммы в API идут в долларах парой: *_minor в центах для расчётов и *_usd строкой для показа. Лимит 60 запросов в минуту на ключ.
#Шаг второй: тарифы
curl https://api.relayhub.surf/api/v1/vpn/plans \
-H "Authorization: Bearer $VPN_API_KEY"price_minor это ваша закупка, её и спишут с депозита. Розничную цену вы считаете у себя. Кешировать тарифы дольше часа справочник не советует: цена может измениться, а продажа по устаревшей уходит в минус.
#Шаг третий: пробный период
Первой выдайте пробную подписку. Она бесплатная, один раз на external_user_id, и проверяет всю цепочку: запрос, ответ, ссылку в VPN-приложении. Если пробный период выключен в кабинете, придёт отказ trial_disabled.
curl -X POST https://api.relayhub.surf/api/v1/vpn/subscriptions/trial \
-H "Authorization: Bearer $VPN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"external_user_id": "user-42",
"custom_id": "11111111-1111-4111-8111-111111111111"}'charged_minor: 0 и подписка с is_trial: true. Метод: POST /subscriptions/trial.#Шаг четвёртый: платная подписка
Перед покупкой проверьте остаток: GET /balance отдаёт spendable_minor, он должен покрывать price_minor тарифа. Делайте это до того, как покупатель заплатил вам.
curl -X POST https://api.relayhub.surf/api/v1/vpn/subscriptions \
-H "Authorization: Bearer $VPN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"custom_id": "22222222-2222-4222-8222-222222222222",
"plan_uuid": "550e8400-e29b-41d4-a716-446655440000",
"external_user_id": "user-42"}'subscription_url пользователь вставляет в VPN-приложение. При продлении и смене тарифа ссылка не меняется. Если у человека есть Telegram, передайте telegram_id и bot_id своего бота: подписка появится и в мини-аппе бота.
Если ответ не пришёл (таймаут, 500 или 503 upstream_unavailable), повторите запрос с тем же custom_id: вернётся та же подписка с idempotent_replay: true, второго списания не будет.
| Код | HTTP | Что значит | Повторять |
|---|---|---|---|
insufficient_balance | 402 | на депозите не хватает денег, подписка не создана | после пополнения |
forbidden | 403 | bot_id принадлежит другому аккаунту | нет, проверьте bot_id |
plan_not_found | 404 | тариф не найден или недоступен | нет, возьмите plan_uuid из /plans |
custom_id_conflict | 409 | этот custom_id уже был с другими параметрами | нет, нужен новый custom_id |
operation_in_progress | 409 | первая попытка с этим custom_id ещё идёт | да, через секунду |
external_user_conflict | 409 | telegram_id уже привязан к другому external_user_id | нет, возьмите прежний external_user_id |
rate_limited | 429 | больше 60 запросов в минуту | да, через Retry-After |
upstream_unavailable | 503 | сеть VPN временно недоступна | да, с тем же custom_id |
#Шаг пятый: продление и события
Продление на период тарифа идёт запросом POST /subscriptions/{id}/renew, на своё число дней от 3 до 1095 через renew-custom. У каждого продления свой новый custom_id.
Узнавать о конце срока удобнее событием, чем опросом. Задайте адрес в кабинете или запросом PUT /webhook:
{
"url": "https://example.com/vpn-webhook",
"events": [
"subscription.expires_in_24h",
"subscription.expired",
"deposit.credited"
]
}events: null подписывает на все события, включая будущие.Как проверять подпись события и не продлить подписку дважды, читайте в статье про вебхуки и продления.
#Сколько это занимает
Прототип с первой подпиской собирается за вечер вместе с чтением справочника. Рабочая интеграция занимает несколько дней, и большая часть этого времени уходит на ваш продукт: витрину, оплату, письма и тексты на отказы. Если код пишет нейросеть, дайте ей спецификацию целиком; что ещё ей нужно знать, есть в статье про интеграцию руками агента.
#Чек-лист
Первая подписка без денег
Пробный период через API депозит не списывает. Выпустите ключ в кабинете VPN API и прогоните цепочку бесплатно.
Читать дальше
Как принимать события и не продлить подписку дважды.
Когда своя интеграция вообще нужна.
Как дать нейросети спецификацию, чтобы она не выдумывала поля.