← Back to all projects

Personal project

CactusPlay - Marketplace for games and in-game items

CactusPlay - Marketplace for games and in-game items

Tech stack

WordpressPHPNodeJSGolangWooCommerceMySQLE-commerceMarketplaceStartup

Services

  • Co-founding and fullstack engineering
  • Node.js scrapers for PlayStation Store and Xbox Store
  • WooCommerce storefront with custom PHP extensions
  • Differential price sync pipeline and alert system
  • Payment gateway and digital delivery integration
  • Yandex.Market integration: offer mapping, price sync and instant code delivery
  • Five custom WordPress plugins: sync engine, pricing, digital codes, social sign-in, campaigns
  • Go cover-styling service and self-hosted FreeScout support desk

Deliverables

  • Gaming marketplace with thousands of live SKUs across PS and Xbox
  • Automated daily price sync, with zero manual updates
  • Price-change alert system for promotional opportunity detection
  • CIS payment gateway integrations with digital key delivery
  • End-to-end price chain: PS Store, site and then Yandex.Market, no manual step
  • Fully attributed Yandex.Market product cards

Infrastructure and Capabilities Walkthrough

A walkthrough of the running system: the WooCommerce storefront, the catalogue the grabber keeps filled, the admin side where prices and orders are handled, and how the pieces fit together. Commentary in Russian.

Challenge

CIS gamers buying PlayStation and Xbox titles faced a fragmented market: official store prices varied significantly by region, gift card resellers were scattered across forums, and there was no single place to compare and buy. Co-founded CactusPlay to fill that gap, a marketplace for games and in-game items with real pricing data from official sources.

The core technical challenge: PlayStation Store and Xbox Store don't expose public APIs. Keeping thousands of SKUs priced accurately in near-real-time required building a scraping and synchronisation layer from scratch.

Discovery

Initial research mapped the full buyer journey: discover a game, compare price across regions, buy the cheapest legitimate key, redeem. The pain was at the compare and buy steps. Sellers existed but were fragmented; buyers had no trust signals. A marketplace with consistent pricing data, verified sellers, and a clean checkout could own that middle of the funnel.

Options Considered

  1. Manual price entry by admins: rejected. Doesn't scale; prices change daily, sometimes hourly during sales.
  2. Third-party price aggregation API, none existed for CIS PlayStation/Xbox at the time.
  3. Custom Node.js scrapers + WordPress/WooCommerce storefront: chosen. Scrapers give real data ownership; WooCommerce gives a battle-tested commerce layer without building checkout from scratch.

Decision

Node.js scrapers running on a cron schedule hit PlayStation Store and Xbox Store endpoints, parse the response, and push price/availability updates to WooCommerce via its REST API. WordPress + WooCommerce handles the storefront, cart, and payment gateway integrations. Custom PHP extensions handle seller onboarding flows that WooCommerce didn't support out of the box.

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

cactusplay.ru, scrollable snapshot of the live storefront

Implementation

Built reverse-engineered scrapers for both storefronts, handling session management, region switching, and anti-bot measures. Set up a differential update system: only changed prices trigger WooCommerce updates, keeping API load low. A price-change alert system notifies admins when a product drops below a threshold, enabling promotional timing decisions.

Integrated payment gateways supporting CIS payment methods. Custom PHP WooCommerce extensions handled digital delivery (key codes) and seller payout logic. Deployed on a VPS with automated deployment via Git hooks.

Legal groundwork for taking payments

Accepting Russian bank cards was a paperwork problem before it was a plugin problem. No provider signs up a shop that has no legal shape, so the shape came first: the full document set (public offer, privacy policy, consent to personal data processing, refund and delivery terms), registration in the state register of personal data operators, and registered self-employed status to invoice against.

Only then did the payment stack go in: acquiring through Tochka bank's WooCommerce plugin, with LifePay CloudPrint beside it to issue fiscal receipts, which the acquiring plugin did not do on its own. What the buyer sees is the part that matters: paying straight from a Russian bank card, on the site, and getting the receipt the law requires.

The system behind the storefront

The shop is one face of a larger system. A Node.js grabber walks the official PlayStation Store, and the Xbox Store on the same abstraction, pulling 20,000+ positions every hour. It iterates games, DLC and services separately, renames categories to the shop's own taxonomy, and merges store tags, so the catalogue arrives structured rather than as a flat dump.

Five custom WordPress plugins carry the rest:

  • slave-api. The synchronisation engine. It owns the platform abstraction (one interface over the PlayStation and Xbox APIs), the product handlers that change visibility or retire SKUs, CLI commands for full and partial syncs, and versioned upgrade scenarios so a catalogue rule can be rolled out across a live shop without a manual pass.
  • product-manager. The storefront side of pricing. It queries the PlayStation Store for live prices across currencies, picks one through a pluggable price strategy (the shipped one takes the lowest legitimate regional price), caches the result in transients so a product page never waits on an upstream call, and injects the custom fields the flow needs into the product form, cart, checkout and order.
  • product-code-manager. The digital delivery half. It generates and stores product codes, resolves and activates them, and exposes a JWT-authenticated API so an order placed anywhere in the system can be fulfilled without a person in the loop. Order handlers react to creation and status changes, which is what makes a code arrive immediately after a digital purchase instead of after a manual dispatch.
  • aw-auth, sign-in. It puts VK, Yandex, Steam and Discord on the WordPress and WooCommerce login forms behind one provider contract, so each new network is a client, a service and a settings screen rather than a fork of the flow. Steam is verified against OpenID 2.0 signatures, the rest through an OAuth code exchange; a returning visitor is matched to their existing account and a new one is created only when no match exists. Because a callback can arrive twice, each code is claimed in a lock table with a hit counter. The second request waits for the first and follows its redirect instead of minting a duplicate account. Steam hands over no email address, so those users land on a details form for email and phone before checkout. Login is email-only by policy, addresses must sit on a .ru domain to satisfy Russian data residency rules, and the private page cache is purged on login so nobody meets a stale signed-out page.
  • mail-manager. The shop's own campaign engine, so customer mail did not depend on an outside provider. Audiences are categories, campaigns attach to categories, and the pending queue is derived by join: a recipient who already has a delivery row for a campaign is never picked again, so an interrupted run resumes exactly where it stopped. A cron tick sends a fixed batch with a pause between messages, keeping the shop inside the SMTP rate limit instead of firing a burst that gets the domain flagged. Sending starts disabled after activation and an interceptor logs messages instead of delivering them, so a copy of the production database cannot email real customers; the admin table reports pending, sent and failed per campaign. Open and click columns and a tracking endpoint are scaffolded, delivery is the part that shipped.

Yandex.Market: one price chain, end to end

The marketplace integration closes the loop. Offers are mapped to Yandex.Market's schema through a dedicated mapper layer, with per-store configuration, and a price service keeps them current. The chain runs without a human at any step: a price changes in the official PlayStation Store, the grabber picks it up, the site updates and then the Yandex.Market offer updates. Codes are shipped fast enough that a digital purchase on the marketplace is fulfilled in the same session, and the product cards carry every attribute Yandex.Market accepts rather than the minimum required to publish.

Cover styling service, built, not shipped

A separate Go service composed marketplace-ready covers: original art, a blurred bleed to fill the required aspect, a shop-branded header and edition badges. It was finished and produced the result below, but the integration into the catalogue pipeline was never completed. The covers went out unstyled.

Marketplace cover generated by the Go styling service, blurred bleed, shop header and edition badges

Support desk

Customer support runs on a self-hosted FreeScout at support.cactusplay.ru, taking web chat and Telegram into one inbox, with the customer's recent orders pulled in beside the conversation so an agent answers without switching systems.

FreeScout support desk with web chat and Telegram in one inbox and the customer's recent orders alongside

The shop’s own mail engine

mail-manager, the campaign engine described above, in use. It runs on CactusPlay and on the consulting sites, which is what keeps customer mail off an outside provider.

The form before it was final

Both the activation form and the guide sit on top of product-code-manager, on CactusPlay and on 1gros alike. This is an earlier, intermediate version of the form, recorded to walk the partner through the approach and check it was the right one before building it out.

Another shot of that early form, this time with the preview in it: before confirming, the buyer sees which product the code will give them. The JS bugs on screen were fixed later.

Code activation guide for buyers

A demo video put on the site itself, walking the buyer through activating their code. It runs on CactusPlay and on the 1gros consulting build. Onboarding people this way made the process clear enough that they stopped filing complaints with Yandex over it.

Sign-in through Russian providers

The aw-auth module, shipped separately and reused on CactusPlay and on the consulting sites. It covers OAuth sign-in through providers nobody had ready-made solutions for at the time - Yandex was, as far as I know, the first one available - plus VK and Discord. Built for the Russian market, with the data residency rules that come with it.

Instant delivery for Yandex.Market

Listing on the Yandex.Market marketplace came with a hard constraint: once someone buys, there are 15 minutes to deliver the digital goods. Our products are activated onto an account rather than handed over as a bare key, and that does not fit into 15 minutes.

The way through was a custom key activation form. On purchase our API issues the buyer a key that is really a deferred obligation, which satisfies the Yandex delivery window; the activation onto the account is then carried out on our own platform, closing the order on the Yandex side.

Consulting for other shops

The grabber and the solutions around it went out as consulting too. I applied them on 1gros.ru, ps-store.ru, playstaionplus.ru and xbox-store.ru: syncing the catalogue database through my own parser, plugging in third-party shops, tuning their WordPress, and keeping the whole thing serviced. The video shows one of those sites, 1gros.

The Grabber in Action

Grabbing the static product listings off the PlayStation site, the raw material the catalogue is built from.

The other half of the same job: setting the price coefficient that turns a store price into the price the shop sells at.

Outcome

The grabber sustains 20,000+ positions an hour, and price changes reach both the shop and the Yandex.Market listing without anyone touching them. The platform handled real transactions from launch, and the scraping infrastructure survived multiple PlayStation and Xbox Store front-end redesigns through targeted selector updates.

Support: LLM Crawler Load

During ongoing technical support the site showed abnormally high load that did not match the real traffic. The logs traced it to crawlers from the popular LLM vendors, ClaudeBot among them, working through the catalogue. The configuration was adjusted and their agents were added to the exclusion rules: they brought no visitors and no orders, only load.

Open for contract collaboration

I am available for contract-based collaboration. If you have an interesting project idea, schedule a call via Calendly.

Schedule a 30-min call