id = 'tier_package_prices'; $this->label = __('Tier & Package Prices', 'wc-tier-package-prices'); parent::__construct(); // 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')); } /** * Get own sections - Modern WooCommerce pattern * * @return array */ protected function get_own_sections() { return array( '' => __('General', 'wc-tier-package-prices'), 'license' => __('License', 'wc-tier-package-prices'), 'updates' => __('Auto-Updates', 'wc-tier-package-prices'), ); } /** * Get settings for the default (General) section * * @return array */ protected function get_settings_for_default_section() { return array( array( 'title' => __('Tier & Package Prices Settings', 'wc-tier-package-prices'), 'type' => 'title', 'desc' => __('Configure tier pricing and package pricing options for your WooCommerce products.', 'wc-tier-package-prices'), 'id' => 'wc_tpp_settings', ), array( 'title' => __('Enable Tier Pricing', 'wc-tier-package-prices'), 'desc' => __('Enable tier pricing for products', 'wc-tier-package-prices'), 'id' => 'wc_tpp_enable_tier_pricing', 'default' => 'yes', 'type' => 'checkbox', 'desc_tip' => __('Allow quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities.', 'wc-tier-package-prices'), ), array( 'title' => __('Enable Package Pricing', 'wc-tier-package-prices'), 'desc' => __('Enable fixed-price packages for products', 'wc-tier-package-prices'), 'id' => 'wc_tpp_enable_package_pricing', 'default' => 'yes', 'type' => 'checkbox', 'desc_tip' => __('Allow fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100.', 'wc-tier-package-prices'), ), array( 'title' => __('Display Pricing Table', 'wc-tier-package-prices'), 'desc' => __('Show tier and package pricing table on product pages', 'wc-tier-package-prices'), 'id' => 'wc_tpp_display_table', 'default' => 'yes', 'type' => 'checkbox', 'desc_tip' => __('Display the pricing table to customers on product pages.', 'wc-tier-package-prices'), ), array( 'title' => __('Display Position', 'wc-tier-package-prices'), 'desc' => __('Choose where to display the pricing table on product pages.', 'wc-tier-package-prices'), 'id' => 'wc_tpp_display_position', 'default' => 'after_add_to_cart', 'type' => 'select', 'class' => 'wc-enhanced-select', 'css' => 'min-width:300px;', 'desc_tip' => true, 'options' => array( 'before_add_to_cart' => __('Before Add to Cart Button', 'wc-tier-package-prices'), 'after_add_to_cart' => __('After Add to Cart Button', 'wc-tier-package-prices'), 'after_price' => __('After Price', 'wc-tier-package-prices'), ), ), array( 'title' => __('Restrict to Package Quantities', 'wc-tier-package-prices'), 'desc' => __('Limit quantities to defined package sizes only', 'wc-tier-package-prices'), 'id' => 'wc_tpp_restrict_package_quantities', 'default' => 'no', 'type' => 'checkbox', 'desc_tip' => __('When enabled, customers can only purchase products in the exact quantities defined in packages. The quantity input field will be hidden and replaced with package selection buttons.', 'wc-tier-package-prices'), ), array( 'type' => 'sectionend', 'id' => 'wc_tpp_settings', ), ); } /** * Get settings for the License section * * @return array */ protected function get_settings_for_license_section() { return array( array( 'title' => __('License Management', 'wc-tier-package-prices'), 'type' => 'title', 'desc' => __('Enter your license key to receive updates and support.', 'wc-tier-package-prices'), 'id' => 'wc_tpp_license_settings', ), array( 'title' => __('License Server URL', 'wc-tier-package-prices'), 'desc' => __('The URL of the license server.', 'wc-tier-package-prices'), 'id' => 'wc_tpp_license_server_url', 'type' => 'url', 'default' => '', 'css' => 'min-width:400px;', 'desc_tip' => true, ), array( 'title' => __('License Key', 'wc-tier-package-prices'), 'desc' => __('Your license key for this plugin.', 'wc-tier-package-prices'), 'id' => 'wc_tpp_license_key', 'type' => 'text', 'default' => '', 'css' => 'min-width:400px;', 'desc_tip' => true, ), array( 'title' => __('Server Secret', 'wc-tier-package-prices'), 'desc' => __('The shared secret for secure communication with the license server.', 'wc-tier-package-prices'), 'id' => 'wc_tpp_license_server_secret', 'type' => 'password', 'default' => '', 'css' => 'min-width:400px;', 'desc_tip' => true, ), array( 'title' => __('License Status', 'wc-tier-package-prices'), 'type' => 'wc_tpp_license_status', 'id' => 'wc_tpp_license_status_display', ), array( 'type' => 'sectionend', 'id' => 'wc_tpp_license_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 * * @return array|false */ private function get_cached_license_status() { return get_transient('wc_tpp_license_status'); } /** * AJAX handler for license validation */ public function ajax_validate_license() { check_ajax_referer('wc_tpp_license_nonce', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(array('message' => __('Permission denied.', 'wc-tier-package-prices'))); } $license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? '')); $server_url = esc_url_raw(wp_unslash($_POST['server_url'] ?? '')); $server_secret = sanitize_text_field(wp_unslash($_POST['server_secret'] ?? '')); if (empty($license_key) || empty($server_url) || empty($server_secret)) { wp_send_json_error(array('message' => __('License key, server URL, and server secret are required.', 'wc-tier-package-prices'))); } try { $client = $this->get_license_client($server_url, $server_secret); $domain = $this->get_current_domain(); $result = $client->validate($license_key, $domain); // Cache the status set_transient('wc_tpp_license_status', 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'), ), DAY_IN_SECONDS); wp_send_json_success(array( 'message' => __('License is valid!', 'wc-tier-package-prices'), 'status' => $this->get_cached_license_status(), )); } catch (\Magdev\WcLicensedProductClient\Exception\RateLimitExceededException $e) { wp_send_json_error(array( 'message' => sprintf( /* translators: %d: Number of seconds to wait */ __('Rate limit exceeded. Please try again in %d seconds.', 'wc-tier-package-prices'), $e->retryAfter ?? 60 ), 'code' => 'rate_limit_exceeded', 'retry_after' => $e->retryAfter, )); } catch (\Magdev\WcLicensedProductClient\Security\SignatureException $e) { delete_transient('wc_tpp_license_status'); wp_send_json_error(array( 'message' => __('Response signature verification failed. Please check your server secret.', 'wc-tier-package-prices'), 'code' => 'signature_error', )); } catch (\Magdev\WcLicensedProductClient\Exception\LicenseException $e) { delete_transient('wc_tpp_license_status'); wp_send_json_error(array( 'message' => $e->getMessage(), 'code' => $e->errorCode ?? 'unknown', )); } catch (\InvalidArgumentException $e) { wp_send_json_error(array( 'message' => $e->getMessage(), 'code' => 'invalid_url', )); } catch (\Exception $e) { delete_transient('wc_tpp_license_status'); wp_send_json_error(array( 'message' => __('An unexpected error occurred. Please try again.', 'wc-tier-package-prices'), 'code' => 'exception', )); } } /** * AJAX handler for license activation */ public function ajax_activate_license() { check_ajax_referer('wc_tpp_license_nonce', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(array('message' => __('Permission denied.', 'wc-tier-package-prices'))); } $license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? '')); $server_url = esc_url_raw(wp_unslash($_POST['server_url'] ?? '')); $server_secret = sanitize_text_field(wp_unslash($_POST['server_secret'] ?? '')); if (empty($license_key) || empty($server_url) || empty($server_secret)) { wp_send_json_error(array('message' => __('License key, server URL, and server secret are required.', 'wc-tier-package-prices'))); } try { $client = $this->get_license_client($server_url, $server_secret); $domain = $this->get_current_domain(); $result = $client->activate($license_key, $domain); if ($result->success) { // Validate to get full status after activation $validate_result = $client->validate($license_key, $domain); set_transient('wc_tpp_license_status', array( 'valid' => true, 'product_id' => $validate_result->productId, 'expires_at' => $validate_result->expiresAt?->format('Y-m-d H:i:s'), 'is_lifetime' => $validate_result->isLifetime(), 'checked_at' => current_time('mysql'), ), DAY_IN_SECONDS); wp_send_json_success(array( 'message' => __('License activated successfully!', 'wc-tier-package-prices'), 'status' => $this->get_cached_license_status(), )); } wp_send_json_error(array('message' => $result->message)); } catch (\Magdev\WcLicensedProductClient\Exception\RateLimitExceededException $e) { wp_send_json_error(array( 'message' => sprintf( /* translators: %d: Number of seconds to wait */ __('Rate limit exceeded. Please try again in %d seconds.', 'wc-tier-package-prices'), $e->retryAfter ?? 60 ), 'code' => 'rate_limit_exceeded', 'retry_after' => $e->retryAfter, )); } catch (\Magdev\WcLicensedProductClient\Security\SignatureException $e) { wp_send_json_error(array( 'message' => __('Response signature verification failed. Please check your server secret.', 'wc-tier-package-prices'), 'code' => 'signature_error', )); } catch (\Magdev\WcLicensedProductClient\Exception\LicenseException $e) { wp_send_json_error(array( 'message' => $e->getMessage(), 'code' => $e->errorCode ?? 'unknown', )); } catch (\InvalidArgumentException $e) { wp_send_json_error(array( 'message' => $e->getMessage(), 'code' => 'invalid_url', )); } catch (\Exception $e) { wp_send_json_error(array( 'message' => __('An unexpected error occurred. Please try again.', 'wc-tier-package-prices'), 'code' => 'exception', )); } } /** * Get license client instance * * Uses SecureLicenseClient for HMAC signature verification. * * @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, ); } /** * Get current domain for license validation * * @return string */ private function get_current_domain(): string { return wp_parse_url(home_url(), PHP_URL_HOST); } /** * Output the settings */ public function output() { global $current_section; // 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(); // Add JavaScript for license section if ('license' === $current_section) { $this->output_license_scripts(); } // Add JavaScript for updates section if ('updates' === $current_section) { $this->output_updates_scripts(); } } /** * Output license status custom field * * @param array $value Field configuration. */ public function output_license_status_field($value) { $status = $this->get_cached_license_status(); ?>
render_license_status_html($status); ?>

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; } if (!empty($status['valid'])) { echo '' . esc_html__('License Active', 'wc-tier-package-prices') . ''; if (!empty($status['expires_at']) && empty($status['is_lifetime'])) { echo '
' . sprintf( /* translators: %s: Expiration date */ esc_html__('Expires: %s', 'wc-tier-package-prices'), esc_html($status['expires_at']) ) . ''; } elseif (!empty($status['is_lifetime'])) { echo '
' . esc_html__('Lifetime License', 'wc-tier-package-prices') . ''; } if (!empty($status['checked_at'])) { echo '
' . sprintf( /* translators: %s: Last check timestamp */ esc_html__('Last checked: %s', 'wc-tier-package-prices'), esc_html($status['checked_at']) ) . ''; } } else { echo '' . esc_html__('License Invalid', 'wc-tier-package-prices') . ''; } } /** * 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 */ private function output_license_scripts() { $nonce = wp_create_nonce('wc_tpp_license_nonce'); ?> __('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'); ?>