Stock and price synchronization between website and marketplaces

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Stock and Price Synchronization Between Website and Marketplaces

When a store sells simultaneously through its website and multiple marketplaces, inventory management becomes critical: if an item sells on the website, stock needs to decrease on Ozon and Wildberries; when new goods arrive, inventory must be updated everywhere. Price synchronization ensures price parity or markup across marketplaces.

System Architecture

Source of Truth (Main Warehouse / Website)
    ↓
Stock Manager Service
    ├── Reservation on website order
    ├── Release on cancellation
    └── Replenishment on goods receipt
         ↓
    Sync Queue (Redis)
         ↓
    Marketplace Workers
    ├── Ozon Worker    → Ozon API
    ├── WB Worker      → WB API
    └── YM Worker      → Yandex.Market API

Available Stock Calculation

class StockCalculator
{
    public function getAvailableForMarketplace(int $productId, string $marketplace): int
    {
        $product = Product::with(['reservations', 'warehouseItems'])->findOrFail($productId);

        $totalStock    = $product->warehouseItems->sum('quantity');
        $reservedSite  = $product->reservations()->where('source', 'site')->sum('quantity');
        $reservedOther = $product->reservations()->where('source', '!=', $marketplace)->sum('quantity');

        // Marketplaces can see no more than 80% of available stock
        $available = $totalStock - $reservedSite - $reservedOther;
        return max(0, (int)($available * 0.8));
    }
}

The 0.8 coefficient protects against situations where multiple marketplaces see the full inventory and simultaneously accept orders.

Synchronization Queue

class StockSyncQueue
{
    public function enqueue(int $productId): void
    {
        // Deduplication: if already in queue — update timer
        Redis::setex("sync:pending:{$productId}", 30, 1);
    }

    public function processQueue(): void
    {
        // Batching: collect all changes over 30 seconds and send as batch
        $keys      = Redis::keys('sync:pending:*');
        $productIds = array_map(fn($k) => (int)explode(':', $k)[2], $keys);

        if (empty($productIds)) return;

        Redis::del($keys);
        $this->syncToMarketplaces($productIds);
    }
}

Price Synchronization

class PriceSyncService
{
    private array $marketplacePriceRules = [
        'ozon'    => ['type' => 'markup', 'value' => 5.0],   // +5%
        'wb'      => ['type' => 'markup', 'value' => 7.0],   // +7%
        'ym'      => ['type' => 'fixed',  'value' => 0],     // no markup
    ];

    public function calculateMarketplacePrice(float $basePrice, string $marketplace): float
    {
        $rule = $this->marketplacePriceRules[$marketplace];

        return match($rule['type']) {
            'markup' => round($basePrice * (1 + $rule['value'] / 100), 0),
            'fixed'  => $basePrice + $rule['value'],
            default  => $basePrice,
        };
    }
}

Handling Overlapping Orders

class OrderProcessor
{
    public function process(Order $order): void
    {
        DB::transaction(function () use ($order) {
            foreach ($order->items as $item) {
                $reserved = ProductReservation::create([
                    'product_id' => $item->product_id,
                    'quantity'   => $item->quantity,
                    'source'     => $order->source,  // 'site', 'ozon', 'wb'
                    'order_id'   => $order->id,
                ]);

                // Check if physical stock is not exceeded
                $totalReserved = ProductReservation::where('product_id', $item->product_id)->sum('quantity');
                $actualStock   = WarehouseItem::where('product_id', $item->product_id)->sum('quantity');

                if ($totalReserved > $actualStock) {
                    throw new InsufficientStockException($item->product_id);
                }
            }

            // Queue job to reduce inventory on all platforms
            StockSyncJob::dispatch($order->items->pluck('product_id')->unique()->all());
        });
    }
}

Discrepancy Monitoring

We periodically reconcile actual marketplace inventory with our data:

class StockDiscrepancyChecker
{
    public function check(): array
    {
        $discrepancies = [];
        $ozonStocks = $this->ozon->getAllStocks();

        foreach ($ozonStocks as $ozonItem) {
            $ourStock = $this->calculator->getAvailableForMarketplace($ozonItem['offer_id'], 'ozon');

            if (abs($ourStock - $ozonItem['stock']) > 1) {
                $discrepancies[] = [
                    'sku'      => $ozonItem['offer_id'],
                    'our'      => $ourStock,
                    'ozon'     => $ozonItem['stock'],
                    'delta'    => $ourStock - $ozonItem['stock'],
                ];
            }
        }

        return $discrepancies;
    }
}

Timeline

Stock and price synchronization system for 2–3 marketplaces: 14–20 business days.