E-commerce platforms process highly sensitive personal data daily, making compliance with GDPR, CCPA, and regional privacy regulations a legal necessity rather than a checkbox feature. While Shopware 6.7 ships with an improved Consent Management System, built-in anonymization routines, and enhanced data portability tools, real-world business environments frequently demand customized privacy pipelines. Complex retention policies, multi-jurisdictional consent hierarchies, or legacy system integrations often exceed the out-of-the-box capabilities. This guide explains how to design and implement custom GDPR/Data Privacy workflows in Shopware 6.7 by leveraging its modern event architecture, dependency injection, and message queue ecosystem.
Why Build a Custom Privacy Workflow?
Shopware’s native privacy features handle standard use cases efficiently: consent logging, address anonymization upon request, and JSON/CSV data exports. However, enterprises often require:
- Tiered consent states (marketing vs. analytics vs. third-party tracking)
- Conditional retention periods based on purchase history or legal obligations
- Automated archival workflows that comply with fiscal documentation laws while satisfying privacy erasure requests
- Cross-system synchronization (ERP, CRM, CDP) during data portability operations
Hardcoding these rules into controllers or plugins quickly violates separation of concerns and creates maintenance debt. Shopware 6.7 solves this through a highly decoupled event dispatcher and an extensible workflow component designed for domain-specific business logic.
Leveraging Shopware 6.7’s Architecture
Shopware 6.7 modernizes privacy implementation in three critical ways:
- Attribute-based service registration reduces boilerplate configuration while maintaining strict DI boundaries.
- Enhanced DAL (Data Abstraction Layer) provides safer entity extension patterns, allowing you to append privacy metadata without altering core tables.
- Improved async message handling enables bulk data export or anonymization without blocking the web worker process.
Instead of directly manipulating Doctrine entities during consent revocation, you should dispatch domain events and let dedicated subscribers handle state transitions, logging, and queue routing.
Step 1: Extending Entities for Privacy Metadata
Privacy workflows require reliable state tracking. Shopware 6.7 encourages entity extension via migrations rather than raw SQL. Create a custom field set to store consent timestamps, processing purposes, and archival status:
// src/Migration/Vxxxx/GdprConsentExtension.php
namespace YourPlugin\Migration\Vxxxx;
use Doctrine\DBAL\Types\Types;
use Shopware\Core\Framework\Migration\MigrationStep;
class VxxxxGdprConsentExtension extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1710000000;
}
public function update(): void
{
$this->addSql(
'ALTER TABLE `customer` ADD `gdpr_consent_history` TEXT NULL,
ADD `data_exported_at` DATETIME NULL,
ADD `retention_expiry` DATETIME NULL'
);
}
}
In the Administration, these fields become accessible via the Entity API and can be mapped to custom input components using the @ShopwareAdministration module system. Never modify core tables directly; always use this extension pattern to preserve upgrade compatibility.
Step 2: Building the Anonymization Subscriber
When a customer withdraws consent or submits an erasure request, your workflow should trigger safely. Below is a Shopware 6.7-compatible subscriber that processes anonymization tasks, respects database transactions, and logs actions immutably.
// src/Subscriber/GdprAnonymizationSubscriber.php
namespace YourPlugin\Subscriber;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class GdprAnonymizationSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly EntityRepository $customerRepository,
private readonly Connection $connection
) {}
public static function getSubscribedEvents(): array
{
return [
'gdpr.consent.revoked' => 'onConsentRevocation',
'gdpr.data.anonymize' => 'onDataAnonymization',
];
}
public function onConsentRevocation(EntityWrittenEvent $event): void
{
if ($event->getPrimaryKeys() !== ['customer']) {
return;
}
// Dispatch async task for heavy operations (export/archival)
// In practice, you'd push to shopware:message:consume here
$this->connection->insert('gdpr_audit_log', [
'id' => \Ramsey\Uuid\Uuid::uuid4()->toString(),
'customer_id' => current($event->getPrimaryKeys()),
'action' => 'consent_revoked',
'created_at' => date('Y-m-d H:i:s'),
]);
}
public function onDataAnonymization(EntityWrittenEvent $event): void
{
foreach ($event->getPrimaryKeys() as $customerId) {
// Anonymize address book while preserving order referential integrity
$this->connection->update('address', [
'first_name' => '', 'last_name' => '',
'company' => '', 'street' => '', 'city' => '',
'zip_code' => '', 'country_id' => null,
], ['customer_id' => $customerId]);
// Update custom privacy fields via DAL
$this->customerRepository->update([[
'id' => $customerId,
'customFields' => [
'gdpr_consent_history' => json_encode(array_merge(
(array) json_decode($this->getExistingHistory($customerId), true) ?? [],
[['purpose' => 'all_marketing', 'status' => 'revoked', 'at' => date('c')]]
)),
'data_exported_at' => null,
'retention_expiry' => date('Y-m-d H:i:s', strtotime('+7 years')),
],
]]);
// Log for compliance verification
$this->connection->insert('gdpr_audit_log', [
'id' => \Ramsey\Uuid\Uuid::uuid4()->toString(),
'customer_id' => $customerId,
'action' => 'data_anonymized',
'created_at' => date('Y-m-d H:i:s'),
]);
}
}
private function getExistingHistory(string $customerId): ?string
{
$row = $this->connection->fetchOne(
'SELECT gdpr_consent_history FROM customer WHERE id = :id',
['id' => $customerId]
);
return $row ?: null;
}
}
Step 3: Triggering & Async Routing
In Shopware 6.7, you typically expose a custom Administration route or CLI command that validates the request, verifies legal grounds (e.g., consent withdrawal confirmation), and dispatches the gdpr.data.anonymize event. For bulk operations, never execute anonymization synchronously on the web thread. Instead, serialize the customer ID list into the message queue:
// In your Service or Route Handler
use Shopware\Core\Framework\MessageQueue\MessageName;
use Symfony\Component\Messenger\Stamp\DelayStamp;
$message = new GdprAnonymizeMessage($customerIds);
$this->messageBus()->dispatch(
$message,
[new DelayStamp(0)] // or schedule via cron if needed
);
Ensure your plugin registers the message handler in services.xml and that shopware:message:consume is running. This architecture prevents timeout failures during large data exports and aligns with Shopware 6.7’s recommended infrastructure patterns.
Compliance & Testing Best Practices
- Immutability: Audit logs must never be overwritten. Use append-only tables or write-once storage solutions for legal verification.
- Idempotency: GDPR requests may repeat due to network retries or manual resubmission. Always check
gdpr_consent_historybefore processing to avoid duplicate anonymization attempts. - Field Isolation: Keep privacy metadata separate from behavioral analytics fields. Use distinct custom field namespaces (
gdpr_,privacy_) to prevent accidental exposure in reporting layers. - Testing: Write PHPUnit tests using
DatabaseTestCaseand mock the event dispatcher. Verify that addresses are zeroed out, custom fields update correctly, and audit entries persist across transaction boundaries. - Legal Alignment: Technical implementation must match policy. Define retention windows explicitly, consult legal counsel on cross-border data transfers, and document every workflow stage in your internal compliance matrix.
Conclusion
Custom GDPR/Data Privacy workflows in Shopware 6.7 thrive when built alongside the framework’s event system, modern DAL extensions, and async message routing rather than against it. By structuring consent states as first-class domain events, isolating privacy metadata through entity extensions, and delegating heavy operations to the message queue, you create a compliant, scalable pipeline that withstands regulatory scrutiny and business growth. Privacy engineering isn’t about restricting functionality; it’s about enforcing transparency, retention clarity, and user control at the architectural level. Build your workflows with auditability as a first-class concern, test them rigorously, and remember that GDPR compliance is an ongoing operational discipline—not a one-time plugin deployment.