Version 1.3.1 - Switch to SecureLicenseClient with signature verification

- Upgraded from LicenseClient to SecureLicenseClient with HMAC-SHA256 response signature verification
- Added Server Secret configuration field for secure communication
- Added rate limit exception handling with retry time display
- Added signature verification error handling
- Added URL validation error handling (SSRF protection)
- Updated all translation files with new strings
- Compiled .mo files for all 7 language variants

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-27 19:23:42 +01:00
parent cbe758267e
commit 7286459ff2
19 changed files with 317 additions and 51 deletions

View File

@@ -5,6 +5,46 @@ 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.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

View File

@@ -1,7 +1,7 @@
{
"name": "magdev/wc-tier-package-prices",
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
"version": "1.3.0",
"version": "1.3.1",
"type": "wordpress-plugin",
"license": "GPL-2.0-or-later",
"authors": [
@@ -19,7 +19,7 @@
"require": {
"php": ">=8.3",
"twig/twig": "^3.0",
"magdev/wc-licensed-product-client": "^0.1"
"magdev/wc-licensed-product-client": "dev-main"
},
"autoload": {
"classmap": [

View File

@@ -154,6 +154,16 @@ if (!class_exists('WC_TPP_Settings')) {
'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',
@@ -188,13 +198,14 @@ if (!class_exists('WC_TPP_Settings')) {
$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)) {
wp_send_json_error(array('message' => __('License key and server URL are required.', 'wc-tier-package-prices')));
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);
$client = $this->get_license_client($server_url, $server_secret);
$domain = $this->get_current_domain();
$result = $client->validate($license_key, $domain);
@@ -212,16 +223,37 @@ if (!class_exists('WC_TPP_Settings')) {
'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' => $e->getMessage(),
'message' => __('An unexpected error occurred. Please try again.', 'wc-tier-package-prices'),
'code' => 'exception',
));
}
@@ -239,13 +271,14 @@ if (!class_exists('WC_TPP_Settings')) {
$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)) {
wp_send_json_error(array('message' => __('License key and server URL are required.', 'wc-tier-package-prices')));
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);
$client = $this->get_license_client($server_url, $server_secret);
$domain = $this->get_current_domain();
$result = $client->activate($license_key, $domain);
@@ -269,14 +302,34 @@ if (!class_exists('WC_TPP_Settings')) {
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 (\Exception $e) {
} 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',
));
}
@@ -285,14 +338,18 @@ if (!class_exists('WC_TPP_Settings')) {
/**
* Get license client instance
*
* @param string $server_url License server URL.
* 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): \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\LicenseClient(
return new \Magdev\WcLicensedProductClient\SecureLicenseClient(
httpClient: $httpClient,
baseUrl: $server_url,
serverSecret: $server_secret,
);
}
@@ -402,6 +459,7 @@ if (!class_exists('WC_TPP_Settings')) {
return {
license_key: $('#wc_tpp_license_key').val(),
server_url: $('#wc_tpp_license_server_url').val(),
server_secret: $('#wc_tpp_license_server_secret').val(),
nonce: '<?php echo esc_js($nonce); ?>'
};
}
@@ -420,8 +478,8 @@ if (!class_exists('WC_TPP_Settings')) {
$validateBtn.on('click', function() {
var data = getLicenseData();
if (!data.license_key || !data.server_url) {
alert('<?php echo esc_js(__('Please enter both license server URL and license key.', 'wc-tier-package-prices')); ?>');
if (!data.license_key || !data.server_url || !data.server_secret) {
alert('<?php echo esc_js(__('Please enter license server URL, license key, and server secret.', 'wc-tier-package-prices')); ?>');
return;
}
@@ -443,8 +501,8 @@ if (!class_exists('WC_TPP_Settings')) {
$activateBtn.on('click', function() {
var data = getLicenseData();
if (!data.license_key || !data.server_url) {
alert('<?php echo esc_js(__('Please enter both license server URL and license key.', 'wc-tier-package-prices')); ?>');
if (!data.license_key || !data.server_url || !data.server_secret) {
alert('<?php echo esc_js(__('Please enter license server URL, license key, and server secret.', 'wc-tier-package-prices')); ?>');
return;
}

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "Lizenzschlüssel und Server-URL sind erforderlich."
msgid "Server Secret"
msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Bitte geben Sie sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein."
msgid "The shared secret for secure communication with the license server."
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Anfragelimit überschritten. Bitte versuchen Sie es in %d Sekunden erneut."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfen Sie Ihr Server-Geheimnis."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Bitte geben Sie die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "Lizenzschlüssel und Server-URL sind erforderlich."
msgid "Server Secret"
msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Bitte gib sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein."
msgid "The shared secret for secure communication with the license server."
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Anfragelimit überschritten. Bitte versuche es in %d Sekunden erneut."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfe dein Server-Geheimnis."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Bitte gib die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "Lizenzschlüssel und Server-URL sind erforderlich."
msgid "Server Secret"
msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Bitte geben Sie sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein."
msgid "The shared secret for secure communication with the license server."
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Anfragelimit überschritten. Bitte versuchen Sie es in %d Sekunden erneut."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfen Sie Ihr Server-Geheimnis."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Bitte geben Sie die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "Lizenzschlüssel und Server-URL sind erforderlich."
msgid "Server Secret"
msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Bitte gib sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein."
msgid "The shared secret for secure communication with the license server."
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Anfragelimit überschritten. Bitte versuche es in %d Sekunden erneut."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfe dein Server-Geheimnis."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Bitte gib die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Permission denied."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "License key and server URL are required."
msgid "Server Secret"
msgstr "Server Secret"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Please enter both license server URL and license key."
msgid "The shared secret for secure communication with the license server."
msgstr "The shared secret for secure communication with the license server."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "License key, server URL, and server secret are required."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Rate limit exceeded. Please try again in %d seconds."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "Response signature verification failed. Please check your server secret."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "An unexpected error occurred. Please try again."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Please enter license server URL, license key, and server secret."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Accès refusé."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "La clé de licence et l'URL du serveur sont requises."
msgid "Server Secret"
msgstr "Secret serveur"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Veuillez entrer l'URL du serveur de licence et la clé de licence."
msgid "The shared secret for secure communication with the license server."
msgstr "Le secret partagé pour la communication sécurisée avec le serveur de licence."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "La clé de licence, l'URL du serveur et le secret serveur sont requis."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Limite de requêtes dépassée. Veuillez réessayer dans %d secondes."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "La vérification de la signature a échoué. Veuillez vérifier votre secret serveur."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "Une erreur inattendue s'est produite. Veuillez réessayer."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Veuillez entrer l'URL du serveur de licence, la clé de licence et le secret serveur."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Accesso negato."
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgstr "La chiave di licenza e l'URL del server sono obbligatori."
msgid "Server Secret"
msgstr "Segreto del server"
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgstr "Inserisci sia l'URL del server di licenza che la chiave di licenza."
msgid "The shared secret for secure communication with the license server."
msgstr "Il segreto condiviso per la comunicazione sicura con il server di licenza."
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr "La chiave di licenza, l'URL del server e il segreto del server sono obbligatori."
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr "Limite di richieste superato. Per favore riprova tra %d secondi."
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr "Verifica della firma della risposta fallita. Per favore controlla il tuo segreto del server."
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr "Si è verificato un errore imprevisto. Per favore riprova."
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr "Inserisci l'URL del server di licenza, la chiave di licenza e il segreto del server."
#: includes/class-wc-tpp-settings.php
msgid "Validation failed."

View File

@@ -2,9 +2,9 @@
# 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.3.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"
"POT-Creation-Date: 2026-01-27 00:00+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -332,11 +332,32 @@ msgid "Permission denied."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required."
msgid "Server Secret"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Please enter both license server URL and license key."
msgid "The shared secret for secure communication with the license server."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "License key, server URL, and server secret are required."
msgstr ""
#: includes/class-wc-tpp-settings.php
#. translators: %d: Number of seconds to wait
msgid "Rate limit exceeded. Please try again in %d seconds."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Response signature verification failed. Please check your server secret."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "An unexpected error occurred. Please try again."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Please enter license server URL, license key, and server secret."
msgstr ""
#: includes/class-wc-tpp-settings.php

View File

@@ -4,7 +4,7 @@
* Plugin Name: WooCommerce Tier and Package Prices
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices
* Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices
* Version: 1.3.0
* Version: 1.3.1
* Author: Marco Graetsch
* Author URI: https://src.bundespruefstelle.ch/magdev
* Text Domain: wc-tier-package-prices
@@ -46,7 +46,7 @@ if (!function_exists('wc_tpp_php_version_notice')) {
// Define plugin constants
if (!defined('WC_TPP_VERSION')) {
define('WC_TPP_VERSION', '1.3.0');
define('WC_TPP_VERSION', '1.3.1');
}
if (!defined('WC_TPP_PLUGIN_DIR')) {
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));