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:
- Raw Stock vs. Available Stock: The
stockfield represents the raw quantity on hand. However, customers seeavailable_stock, which is calculated based on theavailability_rule_id. A product may have stock but be marked unavailable due to rules or sales channel restrictions. - 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.
- 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
- Setup: Navigate to Settings > Webhooks in the Admin panel. Create a new webhook targeting your external endpoint with the event type
product.stock.changed. - Payload Structure: The payload includes the product ID, updated stock values, and contextual data. Ensure your receiver parses the
stockobject carefully, noting that it may contain channel-specific data if relevant. - Signature Verification: Security is paramount. Shopware webhooks support signature verification. Configure a secret key in the webhook settings and validate the
X-Shopware-Signatureheader on your receiving endpoint to prevent spoofing. - 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
limitandoffsetto 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/productendpoint 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:
- 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. - 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_purchaseflags and handles optimistic locking exceptions gracefully. - 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. - 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.
- Testing Tools: Shopware 6.7 includes CLI commands to simulate events for testing. Use
bin/sw event:dispatchto 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.