Description
Digital Product Passport for WooCommerce adds the product data the EU Ecodesign for Sustainable Products Regulation (ESPR / EU 2024/1781) asks for, and publishes it three ways: as a passport people can open on the product page, as a QR code that links to it, and as JSON-LD a machine can read through the REST API.
Starting in 2025, the ESPR regulation requires many product categories sold in the EU to carry a Digital Product Passport covering sustainability, material composition, repairability, and end-of-life information. This plugin helps you meet those requirements today.
Key Features
- Full ESPR / EU 2024/1781 Compliance — all mandatory data fields included
- QR Code Generation — one code per product, generated inside the plugin as SVG. No external service, no API key, nothing leaves the site
- GS1 Digital Link — QR codes follow the
/01/{gtin}/21/{serial}standard - JSON-LD Structured Data — schema.org + W3ID DPP ontology (
https://w3id.org/dpp/) - REST API — GET
/wp-json/dpp/v1/passport/{id}with HTTP caching headers - Tab or Button Display — show the passport in a product tab or via a modal button
- Completeness Score — live progress bar showing how complete each passport is
- WooCommerce HPOS Ready — compatible with High Performance Order Storage
- Cart & Checkout Blocks — full Block Editor compatibility declared
- Fully Translatable — all strings use the
digital-product-passport-for-woocommercetext domain
Passport Data Fields
Identity & Traceability
– GTIN / EAN-13 barcode
– Serial number & batch number
– Brand name & manufacturer name
– Manufacturer address
– Production country & production date
Sustainability & Environmental Impact
– Carbon footprint (kg CO₂eq) + measurement methodology
– Energy efficiency class (A+++ to G)
– Material composition (name, % weight, % recycled content)
– Recyclability percentage
– Recycling instructions / end-of-life info
Repairability & Durability
– Repairability index (0–10)
– Spare parts availability & availability period
– Durability / expected lifespan (years)
– Warranty duration (months)
– Repairability documentation available (yes/no)
Compliance
– Substances of concern (REACH / RoHS)
– Applicable regulation reference
– Compliance document URL
Free add-on
Nothing in this plugin is locked or limited. Everything described above works, for every
product, with no key and no account.
A separate add-on, also free, adds work that only larger catalogues need. It is a second
plugin you download from us; this one does not change when you install it, it simply
finds it:
- Analytics Dashboard — track passport views per product with Chart.js charts
- PDF Passport Export — print-optimized HTML passport with your branding
- Bulk Passport Editor — edit all products in a spreadsheet-like grid
- CSV & JSON Import/Export — migrate data or sync with external systems
- Email Notifications — notify admins on passport updates + weekly digest reports
- ACF Integration — sync Advanced Custom Fields with passport data
- WPML & Polylang Support — multilingual passport data
- Branded PDFs — custom logo, company name, and accent color in exports
Developer Hooks
Actions
dpp_passport_saved( $product_id )— fired after passport data is saveddpp_passport_viewed( $product_id )— fired when a passport is viewed (used by Pro analytics)dpp_passport_content_end( $product_id )— fired at the end of passport content (used by Pro PDF button)dpp_before_passport_display( $product_id )— before passport HTML outputdpp_after_passport_display( $product_id )— after passport HTML output
Filters
dpp_passport_data( $data, $product_id )— modify passport data array before displaydpp_json_ld_output( $json_ld, $product_id )— modify JSON-LD structured datadpp_qr_code_url( $url, $product_id )— modify QR code URLdpp_rest_passport_response( $response, $product_id )— modify REST API response
REST API
GET /wp-json/dpp/v1/passport/{id}— passport by product IDGET /wp-json/dpp/v1/passport/gtin/{gtin}— passport by GTINGET /wp-json/dpp/v1/passports?search={term}&per_page={n}— search passports (max 100)
Support
- Documentation: aos-digital.de/plugin-verzeichnis/digital-product-passport
- Support: support@aos-digital.de
- Issues: support@aos-digital.de
Screenshots






Installation
- Upload the plugin to
/wp-content/plugins/digital-product-passport-for-woocommerce/or install via the WordPress plugin directory - Activate through the Plugins menu
- Ensure WooCommerce 8.0+ is installed and active
- Navigate to any product edit page — a Digital Passport tab appears in the product data panel
- Fill in at least the manufacturer name and save
Requirements
- WordPress 6.4 or higher
- WooCommerce 8.0 or higher
- PHP 8.0 or higher
FAQ
-
Is this plugin GDPR compliant?
-
All passport data stays in your WordPress database, and the plugin contacts no external service at all — QR codes are generated on your own server. No analytics, no telemetry, nothing sent anywhere. Whether your site as a whole is GDPR compliant depends on the rest of it, but this plugin adds no data flow you would have to declare.
-
Does it work with variable products?
-
Yes. Each product (including variations) can have its own complete passport. HPOS and legacy order storage are both supported.
-
How are QR codes generated?
-
Inside the plugin, with no network request. The encoder writes the symbol as SVG and stores it in your uploads folder so the work is done once per product. Addresses longer than 213 characters do not fit a version 10 symbol; there you get a visible note instead of an unreadable code.
-
Can I customize the display?
-
Yes. Use the
display_positionsetting to choose between a modal button or an inline product tab. CSS custom properties (--dpp-primary,--dpp-radius, etc.) let you restyle without editing plugin files. The add-on adds PDF branding options. -
What shortcodes are available?
-
[dpp_button]— renders the “View Passport” button[dpp_passport]— renders the full passport inline[dpp_qr_code]— renders the product’s QR code
All shortcodes accept a
product_idattribute. When omitted, the current product is used. -
Is the REST API public?
-
GET requests to
/wp-json/dpp/v1/passport/{id}are public by default (configurable in settings). The API returns JSON-LD withCache-ControlandX-Robots-Tag: noindexheaders. -
Does it support HPOS?
-
Yes. The plugin explicitly declares
custom-order-tablescompatibility, so it works whether you use HPOS or the legacy posts table. -
How does the completeness score work?
-
Ten key fields are checked (manufacturer name, GTIN, country, CO₂, energy class, materials, recyclability, repair index, warranty, compliance). Each filled field adds weight to the score displayed as a progress bar in the product panel.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“Digital Product Passport for WooCommerce” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “Digital Product Passport for WooCommerce” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
1.6.3
- Fixed: the passport button did nothing, and several admin controls were dead. The prefix rename in 1.6.1 renamed the stylesheet and the scripts but not the class names that PHP prints into the markup. Twenty-two class names went out as
dpp-*while the CSS and the JavaScript listened fordppass-*. The visible result: clicking “Digital Product Passport” on a product page opened nothing, and in the admin the buttons for adding and removing a material, opening the material library, saving and applying the company profile, the template buttons and the supplier edit and delete buttons had no handler. Everything is on one prefix now. - Fixed: three links in the admin led to a page that does not exist. The menu slugs are registered as
dppass-settingsanddppass-upgrade, but the “Settings” link WordPress shows next to the plugin, the add-on button in the settings sidebar and the link from the AOS Hub all pointed atdpp-settingsanddpp-upgrade. - Fixed: decimal numbers on the passport always used English separators. The material shares, the carbon footprint, the repair index and the recyclability figure went through
number_format(), which is not locale-aware. A German or French visitor saw 12.40 where 12,40 belongs. Nownumber_format_i18n(). The REST and JSON-LD output is untouched and stays machine-readable. - Neither of these was caught by Plugin Check, PHPCS or PHPStan: none of them looks at CSS class names, and a link to a missing admin page is valid PHP. They were found by taking screenshots of the plugin in a browser.
1.6.2
- Fixed the same security issue on three more paths. 1.6.0 closed the REST route, and that was not enough: passport data leaves the site through five doors, and only one of them had been checked. The admin-ajax endpoint was the wide one, because it is guarded by a single site-wide nonce that every anonymous visitor of any passport page receives; with it, any product ID could be requested, drafts included. The three shortcodes accepted a
product_idattribute without a check, and the free add-on served the same data as a printable page. All five now ask one shared guard (Utils\Access), so the next door cannot be forgotten. A nonce answers whether a request came from our page, never whether the visitor may see the data.
1.6.1
- Changed: the CSS custom properties in the stylesheet still carried the short
--dpp-prefix; the rename in 1.6.0 was anchored on quotes and dots, and a custom property is preceded by a hyphen, so no rule matched it. They are now--dppass-, in step with the free add-on, which reads two of them. - Fixed: the add-on described its custom CSS field as automatically scoped to a wrapper class that the plugin does not render. The CSS is inserted as written; the help text now says so and names the classes that actually exist.
1.6.0
- Fixed a security issue: unpublished products could be read through the REST API. The passport route checked the post status, but it read its cache before that check and filled it after. So an editor opening a draft passport put the draft into the shared cache, and every anonymous request afterwards was answered from that cache without ever reaching the check. Measured on WordPress 7.1: a draft product was handed out in full, manufacturer and GTIN included, status 200. Authorisation now runs first, an unpublished passport is never written to the shared cache, and its response carries
Cache-Control: private, no-storeso no proxy in between can hand it on either. - Fixed: the QR symbol was printed without filtering. The markup is generated inside the plugin, but
generate()may also return it from a file inwp-content/uploads— anything able to write there could have put arbitrary markup on the page. Both output paths now run throughwp_kses()against an allowlist of the five elements a QR symbol consists of. Verified that the generated symbol passes through byte-identical and that script, event handlers,foreignObject,useand links are removed. - Changed: every option, meta key, hook and handle moved from the
dpp_prefix todppass_. WordPress.org discards any prefix shorter than four characters. Existing installations are migrated automatically on the first page load after the update — options, the material library, the supplier list and all 29 passport fields per product. The migration runs onplugins_loaded, not on activation, because a plugin update does not activate anything. Nothing has to be re-entered. - Changed: the Pro add-on moves with it. The two share five meta keys, two options, three hooks and one nonce — please update both together, otherwise the add-on writes under the old names and the free plugin reads under the new ones.
- Fixed: the two global helper functions were still declared as
dpp_is_pro()anddpp_upgrade_url()while theirfunction_exists()guards already asked for the new names — so the guards could never match. - Fixed: the support section linked to a repository that does not exist.
- Added: the three public REST routes now document why they carry no authentication gate, and what limits what they hand out.
- Fixed: deleting the plugin left data behind. Only three of the seven options were removed, so the material library, the supplier list, the company profile and the add-on settings stayed in the database. The QR cache directory survived too, because the routine that emptied it skipped dot files and then failed to remove a folder that was not empty — with a PHP warning.
- Fixed: uninstalling now also removes data written under the old prefix, for anyone who deletes the plugin before the migration has had a chance to run.
1.5.6
- QR codes are now generated inside the plugin. Until 1.5.5 every symbol was fetched from api.qrserver.com — an external request that was never declared, that sent the address of each passport to a third party, and that left the feature dead whenever the service did not answer (the fallback was a grey box, not a scannable code). The encoder now runs locally: no network call, no API key, no third party. Verified by decoding the generated symbols with an independent QR reader.
- Fixed: the description called the plugin “the complete solution”; it now says what it does.
- Fixed: the readme still described the add-on as a “Pro Version” that “unlocks” features. It has been free since 0.11, and nothing in this plugin is locked — the wording now matches the admin screens, which have said “free add-on” since 1.5.4.
1.5.5
- Fixed: structured data. WooCommerce already prints a schema.org Product node on the product page; the plugin printed a second one with the passport fields, so search engines saw two products per page. The passport data (manufacturer, country of origin, GTIN, dpp: blocks) now merges into WooCommerce’s own Product node via the woocommerce_structured_data_product filter, and the dpp/un namespaces join the context. Measured: two Product nodes before, one after.
1.5.4
- Removed: every artificial limit in the free plugin. Custom materials were capped at 10 and suppliers at a fixed number, product fields could be marked “Pro only”, and a 30-day notice plus several “Upgrade” links pointed at an add-on page — while the code to lift each limit sat in this very plugin. WordPress.org guideline 5/6 forbids exactly that, and the add-on being free does not change it. The free plugin is now complete for what it does; the add-on adds separate features (analytics, PDF, bulk import/export, compliance checker, e-mail, WPML).
- Removed: a “Download PDF (Pro)” link shown to shop visitors that pointed into wp-admin.
- Removed: the last Freemius branch in
dpp_upgrade_url(). - Added: the plugin registers with the AOS Hub and shows a footer line on its own admin screens only.
1.5.3
- Changed: the text domain is now
digital-product-passport-for-woocommerce, matching the
plugin name. WordPress.org derives a plugin’s permalink from the plugin name, and language
packs are looked up by text domain — with the two out of step, no translation would ever
have loaded. Nothing users store changes: every option, hook and meta key keeps itsdpp_
prefix. - Removed: the runtime .po .mo compiler. It wrote into the plugin’s own directory, which is
overwritten on every update and is not writable on many hosts — and it could never do
anything in a released package anyway, because .po files are not shipped (WordPress.org
builds translations itself). With it went theload_plugin_textdomain()call it required;
WordPress has loaded language packs on its own since 4.6. - Fixed: prefixed the seven plugin constants (
DPP_DPPASS_). Plugin Check discards any
prefix shorter than four characters, so all seven were reported as unprefixed globals. - Fixed:
fgetcsv()/fputcsv()now pass$escapeexplicitly. PHP 8.4 deprecates the implicit
default, and the plugin declares PHP 8.0, which includes 8.4. - Fixed: the QR cache protection files are written through
WP_Filesystem. On installations
that write over FTP the previous direct write silently did nothing — and that is exactly
where the directory would have been left unprotected.
1.5.2
- The “Powered by” badge is off by default. Until now it appeared on every passport unless the site owner bought the Pro add-on to switch it off. A credit on the visitor-facing page has to be opt-in — that is plugin guideline 11, and gating the off-switch behind a paid upgrade made it worse. The setting is still there under Settings General; it simply starts switched off.
1.5.1
- Fixed: every request to /dpp/v1/passport/{id} ended in a fatal error on PHP 8. The route used bare ‘is_numeric’ as its validate_callback; WordPress calls that with three arguments and PHP-internal functions reject the surplus.
- Security: the passport route served unpublished products. A draft was handed out in full — name, permalink, manufacturer — to anyone walking the IDs. Only published products are public now; users who may edit the product still get it, so previewing from the admin keeps working.
- Fixed: six links in the plugin and the readme pointed at pages that do not exist (/docs/… and /plugins/…). They now point at the product page.
- Added: “Requires Plugins” header, so WordPress blocks activation without WooCommerce and offers to install it.
1.5.0
- Fix: Fatal error when re-activating the Pro plugin after deactivation (undefined constant
DPP_PRO_VERSIONat activation time — now usesDPP_PRO_VERSION_RAW) - Fix: Replaced direct PHP
is_writable()withwp_is_writable()in the MO generator for full WP Filesystem API compliance - Fix: Added missing translators comment for the material library counter string
- Fix: Supplier manager
$_POSTaccess now fully phpcs-annotated for WordPress.Security compliance - Fix:
uninstall.phpglobal variable names annotated for Plugin Check compliance - Fix:
load_plugin_textdomain()phpcs-annotated (kept intentionally for MO auto-compilation fallback) - Code: All Plugin Check ERRORs and WARNINGs resolved to pass WP.org automated review
1.4.0
- New: Bulk editor expanded with Brand, Serial No., CO₂ Methodology, and Durability columns
- New: Supplier modal rebuilt as sticky flex layout (header/body/footer scroll independently)
- New: Apply Template always overwrites existing values with a yellow-flash confirmation
- New: QR code block spans full width in multi-column frontend layouts
- Fix: Analytics chart now loads reliably via
wp_add_inline_script(timing fix) - Fix: jsDelivr CDN fallback for Chart.js when local file is missing
- Fix: Import/Export button dashicons aligned correctly on all screen sizes
- Fix: Custom CSS & Branding Pro feature card added to upgrade page
1.2.0
- New: Full ESPR / EU 2024/1781 field set — brand name, manufacturer address, CO₂ methodology, substances of concern, repairability index, spare parts, durability, compliance fields
- New: GS1 Digital Link QR code URLs (
/01/{gtin}/21/{serial}) - New: QR codes switched from deprecated Google Charts API to qrserver.com (reliable SVG, locally cached)
- New: Completeness score progress bar in product panel
- New: Display position setting — modal button or inline product tab
- New: Repairability section in frontend passport display
- New: ESPR compliance footer badge in passport
- New:
dpp_passport_viewedanddpp_passport_content_endaction hooks for Pro extensions - New: CSS custom properties for easy theme integration
- New: ARIA focus trap and keyboard navigation in passport modal
- New: Cart and Checkout Blocks compatibility declaration
- New: Dismissible admin upgrade notice
- Improved: Admin panel UI — section icons, locked Pro fields, panel-level CTA
- Improved: Material rows now include a recycled content % column
- Improved: Settings page redesigned with Upgrade page feature cards
- Improved: REST API adds transient caching and rate limiting
- Improved: JSON-LD output extended with all new ESPR fields and W3ID DPP ontology
- Improved: Frontend CSS fully redesigned with mobile bottom-sheet animation
- Requires: WordPress 6.4+, WooCommerce 8.0+, PHP 8.0+
1.0.0
- Initial release
- ESPR compliance fields
- QR code generation
- REST API
- WooCommerce product tab integration
