Data Protection in SAP Commerce: Consent, Erasure, Encryption, and Anonymization
The engineering side of GDPR and data protection on SAP Commerce: the consent framework, personal data erasure and reporting, transparent attribute encryption, working anonymization scripts, and authentication hardening.
No SAP Commerce version makes you GDPR compliant. The platform ships the machinery (consent management, erasure jobs, audit reporting, attribute encryption) and your implementation decides whether that machinery actually covers the personal data your project collects. This guide walks the machinery end to end, with the custom-code obligations flagged, because the gap between "the platform has a feature" and "our custom types are covered" is where findings live.
The stakes are not abstract: GDPR fines reach 4 percent of global annual turnover or 20 million euros, it applies to any company serving EU residents regardless of where the company sits, and the data controller (you or your merchant, not SAP) carries the accountability.
Consent: Collect It, Display It, Honour It#
The commerce consent framework models consent as ConsentTemplate items (versioned definitions of what the customer agrees to) and Consent items (a customer's given or withdrawn agreement). The implementation obligations:
- Entry points everywhere data collection starts. Registration is the obvious one (templates have been integrated into registration and My Account flows since 6.6), but every new collection point your project adds (newsletter capture, review submission, personalization opt-in) needs its consent hook.
- Per-store consent when you run multiple base stores. Consent templates bind per store; a customer's agreement on store A does not cover store B, and your data model and OCC flows must respect that.
- Visibility and withdrawal. Customers must be able to see what they consented to and withdraw it, at any time, as self-service. Withdrawal is not a soft delete of a checkbox: downstream consumers (marketing integrations, personalization) must stop processing on withdrawal, which for composable storefronts means the consent state flows through the OCC consent endpoints and is enforced server-side, never only in the frontend.
Retention and Erasure: The Right to Be Forgotten, Operationalized#
Two triggers force deletion: a retention period expiring, and a customer requesting erasure. The platform ships retention and erasure jobs for the core types (customers, orders, tickets, personal data reports), built on the same retention framework covered in the data maintenance guide.
The part the platform cannot do for you: custom types carrying personal data. The erasure framework exposes hooks, customerCleanupHooks and orderCleanupHooks, and every custom type that references a customer's personal data must register one. The discipline that works: the pull request introducing a PII-bearing type includes its cleanup hook, and your data-protection requirements document maintains the retention period table (orders, tickets, addresses, consents, and each custom type) as a living artifact. An erasure request that leaves rows in a forgotten custom table is a breach, not a bug.
Personal Data Reporting: What Do You Know About Me?#
Customers have the right to a report of the data you hold. The Data Annotation Framework (since 6.5) drives this: types and attributes are annotated as personal data, and audit-view configurations define what a report contains. Generation is available through the support-ticket process out of the box, or programmatically wherever your service flow needs it:
final UserModel user = userService.getUserForUID("sampleUser");
final Stream<ReportView> report = auditViewService.getViewOn(
TypeAuditReportConfig.builder()
.withConfigName("sampleConfig")
.withRootTypePk(user.getPk())
.build());
Decide early: how many report types, which attributes appear, how they render for a human reader, and who is authorized to generate them. Then keep the annotation config in sync as the data model grows; an unannotated PII attribute silently drops out of every report.
Encryption at the Attribute Level#
Transport security is table stakes (CCv2 terminates TLS on every endpoint, and you should load your own certificates rather than shipping defaults). The interesting layer is at-rest protection for specific attributes: Transparent Attribute Encryption (TAE) encrypts declared attributes before they hit the database, transparently to service-layer code.
Use TAE for the short list of genuinely sensitive fields your project stores: tokens, government identifiers, anything payment-adjacent that PCI scope forces you to hold. Two operational warnings from real projects: key management is a first-class concern (losing the key is losing the data, and CCv2 security files are the right home for key material), and encrypted attributes cannot be usefully queried or indexed, so design lookups around a hash or token column instead.
Passwords are their own case: the platform stores them through configurable encoders. Verify your project is on a strong, current encoding, and if it began life years ago on a weak one, run password encoding migration rather than accepting a mixed population of hashes.
Anonymization: Production Data Outside Production#
Whenever production data leaves production (a stage refresh via Cloud Portal snapshot and restore, a dataset for analysis, a support reproduction), it must be anonymized first. Anonymized data falls outside personal-data processing restrictions; identifiable data carries them everywhere it goes.
The process is always the same four steps: identify the PII locations (items.xml audit), script the obfuscation, execute, and validate with queries and Backoffice spot checks. Two script families exist, with an explicit trade-off:
Direct SQL is fast because it bypasses the service layer, which also means no interceptors, no validation, no cascades. It is the tool for large datasets in non-production copies. The shape (adapt the type PKs and the exclusion list to your system, and never run it against production):
UPDATE users SET uniqueid = CONCAT('fake-uid', ABS(RAND() * 10000000000))
WHERE TypePKString = '8796094267474'
AND uniqueid NOT IN ('admin', 'anonymous', 'autotestuser');
UPDATE users SET passwd = null
WHERE TypePKString = '8796094267474'
AND uniqueid NOT IN ('admin', 'anonymous', 'autotestuser');
UPDATE addresses SET p_email = CONCAT('email', ABS(RAND() * 10000000000), '@domain.com.noresolve');
UPDATE addresses SET p_firstname = 'Firstname', p_lastname = 'Lastname';
UPDATE addresses SET p_phone1 = '00000000000', p_phone2 = '00000000000';
UPDATE paymentinfos SET p_ccowner = 'redacted', p_number = CONCAT('fake-n', ABS(RAND() * 10000000000));
UPDATE paymenttransactions SET p_requesttoken = CONCAT('fake-rtoken', ABS(RAND() * 10000000000));
The full sweep must cover every table with PII: users, addresses, payment infos and transactions (including PSP-specific tables like PayPal's), contact-us records, email addresses, voucher codes tied to individuals, social accounts, and every custom table your project added. The last clause is the one that matters; the copied script never knows your types.
Groovy through the service layer is slow but safe: interceptors and validation run, so nothing ends up in an inconsistent state:
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery
def flexibleSearchService = spring.getBean "flexibleSearchService"
def modelService = spring.getBean "modelService"
def findCustomers() {
def query = new FlexibleSearchQuery(
"select {pk} from {Customer} where {uid} != 'anonymous' and {uid} not like '%@anonymous.com'")
flexibleSearchService.search(query).result
}
def stamp = new Date().format("yyyyMMddHHmmss")
findCustomers().eachWithIndex { customer, i ->
customer.uid = "customer${i}.${stamp}@anonymous.com"
customer.name = "Customer ${i}"
modelService.save(customer)
}
Run it batched (the example's limit clauses exist for a reason) and expect it to take hours on large datasets. For the recurring stage-refresh case, promote the logic into a proper AbstractJobPerformable as shown in the data loading guide, so anonymization is a scheduled step of every restore rather than someone remembering a console script.
A third technique for outbound integrations: hash or tokenize PII before it leaves the platform. If the third party only needs a stable identifier, send SHA-256(customerId + salt) and store nothing new.
Authentication Hardening#
- Identity provider integration beats local credentials wherever an organization already runs one. The
samlsinglesignonextension gives Backoffice, assisted service, and related tooling SSO against any SAML IDP; OAuth2 covers the API side, with the platform able to act as its own ID provider for service-to-service cases. Every credential that stops existing in commerce is one you cannot leak. - Password expiration is not a vanilla feature. Implementing it means a change-date attribute on the user, a login filter that redirects expired credentials to a change flow, and re-authentication after the change. Straightforward, but budget it; several compliance regimes require it for business users.
- Two-factor authentication likewise requires custom work: an extended authentication filter, an
AuthenticationProviderimplementation, token generation, storage, delivery (email or TOTP app; SMS is the weakest option given interception and roaming problems), and validation. Prioritize 2FA for Backoffice and admin access before customer accounts: the blast radius of one compromised admin exceeds thousands of compromised shoppers.
Authorization and Audit#
Access control runs on user groups and type-system permissions: business users see and edit exactly the types (and catalog versions) their role requires. Resist permission sprawl by reviewing group membership quarterly; every over-permissioned account is audit surface.
Generic Audit answers the breach-investigation question, who touched this item and when, but it is a volume trade-off: audit only the types a requirement names, and pair every audit decision with an archival plan, because audit tables grow without mercy (details in the data maintenance guide). The point of audit logging is not the logging; it is being able to reconstruct access during an incident with timestamps you trust.
Third Parties: Minimize What You Send, Minimize What You Keep#
Every integration processing personal data belongs in the data-protection requirements with two questions answered: what is the minimum PII this party needs, and what do we actually need to store locally? The canonical example is payment: the PSP stores the card, you store a token, and PCI scope shrinks accordingly. The general form: if the third party holds the data and you can fetch it on demand, do not duplicate it; if you fetch critical data from them, have a documented degradation path for when their API is down.
The Compliance Posture, Summarized#
| Obligation | Platform machinery | Your work |
|---|---|---|
| Lawful collection | Consent templates and framework | Entry points, per-store model, OCC enforcement |
| Right to erasure | Erasure jobs, cleanup hooks | Hooks for every custom PII type, retention table |
| Right to access | Data Annotation Framework, audit views | Report configs kept in sync with the model |
| Storage protection | TAE, password encoders | Key management, field selection, encoder review |
| Non-production data | Snapshot/restore with anonymization | Scripts covering all custom types, scheduled execution |
| Breach investigation | Generic Audit | Scoped type list, archival, access reviews |
Treat the table as the standing agenda for a quarterly data-protection review between engineering and whoever owns compliance. The platform gave you the tools; the audit will ask what you did with them.