Skip to content

Возвраты

Возврат делается из кабинета в пару кликов. Если касса умеет возвраты по API — деньги уходят клиенту через провайдера прямо из кабинета; если нет — возврат фиксируется в базе, а переводите вы сами. В обоих случаях транзакция помечается REFUNDED, юзер получает уведомление, а выданную подписку можно тут же отключить.

Включение

Возвраты по умолчанию выключены. Включаются настройкой REFUND_ENABLED в разделе «Настройки» (группа «Платежи») — пока она выключена, запрос возврата вернёт 403.

Как сделать возврат

  1. Раздел Платежи → вкладка «Транзакции» — у каждой завершённой транзакции есть кнопка ↩.
  2. Подтвердите возврат.
  3. Ответьте на вопрос «отключить выданную подписку?» — можно вернуть деньги и оставить доступ, а можно отозвать подписку сразу.

Вернуть можно только транзакцию в статусе COMPLETED — на любой другой статус кабинет ответит 409.

По API провайдера или вручную

КассыКак уходят деньги
YooKassa, Cryptomus, Heleket, CloudPaymentsВозврат по API провайдера: YooKassa — POST /v3/refunds (идемпотентный — один полный возврат на платёж), CloudPayments — POST /payments/refund по id транзакции
Все остальныеRecord-only: транзакция помечается REFUNDED, кабинет предупредит «нет API-возврата — деньги нужно вернуть вручную»

Если провайдер отказал в возврате, кабинет ответит 502 и транзакция останется COMPLETED — статус не поменяется, пока деньги реально не ушли.

Что происходит под капотом

  • Статус меняется атомарно COMPLETED → REFUNDED. Строка блокируется FOR UPDATE: двойной клик или два админа одновременно — второй запрос увидит уже сделанный возврат и получит 409, повторный возврат у провайдера не выстрелит.
  • Возврат пополнения баланса списывает сумму обратно с кошелька юзера — но никогда не уводит баланс в минус: если юзер уже потратил деньги, кабинет предупредит «на балансе меньше суммы возврата — баланс не списан».
  • Каждый возврат пишется в аудит-лог: кто, какую транзакцию, через что вернул и отзывалась ли подписка.

Отключение подписки

Если при возврате выбрано «отключить подписку», база гасит её panel-first: сначала выключает юзера на панели Remnawave, затем помечает подписку DISABLED локально. Если панель в этот момент недоступна, подписка всё равно отключается локально сразу, а команда панели уходит в фоновую очередь ретраев — как только панель ответит, доступ будет закрыт. Кабинет предупредит: «панель недоступна — выключим подписку в фоне, как ответит».

Уведомление юзеру

После возврата юзер получает сообщение в Telegram по редактируемому шаблону «Возврат» (правится в кабинете вместе с остальными шаблонами уведомлений; шаблон можно и отключить). К сообщению добавляется комментарий, если вы указали его при возврате — удобно объяснить причину.

MIT License · сделано для тех, кто продаёт VPN, а не настраивает ботов