- Upgraded from LicenseClient to SecureLicenseClient with HMAC-SHA256 response signature verification - Added Server Secret configuration field for secure communication - Added rate limit exception handling with retry time display - Added signature verification error handling - Added URL validation error handling (SSRF protection) - Updated all translation files with new strings - Compiled .mo files for all 7 language variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
50 KiB
Changelog
All notable changes to WooCommerce Tier and Package Prices will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[1.3.1] - 2026-01-27
Changed
-
Switched to SecureLicenseClient: Upgraded from basic
LicenseClienttoSecureLicenseClientwith HMAC-SHA256 response signature verification for enhanced security against tampering and replay attacks -
Added Server Secret Configuration: New "Server Secret" field in License settings for secure communication with the license server
Added
- Rate Limit Handling: Added proper handling of
RateLimitExceededExceptionwith user-friendly messages showing retry wait time - Signature Verification Error Handling: Added dedicated handling for
SignatureExceptionwhen response signatures fail verification - URL Validation Error Handling: Added handling for
InvalidArgumentExceptionfrom SSRF protection in the license client
Security
- Response signatures are now verified using HMAC-SHA256 with license-specific derived keys (RFC 5869 HKDF)
- The license client now validates server URLs to prevent SSRF attacks (blocks private IP ranges)
- HTTP connections require HTTPS unless explicitly allowed for localhost testing
Technical Details
License Client Upgrade:
- Changed from
LicenseClienttoSecureLicenseClient - Added
serverSecretparameter for signature verification - Library updated from
v0.1.0todev-mainwith new security features
New Exception Handling:
RateLimitExceededException- shows retry time to userSignatureException- indicates server secret mismatchInvalidArgumentException- invalid/blocked URL detected
New Settings Field:
wc_tpp_license_server_secret(password type) for the shared secret
[1.3.0] - 2026-01-25
Breaking Changes
- PHP 8.3 Required: Minimum PHP version increased from 7.4 to 8.3 to support modern dependencies and the license client library. Users on older PHP versions will see an admin notice and the plugin will not load.
Added
-
License Management: Integrated
magdev/wc-licensed-product-clientlibrary for license validation and activation- New "License" settings tab for entering license server URL and license key
- License validation and activation via AJAX with visual feedback
- License status display showing active/inactive state, expiration date, and last check time
- Cached license status with daily auto-refresh
-
Settings Page Sub-tabs: Split the settings page into "General" and "License" tabs using modern WooCommerce patterns
- Refactored to use
get_own_sections()andget_settings_for_{section}_section()methods - Improved navigation and organization of settings
- Refactored to use
-
PHP Version Check: Added runtime PHP version validation with admin notice for incompatible servers
Changed
- Updated composer.json to require PHP 8.3+ and added
magdev/wc-licensed-product-clientdependency - Settings class now uses modern WooCommerce settings API patterns
Technical Details
New Dependencies:
magdev/wc-licensed-product-client: ^0.1(from private repository)symfony/http-client: ^7.0(transitive)psr/log: ^3.0,psr/cache: ^3.0,psr/http-client: ^1.0(transitive)
License Client Integration:
- Uses
LicenseClientclass for API communication - AJAX endpoints:
wc_tpp_validate_license,wc_tpp_activate_license - License status cached in WordPress transient (
wc_tpp_license_status)
[1.2.9] - 2025-12-30
Fixed
-
Price Header Not Translated: The "Price (%s)" header in admin tables was not being properly translated because the translation function was placed incorrectly within the printf statement. Changed from
printf(__('Price (%s)', ...), ...)toprintf(esc_html__('Price (%s)', 'wc-tier-package-prices'), ...)to ensure proper translation while maintaining the currency placeholder functionality. -
Placeholder HTML Entity Encoding Issue: Currency symbols in price input placeholders were being displayed as HTML entities (e.g., "€" instead of "€") because the concatenated string was being passed through the translation filter which was encoding special characters. Removed the unnecessary translation filter from concatenated placeholder strings since they are example values that should not be translated.
-
Variation Pricing Still Not Deletable (Regression from v1.2.8): Despite the fix in v1.2.8, variation pricing data was still not being properly deleted in all scenarios. The issue was with the conditional logic structure - the code had separate
if/elsebranches that could fail in edge cases. Restructured the save logic to be more defensive: initialize arrays at the start, populate only if valid POST data exists, then unconditionally perform either update (if not empty) or delete (if empty). This guarantees proper cleanup regardless of POST data structure.
Technical Details
Translation Fix:
- Changed all 6 instances of
printf(__('Price (%s)', 'wc-tier-package-prices'), ...) - To:
printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), ...) - The
__()function now receives the text domain parameter correctly - Added
esc_htmlfor proper output escaping
Placeholder Encoding Fix:
- Changed tier-row.twig placeholder from:
{{ ('e.g., 9.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }} - To:
{{ 'e.g., 9.99 ' ~ currency_symbol }} - Changed package-row.twig placeholder from:
{{ ('e.g., 99.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }} - To:
{{ 'e.g., 99.99 ' ~ currency_symbol }} - Removed translation filter from concatenated example values to prevent HTML entity encoding
Variation Save Logic Refactor:
// Old pattern (v1.2.8):
if (isset($_POST['wc_tpp_tiers'][$loop])) {
$tiers = array();
// ... populate tiers ...
if (!empty($tiers)) {
update_post_meta(...);
} else {
delete_post_meta(...);
}
} else {
delete_post_meta(...);
}
// New pattern (v1.2.9):
$tiers = array();
if (isset($_POST['wc_tpp_tiers'][$loop]) && is_array($_POST['wc_tpp_tiers'][$loop])) {
// ... populate tiers ...
}
// Always perform update or delete based on final state
if (!empty($tiers)) {
update_post_meta(...);
} else {
delete_post_meta(...);
}
- Eliminated conditional branching that could miss edge cases
- Added explicit
is_array()check for extra safety - Guaranteed that one of update_post_meta() or delete_post_meta() is always called
- Applied to both
save_variation_pricing_fields()for tier and package pricing
User Impact:
- Price headers now display in the administrator's configured language
- Currency symbols display correctly without HTML encoding in placeholders
- Variation pricing deletion now works reliably in all scenarios
- Database remains clean with no orphaned empty arrays
Changed Files
includes/class-wc-tpp-product-meta.php- Fixed translation function calls in 6 table headers; refactored save_variation_pricing_fields() logic for tiers and packagestemplates/admin/tier-row.twig- Removed translation filter from placeholder concatenationtemplates/admin/package-row.twig- Removed translation filter from placeholder concatenation
[1.2.8] - 2025-12-30
Fixed
-
Currency Symbol Missing in Admin Headers and Placeholders: Table headers in the admin pricing configuration now display "Price (CURRENCY)" instead of just "Price", making it immediately clear which currency is being used. Price input placeholders now show currency symbol (e.g., "e.g., 9.99 $" instead of "e.g., 9.99"), providing better UX for administrators configuring pricing in different currencies.
-
Variation Pricing Data Not Deleted Properly (Critical): When administrators deleted all tier or package pricing entries from a variation (or simple/parent product) and saved, the empty pricing data was still stored in the database instead of being deleted. This caused variations to retain deleted pricing rules. The save logic now properly detects when the filtered pricing arrays are empty after removing invalid entries and deletes the post meta instead of saving empty arrays.
Technical Details
Currency Symbol Enhancement:
- Updated all table headers to use
printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()) - Modified
render_tier_row()andrender_package_row()methods to passcurrency_symbolto Twig templates - Updated
render_variation_tier_row()andrender_variation_package_row()with same currency symbol parameter - Changed Twig template placeholders from
'e.g., 9.99'to('e.g., 9.99 ' ~ currency_symbol) - Affects all pricing contexts: simple products, variable product parents, and variations
Pricing Deletion Fix:
- Modified
save_tier_package_fields()method (simple/parent products) to checkif (!empty($tiers))before saving - Modified
save_variation_pricing_fields()method (variations) with same empty check logic - Changed logic from "save on isset, delete otherwise" to "filter entries, then save if not empty, delete if empty"
- Applies to both tier pricing and package pricing for all product types
- Root cause was filtering out empty entries but still calling
update_post_meta()with an empty array
User Impact:
- Administrators see currency symbol in all pricing configuration interfaces
- Clear indication of which currency prices should be entered in
- Deleting all pricing rules now properly removes them from the database
- No orphaned pricing data remains after deletion
- Works correctly for simple products, variable product parents, and variations
Changed Files
includes/class-wc-tpp-product-meta.php- Added currency symbol to all table headers; updated all render methods to pass currency symbol; fixed empty array deletion logic in both save methodstemplates/admin/tier-row.twig- Updated placeholder to include currency symboltemplates/admin/package-row.twig- Updated placeholder to include currency symbol
[1.2.7] - 2025-12-30
Fixed
-
Variable Product Forms Still Not Showing (Critical): The v1.2.6 fix used the wrong WooCommerce hook. The
woocommerce_product_options_pricinghook only fires for simple products, not variable products. Changed to usewoocommerce_product_options_general_product_datahook which fires for all product types after the general tab, allowing the code to check product type and conditionally display the parent pricing fields. -
Table Headers Still Visible When Empty (Critical): The CSS
:has()pseudo-class approach from v1.2.6 wasn't working reliably across all browsers. Implemented a JavaScript-based solution that adds/removes ahas-rowsclass on tables based on whether they contain pricing rules. Headers now hide by default and show only when the table has rows, with JavaScript updating the state when rows are added or removed.
Technical Details
Variable Product Hook Fix:
- Changed from
woocommerce_product_options_pricingtowoocommerce_product_options_general_product_data - The general product data hook fires for all product types
- Method still checks
$product->is_type('variable')to only show for variable products - This ensures forms appear in the correct location in the WordPress admin
Table Header Visibility Fix:
- Replaced CSS-only
:has()solution with JavaScript + CSS class approach - CSS now uses
.wc-tpp-tiers-table.has-rows theadto show headers - Added
updateTableHeaders()JavaScript function that checks row count and toggles class - Function is called on page load and after any add/remove row operation
- Works reliably across all browsers without requiring modern CSS features
User Impact:
- Variable product parent pricing forms now actually appear in the WordPress admin
- Table headers properly hide when empty and show when populated
- No browser compatibility issues - works in all modern browsers
Changed Files
includes/class-wc-tpp-product-meta.php- Changed hook fromwoocommerce_product_options_pricingtowoocommerce_product_options_general_product_dataassets/css/admin.css- Replaced:has()pseudo-class with class-based approachassets/js/admin.js- AddedupdateTableHeaders()function and calls after all row operations; added handlers for variable product parent forms
[1.2.6] - 2025-12-30
Fixed
-
Parent Product Pricing Forms Not Visible (Critical): Variable products were missing the pricing configuration forms on the parent product edit page. The v1.2.5 feature for parent product default pricing was implemented in the backend logic (cart calculations and frontend display) but the admin UI to configure these defaults was not added. Now variable product parents have a dedicated "Default Tier & Package Pricing for All Variations" section in the product edit page where administrators can configure default pricing that applies to all variations unless a specific variation overrides it.
-
Table Headers Not Hiding When Empty: The CSS selector for hiding table headers when no pricing rules exist was using an incorrect approach. The sibling selector
~doesn't work when<thead>comes before<tbody>in the HTML structure. Removed the sibling selector and kept only the:has()pseudo-class approach with!importantflag for proper specificity.
Technical Details
Parent Product Forms Fix:
- Added new method
add_variable_parent_pricing_fields()inWC_TPP_Product_Metaclass - Hooked to
woocommerce_product_options_pricingaction but only displays for variable products - Modified existing
add_tier_pricing_fields()andadd_package_pricing_fields()to skip variable products (they now only show for simple products) - Parent product forms use same field names as simple products (
_wc_tpp_tiers,_wc_tpp_packages,_wc_tpp_restrict_to_packages) - Data is saved to parent product post meta using existing
save_tier_package_fields()method - Backend fallback logic from v1.2.5 now has matching admin UI for configuration
CSS Selector Fix:
- Removed incorrect
.wc-tpp-tiers-container:empty ~ theadselector (sibling selector can't target previous elements) - Kept only
.wc-tpp-tiers-table:has(tbody.wc-tpp-tiers-container:empty) theadwith!importantflag :has()pseudo-class is supported in modern browsers (Chrome 105+, Firefox 121+, Safari 15.4+)
User Impact:
- Administrators can now configure default tier/package pricing on variable product parents (feature was non-functional in v1.2.5)
- Table headers properly hide when no pricing rules exist, creating cleaner admin interface
- No data migration needed - existing configurations remain intact
Changed Files
includes/class-wc-tpp-product-meta.php- Addedadd_variable_parent_pricing_fields()method; modifiedadd_tier_pricing_fields()andadd_package_pricing_fields()to skip variable productsassets/css/admin.css- Fixed table header hiding CSS selector; removed incorrect sibling selector; added!importantflag
[1.2.5] - 2025-12-30
Added
-
Parent Product Default Pricing: Variable products can now define tier and package pricing at the parent product level that applies as defaults to all variations. Individual variations can override these defaults with their own specific pricing. This makes it much easier to set up pricing for products with many variations - set defaults once on the parent, then only customize the variations that need different pricing.
-
Hide Empty Table Headers: Table headers for tier and package pricing in the admin area now automatically hide when no pricing rules are defined. This creates a cleaner interface when starting to configure a product, showing only the helpful empty state message and "Add" button.
Technical Details
Parent Fallback Implementation:
- Modified
WC_TPP_Frontend::get_tier_price()andWC_TPP_Frontend::get_package_price()to fall back to parent product pricing when variation doesn't have its own pricing - Updated
WC_TPP_Cartto use helper methodsget_packages_with_fallback()andis_restriction_enabled()for consistent parent fallback behavior - All cart validation, quantity restriction, and display methods now support parent product defaults
- Fixed cart pricing calls to pass parent
$product_idinstead of$effective_idfor proper fallback resolution
CSS Enhancement:
- Added
:has()pseudo-class selectors to hide table headers when tbody is empty - Leverages existing empty state message styling for consistent UX
Backward Compatibility:
- 100% backward compatible - existing products continue working as before
- No database migrations required
- Variations with specific pricing take precedence over parent defaults
Changed Files
includes/class-wc-tpp-frontend.php- Added parent fallback logic toget_tier_price()andget_package_price()methodsincludes/class-wc-tpp-cart.php- Added helper methodsget_packages_with_fallback()andis_restriction_enabled(); updated all cart methods to support parent fallback; fixed pricing calls to use correct product IDassets/css/admin.css- Added CSS rules to hide table headers when no pricing rules exist
[1.2.4] - 2025-12-30
Fixed
-
Admin Table Borders (Critical): Fixed table borders still appearing in v1.2.3 despite borderless styling attempt. WooCommerce's default CSS was overriding
border: nonedeclarations. Added!importantflags to all border removal rules andborder-collapse: collapse !importantto force borderless styling. Now all tier/package pricing tables (simple and variable products) display correctly without borders, matching WooCommerce's clean admin UI. -
Checkbox and Help Icon Layout: Fixed help icon positioning and checkbox spacing issues from v1.2.3. The help icon was appearing at the right edge of the container instead of next to the label text. Increased checkbox-label margin from 8px to 12px for better spacing. Changed label layout from float positioning to flexbox (
inline-flex) to keep help icon directly adjacent to label text. Added inline description hiding when tooltip is shown.
Technical Details
Root Cause - Table Borders: WooCommerce's core admin CSS has higher specificity border rules that override simple border: none declarations. The solution required:
- Adding
!importantto allborder: nonerules targeting tables, th, td, thead, tbody, and tr elements - Adding
border-collapse: collapse !importantto prevent cell borders from being visible - Comprehensive targeting of all table structural elements for complete border removal
Root Cause - Help Icon Position: WooCommerce's default .woocommerce-help-tip styling uses float: right which positions the icon at the container's right edge. The fix:
- Removed float positioning with
float: none - Changed to
display: inline-blockwithvertical-align: middle - Wrapped label and help-tip in flexbox container (
display: inline-flex; align-items: center) - Controlled spacing with precise margins (checkbox: 12px right, help-tip: 6px left)
Changed Files
assets/css/admin.css- Added!importantflags to all border removal rules; addedborder-collapse: collapse; increased checkbox margin to 12px; converted label layout to flexbox; positioned help-tip with inline-block; added inline description hiding
[1.2.3] - 2025-12-29
Fixed
-
Admin Table Styling: Applied borderless table styling to all tier/package tables (both simple and variable products). Previously only variation tables had borders removed in v1.2.2. Now all pricing tables in the admin have a consistent borderless appearance matching WooCommerce's clean admin UI style.
-
Checkbox Styling and Tooltip: Fixed checkbox styling issues where the help text was displayed inline instead of as a tooltip, and the margin between checkbox and label was too small. Added
desc_tip => trueto the variation restriction checkbox to enable tooltip display. Added CSS rules to increase checkbox-label margin to 8px and hide inline description text when tooltips are used.
Changed Files
assets/css/admin.css- Appliedborder: noneto all tier/package table elements; added checkbox margin and description hiding rulesincludes/class-wc-tpp-product-meta.php- Addeddesc_tip => trueparameter to variation checkbox (line 213)
[1.2.2] - 2025-12-29
Fixed
-
Variation UI Styling: Removed table borders for variation pricing tables to match WooCommerce's borderless variation UI style. Added CSS rules specifically targeting
.wc-tpp-variation-pricingtables to remove borders while keeping them for simple product tables. -
Missing Translations: Added missing admin template translations for "Min Quantity", "Price", and "Label (optional)" to all language files (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US). These strings were used in the variation admin UI added in v1.2.0 but weren't included in translation files.
-
Checkbox Rendering: Fixed variation restriction checkbox rendering issue. The
wc_tpp_restrict_to_packages[]checkbox in variation pricing fields was using a ternary expression that prevented proper checked state handling. Simplified to direct value assignment for WooCommerce's checkbox function to work correctly.
Changed Files
assets/css/admin.css- Added border removal for variation pricing tablesincludes/class-wc-tpp-product-meta.php- Fixed checkbox value parameter (line 213)languages/*.po- Added missing translation entrieslanguages/*.mo- Recompiled from updated .po files
[1.2.1] - 2025-12-29
Fixed
-
Admin UI Display: Fixed table layout in admin product edit screens. The CSS was still using flexbox styling from the old
<div>/<p>structure, which broke the new<table>/<tr>/<td>layout introduced in v1.2.0. Updatedassets/css/admin.cssto properly style table rows with standard table cell padding and removed obsolete flexbox properties. -
Frontend Pricing Display: Fixed pricing tables not showing on simple product pages. Removed global "Enable Tier Pricing" and "Enable Package Pricing" checks from the frontend template (
templates/frontend/pricing-table.twig). Pricing tables now display if configured on a product AND the "Display Pricing Table" setting is enabled, regardless of individual feature enable settings. Cart calculations still respect global enable settings for proper pricing application.
Technical Details
Root Cause - Admin UI Bug: In v1.2.0, admin templates were converted from a <div> with nested <p> elements to <tr> with <td> elements for proper table structure. However, the CSS file (assets/css/admin.css) was not updated accordingly, leaving flexbox styling (.wc-tpp-tier-row { display: flex; gap: 15px; ... }) that conflicted with table display. This caused columns to not align with table headers.
Root Cause - Frontend Display Bug: The frontend pricing table template was checking both get_option('wc_tpp_enable_tier_pricing') AND get_option('wc_tpp_enable_package_pricing') before displaying pricing. This meant if these global settings were disabled (even though defaults are 'yes'), pricing configured on products wouldn't show. The better UX is: if pricing is configured AND display is enabled, show it. The global enable settings now only control cart calculation and admin UI visibility.
Changed Files
assets/css/admin.css- Replaced flexbox styling with table cell stylingtemplates/frontend/pricing-table.twig- Removed global enable setting checks from display conditions
[1.2.0] - 2025-12-29
Added - Variable Product Support
Major Feature: Complete support for WooCommerce variable products with variation-level pricing
- Variable products can now have tier and package pricing configured independently for each variation
- Admin UI: Each variation displays tier/package pricing fields in the variation edit panel
- Frontend: Pricing tables load dynamically via AJAX when customer selects a variation
- Cart: Variation-specific pricing correctly applied during checkout
- Quantity restrictions work per-variation (not just per-product)
- Catalog buttons: "View Options" appears for variable products with restricted variations
Changed
- Admin Templates: Converted tier/package row templates from
<div>to<tr>table structure for better layout - Admin UI: Simple product pricing fields now use table layout for consistency with variations
- Frontend Display: Variable products show placeholder container; pricing appears on variation selection
- Cart Logic: All cart methods now use "effective ID" pattern (variation ID when present, product ID otherwise)
- Template System: Added
field_prefixparameter support to admin templates for variation field naming
Technical Details
Backend Changes
- class-wc-tpp-cart.php: Added variation ID resolution throughout; updated all meta lookups to use effective ID
- class-wc-tpp-frontend.php:
- Updated
get_tier_price()andget_package_price()to acceptvariation_idparameter - Added AJAX endpoint
ajax_get_variation_pricing()for fetching variation pricing data - Updated
display_pricing_table()to detect variable products and show placeholder - Fixed
modify_catalog_add_to_cart_button()to check variations for restrictions
- Updated
- class-wc-tpp-product-meta.php:
- Added hooks:
woocommerce_variation_options_pricing,woocommerce_save_product_variation - New method:
add_variation_pricing_fields()- renders pricing UI in variation panels - New method:
save_variation_pricing_fields()- saves variation-specific pricing data - New methods:
render_variation_tier_row(),render_variation_package_row()- variation rendering helpers
- Added hooks:
Frontend Changes
- frontend.js:
- Added variation selector integration listening to
found_variationandreset_dataevents - Implemented AJAX fetching of variation pricing when variation selected
- Dynamic quantity restriction handling per-variation
- Re-initialization of event handlers for dynamically loaded pricing tables
- Added variation selector integration listening to
- admin.js:
- Separated simple product and variation handlers
- Variation-specific add/remove tier/package row management
- Context-aware template selection using variation loop index
Template Changes
- tier-row.twig: Added
field_prefixvariable for variation field naming; changed to<tr>structure - package-row.twig: Added
field_prefixvariable for variation field naming; changed to<tr>structure
Data Storage
- Meta keys remain the same:
_wc_tpp_tiers,_wc_tpp_packages,_wc_tpp_restrict_to_packages - Simple products: Stored on product post meta
- Variations: Stored on variation post meta (independent per-variation)
Backward Compatibility
- 100% backward compatible - No breaking changes
- Simple products continue working exactly as before
- Existing tier/package data unaffected
- No database migrations required
- Templates remain compatible (field_prefix optional)
Migration Notes
- Existing installations can upgrade seamlessly
- Variable products simply gain new functionality
- No action required for existing simple product configurations
Performance Considerations
- AJAX requests only made when variation selected (not on page load)
- Pricing data fetched per-variation (not all variations at once)
- Nonce verification on all AJAX requests for security
- Template rendering server-side for SEO/performance
Testing Performed
- Simple products: All existing functionality verified
- Variable products: Tier pricing, package pricing, restrictions tested per-variation
- Mixed carts: Simple + variable products working correctly
- WooCommerce Blocks: Cart block, mini-cart block, checkout block compatibility verified
- Admin UI: Add/remove rows working for both simple products and variations
- Quantity restrictions: Enforced correctly per-variation in cart and checkout
[1.1.22] - 2025-12-23
Changed
- Increased width of label input fields for tier pricing and package pricing in admin interface
- Changed label field CSS class from
shorttoregular(approximately 2x wider)
Technical Details
- Updated
templates/admin/tier-row.twig- Changed label input class fromshorttoregular - Updated
templates/admin/package-row.twig- Changed label input class fromshorttoregular - Provides more space for descriptive labels like "Wholesale", "Bulk Discount", "Starter Pack", etc.
- Uses WooCommerce standard input field sizing classes
Known Issues
- Double-install bug: When manually updating the plugin by uploading a new version, WordPress may install it as a separate plugin instead of updating the existing one
- Workaround: Before installing a new version, deactivate and delete the old version first, then install the new version
- Root cause: Plugin lacks automatic update mechanism; requires manual installation
- Future fix: Consider implementing update server or WordPress.org repository integration
[1.1.21] - 2025-12-23
Added
- New translation for
de_CH(Swiss German - formal) - New translation for
de_DE_informal(Informal German) - New translation for
fr_CH(Swiss French) - New translation for
it_CH(Swiss Italian)
Changed
- Updated all translation files (.po) to version 1.1.21
- Compiled all translation files to .mo format for runtime use
Technical Details
- Created 4 new locale-specific translation files following WordPress i18n standards
- Swiss locales use CHF currency formatting in examples (CHF 50.-, CHF 100.-)
- German informal translations use "du/dein" instead of "Sie/Ihr"
- All translations maintain consistent terminology across plugin UI
- Compiled .mo files included for immediate WordPress language support
[1.1.20] - 2025-12-22
Fixed
- CRITICAL: Fatal error in WooCommerce Blocks cart/mini-cart: "Cannot use object of type WC_Product_Simple as array"
- Filter
woocommerce_store_api_product_quantity_editablesignature mismatch
Changed
- Updated
is_quantity_editable_in_blocks()method to acceptWC_Productobject instead of cart item array - Changed method signature from
is_quantity_editable_in_blocks($cart_item)tois_quantity_editable_in_blocks(WC_Product $product) - Use
$product->get_id()instead of$cart_item['product_id']for product identification
Technical Details
- WooCommerce Store API passes product object to this filter, not cart item array
- Previous code attempted array access on product object causing fatal error
- Error occurred at
includes/class-wc-tpp-cart.php:233 - Affects WooCommerce Blocks-based cart, mini-cart, and checkout
- Classic cart/checkout unaffected (uses different hooks)
[1.1.19] - 2025-12-22
Fixed
- Settings page still appearing twice despite instance caching
- Duplicate detection using strict instance comparison failing for different object instances
Changed
- Enhanced duplicate detection to check by class type and ID instead of instance
- Added
instanceof WC_TPP_Settingscheck - Added ID-based duplicate detection via
get_id()method and direct property access - Multiple fallback checks to catch duplicates regardless of instance identity
Technical Details
- Previous strict comparison (
===) failed when different instances of same class existed - New approach checks: instanceof, get_id() method, and id property
- Returns early if any settings page with ID 'tier_package_prices' found
- Prevents duplicates even if settings instance recreated or serialized
[1.1.18] - 2025-12-22
Fixed
- ROOT CAUSE IDENTIFIED: Settings page rendering twice due to automatic instantiation in settings file
- Settings file being included multiple times via Composer autoloader creating duplicate instances
Changed
- Removed
return new WC_TPP_Settings();from bottom of settings file - Changed admin class to explicitly instantiate settings with
new WC_TPP_Settings() - Changed from
includetorequire_oncefor settings file to prevent multiple loads
Technical Details
- Settings file (class-wc-tpp-settings.php) was creating instance automatically on include
- File is in Composer's classmap, so when autoloaded it executed instantiation again
- Each include/autoload created new instance even with singleton pattern in admin class
- Solution: Remove automatic instantiation, use
require_once+ explicitnewin admin class - Now settings instance only created once, explicitly, when needed by filter
- Composer autoload can load class definition without side effects
[1.1.17] - 2025-12-22
Fixed
- Settings page still rendering twice despite singleton pattern in v1.1.16
- Filter adding settings instance to array multiple times when called repeatedly
Changed
- Added duplicate detection in
add_settings_page()filter method - Filter now checks if settings instance already exists in array before adding
Technical Details
- Added foreach loop to check existing settings pages in array
- Uses strict comparison (
===) to detect if exact instance already present - Returns early if settings instance found, preventing duplicate array entries
- Complements singleton pattern from v1.1.16 with array-level duplicate prevention
- Handles edge case where WooCommerce calls filter multiple times
[1.1.16] - 2025-12-22
Fixed
- Settings page still rendering twice in WooCommerce backend despite v1.1.15 fix
- Multiple instantiation of WC_TPP_Admin and WC_TPP_Settings classes
Changed
- Implemented singleton pattern for WC_TPP_Admin class with
get_instance()method - Made WC_TPP_Admin constructor private to prevent direct instantiation
- Added static caching of WC_TPP_Settings instance to prevent duplicate creation
- Changed class instantiation from
new WC_TPP_Admin()toWC_TPP_Admin::get_instance()
Technical Details
- Added
private static $instanceproperty to WC_TPP_Admin class - Added
private static $settings_instanceproperty to cache settings page instance - Modified
add_settings_page()to check and reuse cached settings instance - Ensures only one instance of each class exists throughout plugin lifecycle
- Prevents duplicate filter registrations even if called multiple times
[1.1.15] - 2025-12-22
Fixed
- Settings page rendering twice in WooCommerce settings
- Duplicate instantiation of WC_TPP_Settings class causing double rendering
Technical Details
- Removed conditional
if (class_exists('WC_TPP_Settings'))wrapper from settings return statement - Settings class now only instantiated via
return new WC_TPP_Settings();when included by admin class - Matches v1.1.2 pattern where settings file returns instance without automatic instantiation
- Prevents double registration in WooCommerce settings pages array
[1.1.14] - 2025-12-22
Fixed
- CRITICAL: Plugin completely non-functional in v1.1.8-1.1.13 - no settings, no frontend, no backend
- Classes never instantiated due to incorrect initialization pattern introduced in v1.1.8
- Restored v1.1.2 pattern: classes auto-instantiate when files are included
- All plugin functionality now working: settings page, product meta boxes, frontend display, cart integration
Changed
- Reverted to direct class instantiation pattern from v1.1.2 (last known working version)
- Removed
init_classes()method andwoocommerce_loadedhook approach from v1.1.8 - Each class file now instantiates itself with
new ClassName()at end of file - Simplified plugin initialization for better reliability
Technical Details
- Restored class instantiation in all 5 component files:
class-wc-tpp-admin.php: Addednew WC_TPP_Admin();after class declarationclass-wc-tpp-product-meta.php: Addednew WC_TPP_Product_Meta();after class declarationclass-wc-tpp-frontend.php: Addednew WC_TPP_Frontend();after class declarationclass-wc-tpp-cart.php: Addednew WC_TPP_Cart();after class declarationclass-wc-tpp-settings.php: Already has instantiation via return statement
- Removed
init_classes()method from main plugin class - Removed
woocommerce_loadedhook that delayed class instantiation - Classes now instantiate immediately when
require_onceloads them - All
class_exists()guards remain in place for redeclaration protection
[1.1.13] - 2025-12-22
Fixed
- CRITICAL: Class redeclaration errors for all plugin component classes affecting version 1.1.12
- Fatal errors "Cannot redeclare class WC_TPP_Admin", "Cannot redeclare class WC_TPP_Product_Meta", "Cannot redeclare class WC_TPP_Frontend", "Cannot redeclare class WC_TPP_Cart", "Cannot redeclare class WC_TPP_Settings"
- Plugin functionality completely broken in v1.1.12 - no settings page, no frontend display, no backend controls
- All plugin features now working correctly after adding class guards
Technical Details
- Wrapped all 5 plugin component class declarations in
class_exists()checks:WC_TPP_Admin(includes/class-wc-tpp-admin.php)WC_TPP_Product_Meta(includes/class-wc-tpp-product-meta.php)WC_TPP_Frontend(includes/class-wc-tpp-frontend.php)WC_TPP_Cart(includes/class-wc-tpp-cart.php)WC_TPP_Settings(includes/class-wc-tpp-settings.php)
- Completes comprehensive redeclaration protection started in v1.1.9-1.1.12
- All functions, constants, and classes now fully protected against redeclaration
- Plugin now activates and functions correctly without fatal errors
[1.1.12] - 2025-12-22
Fixed
- CRITICAL: Class redeclaration error for
WC_Tier_Package_Pricesaffecting version 1.1.11 - Fatal error "Cannot redeclare class WC_Tier_Package_Prices" when plugin file loaded multiple times
- Plugin activation failures caused by class redeclaration
Technical Details
- Wrapped
WC_Tier_Package_Pricesclass declaration inclass_exists()check - Completes comprehensive redeclaration protection for all plugin components
- Prevents fatal errors during WordPress plugin activation/deactivation cycles
- All functions, constants, and classes now safely guarded against redeclaration
[1.1.11] - 2025-12-22
Fixed
- CRITICAL: Constant redeclaration warnings/errors for plugin constants affecting versions 1.1.3-1.1.10
- Potential errors when plugin constants (WC_TPP_VERSION, WC_TPP_PLUGIN_DIR, etc.) already defined
- Plugin initialization failures caused by constant redeclaration
Technical Details
- Wrapped all
define()calls indefined()checks for WC_TPP_VERSION, WC_TPP_PLUGIN_DIR, WC_TPP_PLUGIN_URL, WC_TPP_PLUGIN_BASENAME - Prevents warnings/errors during WordPress plugin activation/deactivation cycles
- Completes comprehensive protection against all redeclaration issues
- All global functions and constants now safely guarded
[1.1.10] - 2025-12-22
Fixed
- CRITICAL: Function redeclaration error for
wc_tpp_init()affecting version 1.1.9 - Fatal error "Cannot redeclare function wc_tpp_init()" when plugin file loaded multiple times
- Plugin activation failures caused by function redeclaration
Technical Details
- Wrapped
wc_tpp_init()function infunction_exists()check - Completes the fix started in v1.1.9 by protecting all global functions
- Prevents fatal errors during WordPress plugin activation cycles
- Both
wc_tpp_woocommerce_missing_notice()andwc_tpp_init()now safely guarded
[1.1.9] - 2025-12-22
Fixed
- CRITICAL: Function redeclaration error for
wc_tpp_woocommerce_missing_notice()affecting versions 1.1.3-1.1.8 - Fatal error "Cannot redeclare function wc_tpp_woocommerce_missing_notice()" when plugin file loaded multiple times
- Plugin activation and deactivation failures caused by function redeclaration
Technical Details
- Wrapped
wc_tpp_woocommerce_missing_notice()function infunction_exists()check - Prevents fatal error during WordPress plugin activation/deactivation cycles
- Ensures function can safely be declared even if file is included multiple times
- Moved function declaration before WooCommerce check for better code organization
[1.1.8] - 2025-12-22
Fixed
- CRITICAL: Plugin activation fatal error introduced in v1.1.3-v1.1.7
- Fixed premature class instantiation of
WC_TPP_AdminandWC_TPP_Product_Meta - Both classes now instantiated via
woocommerce_loadedhook after WooCommerce is available - Resolves WordPress 6.9.x and WooCommerce 10.x compatibility issues
Technical Details
- Removed
new WC_TPP_Admin();from bottom of class-wc-tpp-admin.php - Removed
new WC_TPP_Product_Meta();from bottom of class-wc-tpp-product-meta.php - Added both classes to
init_classes()method in main plugin file - All four main classes (Admin, Product Meta, Frontend, Cart) now follow same initialization pattern
- Ensures WooCommerce hooks are available before registration
[1.1.7] - 2025-12-22
Added
- Optional text labels for tier pricing (similar to package labels)
- Clickable tier pricing rows that auto-populate quantity field
- Add to Cart button auto-disable when quantity is 0 or less
Enhanced
- Tier pricing table rows now clickable with visual hover feedback
- Clicking a tier row sets quantity to that tier's minimum quantity
- Smooth scroll animation to quantity field when tier is clicked
- Add to Cart button disabled state with visual feedback (opacity, cursor)
- Tier labels display below quantity in frontend table (italic, gray text)
Technical Details
- Added optional
labelfield to tier pricing meta box (admin/tier-row.twig) - Updated tier save logic to store label field (class-wc-tpp-product-meta.php)
- Enhanced tier pricing template to display labels (frontend/tier-pricing-table.twig)
- Added click handler for tier rows (assets/js/frontend.js)
- Added
updateAddToCartButton()function to manage button state - CSS:
.wc-tpp-tier-labelstyling for tier labels - CSS: Clickable cursor and hover animation for tier rows
- CSS: Disabled button styling (
.single_add_to_cart_button:disabled)
[1.1.6] - 2025-12-21
Fixed
- CRITICAL: Plugin activation fatal error in v1.1.3, v1.1.4, and v1.1.5
- Fatal error caused by premature class instantiation before WooCommerce is loaded
- Removed immediate class instantiation from
class-wc-tpp-cart.phpandclass-wc-tpp-frontend.php
Technical
- Moved
WC_TPP_CartandWC_TPP_Frontendinstantiation towoocommerce_loadedhook - Added
init_classes()method to main plugin class for controlled class initialization - Ensures WooCommerce is fully loaded before registering hooks that depend on WC functions
- Fixed hook registration timing to prevent accessing WooCommerce before it's available
[1.1.5] - 2025-12-21
Fixed
- CRITICAL: Plugin activation error in v1.1.3 and v1.1.4 caused by
add_cart_quantity_css()method - Fatal error when WooCommerce cart object not available during plugin initialization
- Frontend errors on admin pages and during activation
Technical
- Added
function_exists('WC')check before accessing WooCommerce functions - Added
is_admin()check to prevent CSS injection on admin pages - Enhanced error prevention in
add_cart_quantity_css()method
[1.1.4] - 2025-12-21
Added
- WooCommerce Blocks support for quantity restrictions
woocommerce_store_api_product_quantity_editablefilter for block-based cartsblock_quantity_editable()method in WC_TPP_Cart class- CSS targeting for
.wc-block-components-quantity-selectorelements
Enhanced
- "View Options" button styling to match standard WooCommerce "Add to Cart" buttons
- Button padding, font weight, and border radius for better visual consistency
- Hover effects with smooth transitions
Fixed
- WooCommerce blocks cart quantity selector visibility for restricted products
- WooCommerce blocks mini-cart quantity selector visibility
Technical
- Added Store API integration for block-based cart/mini-cart
- Enhanced CSS for block cart items with product-specific selectors
- Improved button styling with WooCommerce standard values (0.618em × 1em padding)
- Added transition effects for better UX
[1.1.3] - 2025-12-21
Fixed
- Cart quantity input visibility issue in cart and cart sidebar for restricted products
- Enhanced filter priority (999) to ensure quantity hiding runs after other plugins
- Mini-cart quantity input now properly hidden for restricted products
Added
woocommerce_widget_cart_item_quantityfilter support for mini-cartadd_cart_quantity_css()method for dynamic CSS injectiondata-product-idattribute to quantity spans for targeted CSS selectors- CSS class
wc-tpp-restricted-qtyfor improved targeting
Technical
- Increased filter priority from 10 to 999 for
woocommerce_cart_item_quantity - Added
maybe_hide_mini_cart_quantity_input()method in WC_TPP_Cart class - Dynamic CSS injection via
wp_headaction as fallback - Used both sibling (+) and general sibling (~) CSS selectors for DOM variations
[1.1.2] - 2025-12-21
Added
- Catalog "View Options" button for products with quantity restrictions
- Automatic button replacement in shop/category/archive pages
- Eye icon (Dashicons) for "View Options" button styling
Changed
- "Add to Cart" button replaced with "View Options" link on catalog pages for restricted products
- CSS now loads on all WooCommerce pages (shop, cart, checkout, product)
- Catalog buttons now direct to product page instead of adding to cart
Technical
- Added
has_quantity_restriction()static method in WC_TPP_Frontend class - Added
modify_catalog_add_to_cart_button()method in WC_TPP_Frontend class - Extended
woocommerce_loop_add_to_cart_linkfilter hook - CSS classes:
wc-tpp-view-options,wc-tpp-cart-quantity,wc-tpp-restriction-notice - Updated
enqueue_scripts()to load CSS on all WooCommerce pages
Translations
- Added 2 new translatable strings
- Updated all translations (en_US, de_DE, de_CH_informal)
- Compiled all .mo files with new strings
[1.1.1] - 2025-12-21
Added
- Cart quantity field hiding when package restriction is enabled
- Automatic read-only quantity display in cart for restricted products
Changed
- Cart quantity input replaced with plain text when restrictions apply
- Enhanced cart display to prevent quantity modification for restricted products
Fixed
- Cart quantity bypass vulnerability for package-restricted products
Technical
- Added
maybe_hide_cart_quantity_input()method in WC_TPP_Cart class - Extended
woocommerce_cart_item_quantityfilter hook - CSS class
wc-tpp-cart-quantityfor styled quantity display
[1.1.0] - 2025-12-21
Added
- Package quantity restriction feature
- Global setting to restrict quantities to defined package sizes
- Per-product setting to restrict quantities to defined package sizes
- Frontend validation preventing non-package quantities
- Server-side cart validation for package quantities
- User-friendly error messages showing available package sizes
- Automatic quantity field hiding when restriction is enabled
- Package selection UI with highlighted states
Changed
- Enhanced package pricing display template with restriction mode support
- Improved JavaScript to handle restricted mode package selection
- Updated frontend to show "Choose a package size below" notice in restricted mode
Technical
- Added
validate_package_quantity()method in WC_TPP_Cart class - Added
maybe_hide_quantity_input()method in WC_TPP_Frontend class - Extended
woocommerce_add_to_cart_validationfilter hook - Added
wc-tpp-restricted-modeCSS class for styling - New product meta:
_wc_tpp_restrict_to_packages - New global option:
wc_tpp_restrict_package_quantities
Translations
- Added 7 new translatable strings
- Updated all translations (en_US, de_DE, de_CH_informal)
- Compiled all .mo files with new strings
[1.0.2] - 2025-12-21
Changed
- Migrated settings to WooCommerce Settings page as dedicated tab
- Settings now appear under WooCommerce > Settings > Tier & Package Prices
- Improved integration with WooCommerce native settings API
Added
- WC_TPP_Settings class extending WC_Settings_Page
- Better integration with WooCommerce settings system
- Consistent UI with other WooCommerce settings tabs
Removed
- Standalone settings submenu (WooCommerce > Tier & Package Prices)
- Custom settings template (templates/admin/settings-page.twig)
Technical
- Implemented WooCommerce settings filter hook (woocommerce_get_settings_pages)
- Uses WC_Admin_Settings for rendering and saving
- Automatic settings persistence via WooCommerce API
[1.0.1] - 2025-12-21
Added
- Twig template engine integration for all templates
- Template loader class with WordPress integration
- German (Switzerland, Informal) translation (de_CH_informal)
- Composer dependency management
- Comprehensive translation support in Twig templates
- Template caching support (disabled in debug mode)
Changed
- Migrated all PHP templates to Twig format (.twig)
- Improved template organization and separation of concerns
- Enhanced security with automatic HTML escaping in templates
- Updated composer.json with complete package metadata
Removed
- Old PHP template files (replaced with Twig)
Technical
- Added Twig v3.22.2 dependency
- Created WC_TPP_Template_Loader class for centralized template rendering
- Integrated WordPress functions (__(), e(), esc*, wc_price()) into Twig
- Added Swiss German localization with informal address form
- Organized templates into admin/ and frontend/ directories
[1.0.0] - 2025-12-21
Added
- Initial release
- Tier pricing functionality (quantity-based discounts)
- Package pricing functionality (fixed-price bundles)
- Admin settings page for plugin configuration
- Product meta boxes for configuring tier and package prices
- Frontend pricing tables display
- Cart integration for automatic price calculation
- WooCommerce HPOS compatibility
- Multilingual support with text domain
- German (Germany) translation (de_DE)
- English (US) translation (en_US)
Features
- Volume discounts based on quantity thresholds
- Fixed-price packages with custom labels
- Configurable display positions (before/after cart button, after price)
- Real-time price updates in cart
- Responsive pricing tables
- Admin-friendly interface for price management