Introduction

Connecting an e-commerce storefront to an enterprise resource planning (ERP) system is no longer optional—it’s foundational for inventory accuracy, financial reconciliation, and automated order fulfillment. With Shopware 6.7, this process has become significantly more structured thanks to a modernized event architecture, refined API security, and native support for asynchronous message handling. This guide walks you through the architectural decisions, integration patterns, and practical implementation steps required to build a reliable, production-ready ERP sync layer on Shopware 6.7 or newer.

Architectural Foundations

Before writing a single line of code, define your synchronization boundaries. Most enterprises use one of three models:

  • One-way push: Shopware → ERP (orders, stock updates)
  • One-way pull: ERP → Shopware (product master data, pricing tiers)
  • Bidirectional sync: Both directions with conflict resolution

Shopware 6.7 encourages a decoupled approach. Instead of blocking HTTP requests during sync, leverage the built-in Symfony Messenger component to queue operations. This protects storefront performance during ERP latency spikes and provides automatic retry capabilities. Centralize your integration logic in a dedicated Shopware plugin rather than scattering calls across themes or custom modules.

Authentication & API Access

Shopware 6.7 enforces strict API security by default. Machine-to-machine communication should use the OAuth2 Client Credentials flow or JWT tokens assigned to an integration user. Create a dedicated system user via the Admin Panel with scopes limited to write:product, write:order, and read:stock. Store credentials in environment variables, never in version control.

On the ERP side, ensure your endpoints support HTTPS, rate limiting headers, and idempotency keys. Shopware’s Http\Client implementation is PSR-18 compliant, making it straightforward to configure retry policies and timeout handling. Always validate ERP response payloads against a strict schema (JSON Schema or OpenAPI) before persisting data into Shopware entities.

Data Mapping & Synchronization Patterns

Successful integrations hinge on consistent ID mapping. Never rely on Shopware’s auto-incremented primary keys as your source of truth across systems. Instead, create a custom entity erp_id_mapping with fields like shopware_entity_type, shopware_uuid, and erp_reference_id. Run this via a migration in your plugin’s migrations/ directory.

For product data, map attributes carefully. ERP systems often use internal SKU, EAN, or PLU codes that must be stored as Shopware custom fields. Use the Context service to preserve tenant isolation when fetching or writing entities:

$repository = $this->container->get('product.repository');
$criteria->addFilter(new EqualsFilter('customFields.erp_sku', 'ERP-8821'));
$product = $repository->search($criteria, $context)->first();

Orders require special attention. Financial data should never be rewritten by Shopware; instead, mark ERP-synced orders as processed or push fulfillment webhooks back to the ERP only after payment confirmation. Always version your API contracts and document field mappings in a shared Confluence or markdown file.

Real-Time Event Handling

Shopware 6.7 replaces legacy hooks with Symfony’s event system. Subscribe to critical lifecycle events using attributes:

use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
use Shopware\Core\Checkout\Order\Events\OrderEvents;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener(event: OrderEvents::ORDER_WRITTEN)]
public function onOrderCreated(EntityWrittenEvent $event): void
{
    if (!$event->has('order')) return;
    
    foreach ($event->getPrimaryKeys() as $orderId) {
        $this->messageBus->dispatch(new SyncOrderToErp($orderId));
    }
}

The SyncOrderToErp message class should be handled by an async service that constructs the ERP payload, attaches an idempotency header (X-Idempotency-Key: {$orderId}), and dispatches via Guzzle. Use dead-letter queues for failures that exceed retry thresholds, and log context-aware errors with Context::getScope() to avoid cross-tenant leakage.

Production Best Practices

  • Idempotency: Every ERP endpoint must tolerate duplicate payloads. Verify against stored hashes or use unique keys.
  • Backpressure: Throttle batch operations during peak hours using Shopware’s CLI scheduler or a cron layer.
  • Observability: Instrument sync jobs with OpenTelemetry traces. Track queue depth, latency percentiles, and failure rates.
  • Sandbox First: Mirror your ERP test environment and use dummy SKUs during UAT. Validate rollback procedures before go-live.
  • Version Pinning: Lock API response structures to specific ERP versions. Break changes require migration paths, not hotfixes.

Conclusion

Integrating Shopware 6.7 with a third-party ERP is less about wiring APIs together and more than designing a resilient data exchange layer. By leveraging modern authentication, decoupled message handling, strict ID mapping, and observability-first practices, you transform integration from a fragile bridge into a scalable pipeline. Plan your sync boundaries early, iterate in phases, and let Shopware 6.7’s event-driven architecture do the heavy lifting. The result is a storefront that feels instantaneous to customers while staying tightly synchronized with enterprise operations behind the scenes.