ДОКУМЕНТАЦИЯ

ПЛАТЕЖИ · § 012

Подключение платёжных систем

FreshDonate из коробки умеет работать с шестью шлюзами: ЮKassa, Heleket, CryptoBot, Wata, Robokassa и Tebex. Получаешь ключи у провайдера, вписываешь в админ-панель, прописываешь webhook - готово.

6 шлюзов из коробкиRUB · USD · EURТестовый режим~10 минут на провайдера

§ 012.01

Как это работает

Жизненный цикл платежа от клика по кнопке до выдачи товара

  1. 01 -

    Игрок выбрал товар на витрине, нажал "Оплатить" → shop отправляет POST /payments в backend с productId, ником, email и id метода оплаты.

  2. 02 -

    Backend создаёт запись payment, идёт в API провайдера (например YooKassa) и получает confirmation_url - ссылку, куда отправить игрока.

  3. 03 -

    Игрок попадает на страницу провайдера, вводит карту/крипту/СБП и оплачивает. По результату его редиректит на PAYMENT_RETURN_URL магазина.

  4. 04 -

    Параллельно провайдер шлёт серверный вебхук на WEBHOOK_BASE_URL/webhooks/<provider>. Backend проверяет подпись/IP, помечает платёж paid и кладёт его в очередь доставки.

  5. 05 -

    Плагин или RCON забирает покупку из очереди и выдаёт её игроку. Покупателю на почту уходит email-чек.

§ 012.02

Какие шлюзы поддерживаются

Каждый можно включить независимо и подключить хоть все шесть одновременно

ЮKassa

Карты РФ, СБП, ЮMoney, SberPay, T-Pay

RUB

~2%

РФ

Heleket

Криптовалюта: BTC, ETH, USDT и другие

USD

~0.5%

Весь мир

CryptoBot

Криптоплатежи через Telegram @CryptoBot

USD

~3%

Весь мир

Wata

Карты, СБП. Имеет вывод дохода в крипту

RUB

~10%

РФ + СНГ

Robokassa

Карты, СБП, электронные кошельки

RUB

~3%

РФ

Tebex

Visa, MasterCard, Google/Apple pay, Revolut.. Через Headless API

USD / EUR

~5%

Весь мир

※ Комиссии указаны ориентировочно - реальный процент зависит от твоего договора с провайдером.

§ 012.03

Что понадобится

Минимальный набор перед началом

ДОМЕН

Публичный HTTPS-адрес backend

WEBHOOK_BASE_URL

АККАУНТ

Активный аккаунт у провайдера

с подтверждением личности

ПАНЕЛЬ

Доступ к админ-панели FreshDonate

panel.your-domain.com

ДОКУМЕНТЫ

Вероятно нужно будет изменить документы на странице магазина

зависит от платёжки

Важно

Все провайдеры требуют публичный HTTPS-URL для приёма вебхуков. localhost или http:// работать не будут. Если ты тестируешь локально - подними туннель через cloudflared, ngrok или localhost.run и подставь его в WEBHOOK_BASE_URL.

§ 012.04

Шаг 01 — Открой раздел в админ-панели

Всё подключение делается через UI, ничего трогать в .env не надо

В админ-панели открой Магазин → Платёжные системы. Слева - форма провайдера, справа - список всех доступных шлюзов. Выбери нужный и переключи "Включить" наверху страницы.

После сохранения настройки лежат в БД (payment_providers), backend не нужно перезапускать - изменения подхватятся на следующем запросе.

※ Бекенд при старте делает seed - создаёт все 6 шлюзов с пустыми credentials в выключенном состоянии. Если в базе уже есть запись с твоими ключами, повторный seed её не затрёт.

§ 012.05

Шаг 02a — ЮKassa

Карты, СБП, ЮMoney, SberPay, T-Pay. Только RUB

В личном кабинете yookassa.ru зайди в Интеграция → Ключи API. Тебе нужны:

shopId

Идентификатор магазина (число в правом верхнем углу личного кабинета и на странице ключей).

secretKey

Секретный ключ. Бывает test_* и live_* - используй тот, который соответствует режиму магазина в кабинете.

Затем настрой webhook: Интеграция → HTTP-уведомления → Добавить. Адрес:

WEBHOOK URL

https://api.your-domain.com/webhooks/yookassa

Подпиши уведомления на события: payment.succeeded, payment.canceled, refund.succeeded.

※ ЮKassa не подписывает вебхуки - аутентификация по списку IP. Backend проверяет, что вебхук пришёл с 185.71.76/77.*, 77.75.153/156/157.* или 2a02:5180:*. Если ты за прокси без x-forwarded-for - поставь в .env YOOKASSA_SKIP_IP_CHECK=true, но только в тестовом режиме.

§ 012.06

Шаг 02b — Heleket

Криптовалютные платежи. BTC, ETH, USDT и десятки других монет

В личном кабинете heleket.com создай мерчанта и забери:

merchantId

UUID мерчанта из карточки в кабинете.

apiKey

Payment API Key. Используется и для подписи запросов в Heleket, и для проверки подписи вебхука.

При создании мерчанта (или в его настройках) укажи URL вебхука:

WEBHOOK URL

https://api.your-domain.com/webhooks/heleket

Валюта аккаунта - в карточке провайдера выбери одну из: USD, EUR, RUB. Если товары в другой валюте, сумма пересчитается по курсам из общих настроек магазина.

※ Heleket дополнительно проверяет IP-источник (31.133.220.*) И MD5-подпись каждого webhook. Подпись считается как md5(base64(json_body) + apiKey).

§ 012.07

Шаг 02c — CryptoBot

Криптоплатежи через Telegram-бота @CryptoBot. USDT, TON, BTC и другие

Открой в Telegram бота @CryptoBot (или @CryptoTestnetBot для песочницы) → Crypto Pay (/pay)Create App. Получишь:

apiToken

Токен приложения, выглядит как 12345:AAaaBbCc.... Вставь его в поле API Token в админ-панели.

В том же приложении CryptoBot включи Webhooks и укажи URL:

WEBHOOK URL

https://api.your-domain.com/webhooks/cryptobot

Включи тестовый режим в админке, если используешь токен от @CryptoTestnetBot - тогда backend будет ходить в testnet-pay.crypt.bot вместо боевого pay.crypt.bot.

※ CryptoBot подписывает вебхук HMAC-SHA256 от сырого тела, ключ - sha256(apiToken), заголовок - crypto-pay-api-signature. Подпись считается по байтам, которые приходят в запросе.

§ 012.08

Шаг 02d — Wata

Карты, СБП. Вывод дохода в крипту

Для подключения подайте заявку на сайте wata.pro, при подаче укажите тип верификации "Рекомендация" и введите FreshDonate

В кабинете wata.pro (или api-sandbox.wata.pro для теста) выпусти Access Token. Это JWT - подпиши его в кабинете и сразу скопируй (после закрытия модалки токен будет недоступен).

apiKey

JWT-токен. Один токен работает либо в prod, либо в sandbox - не оба сразу. Тестовый режим в админке переключает базовый URL Wata.

В терминале Wata укажи URL для уведомлений (Предварительно включите "Вебхук оплаты"):

WEBHOOK URL

https://api.your-domain.com/webhooks/wata

※ Wata подписывает вебхуки RSA-SHA512 по сырому телу, подпись приходит в заголовке X-Signature в base64. Публичный ключ backend сам подтягивает с /api/h2h/public-key и кеширует на время жизни процесса.

§ 012.09

Шаг 02e — Robokassa

Карты, СБП, электронные кошельки. Redirect-only API

В кабинете robokassa.ru зайди в Технические настройки → Технические настройки магазина и забери:

merchantLogin

Идентификатор магазина (логин). Используется в подписи и в редиректе.

password1

Пароль #1 - подписывает редирект на оплату (что мы отправляем покупателю).

password2

Пароль #2 - подписывает ResultURL-уведомление (что приходит к нам от Robokassa). Никогда не путай: пароль #2 ушёл в редирект = подделка.

В тех. настройках укажи ResultURL и метод POST:

WEBHOOK URL

https://api.your-domain.com/webhooks/robokassa

В админ-панели выбери алгоритм подписи (SHA256 по умолчанию, либо MD5) - он должен совпадать с настройкой в кабинете Robokassa, иначе все вебхуки будут отбиваться 403.

※ Robokassa требует, чтобы InvId был положительным 32-битным числом, а у нас платежи - UUID. Backend хеширует UUID в 31-битный int и передаёт оригинал UUID в Shp_paymentId - именно по нему ResultURL находит платёж и завершает его. На ResultURL обязательно нужно ответить телом OK<InvId>, иначе Robokassa будет долбить вебхук снова и снова.

§ 012.10

Шаг 02f — Tebex

Headless API. Только премиум-ники Mojang. Платежи через Tebex Checkout

В Tebex Dashboard создай Webstore типа Minecraft. Далее Webstore Builder → API Keys, забери:

webstoreToken

Публичный токен витрины (Public Token).

privateKey

Приватный ключ. Используется как пароль для HTTP Basic к Headless API.

webhookSecret

Developers → Webhooks → Endpoints. Создай endpoint, скопируй секрет.

URL вебхука для Tebex:

WEBHOOK URL

https://api.your-domain.com/webhooks/tebex

После добавления endpoint Tebex пришлёт validation.webhook с уникальным id - backend сам ответит этим же id, и endpoint в Tebex перейдёт в "Validated".

Coin-пакеты: обязательны

Tebex Headless API не позволяет передать произвольную сумму - корзина собирается из заранее созданных пакетов фиксированного номинала. В Tebex Dashboard → Packages создай 6 пакетов с ценами 1000, 100, 10, 1, 0.1, 0.01 в валюте аккаунта (USD/EUR). Тип - Single Payment, имя любое ("Coin 1000", "Coin 10" и т.д.). Скопируй Package ID каждого и вставь в админ-панели в раздел "Coin-пакеты Tebex". Без них checkout вернёт TEBEX_COINS_NOT_CONFIGURED.

Tebex также валидирует ник игрока через Mojang. На offline-mode серверах и для пиратских ников Tebex отвечает 404 "Invalid Username". Backend в этом случае подставляет известный премиум-ник из fallback-списка, чтобы корзина создалась - реальный ник игрока всё равно лежит в basket.custom.payment_id и используется при доставке.

§ 012.11

Шаг 03 — Сводная таблица вебхуков

Все URL и способы проверки подлинности в одном месте

yookassa

/webhooks/yookassa

IP-allowlist (185.71.76/77.*, 77.75.153/156/157.*)

heleket

/webhooks/heleket

IP 31.133.220.* + MD5 подпись

cryptobot

/webhooks/cryptobot

HMAC-SHA256 по сырым байтам

wata

/webhooks/wata

RSA-SHA512 по сырым байтам

robokassa

/webhooks/robokassa

SHA256/MD5 от Password2 + InvId + OutSum

tebex

/webhooks/tebex

HMAC-SHA256(sha256(body)) + handshake

※ Базовый URL берётся из WEBHOOK_BASE_URL в .env бекенда. На проде это https://api.your-domain.com, локально - твой ngrok/cloudflared-туннель.

※ Каждый /webhooks/* endpoint защищён rate-limit (200 req/min). Если провайдер ретраит вебхуки часто - повысь RATE_LIMIT_MAX в .env.

§ 012.12

Шаг 04 — Распределение комиссии

Кто платит комиссию платёжной системы - ты или покупатель

В карточке провайдера есть две связанные настройки: commissionPercent (сколько процентов берёт провайдер) и commissionRule (на кого вешать эту комиссию).

Продавец оплачивает

Комиссия вычитается из суммы платежа. Покупатель видит цену товара, вы получаете меньше.

Покупатель оплачивает

Комиссия добавляется к цене товара. Покупатель видит цену + комиссию, вы получаете полную цену товара.

Поровну (50/50)

Комиссия делится пополам. Покупатель доплачивает половину, вы недополучаете половину.

Пример: товар 100 ₽, комиссия 2.8%.

ПРИМЕР

seller: покупатель платит 100₽ → ты получаешь 97.20₽

buyer: цена 100₽ → покупатель платит 102.80₽ → ты получаешь 100₽

split: покупатель платит 101.40₽ → ты получаешь 98.60₽

※ Комиссия из настроек используется только для предварительного расчёта при создании платежа. Когда платёж проходит, backend перезаписывает её из реального ответа провайдера (income_amount у YooKassa, commission у Heleket и т.п.). Так что в статистике панели цифры будут точными, даже если ты в админке указал чуть-чуть не ту ставку.

§ 012.13

Шаг 05 — Валюта и минимальная сумма

В какой валюте провайдер принимает деньги и от какой суммы

У каждого провайдера в админке два важных поля:

Валюта аккаунта

В какой валюте у тебя настроен аккаунт у провайдера. Если товар в магазине в другой валюте - сумма пересчитается по курсам из общих настроек магазина перед отправкой провайдеру. Для ЮKassa и Robokassa зашито в RUB, у Heleket / Wata / Tebex выбирается в дропдауне.

Минимальная сумма

Платежи на сумму меньше этой не будут отправляться через этого провайдера. Полезно если у YooKassa лимит 1₽, а ты не хочешь дёргать API из-за грошей. Диапазон 0.01 .. 10000.

§ 012.14

Шаг 06 — Кнопки оплаты в магазине

То, что увидит игрок на витрине

Провайдер - это бекенд-сущность. Чтобы игрок увидел его в магазине, нужно создать платёжный метод - кнопку с названием, иконкой и привязкой к провайдеру.

В админ-панели открой Магазин → Методы оплаты → Добавить и заполни:

Название

Текст на кнопке. Например "Российские карты и СБП" или "Telegram CryptoBot".

Иконка

Имя Lucide-иконки (i-lucide-credit-card, i-lucide-bitcoin, i-lucide-bot, ...). Все доступные иконки - на lucide.dev/icons.

Тип обработки

provider - оплата через подключённый шлюз. redirect - просто редирект по произвольной ссылке с плейсхолдерами {nickname}, {amount}, {email}, {product} и т.п. Полезно для интеграции с самописными платёжками или Telegram-ботами.

※ Можно создать несколько методов на одного провайдера - например "Карты РФ" и "СБП" оба на YooKassa, с разными иконками. Покупатель видит только включённые методы.

§ 012.15

Тестовый режим

Как погонять платежи без настоящих денег

У некоторых провайдеров в карточке есть тумблер testMode. Что он делает:

CryptoBot

Backend ходит в testnet-pay.crypt.bot. Нужен токен от @CryptoTestnetBot, не от боевого.

Wata

Backend ходит в api-sandbox.wata.pro. Токен sandbox != prod.

Robokassa

В редирект добавляется IsTest=1 - Robokassa открывает тестовую страницу без реального списания.

YooKassa, Heleket, Tebex

У этих провайдеров нет отдельного sandbox - тестовый режим включается в самом кабинете провайдера (отдельный test_* shopId/secretKey у YooKassa, тестовый мерчант у Heleket).

Демо-платежи (созданные через кнопку "Тест" в админке, без реального захода в API) не шлют email-чеки покупателю и не попадают в очередь доставки - чтобы не спамить плагин при отладке.

※ В NODE_ENV=development backend пропускает проверку IP/подписи для всех вебхуков. На проде такого делать нельзя - иначе любой сможет послать фейковый "оплачено" и забрать товар.

§ 012.16

Что делать, если не работает

Самые частые проблемы при первой настройке

Платёж создаётся, но вебхук не приходит

Проверь, что WEBHOOK_BASE_URL в .env действительно публичный HTTPS, что в кабинете провайдера прописан правильный URL и что nginx пробрасывает /webhooks/* на backend без редиректов. Логи: docker compose logs backend -f.

403 Forbidden в логах на /webhooks/yookassa

Backend не видит реальный IP клиента из-за прокси. В nginx-конфиге обязательно proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for. Для отладки можно временно поставить YOOKASSA_SKIP_IP_CHECK=true - но только в тестовом режиме.

403 Invalid signature

Не тот ключ. У Heleket apiKey должен быть Payment API Key (не General). У CryptoBot - apiToken от того же приложения, чьи ключи в кабинете. У Robokassa password2 в админке должен совпадать с тем, что в кабинете Robokassa.

TEBEX_INVALID_USERNAME

Mojang не знает такого ника. Сервер в offline-mode или игрок указал ник с опечаткой. Backend сам подставит fallback-ник, чтобы checkout прошёл - но если хочется убрать ворнинги, переходи на онлайн-сервер или используй другой шлюз.

TEBEX_COINS_NOT_CONFIGURED

Не заполнены Package ID для 6 coin-номиналов. Создай в Tebex Dashboard пакеты 1000 / 100 / 10 / 1 / 0.1 / 0.01 и вставь их ID в админке.

Robokassa ретраит вебхук бесконечно

ResultURL должен возвращать OK<InvId> в текстовом виде. Backend это уже делает, но если nginx переписывает Content-Type или ответ - Robokassa считает доставку неуспешной. Проверь, что nginx не вмешивается в тело ответа.

Платёж "висит" в статусе pending

Backend каждые несколько минут сам ходит в API провайдера и обновляет статус (см. payment-expiration плагин Fastify). Если за paymentLifetime игрок не оплатил - платёж помечается как expired и удаляется из очереди.

§ 012.17

Что дальше

Деньги принимаются - осталось доставлять покупки

※ Платёж не проходит, вебхук молчит, провайдер отвечает странно? Логи бекенда - docker compose logs backend -f | grep -i <провайдер>. Если не помогает - пиши в Telegram @zWork1 или открой issue на Github.

FreshDonate