# Changelog All notable changes to WooCommerce Tier and Package Prices will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.4.0] - 2026-01-29 ### Added - **Gitea CI/CD Release Pipeline**: Automated release workflow triggered on version tags - Validates plugin version matches tag version - Installs production Composer dependencies - Compiles translation files (.po to .mo) - Creates release package with proper exclusions - Generates SHA256 checksum - Publishes release to Gitea with changelog notes --- ## [1.3.1] - 2026-01-27 ### Changed - **Switched to SecureLicenseClient**: Upgraded from basic `LicenseClient` to `SecureLicenseClient` with 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 `RateLimitExceededException` with user-friendly messages showing retry wait time - **Signature Verification Error Handling**: Added dedicated handling for `SignatureException` when response signatures fail verification - **URL Validation Error Handling**: Added handling for `InvalidArgumentException` from 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 `LicenseClient` to `SecureLicenseClient` - Added `serverSecret` parameter for signature verification - Library updated from `v0.1.0` to `dev-main` with new security features **New Exception Handling**: - `RateLimitExceededException` - shows retry time to user - `SignatureException` - indicates server secret mismatch - `InvalidArgumentException` - 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-client` library 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()` and `get_settings_for_{section}_section()` methods - Improved navigation and organization of settings - **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-client` dependency - 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 `LicenseClient` class 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)', ...), ...)` to `printf(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/else` branches 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_html` for 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**: ```php // 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 packages - `templates/admin/tier-row.twig` - Removed translation filter from placeholder concatenation - `templates/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()` and `render_package_row()` methods to pass `currency_symbol` to Twig templates - Updated `render_variation_tier_row()` and `render_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 check `if (!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 methods - `templates/admin/tier-row.twig` - Updated placeholder to include currency symbol - `templates/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_pricing` hook only fires for simple products, not variable products. Changed to use `woocommerce_product_options_general_product_data` hook 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 a `has-rows` class 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_pricing` to `woocommerce_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 thead` to 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 from `woocommerce_product_options_pricing` to `woocommerce_product_options_general_product_data` - `assets/css/admin.css` - Replaced `:has()` pseudo-class with class-based approach - `assets/js/admin.js` - Added `updateTableHeaders()` 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 `` comes before `
` in the HTML structure. Removed the sibling selector and kept only the `:has()` pseudo-class approach with `!important` flag for proper specificity. ### Technical Details **Parent Product Forms Fix**: - Added new method `add_variable_parent_pricing_fields()` in `WC_TPP_Product_Meta` class - Hooked to `woocommerce_product_options_pricing` action but only displays for variable products - Modified existing `add_tier_pricing_fields()` and `add_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 ~ thead` selector (sibling selector can't target previous elements) - Kept only `.wc-tpp-tiers-table:has(tbody.wc-tpp-tiers-container:empty) thead` with `!important` flag - `: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` - Added `add_variable_parent_pricing_fields()` method; modified `add_tier_pricing_fields()` and `add_package_pricing_fields()` to skip variable products - `assets/css/admin.css` - Fixed table header hiding CSS selector; removed incorrect sibling selector; added `!important` flag ## [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()` and `WC_TPP_Frontend::get_package_price()` to fall back to parent product pricing when variation doesn't have its own pricing - Updated `WC_TPP_Cart` to use helper methods `get_packages_with_fallback()` and `is_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_id` instead of `$effective_id` for 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 to `get_tier_price()` and `get_package_price()` methods - `includes/class-wc-tpp-cart.php` - Added helper methods `get_packages_with_fallback()` and `is_restriction_enabled()`; updated all cart methods to support parent fallback; fixed pricing calls to use correct product ID - `assets/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: none` declarations. Added `!important` flags to all border removal rules and `border-collapse: collapse !important` to 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 `!important` to all `border: none` rules targeting tables, th, td, thead, tbody, and tr elements - Adding `border-collapse: collapse !important` to 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-block` with `vertical-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 `!important` flags to all border removal rules; added `border-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 => true` to 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` - Applied `border: none` to all tier/package table elements; added checkbox margin and description hiding rules - `includes/class-wc-tpp-product-meta.php` - Added `desc_tip => true` parameter 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-pricing` tables 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 tables - `includes/class-wc-tpp-product-meta.php` - Fixed checkbox value parameter (line 213) - `languages/*.po` - Added missing translation entries - `languages/*.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 `` structure, which broke the new `
| ` layout introduced in v1.2.0. Updated `assets/css/admin.css` to 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 ` ` with nested ` ` elements to ` ` 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 styling
- `templates/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 ` | ` to ` |