From fa26247d1b008b0c264ea6f504d8c1a4f0af5d60 Mon Sep 17 00:00:00 2001
From: magdev
Date: Tue, 3 Feb 2026 11:55:41 +0100
Subject: [PATCH] Version 1.4.1 - Localhost license bypass and auto-updates
Added localhost/self-licensing license bypass and WordPress auto-update
integration from license server.
Co-Authored-By: Claude Opus 4.5
---
CHANGELOG.md | 57 +++
CLAUDE.md | 59 ++-
includes/class-wc-tpp-license-checker.php | 333 +++++++++++++
includes/class-wc-tpp-settings.php | 221 ++++++++-
includes/class-wc-tpp-update-checker.php | 468 ++++++++++++++++++
languages/wc-tier-package-prices-de_CH.mo | Bin 9257 -> 11585 bytes
languages/wc-tier-package-prices-de_CH.po | 103 +++-
.../wc-tier-package-prices-de_CH_informal.mo | Bin 9214 -> 11537 bytes
.../wc-tier-package-prices-de_CH_informal.po | 103 +++-
languages/wc-tier-package-prices-de_DE.mo | Bin 9225 -> 11553 bytes
languages/wc-tier-package-prices-de_DE.po | 103 +++-
.../wc-tier-package-prices-de_DE_informal.mo | Bin 9181 -> 11504 bytes
.../wc-tier-package-prices-de_DE_informal.po | 103 +++-
languages/wc-tier-package-prices-en_US.mo | Bin 8865 -> 11101 bytes
languages/wc-tier-package-prices-en_US.po | 103 +++-
languages/wc-tier-package-prices-fr_CH.mo | Bin 9820 -> 12398 bytes
languages/wc-tier-package-prices-fr_CH.po | 103 +++-
languages/wc-tier-package-prices-it_CH.mo | Bin 9522 -> 12010 bytes
languages/wc-tier-package-prices-it_CH.po | 103 +++-
languages/wc-tier-package-prices.pot | 101 +++-
wc-tier-and-package-prices.php | 18 +-
21 files changed, 1940 insertions(+), 38 deletions(-)
create mode 100644 includes/class-wc-tpp-license-checker.php
create mode 100644 includes/class-wc-tpp-update-checker.php
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f2cc61..262f780 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,63 @@ All notable changes to WooCommerce Tier and Package Prices will be documented in
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.1] - 2026-02-03
+
+### Added
+
+- **Localhost License Bypass**: License validation is automatically bypassed for localhost environments
+ - Supports localhost, 127.0.0.1, ::1 (IPv6), `*.localhost`, `*.local`, `*.test`, `*.example`, `*.invalid`
+ - Private IP ranges (Docker, VMs) are also recognized as localhost
+ - Displays "(Localhost environment - license validation bypassed)" in License Status
+
+- **Self-Licensing Bypass**: License validation is bypassed when the site URL matches the license server URL
+ - Useful for testing the plugin on the license server itself
+ - Displays "(Self-licensing server - license validation bypassed)" in License Status
+
+- **Auto-Update System**: Plugin can now receive updates directly from the license server
+ - New "Auto-Updates" settings tab under WooCommerce > Tier & Package Prices
+ - **Enable Update Notifications**: Toggle to check for available updates (default: enabled)
+ - **Automatically Install Updates**: Optional auto-install when WordPress performs background updates
+ - **Check Frequency**: Configurable hours between update checks (1-168 hours, default: 12)
+ - **Update Status**: Shows current version and available update with "Check for Updates" button
+ - Updates appear in WordPress Dashboard > Updates with full plugin info
+
+- **New Classes**:
+ - `WC_TPP_License_Checker`: Singleton class handling license validation with localhost/self-licensing bypass
+ - `WC_TPP_Update_Checker`: Singleton class integrating with WordPress plugin update system
+
+### Technical Details
+
+**License Checker Features**:
+
+- `is_localhost()`: Detects localhost environments using multiple patterns
+- `is_self_licensing()`: Compares license server URL with site URL
+- `is_license_valid()`: Main entry point with bypass logic
+- `get_bypass_reason()`: Returns 'localhost', 'self_licensing', or null
+- Cached validation (1 hour success, 5 minutes error)
+
+**Update Checker Features**:
+
+- Hooks into `pre_set_site_transient_update_plugins` for update detection
+- Hooks into `plugins_api` for update modal information
+- Hooks into `http_request_args` for license authentication
+- Hooks into `auto_update_plugin` for background updates
+- Can be disabled via `WC_TPP_DISABLE_AUTO_UPDATE` constant
+
+**New Settings Options**:
+
+- `wc_tpp_update_notification_enabled` (yes/no, default: yes)
+- `wc_tpp_auto_install_enabled` (yes/no, default: no)
+- `wc_tpp_update_check_frequency` (number, default: 12)
+
+**Update Server Endpoint**:
+
+- POST to `/wp-json/wc-licensed-product/v1/update-check`
+- Request body: `{license_key, domain, plugin_slug, current_version}`
+- Response: `{success, update_available, version, download_url, ...}`
+
+---
+
## [1.4.0] - 2026-01-29
### Added
diff --git a/CLAUDE.md b/CLAUDE.md
index 7b80416..34c15a1 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,7 +1,7 @@
# WooCommerce Tier and Package Prices - AI Context Document
-**Last Updated:** 2026-01-29
-**Current Version:** 1.4.0
+**Last Updated:** 2026-02-03
+**Current Version:** 1.4.1
**Author:** Marco Graetsch
**Project Status:** Production-ready WordPress plugin
@@ -20,9 +20,9 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
**Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file. Always keep the `Known Bugs` section and create a section with the next bugfix and minor version after a release.
-### Version 1.4.1
+### Version 1.4.2
-- No planned changes yet
+- No planned features yet
## Technical Stack
@@ -1270,6 +1270,57 @@ The plugin architecture is solid and well-tested. Most bugs arise from:
- Uses `actions/checkout@v4` with `submodules: recursive`
- Modeled after working wp-fedistream implementation
+### v1.4.1 Release Session (2026-02-03)
+
+**Accomplished:**
+
+1. Implemented localhost license bypass:
+ - Created `WC_TPP_License_Checker` class (singleton pattern)
+ - `is_localhost()` detects localhost environments: localhost, 127.0.0.1, ::1, `*.localhost`, `*.local`, `*.test`, `*.example`, `*.invalid`, and private IP ranges
+ - `is_self_licensing()` compares license server URL with site URL
+ - `is_license_valid()` returns true for localhost/self-licensing without server check
+ - Updated license status display to show bypass reason
+
+2. Implemented auto-update system:
+ - Created `WC_TPP_Update_Checker` class (singleton pattern)
+ - Hooks into WordPress plugin update system (`pre_set_site_transient_update_plugins`, `plugins_api`, `http_request_args`, `auto_update_plugin`)
+ - Fetches update info from license server API endpoint
+ - Supports auto-install when WordPress performs background updates
+ - Configurable check frequency (1-168 hours)
+
+3. Added Auto-Updates settings tab:
+ - Enable Update Notifications (checkbox, default: yes)
+ - Automatically Install Updates (checkbox, default: no, requires valid license)
+ - Check Frequency in Hours (number, default: 12)
+ - Update Status display with "Check for Updates" button
+ - AJAX handler for manual update checks
+
+4. Updated all translation files:
+ - Added 23 new translation strings for v1.4.1
+ - All 7 language variants updated (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US)
+ - Compiled all .mo files
+
+**Key Implementation Details:**
+
+- License checker uses 1-hour cache for successful validation, 5-minute cache for errors
+- Update checker respects `WC_TPP_DISABLE_AUTO_UPDATE` constant to disable updates
+- Update server endpoint: `POST /wp-json/wc-licensed-product/v1/update-check`
+- Both new classes are instantiated in main plugin's `includes()` method
+- Auto-install requires valid license (disabled when no license active)
+
+**New Files:**
+
+- `includes/class-wc-tpp-license-checker.php`
+- `includes/class-wc-tpp-update-checker.php`
+
+**Modified Files:**
+
+- `wc-tier-and-package-prices.php` - Version bump, include new classes
+- `includes/class-wc-tpp-settings.php` - Added Auto-Updates tab, updated license status display
+- All translation files (.pot, .po, .mo)
+- `CHANGELOG.md` - v1.4.1 release notes
+- `CLAUDE.md` - Updated version and session history
+
---
Always refer to this document when starting work on this project. Good luck!
diff --git a/includes/class-wc-tpp-license-checker.php b/includes/class-wc-tpp-license-checker.php
new file mode 100644
index 0000000..0a8e69e
--- /dev/null
+++ b/includes/class-wc-tpp-license-checker.php
@@ -0,0 +1,333 @@
+get_current_domain();
+
+ // Remove port number if present
+ $domain = preg_replace('/:[\d]+$/', '', $domain);
+ $domain = strtolower($domain);
+
+ // Check exact matches
+ if (in_array($domain, self::LOCALHOST_HOSTS, true)) {
+ return true;
+ }
+
+ // Check TLD patterns
+ foreach (self::LOCALHOST_TLDS as $tld) {
+ if (str_ends_with($domain, $tld)) {
+ return true;
+ }
+ }
+
+ // Check for private IP ranges (Docker, VMs, etc.)
+ if ($this->is_private_ip($domain)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if domain is a private IP address
+ *
+ * @param string $domain The domain to check.
+ * @return bool
+ */
+ private function is_private_ip(string $domain): bool {
+ // Check if it's a valid IP first
+ if (!filter_var($domain, FILTER_VALIDATE_IP)) {
+ return false;
+ }
+
+ // Check for private/reserved ranges
+ return !filter_var(
+ $domain,
+ FILTER_VALIDATE_IP,
+ FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
+ );
+ }
+
+ /**
+ * Check if the current site is self-licensing
+ *
+ * Returns true if the license server URL and site URL are on the same domain.
+ * This allows the license server itself to use the plugin without a license.
+ *
+ * @return bool
+ */
+ public function is_self_licensing(): bool {
+ $server_url = get_option('wc_tpp_license_server_url', '');
+
+ if (empty($server_url)) {
+ return false;
+ }
+
+ $server_domain = $this->normalize_domain($server_url);
+ $site_domain = $this->normalize_domain(get_site_url());
+
+ return $server_domain === $site_domain;
+ }
+
+ /**
+ * Normalize a URL to its domain
+ *
+ * @param string $url The URL to normalize.
+ * @return string
+ */
+ private function normalize_domain(string $url): string {
+ $parsed = wp_parse_url($url);
+ $host = $parsed['host'] ?? '';
+
+ // Convert to lowercase
+ $host = strtolower($host);
+
+ // Remove www. prefix
+ $host = preg_replace('/^www\./', '', $host);
+
+ return $host;
+ }
+
+ /**
+ * Get current domain from site URL
+ *
+ * @return string
+ */
+ public function get_current_domain(): string {
+ $site_url = get_site_url();
+ $parsed = wp_parse_url($site_url);
+ $host = $parsed['host'] ?? 'localhost';
+
+ // Include port if non-standard
+ if (isset($parsed['port'])) {
+ $host .= ':' . $parsed['port'];
+ }
+
+ return strtolower($host);
+ }
+
+ /**
+ * Check if the license is valid
+ *
+ * This is the main entry point for license validation.
+ * It implements the bypass logic for localhost and self-licensing.
+ *
+ * @return bool
+ */
+ public function is_license_valid(): bool {
+ // Always valid on localhost
+ if ($this->is_localhost()) {
+ return true;
+ }
+
+ // Always valid for self-licensing
+ if ($this->is_self_licensing()) {
+ return true;
+ }
+
+ // Check cached status
+ $cached = $this->get_cached_status();
+ if (false !== $cached) {
+ return !empty($cached['valid']);
+ }
+
+ // Validate against server
+ return $this->validate_license();
+ }
+
+ /**
+ * Get the license bypass reason if applicable
+ *
+ * @return string|null 'localhost', 'self_licensing', or null
+ */
+ public function get_bypass_reason(): ?string {
+ if ($this->is_localhost()) {
+ return 'localhost';
+ }
+
+ if ($this->is_self_licensing()) {
+ return 'self_licensing';
+ }
+
+ return null;
+ }
+
+ /**
+ * Get cached license status
+ *
+ * @return array|false
+ */
+ public function get_cached_status() {
+ return get_transient(self::CACHE_KEY);
+ }
+
+ /**
+ * Validate license against the server
+ *
+ * @return bool
+ */
+ private function validate_license(): bool {
+ $license_key = get_option('wc_tpp_license_key', '');
+ $server_url = get_option('wc_tpp_license_server_url', '');
+ $server_secret = get_option('wc_tpp_license_server_secret', '');
+
+ // Can't validate without credentials
+ if (empty($license_key) || empty($server_url) || empty($server_secret)) {
+ return false;
+ }
+
+ try {
+ $client = $this->get_license_client($server_url, $server_secret);
+ $domain = $this->get_current_domain();
+
+ // Remove port for validation
+ $domain = preg_replace('/:[\d]+$/', '', $domain);
+
+ $result = $client->validate($license_key, $domain);
+
+ // Cache successful validation
+ set_transient(self::CACHE_KEY, array(
+ 'valid' => true,
+ 'product_id' => $result->productId,
+ 'expires_at' => $result->expiresAt?->format('Y-m-d H:i:s'),
+ 'is_lifetime' => $result->isLifetime(),
+ 'checked_at' => current_time('mysql'),
+ ), self::CACHE_TTL_SUCCESS);
+
+ return true;
+
+ } catch (\Exception $e) {
+ // Cache validation failure
+ set_transient(self::CACHE_KEY, array(
+ 'valid' => false,
+ 'error' => $e->getMessage(),
+ 'checked_at' => current_time('mysql'),
+ ), self::CACHE_TTL_ERROR);
+
+ return false;
+ }
+ }
+
+ /**
+ * Get license client instance
+ *
+ * @param string $server_url License server URL.
+ * @param string $server_secret Shared secret for signature verification.
+ * @return \Magdev\WcLicensedProductClient\LicenseClientInterface
+ */
+ private function get_license_client(string $server_url, string $server_secret): \Magdev\WcLicensedProductClient\LicenseClientInterface {
+ $httpClient = \Symfony\Component\HttpClient\HttpClient::create();
+ return new \Magdev\WcLicensedProductClient\SecureLicenseClient(
+ httpClient: $httpClient,
+ baseUrl: $server_url,
+ serverSecret: $server_secret,
+ );
+ }
+
+ /**
+ * Clear the license cache
+ */
+ public function clear_cache(): void {
+ delete_transient(self::CACHE_KEY);
+ }
+
+ /**
+ * Force revalidation of the license
+ *
+ * @return bool
+ */
+ public function revalidate(): bool {
+ $this->clear_cache();
+ return $this->is_license_valid();
+ }
+ }
+}
diff --git a/includes/class-wc-tpp-settings.php b/includes/class-wc-tpp-settings.php
index 6783ab4..5600795 100755
--- a/includes/class-wc-tpp-settings.php
+++ b/includes/class-wc-tpp-settings.php
@@ -33,6 +33,9 @@ if (!class_exists('WC_TPP_Settings')) {
// Add AJAX handlers for license validation
add_action('wp_ajax_wc_tpp_validate_license', array($this, 'ajax_validate_license'));
add_action('wp_ajax_wc_tpp_activate_license', array($this, 'ajax_activate_license'));
+
+ // Add AJAX handler for update checks
+ add_action('wp_ajax_wc_tpp_check_updates', array($this, 'ajax_check_updates'));
}
/**
@@ -44,6 +47,7 @@ if (!class_exists('WC_TPP_Settings')) {
return array(
'' => __('General', 'wc-tier-package-prices'),
'license' => __('License', 'wc-tier-package-prices'),
+ 'updates' => __('Auto-Updates', 'wc-tier-package-prices'),
);
}
@@ -177,6 +181,75 @@ if (!class_exists('WC_TPP_Settings')) {
);
}
+ /**
+ * Get settings for the Auto-Updates section
+ *
+ * @return array
+ */
+ protected function get_settings_for_updates_section() {
+ // Check if auto-install is available (requires valid license or bypass)
+ $license_checker = WC_TPP_License_Checker::get_instance();
+ $auto_install_disabled = !$license_checker->is_license_valid();
+ $auto_install_desc = __('Automatically install updates when available.', 'wc-tier-package-prices');
+
+ if ($auto_install_disabled) {
+ $auto_install_desc .= ' ' . __('(Requires a valid license)', 'wc-tier-package-prices');
+ }
+
+ return array(
+ array(
+ 'title' => __('Auto-Update Settings', 'wc-tier-package-prices'),
+ 'type' => 'title',
+ 'desc' => __('Configure automatic plugin updates from the license server.', 'wc-tier-package-prices'),
+ 'id' => 'wc_tpp_update_settings',
+ ),
+
+ array(
+ 'title' => __('Enable Update Notifications', 'wc-tier-package-prices'),
+ 'desc' => __('Check for available plugin updates.', 'wc-tier-package-prices'),
+ 'id' => 'wc_tpp_update_notification_enabled',
+ 'default' => 'yes',
+ 'type' => 'checkbox',
+ 'desc_tip' => __('When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin.', 'wc-tier-package-prices'),
+ ),
+
+ array(
+ 'title' => __('Automatically Install Updates', 'wc-tier-package-prices'),
+ 'desc' => $auto_install_desc,
+ 'id' => 'wc_tpp_auto_install_enabled',
+ 'default' => 'no',
+ 'type' => 'checkbox',
+ 'desc_tip' => __('When enabled, updates will be automatically installed when WordPress performs background updates.', 'wc-tier-package-prices'),
+ 'custom_attributes' => $auto_install_disabled ? array('disabled' => 'disabled') : array(),
+ ),
+
+ array(
+ 'title' => __('Check Frequency (Hours)', 'wc-tier-package-prices'),
+ 'desc' => __('How often to check for updates.', 'wc-tier-package-prices'),
+ 'id' => 'wc_tpp_update_check_frequency',
+ 'default' => '12',
+ 'type' => 'number',
+ 'css' => 'width: 80px;',
+ 'desc_tip' => __('Number of hours between update checks. Default is 12 hours.', 'wc-tier-package-prices'),
+ 'custom_attributes' => array(
+ 'min' => '1',
+ 'max' => '168',
+ ),
+ ),
+
+ array(
+ 'title' => __('Update Status', 'wc-tier-package-prices'),
+ 'type' => 'wc_tpp_update_status',
+ 'id' => 'wc_tpp_update_status_display',
+ ),
+
+ array(
+ 'type' => 'sectionend',
+ 'id' => 'wc_tpp_update_settings',
+ ),
+ );
+ }
+
/**
* Get cached license status
*
@@ -368,8 +441,9 @@ if (!class_exists('WC_TPP_Settings')) {
public function output() {
global $current_section;
- // Register custom field type for license status display
+ // Register custom field types
add_action('woocommerce_admin_field_wc_tpp_license_status', array($this, 'output_license_status_field'));
+ add_action('woocommerce_admin_field_wc_tpp_update_status', array($this, 'output_update_status_field'));
parent::output();
@@ -377,6 +451,11 @@ if (!class_exists('WC_TPP_Settings')) {
if ('license' === $current_section) {
$this->output_license_scripts();
}
+
+ // Add JavaScript for updates section
+ if ('updates' === $current_section) {
+ $this->output_updates_scripts();
+ }
}
/**
@@ -415,6 +494,20 @@ if (!class_exists('WC_TPP_Settings')) {
* @param array|false $status License status data.
*/
private function render_license_status_html($status) {
+ // Check for license bypass
+ $license_checker = WC_TPP_License_Checker::get_instance();
+ $bypass_reason = $license_checker->get_bypass_reason();
+
+ if ($bypass_reason) {
+ echo '' . esc_html__('License Active', 'wc-tier-package-prices') . '';
+ if ('localhost' === $bypass_reason) {
+ echo '
' . esc_html__('(Localhost environment - license validation bypassed)', 'wc-tier-package-prices') . '';
+ } elseif ('self_licensing' === $bypass_reason) {
+ echo '
' . esc_html__('(Self-licensing server - license validation bypassed)', 'wc-tier-package-prices') . '';
+ }
+ return;
+ }
+
if (empty($status)) {
echo '' . esc_html__('No license activated', 'wc-tier-package-prices') . '';
return;
@@ -443,6 +536,50 @@ if (!class_exists('WC_TPP_Settings')) {
}
}
+ /**
+ * Output update status custom field
+ *
+ * @param array $value Field configuration.
+ */
+ public function output_update_status_field($value) {
+ $update_checker = WC_TPP_Update_Checker::get_instance();
+ $available_version = $update_checker->get_available_version();
+ ?>
+
+ |
+
+ |
+
+
+ render_update_status_html($available_version); ?>
+
+
+
+
+
+ |
+
+ ' . esc_html__('Current Version:', 'wc-tier-package-prices') . ' ' . esc_html(WC_TPP_VERSION) . '
';
+
+ if ($available_version) {
+ echo '' . esc_html__('Update Available:', 'wc-tier-package-prices') . ' ' . esc_html($available_version) . '
';
+ echo '' . esc_html__('Update Now', 'wc-tier-package-prices') . '
';
+ } else {
+ echo '' . esc_html__('You are running the latest version.', 'wc-tier-package-prices') . '
';
+ }
+ }
+
/**
* Output JavaScript for license management
*/
@@ -525,5 +662,87 @@ if (!class_exists('WC_TPP_Settings')) {
__('Permission denied.', 'wc-tier-package-prices')));
+ }
+
+ $update_checker = WC_TPP_Update_Checker::get_instance();
+ $update_info = $update_checker->force_check();
+
+ if ($update_info && !empty($update_info['update_available'])) {
+ wp_send_json_success(array(
+ 'message' => sprintf(
+ /* translators: %s: Version number */
+ __('Update available: version %s', 'wc-tier-package-prices'),
+ $update_info['version']
+ ),
+ 'update_available' => true,
+ 'version' => $update_info['version'],
+ 'current_version' => WC_TPP_VERSION,
+ ));
+ } else {
+ wp_send_json_success(array(
+ 'message' => __('You are running the latest version.', 'wc-tier-package-prices'),
+ 'update_available' => false,
+ 'current_version' => WC_TPP_VERSION,
+ ));
+ }
+ }
+
+ /**
+ * Output JavaScript for update management
+ */
+ private function output_updates_scripts() {
+ $nonce = wp_create_nonce('wc_tpp_update_nonce');
+ ?>
+
+ is_disabled()) {
+ return;
+ }
+
+ $this->register_hooks();
+ }
+
+ /**
+ * Check if auto-updates are disabled via constant
+ *
+ * @return bool
+ */
+ private function is_disabled(): bool {
+ return defined('WC_TPP_DISABLE_AUTO_UPDATE') && WC_TPP_DISABLE_AUTO_UPDATE;
+ }
+
+ /**
+ * Register WordPress hooks
+ */
+ private function register_hooks(): void {
+ // Check for updates
+ add_filter('pre_set_site_transient_update_plugins', array($this, 'check_for_updates'));
+
+ // Provide plugin information for update modal
+ add_filter('plugins_api', array($this, 'get_plugin_info'), 10, 3);
+
+ // Add authentication headers to download requests
+ add_filter('http_request_args', array($this, 'add_auth_headers'), 10, 2);
+
+ // Handle auto-install setting
+ add_filter('auto_update_plugin', array($this, 'handle_auto_install'), 10, 2);
+
+ // Clear cache when settings change
+ add_action('update_option_wc_tpp_license_key', array($this, 'clear_cache'));
+ add_action('update_option_wc_tpp_license_server_url', array($this, 'clear_cache'));
+ add_action('update_option_wc_tpp_update_notification_enabled', array($this, 'clear_cache'));
+ }
+
+ /**
+ * Check if update notifications are enabled
+ *
+ * @return bool
+ */
+ public static function is_update_notification_enabled(): bool {
+ return get_option('wc_tpp_update_notification_enabled', 'yes') === 'yes';
+ }
+
+ /**
+ * Check if auto-install is enabled
+ *
+ * @return bool
+ */
+ public static function is_auto_install_enabled(): bool {
+ return get_option('wc_tpp_auto_install_enabled', 'no') === 'yes';
+ }
+
+ /**
+ * Get update check frequency in hours
+ *
+ * @return int
+ */
+ public static function get_check_frequency(): int {
+ $frequency = (int) get_option('wc_tpp_update_check_frequency', self::DEFAULT_CHECK_FREQUENCY);
+ return max(1, min(168, $frequency)); // Clamp between 1 and 168 hours
+ }
+
+ /**
+ * Check for plugin updates
+ *
+ * @param object $transient WordPress update transient.
+ * @return object
+ */
+ public function check_for_updates($transient) {
+ if (empty($transient->checked)) {
+ return $transient;
+ }
+
+ // Skip if notifications disabled
+ if (!self::is_update_notification_enabled()) {
+ return $transient;
+ }
+
+ // Get update info (cached)
+ $update_info = $this->get_update_info();
+
+ if (empty($update_info) || !$update_info['update_available']) {
+ return $transient;
+ }
+
+ // Build update object
+ $update_obj = $this->build_update_object($update_info);
+
+ if ($update_obj) {
+ $transient->response[WC_TPP_PLUGIN_BASENAME] = $update_obj;
+ }
+
+ return $transient;
+ }
+
+ /**
+ * Get plugin information for update modal
+ *
+ * @param false|object|array $result The result object or array.
+ * @param string $action The type of information being requested.
+ * @param object $args Plugin API arguments.
+ * @return false|object
+ */
+ public function get_plugin_info($result, $action, $args) {
+ if ('plugin_information' !== $action) {
+ return $result;
+ }
+
+ if (!isset($args->slug) || self::PLUGIN_SLUG !== $args->slug) {
+ return $result;
+ }
+
+ // Get update info
+ $update_info = $this->get_update_info(true); // Force fetch for full info
+
+ if (empty($update_info)) {
+ return $result;
+ }
+
+ // Build plugin info object
+ return $this->build_plugin_info_object($update_info);
+ }
+
+ /**
+ * Add authentication headers to download requests
+ *
+ * @param array $args HTTP request arguments.
+ * @param string $url The request URL.
+ * @return array
+ */
+ public function add_auth_headers(array $args, string $url): array {
+ $server_url = get_option('wc_tpp_license_server_url', '');
+
+ // Only add headers for requests to our license server
+ if (empty($server_url) || strpos($url, $server_url) !== 0) {
+ return $args;
+ }
+
+ $license_key = get_option('wc_tpp_license_key', '');
+
+ if (!empty($license_key)) {
+ $args['headers']['X-License-Key'] = $license_key;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Handle auto-install setting
+ *
+ * @param bool|null $update Whether to auto-update.
+ * @param object $item The plugin update object.
+ * @return bool|null
+ */
+ public function handle_auto_install($update, $item) {
+ // Check if this is our plugin
+ if (!isset($item->plugin) || WC_TPP_PLUGIN_BASENAME !== $item->plugin) {
+ return $update;
+ }
+
+ // Return our setting, or default behavior
+ if (self::is_auto_install_enabled()) {
+ return true;
+ }
+
+ return $update;
+ }
+
+ /**
+ * Get update info from cache or server
+ *
+ * @param bool $force_fetch Force fetching from server.
+ * @return array|null
+ */
+ private function get_update_info(bool $force_fetch = false): ?array {
+ // Check cache first
+ if (!$force_fetch) {
+ $cached = get_transient(self::CACHE_KEY);
+ if (false !== $cached) {
+ return $cached;
+ }
+ }
+
+ // Fetch from server
+ $update_info = $this->fetch_update_info();
+
+ if ($update_info) {
+ // Cache the result
+ $cache_ttl = self::get_check_frequency() * HOUR_IN_SECONDS;
+ set_transient(self::CACHE_KEY, $update_info, $cache_ttl);
+ }
+
+ return $update_info;
+ }
+
+ /**
+ * Fetch update info from license server
+ *
+ * @return array|null
+ */
+ private function fetch_update_info(): ?array {
+ $server_url = get_option('wc_tpp_license_server_url', '');
+ $license_key = get_option('wc_tpp_license_key', '');
+ $server_secret = get_option('wc_tpp_license_server_secret', '');
+
+ if (empty($server_url)) {
+ return null;
+ }
+
+ // Build the update check endpoint
+ $endpoint = trailingslashit($server_url) . 'wp-json/wc-licensed-product/v1/update-check';
+
+ // Get license checker for domain
+ $license_checker = WC_TPP_License_Checker::get_instance();
+ $domain = $license_checker->get_current_domain();
+
+ // Remove port for API call
+ $domain = preg_replace('/:[\d]+$/', '', $domain);
+
+ // Prepare request body
+ $body = array(
+ 'license_key' => $license_key,
+ 'domain' => $domain,
+ 'plugin_slug' => self::PLUGIN_SLUG,
+ 'current_version' => WC_TPP_VERSION,
+ );
+
+ // Make the request
+ $response = wp_remote_post($endpoint, array(
+ 'timeout' => 15,
+ 'headers' => array(
+ 'Content-Type' => 'application/json',
+ 'Accept' => 'application/json',
+ ),
+ 'body' => wp_json_encode($body),
+ ));
+
+ if (is_wp_error($response)) {
+ return null;
+ }
+
+ $response_code = wp_remote_retrieve_response_code($response);
+
+ // Handle rate limiting
+ if (429 === $response_code) {
+ return null;
+ }
+
+ // Handle other errors
+ if ($response_code < 200 || $response_code >= 300) {
+ return null;
+ }
+
+ $body = wp_remote_retrieve_body($response);
+ $data = json_decode($body, true);
+
+ if (empty($data) || !isset($data['success'])) {
+ return null;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Build WordPress update object
+ *
+ * @param array $update_info Update information from server.
+ * @return object|null
+ */
+ private function build_update_object(array $update_info): ?object {
+ if (empty($update_info['version'])) {
+ return null;
+ }
+
+ // Check if update is actually available
+ if (version_compare(WC_TPP_VERSION, $update_info['version'], '>=')) {
+ return null;
+ }
+
+ $obj = new \stdClass();
+ $obj->id = $update_info['id'] ?? self::PLUGIN_SLUG;
+ $obj->slug = $update_info['slug'] ?? self::PLUGIN_SLUG;
+ $obj->plugin = $update_info['plugin'] ?? WC_TPP_PLUGIN_BASENAME;
+ $obj->new_version = $update_info['version'];
+ $obj->url = $update_info['url'] ?? '';
+ $obj->package = $update_info['download_url'] ?? $update_info['package'] ?? '';
+ $obj->tested = $update_info['tested'] ?? '';
+ $obj->requires = $update_info['requires'] ?? '6.0';
+ $obj->requires_php = $update_info['requires_php'] ?? '8.3';
+
+ // Icons
+ if (!empty($update_info['icons'])) {
+ $obj->icons = $update_info['icons'];
+ }
+
+ // Banners
+ if (!empty($update_info['banners'])) {
+ $obj->banners = $update_info['banners'];
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Build plugin info object for update modal
+ *
+ * @param array $update_info Update information from server.
+ * @return object
+ */
+ private function build_plugin_info_object(array $update_info): object {
+ $obj = new \stdClass();
+
+ $obj->name = $update_info['name'] ?? 'WooCommerce Tier and Package Prices';
+ $obj->slug = $update_info['slug'] ?? self::PLUGIN_SLUG;
+ $obj->version = $update_info['version'] ?? WC_TPP_VERSION;
+ $obj->author = $update_info['author'] ?? 'Marco Graetsch';
+ $obj->author_profile = $update_info['author_profile'] ?? 'https://src.bundespruefstelle.ch/magdev';
+ $obj->homepage = $update_info['homepage'] ?? $update_info['url'] ?? '';
+ $obj->requires = $update_info['requires'] ?? '6.0';
+ $obj->tested = $update_info['tested'] ?? '';
+ $obj->requires_php = $update_info['requires_php'] ?? '8.3';
+ $obj->last_updated = $update_info['last_updated'] ?? '';
+ $obj->download_link = $update_info['download_url'] ?? $update_info['package'] ?? '';
+
+ // Sections (description, changelog, etc.)
+ $obj->sections = array();
+
+ if (!empty($update_info['sections']['description'])) {
+ $obj->sections['description'] = $update_info['sections']['description'];
+ } else {
+ $obj->sections['description'] = __('Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices.', 'wc-tier-package-prices');
+ }
+
+ if (!empty($update_info['sections']['changelog'])) {
+ $obj->sections['changelog'] = $update_info['sections']['changelog'];
+ } elseif (!empty($update_info['changelog'])) {
+ $obj->sections['changelog'] = $update_info['changelog'];
+ }
+
+ if (!empty($update_info['sections']['installation'])) {
+ $obj->sections['installation'] = $update_info['sections']['installation'];
+ }
+
+ // Icons
+ if (!empty($update_info['icons'])) {
+ $obj->icons = $update_info['icons'];
+ }
+
+ // Banners
+ if (!empty($update_info['banners'])) {
+ $obj->banners = $update_info['banners'];
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Clear update cache
+ */
+ public function clear_cache(): void {
+ delete_transient(self::CACHE_KEY);
+
+ // Also clear WordPress plugin update transient to force recheck
+ delete_site_transient('update_plugins');
+ }
+
+ /**
+ * Force check for updates
+ *
+ * @return array|null
+ */
+ public function force_check(): ?array {
+ $this->clear_cache();
+ return $this->get_update_info(true);
+ }
+
+ /**
+ * Get the available update version if any
+ *
+ * @return string|null
+ */
+ public function get_available_version(): ?string {
+ $update_info = $this->get_update_info();
+
+ if (empty($update_info) || empty($update_info['version'])) {
+ return null;
+ }
+
+ // Check if it's actually newer
+ if (version_compare(WC_TPP_VERSION, $update_info['version'], '>=')) {
+ return null;
+ }
+
+ return $update_info['version'];
+ }
+
+ /**
+ * Check if an update is available
+ *
+ * @return bool
+ */
+ public function is_update_available(): bool {
+ return null !== $this->get_available_version();
+ }
+ }
+}
diff --git a/languages/wc-tier-package-prices-de_CH.mo b/languages/wc-tier-package-prices-de_CH.mo
index 289760614f8d94bc9a6bb9dbce8173a5b52b90eb..460f8b9461336220dc1b9582d518117c2cdb81f3 100644
GIT binary patch
delta 4423
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
MZL9XQeWiBdKZZQqumAu6
diff --git a/languages/wc-tier-package-prices-de_CH.po b/languages/wc-tier-package-prices-de_CH.po
index a61bc70..2a278b0 100644
--- a/languages/wc-tier-package-prices-de_CH.po
+++ b/languages/wc-tier-package-prices-de_CH.po
@@ -1,12 +1,12 @@
# German (Switzerland) translation for WooCommerce Tier and Package Prices
-# Copyright (C) 2025 Marco Graetsch
+# Copyright (C) 2026 Marco Graetsch
# This file is distributed under the GPL v2 or later.
msgid ""
msgstr ""
-"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
+"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
-"POT-Creation-Date: 2025-12-30 00:00+0000\n"
-"PO-Revision-Date: 2025-12-30 00:00+0000\n"
+"POT-Creation-Date: 2026-02-03 00:00+0000\n"
+"PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n"
"Language-Team: German (Switzerland)\n"
"Language: de_CH\n"
@@ -398,3 +398,98 @@ msgstr "Aktivierung fehlgeschlagen."
#: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again."
msgstr "Anfrage fehlgeschlagen. Bitte versuchen Sie es erneut."
+
+#. v1.4.1 - Auto-Updates and License Bypass
+
+#: includes/class-wc-tpp-settings.php
+msgid "Auto-Updates"
+msgstr "Auto-Updates"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Auto-Update Settings"
+msgstr "Auto-Update Einstellungen"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Configure automatic plugin updates from the license server."
+msgstr "Konfigurieren Sie automatische Plugin-Updates vom Lizenzserver."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Enable Update Notifications"
+msgstr "Update-Benachrichtigungen aktivieren"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check for available plugin updates."
+msgstr "Nach verfügbaren Plugin-Updates suchen."
+
+#: includes/class-wc-tpp-settings.php
+msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
+msgstr "Wenn aktiviert, prüft das Plugin auf Updates vom Lizenzserver und zeigt Benachrichtigungen im WordPress-Admin an."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Automatically Install Updates"
+msgstr "Updates automatisch installieren"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Automatically install updates when available."
+msgstr "Updates automatisch installieren, wenn verfügbar."
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Requires a valid license)"
+msgstr "(Erfordert eine gültige Lizenz)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
+msgstr "Wenn aktiviert, werden Updates automatisch installiert, wenn WordPress Hintergrund-Updates durchführt."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check Frequency (Hours)"
+msgstr "Prüfhäufigkeit (Stunden)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "How often to check for updates."
+msgstr "Wie oft nach Updates gesucht werden soll."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Number of hours between update checks. Default is 12 hours."
+msgstr "Anzahl der Stunden zwischen Update-Prüfungen. Standard ist 12 Stunden."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Status"
+msgstr "Update-Status"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check for Updates"
+msgstr "Nach Updates suchen"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Current Version:"
+msgstr "Aktuelle Version:"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Available:"
+msgstr "Update verfügbar:"
+
+#: includes/class-wc-tpp-settings.php
+msgid "You are running the latest version."
+msgstr "Sie verwenden die neueste Version."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Now"
+msgstr "Jetzt aktualisieren"
+
+#: includes/class-wc-tpp-settings.php
+#. translators: %s: Version number
+msgid "Update available: version %s"
+msgstr "Update verfügbar: Version %s"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Failed to check for updates."
+msgstr "Fehler beim Suchen nach Updates."
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Localhost environment - license validation bypassed)"
+msgstr "(Localhost-Umgebung - Lizenzvalidierung übersprungen)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Self-licensing server - license validation bypassed)"
+msgstr "(Selbstlizenzierender Server - Lizenzvalidierung übersprungen)"
diff --git a/languages/wc-tier-package-prices-de_CH_informal.mo b/languages/wc-tier-package-prices-de_CH_informal.mo
index 64b6c143c74ffcde5abcfbced02a4309e718d594..868dbca3a7b338213fb5c27bf46903ec2a0feeb6 100644
GIT binary patch
delta 4417
zcmai#e{5A}8OPs3Syz4tZTUH1yd5wK_Fh_G$lB>BEwqfnSPPUPY&-Yf_uhNp+;hu0
z=eAPAx&;3)#Qn;Z%_$@bGn)=|TjzAK8kLavYyMcW#2IxuvoQa1ONfgxQ9sW)=e7kj
zzUgy6?|aVs<9WZ&^PK*x`^iJ2f2^-Qqi7zs47)g8sdMnI8h&Um)+%)ed?iooDZLav*9>g4u1+0
z@CuZJ7VvTjY=Vp6HYf)jf-9i|m%=AtJG>b4J$Rb&syRyC2fqd9alWehh*GUQsD}^1
zE+{wqDqH|xgr!pA%Ciche_B0
zM^Qx$(vcS@p@iTuDAHYk>*3oFwbWcr69<+<87H7P(9X|RxChRFk3(_f43u1W7H)#y
zfH%Q+U?co%J^J_1nT4LW!hKMr^r38U7Ro^{z!CTw%)(80nTB72vhly6tlvbuEZ7e_
z;kTiL@E`oh`?%
zz6pomX);nAs3PSbfN8i7{uqi9i^yC_QVm6cu2DMjA`8WlB9sG1;^&V-k?bib4xEF(
zs#5CfaI?%K12udJ-VNV?abP!9Y8l#pJ8{HZtj>4H~bo7DfEr0RJd
z1aKEzq$7{ha3kXqwBXmENIt7Eis@4LF~+S>9IzoOt4H7}_zYYNe*kZX??E|u?rl-1
z_dzZ7pQ6J@O`VGQD3payL)rL8P$c^;oDbiJ5|SANQWTjDC0CZjTov_FUPJ$MUB4!sNIfcIe^
ztX>_Ju4aKp}k4Cxl0yK>0G^2kl9lu#A!m3U}_EcYnHSlLp-oF9G
zvEM_H`fuq<;BIZo#(4;8SVK4gOO6q=00_b2nup^kb|4B?vI*#qfq;+CPvAr^&-HF|e
z?UMmbzMIk-rAWD^+K%nQqz1NO(jLNW?Bf{861D+;tK+_85!#2Js(T?ag*q1D1K1!Y
ztqzmaUx|tSH)B%lDU8o~6n*JRT205upfF$$!eiJBY#LT+eQ~D+?!HtdwJt
zYMaRxJ)=7^866aLr{$G**510yq>t$x9@jH&`h;%kDV98ct?JCnbLLN+HTh&>OFih6
za<;4E1C!-UZF;%IokEYVd#kfvu~2TUTTuRf?H}fKmOM{%KVUo`soTb$nR$EF{8)-T
z#lX&TWeI{`{?4q&@8u3A^g2D^SLIN(!~PWS_IkumbsN`smZN%#!@8IaxIZ_&an0Dl
z*&|i$drE~AfhuNoUM{UuCKxs>iFS^b`AOYnvR26nwC(HG4bjx(*l*?>sqT*5?WjoH
zrY0^hTb`b76O?Y?`>pE(1j`vzNprb2A0oO{#%w4vJOO;VCJjC!!bTTbxnI8|KpFUW3LX
zHwtJr&q^Q5dBqY=M0=H=Y-lN`8p>K*B%4=qT`3B2#UafpyZGYC@&^rHnZ7C6T0S#B
zQ?ohIvLVs3@#O_gRil$7n%G~+nN*4O3EgXt7ducqEvb>}^KslDLENUIt>s=bp<}W>4
z%G$YO#tw8-e?SC8g*_GrRSx${B#s+Bo7{**+Zz^c{OR#^G%s0RwAgE&;;^}X-cYd+
zs~LUd$|H+EsOvZul+eIT*7DIMtE-koJ|wo0)9F0LnI{&ZPUTa#ynHzCAbrZ%1>GO|
zsAX2g@1)v6q6~V*<`d^QIa45@YQQ#9)7Q+@IU~LYXhq~*?H3&W{dXIyt41ZlY+(}%&tXqy+V4+aJ!cw3VDE5HTE{aDvtUU~rbeBUBk&?z>leIQc
zW3AP`5R)2(qA@16YVw+B(^k~KB&kO*{Gn;I+IU5crfFho4DtJW_pOtC-p|Z?&CE0N
z?u&btLeX!1@l%Gfmsm+$^BNPyiwPVkpC%fUf%E9YZ}4XP1C#JNW}=ry4*F5oD{&AT
zF@m#Lg-OY=_cUOxx@xC#D;FZzgop82ynr3pmSRjbp2U545jCJndRdN}umQU<3yu@Em|w2jIVm3$2GcBQNzIc1KcI(ii+@HF;gDZ}57&!b*^
z74>{6)9l0TxE0?-Ez$3&>t05yi58+V5cIMBn!#rGLZ^G77wOB4;(Pc6_Fx?g+l`Z`
znf;2I$yK-Q_Z!3ZFnP!$rVMpo6KdwIr~$O&7an7F`N=qgY`A!JOZ2T6j79;Bkw97Uz(
z1Zr)jUEg*6*mVJQ-xbsh|3YOdjXw_!pa_-v0bGX@sQce>J&zj5JhIoK<}wwHIGMNU
zbIL}o;VRUNgV>2JsNFqEX6aZ2`)Oa{y$N%
zIPo`*;ZZ)?0bIfj*dH*4teR68#`AbL7P9j+lSi-^pF&OKE!4ozB4aV1p*sA{z5XvM
zBPm7vhtR*trqYM!FoJojVlz2{TH|A=KdATJ^K+;a&!aMS3AKd3pf=~9=s^`_vV+i4
zY$a|K?J*5X{X1nnN2RfuxR0Bkpi&v02wruOgJ83SuvzvMYwO
z1lkW9-L?o3L8s09Z*wZK?^IQNUaW+(hK)KwX(P1h?oxwN7sJ_@mDC%EfP1b4O9}0n
zB!Vy6bP+9tHmH_VWsMHjRARa{lNjH+n}&9U7F63-AE}o}AXK!&YMff^gI9GYp&h!N
zxSg=;Q${`3^3*EpMCv{(xa=$2msaAjy3(Ka4LueK2X>4N?T?sHWF#`yH*A+@OnB_?
zG9QSyk7OlztZ>ep70P`jt|nM-{hS+4tgEc9sjOXVcdjV&*uItLJXUXhmerGAU@hi9
zY8@{4A-QY3ZzS9|77kd|9mnSqr>^rLv7Kl0nj}r@w7!d-7a=Zf94E1p=I!Q5(su7|?r!nkp0m4W
z6H9QZf>0?+TezlbONxkElmbPxIBh{qkw_E-0>neA1ca1x|v8;mxoB8{ttn8=i>YpN0j-FF>PIMK$u(avluA>F_L^49~;)@TV{d
zFGJaA1~2Ep7B~xTfU?mcxCAlCGS!|%dr?5`3ZQ>vW@P4Gdu
z5z5KF0cXHh;ad1}I1^5os#G(a2W6u+*aEjf4a;y1d=lcb`UMnae;1Gc0Y%(#Y_gF3
z)kHe7;TiF`t7n?^U=EUV@^`pP(GzpO8P*#={is
zgcVd#{dDBTXQ71PQ7F?RJ(hcZq=aiD{r^>7EA03U;$^{J
z*acsK62cGpk@r)HpBy*^Ig{#`iT)&_+Rg*n=m3;h<)A2X0^+QC0?LPH{1;=r@;
z`w6AK1=ldnkbxRL1Mh*aLpj+;kUuqr)5v%kL_F02rD}FUQKXWmBT|0>y6_Y%z&dKL
z8x|pZsaK#l@Jp!SAK(i3Z^+?Q8>`V(-B9ic17*YGP(u0yl#{;!x4?HH4p-Ejr0Q87
z^ue95Sw|jc;7Z12Xu)qnk$lpkD5i7a#~HUnalnSCtd7H5;nQ##{2{yzegI|Tsf(jf
zAB0-!KTn5CO+6g*D^NZ>4Q1gULy_z)NS>)HP(m`19Dvu(Fj0$LWXyKZ9%FoA7=(8Qn++)dz2f4?{WWOEG^0pJMzQ=*T=K
z9frS!>)>j;`6vf)%3-E5}!OJiY58WAE@fY9&jF+^c|3o?`T9qQK>QRVu>Sb6De-9hr
zdr%zvD-@}(!kgfvWK=y(Fu_<7(!y#@4x!`0YAA=5gGwu4og(jU8PRrNDNH<-W~xW)
zo2$p`>%vP~o`{&@t~4piOxTNlIFdSuwPOyp89RcB`=7#Or=!?jOj;NAX>6AaXrI9D
z!Q{G~
zq)A#z>XZ3l?cEHe*wYx-c@%x=N?MJ_BuEl4iDDl%0UL+a+Mc-62Df0_u(en>c01N9
z+E&)nyBm|X9V=pT8A{t2VdPgTi98PzJPTJ~k|O&sX_C-#P2{kWh|#q@&j$$0oyCNMdJoKsU16GT;J#c%dxXo
zV3%B-KGtvfzR50AEqlz-vh5jPThWBtbYArur#+X9KDXVx_Ki1Sy#KS}@K5Ud6P3<#
zP)hFYXFH>NO%U+8uf{x#21UHVXXlvS;`#v{9er~BTRVPR4j1(GV8OWB8nA4~N;@W{
zHke$=GrBXI)j>&jSzdKZ!-9KFrcZD7IG%Ac$8<|isqFd7R98WsGk;{S(I+EI>VBu3
zw_P1?n5t$QGV?9Y6ncE!Tb=Vt#cF%wjOrT=Z%yked!FdN&v-skuN!`P;%$lPu@pN>
zft};X5(K~c+N8&Jat0H6of+|~wyD}|e};FvJmROijcYv1Q9Y$WUCITVpOfDB&G3QA
zLy3+ZrR(R*pXNQ+lJxS!E~Cwy)b)L{n44zn*fWt~++OvnKI6
zHFAK#TDLc_g0f#5jYzjnlOu9+z0@C0HZ>$xO0bM8k;=B}pkQk94cY|!h9lI}Y_4d_
z&1%2Ef!(pPY2qTQ>@RuQT_i4Z$43
z`ZdY670I@h-<#2xsEn3qa&Iwj(q+C+>TULrafia{v<=JV^~Ljq&+nJMaVf-2;#1am
zRJQFJoxgbA3GBRyzlUhv8{6U%$3yH;F*TaY7HS
zE!-#*P(3aOKT{wiVfltz#~JazC$#E8vh50sH@<#+9nDKsug-d?b&S*I
z`gsGTVytL%(N&$<|7z^)3(DwVM(f$kS(=y=`HwZ-Dbv22k@GmWDNK_=sEMX7uebB1=C9Z+aT7IlNDU_?JqOVt9
zOW_R}J0Iv9Rn|y5QnSfUsef(@u7I&y9%Rp0xmwDM3ICX!4d>zO$cnnhb_3((DZuPV
u8fD8~ra;CPyr9~;q^823?
delta 2165
zcmX}se@xVM9LMp`omYS&aMuZfi3c14$sNB$AW}rZ!{~<~m7)`ze9>#fF09rlcb0#Y
zR<`-oRaE{cw`{sq`}zm9(AlbG+1jZ7u~u3u=T@6D|0rv%=j-lkpYguO=X2lheSW+@
zpASd+cXuU!2>1>d$_C42sLmNmt#9>
z*PcfH%(Hxub@LJ~#>03sev2jeJtn)T{7a=6WAv_+4I^Kf7f}Np#Bn@
z$5&CmuV9&*aTTt_W2i0q4fWj1Y_-r5R0g5}_FpS#b074&4+fC2Oaf2hPTYizY-}Iy
zN3HBKY9&|Qb}(oR$HUA=eqt(7@3o><-hrAxH%@wtSr;V#OwRZ@IXZ-8sCFCDr|H20
zv`{P9fy&HlIEu${8y4|W40j=4nJ;iY{)AlxQ+)O$apR(J)KsT}@!bYLke^;>Zj-$8^SD;S!tEeqH;UllJ?l75mtyC76RcGo8o~<|tO+8PozVr)2-H
zQ+b&$W^q|Z^dfFVf5@2IaTLj_c^l*SG2V?)PM%isB!c
zm8LUNfX5i$gsBYTmpFh?PK+|J7fF$M1@#Z=Bh(5%N2T~1RK_l$w(t+s;k=F>R8c=Y
zgtlTOaii#rX;KKEhsR^nm#U#!=KrAQL5Xw>;
zaUY=xYnycFRJsWccrtZ8l(t$z>0Cyrv=U_O|Du(t%pg{$YUx$$_S6u{P&KiEP|=xD
z(H7JZOVw~o45O(!>%YzYl4Fo6x?b!=Y7bk|Uzsk{p}Wg%H@fy>glHy0gi1M~gQhc+
zNpO?RT4DvE?W-bG7U{!2kdbR2&hT}tr=feI4ecj%Nxg)Qyoyd(eX5qe@T%TV=!C8!
z?jW3|nF)_QC%eYpmpy1lXMgPka>_mS+T3RX@yD&9(3-LM7R$t}QEP1Th*Om}?s0zf
zZ}d64@-sd5(A?8@tnl?2_0cB#QsGcWW2CM=(opO47WqBS-}653*tNxt_Ehmsd&h!b
zokv4qkA14-xPSX2i7_j_-L#L4Y_%S;;-k(dr4t_eVyM#jDtyZ4T&SY&aCOAKvgn}w
HUUlL>w};#A
diff --git a/languages/wc-tier-package-prices-de_DE.po b/languages/wc-tier-package-prices-de_DE.po
index ee2d551..4490949 100644
--- a/languages/wc-tier-package-prices-de_DE.po
+++ b/languages/wc-tier-package-prices-de_DE.po
@@ -1,12 +1,12 @@
# German (Germany) translation for WooCommerce Tier and Package Prices
-# Copyright (C) 2025 Marco Graetsch
+# Copyright (C) 2026 Marco Graetsch
# This file is distributed under the GPL v2 or later.
msgid ""
msgstr ""
-"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
+"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
-"POT-Creation-Date: 2025-12-30 00:00+0000\n"
-"PO-Revision-Date: 2025-12-30 00:00+0000\n"
+"POT-Creation-Date: 2026-02-03 00:00+0000\n"
+"PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -398,3 +398,98 @@ msgstr "Aktivierung fehlgeschlagen."
#: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again."
msgstr "Anfrage fehlgeschlagen. Bitte versuchen Sie es erneut."
+
+#. v1.4.1 - Auto-Updates and License Bypass
+
+#: includes/class-wc-tpp-settings.php
+msgid "Auto-Updates"
+msgstr "Auto-Updates"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Auto-Update Settings"
+msgstr "Auto-Update Einstellungen"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Configure automatic plugin updates from the license server."
+msgstr "Konfigurieren Sie automatische Plugin-Updates vom Lizenzserver."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Enable Update Notifications"
+msgstr "Update-Benachrichtigungen aktivieren"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check for available plugin updates."
+msgstr "Nach verfügbaren Plugin-Updates suchen."
+
+#: includes/class-wc-tpp-settings.php
+msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
+msgstr "Wenn aktiviert, prüft das Plugin auf Updates vom Lizenzserver und zeigt Benachrichtigungen im WordPress-Admin an."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Automatically Install Updates"
+msgstr "Updates automatisch installieren"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Automatically install updates when available."
+msgstr "Updates automatisch installieren, wenn verfügbar."
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Requires a valid license)"
+msgstr "(Erfordert eine gültige Lizenz)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
+msgstr "Wenn aktiviert, werden Updates automatisch installiert, wenn WordPress Hintergrund-Updates durchführt."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check Frequency (Hours)"
+msgstr "Prüfhäufigkeit (Stunden)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "How often to check for updates."
+msgstr "Wie oft nach Updates gesucht werden soll."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Number of hours between update checks. Default is 12 hours."
+msgstr "Anzahl der Stunden zwischen Update-Prüfungen. Standard ist 12 Stunden."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Status"
+msgstr "Update-Status"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check for Updates"
+msgstr "Nach Updates suchen"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Current Version:"
+msgstr "Aktuelle Version:"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Available:"
+msgstr "Update verfügbar:"
+
+#: includes/class-wc-tpp-settings.php
+msgid "You are running the latest version."
+msgstr "Sie verwenden die neueste Version."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Now"
+msgstr "Jetzt aktualisieren"
+
+#: includes/class-wc-tpp-settings.php
+#. translators: %s: Version number
+msgid "Update available: version %s"
+msgstr "Update verfügbar: Version %s"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Failed to check for updates."
+msgstr "Fehler beim Suchen nach Updates."
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Localhost environment - license validation bypassed)"
+msgstr "(Localhost-Umgebung - Lizenzvalidierung übersprungen)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Self-licensing server - license validation bypassed)"
+msgstr "(Selbstlizenzierender Server - Lizenzvalidierung übersprungen)"
diff --git a/languages/wc-tier-package-prices-de_DE_informal.mo b/languages/wc-tier-package-prices-de_DE_informal.mo
index b338813dce3a9d9739490df7410fc3494f4befbe..ec27219e6cad51cfc0f38f22fe10cf1046918329 100644
GIT binary patch
delta 4425
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
Y8~In6-Y1*hC!5|Uo8rUmyY|WEzxUg{k^lez
delta 2162
zcmbu=%Wsrb7{~EvhT2x;<~V4}*gCbA*6K`Yp`~R63bwR5UTCdI#X?Ct4JAfK7GP@P
zNMizN1eCYL1fyJJK?u>s%fc=Q8yBE##GuAdBQXXMSV&-@F~;xjeGmQvp7fc|Iq&6M
zpP5dtJ!bh+9#!0-nE%56amRv$=QEYUP^rW&de@eXAeU?(YM@VW3{T<+)-wGe+>7e?
zChGlKmbnR6<5PGXbwt-u_oK{K3#~%!Ksv_xYXvLZjb3+SBQlod@f5y;8?c#!?ZZjb
z%C4eTa?`craWm4xDv(#K4)t6Hwek+s1hP04Fir*ZmVWB4P#NRG
zZB%yWd0N^1J*wjj%Txz>RCez}W%nPbBWg~D_p_*#Zb9w94%7m6qpp91!Oox-I)$eS
zRL)V^fNzt^eK?I;S<@roN;0VS04lpjP#wSFuD^>~`5x3jlSpz0Mb9hIv=B}DPSFH3
z8I2zlB~K@@l6Zl5p6DR%6isZ2YjA`?DdJOGs>1FE`S3Ef6Kja)h!w<3geLq95hql#
z1iM$TYAQPag@iJEIiZpv*ogm%&P-(n@pMoNSG|aO1EDN`f>=+etP3$tHTZ@tb8XQ@
zq=Po=uMaBW8LC=Fu!F(ZtW6gv-Gs9ISv4rlA=Zm2LYbc=RBDJ?f-g>RYj(!?CJbmY0A^4%ND4~>Bd6Z}hYGwek|f&OKknWo&O6d3EN5l
diff --git a/languages/wc-tier-package-prices-en_US.po b/languages/wc-tier-package-prices-en_US.po
index 1ff2228..ae2f68c 100644
--- a/languages/wc-tier-package-prices-en_US.po
+++ b/languages/wc-tier-package-prices-en_US.po
@@ -1,12 +1,12 @@
# English (US) translation for WooCommerce Tier and Package Prices
-# Copyright (C) 2025 Marco Graetsch
+# Copyright (C) 2026 Marco Graetsch
# This file is distributed under the GPL v2 or later.
msgid ""
msgstr ""
-"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
+"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
-"POT-Creation-Date: 2025-12-30 00:00+0000\n"
-"PO-Revision-Date: 2025-12-30 00:00+0000\n"
+"POT-Creation-Date: 2026-02-03 00:00+0000\n"
+"PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n"
"Language-Team: English\n"
"Language: en_US\n"
@@ -398,3 +398,98 @@ msgstr "Activation failed."
#: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again."
msgstr "Request failed. Please try again."
+
+#. v1.4.1 - Auto-Updates and License Bypass
+
+#: includes/class-wc-tpp-settings.php
+msgid "Auto-Updates"
+msgstr "Auto-Updates"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Auto-Update Settings"
+msgstr "Auto-Update Settings"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Configure automatic plugin updates from the license server."
+msgstr "Configure automatic plugin updates from the license server."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Enable Update Notifications"
+msgstr "Enable Update Notifications"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check for available plugin updates."
+msgstr "Check for available plugin updates."
+
+#: includes/class-wc-tpp-settings.php
+msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
+msgstr "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Automatically Install Updates"
+msgstr "Automatically Install Updates"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Automatically install updates when available."
+msgstr "Automatically install updates when available."
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Requires a valid license)"
+msgstr "(Requires a valid license)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
+msgstr "When enabled, updates will be automatically installed when WordPress performs background updates."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check Frequency (Hours)"
+msgstr "Check Frequency (Hours)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "How often to check for updates."
+msgstr "How often to check for updates."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Number of hours between update checks. Default is 12 hours."
+msgstr "Number of hours between update checks. Default is 12 hours."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Status"
+msgstr "Update Status"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Check for Updates"
+msgstr "Check for Updates"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Current Version:"
+msgstr "Current Version:"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Available:"
+msgstr "Update Available:"
+
+#: includes/class-wc-tpp-settings.php
+msgid "You are running the latest version."
+msgstr "You are running the latest version."
+
+#: includes/class-wc-tpp-settings.php
+msgid "Update Now"
+msgstr "Update Now"
+
+#: includes/class-wc-tpp-settings.php
+#. translators: %s: Version number
+msgid "Update available: version %s"
+msgstr "Update available: version %s"
+
+#: includes/class-wc-tpp-settings.php
+msgid "Failed to check for updates."
+msgstr "Failed to check for updates."
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Localhost environment - license validation bypassed)"
+msgstr "(Localhost environment - license validation bypassed)"
+
+#: includes/class-wc-tpp-settings.php
+msgid "(Self-licensing server - license validation bypassed)"
+msgstr "(Self-licensing server - license validation bypassed)"
diff --git a/languages/wc-tier-package-prices-fr_CH.mo b/languages/wc-tier-package-prices-fr_CH.mo
index b058265a0e3e4baa84e1eb36a69ac5d392a1bd6d..2402f966dd99992a9fb85504ff917d5c0cfdaf2a 100644
GIT binary patch
delta 4637
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