Личный проект
CactusPlay - маркетплейс игр и внутриигровых предметов

Технологии
Ссылки
Услуги
- →Сооснование и fullstack-разработка
- →Скраперы на Node.js для PlayStation Store и Xbox Store
- →Витрина на WooCommerce с кастомными расширениями на PHP
- →Конвейер дифференциальной синхронизации цен и система оповещений
- →Интеграция платёжного шлюза и цифровой доставки
- →Интеграция с Яндекс.Маркетом: маппинг предложений, синхронизация цен и мгновенная отгрузка кодов
- →Пять кастомных плагинов WordPress: синхронизация, цены, цифровые коды, соцвход, рассылки
- →Сервис стилизации обложек на Go и self-hosted поддержка на FreeScout
Результаты
- ✓Игровой маркетплейс с тысячами актуальных SKU по PS и Xbox
- ✓Автоматическая ежедневная синхронизация цен - ноль ручных обновлений
- ✓Система оповещений об изменении цен для поиска возможностей для акций
- ✓Интеграции платёжных шлюзов СНГ с доставкой цифровых ключей
- ✓Сквозная цепочка цены: PS Store, сайт и затем Яндекс.Маркет, без ручного шага
- ✓Карточки на Яндекс.Маркете со всеми доступными атрибутами
Обзор инфраструктуры и возможностей сайта
Разбор работающей системы: витрина на WooCommerce, каталог, который наполняет граббер, админка, где ведутся цены и заказы, и то, как эти части связаны между собой. Комментарии на русском.
Задача
Геймеры из СНГ, покупающие игры для PlayStation и Xbox, сталкивались с фрагментированным рынком: цены в официальных магазинах значительно различались по регионам, продавцы подарочных карт были разбросаны по форумам, и не было единого места, чтобы сравнить и купить. Стал сооснователем CactusPlay, чтобы закрыть этот пробел, маркетплейса игр и внутриигровых предметов с реальными данными о ценах из официальных источников.
Ключевая техническая задача: PlayStation Store и Xbox Store не предоставляют публичных API. Чтобы удерживать тысячи SKU с точными ценами почти в реальном времени, пришлось строить слой скрапинга и синхронизации с нуля.
Исследование
Первичное исследование описало весь путь покупателя: найти игру, сравнить цену по регионам, купить самый дешёвый легальный ключ, активировать. Боль была на шагах сравнения и покупки. Продавцы существовали, но были разрознены; у покупателей не было сигналов доверия. Маркетплейс с согласованными данными о ценах, проверенными продавцами и удобным оформлением заказа мог захватить эту середину воронки.
Рассмотренные варианты
- Ручной ввод цен администраторами - отклонено. Не масштабируется; цены меняются ежедневно, во время распродаж - иногда ежечасно.
- Сторонний API агрегации цен - для PlayStation/Xbox в СНГ на тот момент такого не существовало.
- Кастомные скраперы на Node.js + витрина на WordPress/WooCommerce - выбрано. Скраперы дают реальное владение данными; WooCommerce даёт проверенный в бою коммерческий слой без создания оформления заказа с нуля.
Решение
Скраперы на Node.js, работающие по cron-расписанию, обращаются к эндпоинтам PlayStation Store и Xbox Store, разбирают ответ и отправляют обновления цен и наличия в WooCommerce через его REST API. WordPress + WooCommerce отвечают за витрину, корзину и интеграции платёжных шлюзов. Кастомные расширения на PHP обрабатывают сценарии подключения продавцов, которые WooCommerce не поддерживал из коробки.

cactusplay.ru, scrollable snapshot of the live storefront
Реализация
Построены скраперы для обеих витрин на основе обратной разработки - с управлением сессиями, переключением регионов и обходом анти-бот мер. Настроена система дифференциальных обновлений: обновления в WooCommerce запускают только изменившиеся цены, что удерживает нагрузку на API низкой. Система оповещений об изменении цен уведомляет администраторов, когда товар опускается ниже порога, позволяя принимать решения о таймингах акций.
Интегрированы платёжные шлюзы, поддерживающие способы оплаты СНГ. Кастомные расширения WooCommerce на PHP отвечали за цифровую доставку (коды ключей) и логику выплат продавцам. Развёрнуто на VPS с автоматическим деплоем через Git-хуки.
Юридическая подготовка к приёму платежей
Приём карт российских банков — вопрос документов раньше, чем вопрос плагина. Платёжный сервис не подключит магазин, у которого нет юридической формы, поэтому форма шла первой: полный комплект документов (публичная оферта, политика обработки персональных данных, согласие на обработку, условия возврата и доставки), регистрация в реестре операторов персональных данных и оформленная самозанятость.
Только после этого встал платёжный контур: эквайринг через плагин банка «Точка» для WooCommerce, рядом с ним LifePay CloudPrint для фискальных чеков — сам эквайринговый плагин их не печатал. Для покупателя важна только видимая часть: оплата прямо с карты российского банка на сайте и чек, который приходит как положено.
Система за витриной
Магазин - лишь одна грань системы. Граббер на Node.js обходит официальный PlayStation Store (и Xbox Store через ту же абстракцию), забирая 20 000+ позиций каждый час. Игры, дополнения и сервисы он перебирает раздельно, переименовывает категории в собственную таксономию магазина и сводит теги сторов - каталог приходит структурированным, а не плоской выгрузкой.
Остальное несут пять кастомных плагинов WordPress:
- slave-api - движок синхронизации. На нём абстракция платформ (единый интерфейс поверх API PlayStation и Xbox), обработчики товаров, меняющие видимость и выводящие SKU из оборота, CLI-команды для полной и частичной синхронизации и версионируемые сценарии обновления - правило каталога раскатывается на живой магазин без ручного прохода.
- product-manager - ценовая сторона витрины. Запрашивает у PlayStation Store актуальные цены в разных валютах, выбирает одну через подключаемую стратегию (штатная берёт наименьшую легальную региональную цену), кэширует результат в transient, чтобы страница товара не ждала внешний вызов, и добавляет нужные процессу кастомные поля в форму товара, корзину, оформление и заказ.
- product-code-manager - половина, отвечающая за цифровую доставку. Генерирует и хранит коды товаров, распознаёт и активирует их, отдаёт API с авторизацией по JWT - заказ, оформленный в любой части системы, выполняется без участия человека. Обработчики реагируют на создание заказа и смену статуса, и именно поэтому код приходит сразу после покупки цифрового товара, а не после ручной отправки.
- aw-auth - вход. Добавляет VK, Яндекс, Steam и Discord в формы входа WordPress и WooCommerce по единому контракту провайдера: каждая новая сеть - это клиент, сервис и экран настроек, а не форк общего сценария. Steam проверяется по подписи OpenID 2.0, остальные - обменом OAuth-кода; вернувшийся посетитель сопоставляется со своим аккаунтом, новый создаётся только если совпадения нет. Колбэк может прийти дважды, поэтому код занимается в таблице-замке со счётчиком обращений: второй запрос дожидается первого и уходит по его редиректу вместо создания дубля. Steam не отдаёт email, и такие пользователи попадают на форму с почтой и телефоном до оформления заказа. Вход разрешён только по email, адрес должен быть на домене .ru по требованиям 152-ФЗ, а приватный кэш страниц сбрасывается при входе, чтобы никто не увидел чужую закэшированную страницу.
- mail-manager - собственный движок рассылок, чтобы клиентская почта не зависела от внешнего сервиса. Аудитории - это категории, кампании привязываются к категориям, а очередь на отправку выводится джойном: получатель, у которого уже есть запись о доставке по кампании, повторно не выбирается, и упавший прогон продолжается ровно с места остановки. Крон отправляет фиксированную порцию с паузой между письмами, удерживая магазин в лимитах SMTP вместо залпа, после которого домен уходит в спам. После активации отправка выключена, а перехватчик пишет письма в лог вместо доставки - копия боевой базы не может написать реальным клиентам; в админке по каждой кампании видно «в очереди / отправлено / ошибка». Колонки открытий и кликов и эндпоинт трекинга заложены - до продакшена дошла именно доставка.
Яндекс.Маркет: сквозная цепочка цены
Интеграция с маркетплейсом замыкает контур. Предложения приводятся к схеме Яндекс.Маркета отдельным слоем мапперов с конфигурацией под каждый магазин, а ценовой сервис держит их в актуальном состоянии. Цепочка работает без человека на любом шаге: цена меняется в официальном PlayStation Store, граббер её забирает, обновляется сайт и затем обновляется предложение на Яндекс.Маркете. Коды отгружаются достаточно быстро, чтобы покупка цифрового товара на маркетплейсе закрывалась в ту же сессию, а карточки заполнены всеми атрибутами, которые Яндекс.Маркет принимает, а не минимумом для публикации.
Сервис стилизации обложек - сделан, но не внедрён
Отдельный сервис на Go собирал обложки под маркетплейс: исходный арт, размытая подложка до нужных пропорций, шапка с брендом магазина и бейджи издания. Он был доведён до результата ниже, но интеграция в конвейер каталога так и не была закончена - обложки уходили нестилизованными.

Поддержка
Поддержка клиентов работает на self-hosted FreeScout по адресу support.cactusplay.ru: веб-чат и Telegram сведены в один ящик, а рядом с перепиской подтягиваются последние заказы клиента - оператор отвечает, не переключаясь между системами.

Собственный почтовый движок магазина
mail-manager - тот самый кампанийный движок, описанный выше, - в работе. Он стоит на CactusPlay и на консалтинговых сайтах: именно поэтому клиентская почта не зависит от внешнего провайдера.
Форма до финальной версии
И форма активации, и инструкция стоят поверх product-code-manager - и на CactusPlay, и на 1gros. Здесь более ранняя, промежуточная версия формы: её записали, чтобы обсудить подход с партнёром и убедиться в его корректности до того, как доводить до конца.
Ещё один заход на ту же раннюю форму, уже с превью: до подтверждения покупатель видит, какой товар он получит по коду. Баги JS на экране позже поправили.
Инструкция по активации кода
Демонстрационное видео для самого сайта: как пользователю активировать код. Стоит на CactusPlay и на консалтинговом 1gros. Такой онбординг сделал процесс понятным и прозрачным - и покупатели перестали жаловаться на него в Яндекс.
Вход через российских провайдеров
Модуль aw-auth, который существует и отдельно, а здесь переиспользован на CactusPlay и на консалтинговых сайтах. Он закрывает OAuth-авторизацию через провайдеров, готовых решений под которых тогда не было: Яндекс, насколько знаю, вообще первый из доступного, плюс VK и Discord. Упор на рынок РФ - со всеми требованиями законодательства по данным.
Моментальная отгрузка для Яндекс.Маркета
Выход на маркетплейс Яндекс.Маркета поставил жёсткое условие: после покупки на отгрузку цифрового товара есть 15 минут. Наш товар активируется на аккаунт, а не отдаётся голым ключом, и в 15 минут это не укладывается.
Решением стала кастомная форма активации ключей. При покупке наш API выдаёт покупателю ключ - по сути отложенное обязательство, - и этим закрывается окно отгрузки Яндекса; сама активация на аккаунт выполняется уже на нашей платформе, закрывая заказ на стороне Яндекса.
Консалтинг для других магазинов
Граббер и наработки вокруг него уходили и в консалтинг. Я применял их на 1gros.ru, ps-store.ru, playstaionplus.ru и xbox-store.ru: синхронизация базы через мой парсер, подключение сторонних магазинов, донастройка WordPress и сервисное обслуживание. На видео один из таких сайтов - 1gros.
Граббер в работе
Граббинг статичных списков товаров с сайта PlayStation - то сырьё, из которого потом собирается каталог.
Вторая половина той же работы: настройка коэффициента цены, который превращает цену магазина в цену, по которой продаёт шоп.
Результат
Граббер держит 20 000+ позиций в час, а изменения цен доходят и до магазина, и до карточки на Яндекс.Маркете без чьего-либо участия. Платформа обрабатывала реальные транзакции с момента запуска, а инфраструктура скрапинга пережила несколько редизайнов фронтенда PlayStation и Xbox Store за счёт точечных обновлений селекторов.
Поддержка: нагрузка от краулеров LLM
В ходе технической поддержки сайта обнаружилась аномально высокая нагрузка, которая не билась с реальным трафиком. По логам нашёлся источник: краулеры популярных LLM, в том числе ClaudeBot, вычитывали каталог. Конфигурацию доработали, их агентов добавили в исключения - посетителей и заказов от них ноль, одна нагрузка.
Открыт для работы по контракту
Я доступен для работы по контракту. Если у вас есть интересная идея проекта, запишитесь на звонок через Calendly.
Записаться на 30-минутный звонок