In modern e-commerce, inventory accuracy is the backbone of operational integrity. Overselling out-of-stock items leads to cancelled orders, negative customer experiences, and increased support costs. Whether you are connecting Shopware to an ERP, WMS, PIM, or marketplaces like Amazon and eBay, seamless synchronization is essential.

Shopware 6.7 introduces significant enhancements to data consistency, event handling, and API performance. This blog post explores the architecture of inventory sync in Shopware 6.7, practical implementation strategies, and best practices for building robust integrations.

Understanding Stock Architecture in Shopware 6.7

Before implementing synchronization, it is vital to grasp how Shopware 6.7 manages stock. Inventory is not merely a scalar value; it is governed by the Product entity with associated availability logic:

  1. Raw Stock vs. Available Stock: The stock field represents the raw quantity on hand. However, customers see available_stock, which is calculated based on the availability_rule_id. A product may have stock but be marked unavailable due to rules or sales channel restrictions.
  2. Sales Channel Context: Stock can be defined globally or per sales channel. Changes in one channel may or may not affect others depending on configuration.
  3. Reservation Logic: During checkout, Shopware reserves stock temporarily. Understanding when stock changes versus when reservations occur is critical to avoid race conditions during sync.

Shopware 6.7 leverages a strict event-driven architecture. The Data Abstraction Layer (DAL) triggers specific events for stock modifications, allowing developers to hook into these changes without modifying core files. This ensures your integrations remain upgrade-safe and performant.

Strategy 1: Event-Driven Synchronization with Message Queue

The gold standard for real-time sync in Shopware 6.7 is subscribing to the ProductStockChangedEvent and offloading processing via the Messenger component. Direct API calls within event subscribers can block admin requests and degrade performance under load. Instead, dispatch messages to an async worker.

Implementation Guide

Create a subscriber that listens to stock changes and pushes tasks to the message queue. Shopware 6.7 enforces typed events for better performance and type safety.

declare(strict_types=1);

namespace YourPlugin\Subscriber;

use Psr\Log\LoggerInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\ProductStockChangedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use YourPlugin\Message\SyncInventoryMessage;

class InventorySyncSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private readonly LoggerInterface $logger,
        private readonly MessageBusInterface $messageBus,
        private readonly array $ignoredSalesChannels = []
    ) {}

    public static function getSubscribedEvents(): array
    {
        // Shopware 6.7 requires typed event return keys for optimal performance
        return [
            ProductStockChangedEvent::class => 'onProductStockChanged',
        ];
    }

    /**
     * Handles stock changes triggered by Admin updates, API calls, or cart operations.
     */
    public function onProductStockChanged(ProductStockChangedEvent $event): void
    {
        $changes = $event->getChanges();
        if (empty($changes)) {
            return;
        }

        // Iterate over affected products and dispatch async messages
        foreach ($changes as $productId => $stockData) {
            // Validate stock data structure to avoid processing nulls or incomplete payloads
            if (!isset($stockData['new']['quantity']) || !isset($stockData['new']['available'])) {
                continue;
            }

            try {
                $this->messageBus->dispatch(
                    new SyncInventoryMessage(
                        productId: $productId,
                        rawStock: (int) $stockData['new']['quantity'],
                        availableStock: (int) $stockData['new']['available'],
                        timestamp: new \DateTimeImmutable()
                    )
                );
            } catch (\Throwable $e) {
                // Log failure to dispatch but do not break the admin request
                $this->logger->error('Failed to enqueue inventory sync for product ' . $productId, ['exception' => $e]);
            }
        }
    }
}

Messenger Configuration Best Practices

In Shopware 6.7, ensure your messenger.yaml is configured to use a reliable transport like Redis or RabbitMQ in production:

framework:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'
            failed: 'doctrine://default?queue_name=failed'
        
        routing:
            'YourPlugin\Message\SyncInventoryMessage': async

This configuration ensures inventory updates are processed asynchronously, preventing admin latency spikes during bulk stock adjustments. Failed messages are routed to a dead-letter queue for manual inspection.

Strategy 2: Webhooks for External Integrations

If your external system cannot execute PHP code but can accept HTTP POST requests, Shopware 6.7's webhook system provides a lightweight alternative. You can configure webhooks via the Admin dashboard or API to trigger on product.stock.changed.

Webhook Configuration and Security

  1. Setup: Navigate to Settings > Webhooks in the Admin panel. Create a new webhook targeting your external endpoint with the event type product.stock.changed.
  2. Payload Structure: The payload includes the product ID, updated stock values, and contextual data. Ensure your receiver parses the stock object carefully, noting that it may contain channel-specific data if relevant.
  3. Signature Verification: Security is paramount. Shopware webhooks support signature verification. Configure a secret key in the webhook settings and validate the X-Shopware-Signature header on your receiving endpoint to prevent spoofing.
  4. Retries: Webhooks may fail due to network issues. Ensure your external system acknowledges receipt immediately and implements exponential backoff for retries if it initiates callbacks back to Shopware (e.g., confirming sync status).

Strategy 3: REST API Polling and Bulk Operations

While event-driven approaches are preferred, some legacy systems require polling. Shopware 6.7 improves API efficiency, but developers must still implement smart polling strategies.

Efficient Fetching Strategies

When fetching inventory from an external system into Shopware, avoid iterating over all products. Use batch filters and associations:

POST /api/search/product
{
    "filter": [
        {
            "type": "equalsAny",
            "field": "id",
            "values": ["uuid-batch-1", "uuid-batch-2"]
        }
    ],
    "associations": {
        "stock": {}
    },
    "fields": [
        "id",
        "stock",
        "available_stock",
        "availability_rule_id"
    ]
}
  • Pagination: Use limit and offset to handle large catalogs. Monitor rate limits closely; Shopware 6.7 allows customizing rate limit policies if necessary, but staying within defaults ensures stability.
  • Bulk Updates: If pushing updates to Shopware from an external system, use the /api/bulk/update/product endpoint to minimize HTTP overhead. This is particularly useful for synchronizing thousands of SKUs during nightly batch jobs.

Shopware 6.7 Specific Considerations and Pitfalls

When building integrations in 6.7, keep these nuances in mind to avoid common traps:

  1. Availability Rules: Syncing only quantity is insufficient. Your external system must also respect availability_rule_id. If a product has stock but the rule sets it to unavailable, the sync payload should reflect zero availability to prevent discrepancies.
  2. Race Conditions: A critical issue in inventory sync is the conflict between checkout reservations and backend updates. If your external system reduces stock while a user completes a cart operation, you may hit DAL constraints. Ensure your sync logic checks can_purchase flags and handles optimistic locking exceptions gracefully.
  3. Typed Events: Shopware 6.7 removes support for untyped event listeners in strict mode. Always type-hint your subscriber methods (e.g., ProductStockChangedEvent $event) to leverage IDE support and performance gains.
  4. Idempotency: External systems may receive duplicate events due to messenger retries or webhook failures. Implement idempotency by tracking sync versions or using unique identifiers on the receiver side to prevent double-processing orders or stock deductions.
  5. Testing Tools: Shopware 6.7 includes CLI commands to simulate events for testing. Use bin/sw event:dispatch to trigger inventory changes locally and verify your subscriber logic without manipulating database data directly.

Conclusion

Synchronizing inventory between Shopware 6.7 and external systems requires a blend of robust event handling, performance optimization, and clear data modeling. By leveraging the ProductStockChangedEvent, utilizing typed subscribers, and offloading heavy operations via the Message Queue, you can build an integration that scales with your business.

Choose the strategy that fits your infrastructure: Event-Driven + Messenger for high-performance real-time sync, Webhooks for decoupled external notifications, or API Polling for legacy compatibility. Always prioritize async processing, implement idempotency, and account for availability rules to ensure accuracy. With Shopware 6.7's refined architecture, achieving seamless inventory consistency is more accessible than ever, enabling you to focus on growing your business rather than managing data discrepancies.