Data export, erasure concept and consent management implemented technically
Unlike Adobe Commerce, Magento Open Source ships no built-in tool for the right of access or erasure, even though both rights are clearly regulated under the GDPR. A custom data export service, an anonymization concept that respects retention obligations, and traceable consent management can be cleanly anchored in the shop with PHP 8.4, service contracts and a dedicated cron job.
Table of Contents
- 1. Legal framework and the gap in Magento Open Source
- 2. Identifying personal data in Magento
- 3. Implementing the right of access technically
- 4. Erasure concept and the conflict with retention obligations
- 5. Anonymization implementation
- 6. Consent management
- 7. Automated erasure deadlines via cron
- 8. Audit trail and proof obligation
- 9. Comparison: requirement vs. technical implementation vs. risk
- 10. Summary
- 11. FAQ
1. Legal framework and the gap in Magento Open Source
GDPR compliance in a Magento shop centers on three articles: Art. 15 governs the right of access, meaning the complete export of all data stored about a person. Art. 17 governs the right to erasure, with important exceptions for statutory retention obligations. Art. 20 governs data portability, meaning delivering the data in a structured, machine-readable format. These three rights are legally unambiguous; their technical implementation in a grown Magento shop with years of order and customer data is not.
The decisive difference between Adobe Commerce and Magento Open Source: Adobe Commerce ships a built-in interface for data export and erasure with its "Compliance Tool." Magento Open Source, the foundation of most mid-sized shops, ships no built-in GDPR functionality whatsoever. Anyone working with Magento Open Source as an agency or developer has to implement the right of access, erasure concept and consent management entirely themselves; there is no admin button that handles it.
This article deliberately doesn't cover pure legal theory but the technical implementation: which tables contain personal data, how to build an export service with PHP 8.4, why anonymization instead of hard-delete is the right strategy for order data, and how to concretely implement consent tracking as well as automated erasure deadlines. A shop's GDPR compliance is ultimately always a combination of technical implementation and organizational process; this article covers the technical side.
2. Identifying personal data in Magento
Before an export or erasure concept can even be designed, it has to be established where personal data in Magento actually lives. The most obvious tables are customer_entity with name, email and address, as well as customer_address_entity for shipping and billing addresses. Less obvious but equally relevant: sales_order, sales_order_address and sales_order_payment contain personal data even for guest orders without a customer account, as do quote and quote_address for carts not yet checked out.
Frequently overlooked are customer_log and customer_visitor, which store login timestamps and IP addresses, as well as report_event and report_viewed_product_index, which track user behavior for product recommendations and also store personal identifiers in the process. Newsletter data in newsletter_subscriber is just as much part of the scope as stored payment information at payment providers, which is usually not held in Magento itself but referenced via a customer ID in the payment provider's database and must be considered in the erasure concept. Full GDPR compliance requires a documented, complete list of all these tables before a single export or erasure command is written.
3. Implementing the right of access technically
For the technical implementation of Art. 15, a custom service is recommended that bundles the relevant repository interfaces and delivers a structured export array that can then be output as JSON. The service should deliberately build on CustomerRepositoryInterface, OrderRepositoryInterface, and CartRepositoryInterface instead of writing SQL directly against the tables from section 2, because repositories automatically handle authorization checks and store scope filtering.
It's also important that the export works both for logged-in customers via the customer_id and for guest orders via the linked email address, since Art. 15 makes no distinction between registered and non-registered persons. A cleanly structured export simultaneously simplifies fulfilling Art. 20, because an already machine-readable JSON format directly satisfies the data portability requirement as well.
declare(strict_types=1);
namespace Vendor\GdprTools\Model;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
/**
* Builds a structured personal data export for a customer or guest, per Art. 15 GDPR.
*/
final class PersonalDataExportService
{
/**
* @param CustomerRepositoryInterface $customerRepository Provides registered customer master data
* @param OrderRepositoryInterface $orderRepository Provides order history for the requested identity
* @param SearchCriteriaBuilder $searchCriteriaBuilder Builds filtered order search criteria
*/
public function __construct(
private readonly CustomerRepositoryInterface $customerRepository,
private readonly OrderRepositoryInterface $orderRepository,
private readonly SearchCriteriaBuilder $searchCriteriaBuilder,
) {
}
/**
* Assembles the full export structure for a customer, ready to be encoded as JSON.
*
* @param int $customerId Entity ID of the registered customer
* @return array<string, mixed> Structured export payload covering master data and order history
*/
public function exportForCustomer(int $customerId): array
{
$customer = $this->customerRepository->getById($customerId);
$criteria = $this->searchCriteriaBuilder
->addFilter('customer_id', $customerId)
->create();
$orders = $this->orderRepository->getList($criteria)->getItems();
return [
'master_data' => [
'email' => $customer->getEmail(),
'firstname' => $customer->getFirstname(),
'lastname' => $customer->getLastname(),
'created_at' => $customer->getCreatedAt(),
],
'orders' => array_map(
static fn ($order): array => [
'increment_id' => $order->getIncrementId(),
'created_at' => $order->getCreatedAt(),
'grand_total' => $order->getGrandTotal(),
'shipping_address' => $order->getShippingAddress()?->getData(),
],
$orders,
),
];
}
}
4. Erasure concept and the conflict with retention obligations
Art. 17 GDPR grants the right to erasure, which however isn't absolute. German commercial and tax law, specifically HGB and AO, mandate retaining invoice and bookkeeping records for six to ten years. A shop that immediately hard-deletes order data from sales_order after an erasure request thereby violates statutory retention obligations, even if it formally complies with the data subject's erasure request.
The solution established in practice for this conflict is anonymization instead of hard-delete: name, address, email, and all directly identifying fields get replaced with placeholders, while order_increment_id, amounts, tax rates, and payment method are fully preserved for bookkeeping. After anonymization, the order remains fully traceable for tax purposes but can no longer be attributed to an identifiable person, thereby satisfying the underlying intent of Art. 17 without violating the retention obligation. This strategy is the central building block of any realistic GDPR compliance for order data in Magento.
5. Anonymization implementation
The concrete anonymization class should specifically overwrite only the identifying fields and should never delete the complete order entity. Personal free-text fields such as a company name in the address field or a phone number belong to the anonymization scope just as much as first and last name, while numeric and tax-relevant fields must remain untouched. A clean approach additionally marks a custom flag gdpr_anonymized_at on the order entity, so later processes, for example a repeated export attempt, can recognize that the order has already been anonymized.
Important for the implementation: anonymization must not forget to cascade to related tables like sales_order_grid, which contains a denormalized copy of the customer data for the admin grid display. A common mistake in custom GDPR implementations is anonymizing only the main table and overlooking the grid table, which exists separately for performance reasons, causing personal data to remain visible in the admin panel despite a supposedly completed erasure.
declare(strict_types=1);
namespace Vendor\GdprTools\Model;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
/**
* Anonymizes personally identifiable fields on an order while preserving
* amounts and tax data required for statutory retention (HGB/AO).
*/
final class OrderAnonymizer
{
private const string PLACEHOLDER = 'anonymized';
/**
* @param OrderRepositoryInterface $orderRepository Loads and saves the order entity
* @param ResourceConnection $resourceConnection Direct connection for the denormalized sales_order_grid table
*/
public function __construct(
private readonly OrderRepositoryInterface $orderRepository,
private readonly ResourceConnection $resourceConnection,
) {
}
/**
* Replaces identifying fields with placeholders, keeps totals and tax data untouched.
*
* @param int $orderId Entity ID of the order to anonymize
* @return void
*/
public function anonymize(int $orderId): void
{
$order = $this->orderRepository->get($orderId);
$order->setCustomerEmail(self::PLACEHOLDER . '@example.invalid');
$order->setCustomerFirstname(self::PLACEHOLDER);
$order->setCustomerLastname(self::PLACEHOLDER);
$order->setData('gdpr_anonymized_at', date('Y-m-d H:i:s'));
// Totals, tax rates and order_increment_id are intentionally left untouched
// to preserve statutory bookkeeping requirements.
$this->orderRepository->save($order);
// The order grid is a denormalized read model and must be anonymized separately.
$connection = $this->resourceConnection->getConnection();
$connection->update(
$this->resourceConnection->getTableName('sales_order_grid'),
['customer_email' => self::PLACEHOLDER . '@example.invalid', 'customer_name' => self::PLACEHOLDER],
['entity_id = ?' => $orderId],
);
}
}
6. Consent management
Demonstrating consent under Art. 7(1) GDPR requires more than just a checked box at checkout. For newsletter delivery via Magento\Newsletter, double opt-in is already technically prepared, but the timestamp and the specific version of the consent text that was agreed to are not documented by Magento by default. A custom table storing timestamp, IP address, consent version, and a hash of the consent text closes this gap and provides the required proof in case of a dispute.
The same principle applies to cookie consent: simply storing a cookie "consent given: yes" isn't enough if the consent banner text or the queried categories later change. Every consent should be stored linked to the version of the consent text valid at the time of agreement, so that even months later it's traceable exactly what a person agreed to, not just that some agreement occurred.
<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="vendor_gdpr_consent_log" resource="default" engine="innodb" comment="GDPR consent audit trail">
<column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true"/>
<column xsi:type="varchar" name="email" nullable="false" length="255" comment="Identifying email at consent time"/>
<column xsi:type="varchar" name="consent_type" nullable="false" length="64" comment="newsletter, cookies, marketing"/>
<column xsi:type="varchar" name="consent_version" nullable="false" length="32" comment="Version of the consent text shown"/>
<column xsi:type="varchar" name="ip_address" nullable="false" length="45"/>
<column xsi:type="timestamp" name="consented_at" nullable="false" default="CURRENT_TIMESTAMP"/>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="entity_id"/>
</constraint>
</table>
</schema>
7. Automated erasure deadlines via cron
Manual erasure processes don't scale across many thousands of customer records, which is why a dedicated cron job, registered via crontab.xml, should implement fixed rules for automated erasure deadlines. Typical rules are: guest orders without an associated customer account get automatically anonymized once the statutory retention period expires, inactive customer accounts without a login for several years get deleted or anonymized after prior notification, and cart data in quote without a completed order gets cleaned up after a few weeks.
Important for the implementation: the cron job should never delete immediately and without logging, but should first only flag records and actually anonymize them after a lead time, so that an accidentally too-aggressively configured job doesn't irrevocably destroy data before the mistake is noticed.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
<group id="default">
<job name="vendor_gdpr_anonymize_expired_orders" instance="Vendor\GdprTools\Cron\AnonymizeExpiredOrders" method="execute">
<!-- Runs nightly at 2 AM, well outside typical business hours -->
<schedule>0 2 * * *</schedule>
</job>
</group>
</config>
8. Audit trail and proof obligation
When a supervisory authority makes an inquiry, a shop must not only prove that it processes erasure or export requests, but also when, by whom, and with what result. A dedicated logging table that records every export and anonymization operation with a timestamp, the executing admin user, and the affected entity ID is the central building block for this proof obligation. Without such an audit trail, it can't be demonstrated in a dispute that an erasure request was actually processed on time.
This logging itself should not be subject to the erasure concept, but should be retained beyond the regular retention period, since it precisely provides the proof of proper GDPR compliance. A contradiction that seems surprising at first glance: the logs about deleted data must themselves be retained longer than the original data, because they represent the only evidence of correct implementation.
9. Comparison: requirement vs. technical implementation vs. risk
The overview below maps the most important GDPR articles to their technical implementation in Magento and the risk of non-compliance.
| Requirement | Technical implementation | Risk of non-compliance | Priority |
|---|---|---|---|
| Art. 15 right of access | Export service bundling repositories | Fine, complaint to supervisory authority | High |
| Art. 17 erasure | Anonymization instead of hard-delete | Violation of retention obligation or GDPR | High |
| Art. 20 portability | Structured JSON export format | Formally incomplete fulfillment of the claim | Medium |
| Art. 7(1) proof | Consent log with version and timestamp | Missing proof in case of a complaint | Medium |
| General proof obligation | Audit trail for export and erasure | No evidence under regulatory review | Medium |
This prioritization shows: export and erasure concept are the two most urgent building blocks of any GDPR compliance effort, because they can be directly demanded by data subjects, while consent log and audit trail rather secure long-term demonstrability.
10. Summary
GDPR compliance in Magento Open Source is only achievable through custom development, since unlike Adobe Commerce no built-in compliance tool exists. An export service based on repository interfaces covers Art. 15, an anonymization concept instead of hard-delete resolves the conflict between Art. 17 and commercial and tax law retention obligations, and a structured JSON format simultaneously satisfies Art. 20.
Consent management with versioned consent texts, automated erasure deadlines via a documented cron job, and a dedicated audit trail for all export and erasure operations round out a solid GDPR compliance setup. It remains important: this technical implementation does not replace legal advice; a data protection officer or lawyer should review the concrete erasure concept before going live, especially for industry-specific retention periods.
GDPR Compliance in Magento 2: The Essentials at a Glance
No built-in solution
Magento Open Source ships no compliance tool, unlike Adobe Commerce. Custom development is mandatory.
Anonymization instead of hard-delete
Anonymize order data due to HGB/AO retention obligations rather than fully deleting it. Don't forget sales_order_grid either.
Log consent with versioning
Store timestamp, IP and version of the consent text, not just a simple yes/no flag.
Keep the audit trail long-term
Retain logs about export and erasure longer than the original data itself, as proof.
11. FAQ: GDPR Compliance in Magento 2
1Built-in GDPR tool in Open Source?
2Which tables contain personal data?
3Why no hard-delete for orders?
4Correct alternative to hard-delete?
5Handle sales_order_grid separately?
6Is a simple consent cookie enough?
7Automate erasure deadlines?
8Why an audit trail?
9How long retain the audit trail?
10Does this replace legal advice?
Mironsoft
Magento data protection modules, anonymization concepts and consent management
Still missing GDPR compliance for your Magento shop?
We build data export services, anonymization concepts, and consent management as a custom Magento 2 module, tailored to your retention obligations and data models.
Data audit
Fully document all personal data in your shop
Erasure concept
Anonymization instead of hard-delete, coordinated with your bookkeeping
Consent module
Versioned consent tracking with a complete audit trail