← Назад ко всем проектам

Личный проект

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

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

Технологии

WordpressPHPNodeJSGolangWooCommerceMySQLE-commerceMarketplaceStartup

Услуги

  • Сооснование и 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 с точными ценами почти в реальном времени, пришлось строить слой скрапинга и синхронизации с нуля.

Исследование

Первичное исследование описало весь путь покупателя: найти игру, сравнить цену по регионам, купить самый дешёвый легальный ключ, активировать. Боль была на шагах сравнения и покупки. Продавцы существовали, но были разрознены; у покупателей не было сигналов доверия. Маркетплейс с согласованными данными о ценах, проверенными продавцами и удобным оформлением заказа мог захватить эту середину воронки.

Рассмотренные варианты

  1. Ручной ввод цен администраторами - отклонено. Не масштабируется; цены меняются ежедневно, во время распродаж - иногда ежечасно.
  2. Сторонний API агрегации цен - для PlayStation/Xbox в СНГ на тот момент такого не существовало.
  3. Кастомные скраперы на Node.js + витрина на WordPress/WooCommerce - выбрано. Скраперы дают реальное владение данными; WooCommerce даёт проверенный в бою коммерческий слой без создания оформления заказа с нуля.

Решение

Скраперы на Node.js, работающие по cron-расписанию, обращаются к эндпоинтам PlayStation Store и Xbox Store, разбирают ответ и отправляют обновления цен и наличия в WooCommerce через его REST API. WordPress + WooCommerce отвечают за витрину, корзину и интеграции платёжных шлюзов. Кастомные расширения на PHP обрабатывают сценарии подключения продавцов, которые WooCommerce не поддерживал из коробки.

aA🔒cactusplay.ru/
+
Главная

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 собирал обложки под маркетплейс: исходный арт, размытая подложка до нужных пропорций, шапка с брендом магазина и бейджи издания. Он был доведён до результата ниже, но интеграция в конвейер каталога так и не была закончена - обложки уходили нестилизованными.

Обложка для маркетплейса, собранная сервисом на Go - размытая подложка, шапка магазина и бейджи издания

Поддержка

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

Служба поддержки на FreeScout: веб-чат и 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-минутный звонок