16 Commits
v1.3.0 ... main

Author SHA1 Message Date
e17bdefe06 Add MARKETING.md to gitignore
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 11:58:05 +01:00
fa26247d1b Version 1.4.1 - Localhost license bypass and auto-updates
All checks were successful
Create Release Package / build-release (push) Successful in 1m3s
Added localhost/self-licensing license bypass and WordPress auto-update
integration from license server.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 11:55:41 +01:00
a4b84f7e41 Fix: Exclude CLAUDE.md from vendor symlink path
All checks were successful
Create Release Package / build-release (push) Successful in 1m4s
Zip follows symlinks, so vendor/magdev/wc-licensed-product-client/CLAUDE.md
was being included. Added exclusions for both lib/ and vendor/magdev/ paths.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 20:33:28 +01:00
2e9c948a07 Remove version field from composer.json
Some checks failed
Create Release Package / build-release (push) Failing after 58s
The version field causes composer validate --strict to fail with a warning.
Version is determined by git tags instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 19:18:34 +01:00
dd4333bd11 Update documentation for lib/ submodule location
Some checks failed
Create Release Package / build-release (push) Failing after 4m44s
- Update file structure in README.md and CLAUDE.md
- Document lib/ directory for git submodules
- Update submodule instructions with relative URL
- Add key learnings about vendor/ vs lib/ conflict

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 19:10:53 +01:00
b909221ae2 Fix CI/CD: Move submodule to lib/ directory like wp-fedistream
- Move submodule from vendor/magdev/ to lib/ to avoid Composer conflicts
- Use relative submodule URL (../wc-licensed-product-client.git)
- Pin submodule to v0.2.2 tag
- Update composer.json with ^0.2 version constraint
- Simplify .gitignore (no vendor exceptions needed)
- Update workflow to exclude lib/.git instead of vendor/.git

Based on working wp-fedistream implementation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 19:09:45 +01:00
d80c9d90f9 Update documentation for v1.4.0 CI/CD release process
Some checks failed
Create Release Package / build-release (push) Failing after 4m36s
- README: Update automated releases section with submodule info
- README: Update file structure with .gitea/workflows and submodule
- README: Add v1.4.0 changelog entry
- CLAUDE.md: Update version to 1.4.0, roadmap to 1.4.1
- CLAUDE.md: Rewrite release process for CI/CD workflow
- CLAUDE.md: Add git submodule documentation
- CLAUDE.md: Add v1.4.0 session history

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 19:03:54 +01:00
2bf0cd82fe Add wc-licensed-product-client as git submodule
Some checks failed
Create Release Package / build-release (push) Failing after 4m36s
- Bundle magdev/wc-licensed-product-client as git submodule
- Update composer.json to use path repository instead of VCS
- Update .gitignore to allow submodule in vendor directory
- Update CI workflow to checkout submodules recursively
- Remove private repository authentication step (no longer needed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:58:01 +01:00
9451cc1965 Version 1.4.0 - Add Gitea CI/CD release pipeline
Some checks failed
Create Release Package / build-release (push) Failing after 1m2s
- Automated release workflow triggered on version tags (v*)
- Validates plugin version matches tag
- Installs production Composer dependencies
- Compiles translation files
- Creates release package with proper exclusions
- Generates SHA256 checksum
- Publishes release to Gitea with changelog notes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:53:39 +01:00
02b0308058 Add Gitea CI/CD release pipeline
- Create automated release workflow triggered on version tags (v*)
- Validates plugin version matches tag version
- Installs production dependencies via Composer
- Compiles translation files (.po to .mo)
- Builds release package with proper exclusions
- Generates SHA256 checksum
- Publishes release to Gitea with changelog notes
- Document automated release process in README

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:49:29 +01:00
38e9506d4e Update v1.3.1 session learnings with release package fixes
- Document vendor .git exclusion requirement for release packages
- Update package size to 775KB (650 files) after proper exclusions
- Add correct zip parent directory path for release creation
- Document .claude/settings.json addition to .gitignore

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 19:36:56 +01:00
74cc56005e Add .claude/settings.json to gitignore
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 19:33:15 +01:00
136eed2bdd Document v1.3.1 session learnings in CLAUDE.md
- Update version to 1.3.1 and date to 2026-01-27
- Replace completed v1.3.1 roadmap with v1.3.2 placeholder
- Update license client dependency to dev-main
- Add v1.3.1 session history documenting SecureLicenseClient switch

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 19:31:06 +01:00
178f86f3e6 Update README for v1.3.1 release
- Added license management feature to admin features list
- Updated PHP requirement from 7.4 to 8.3
- Updated file structure version comment to v1.3.1
- Added v1.3.0 and v1.3.1 changelog entries

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 19:27:01 +01:00
7286459ff2 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>
2026-01-27 19:23:42 +01:00
cbe758267e Document v1.3.0 session learnings in CLAUDE.md
- Updated PHP requirement from 7.4 to 8.3 in compatibility notes
- Added Session History section documenting v1.3.0 release session
- Documented key learnings about license client integration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:44:16 +01:00
27 changed files with 2801 additions and 220 deletions

View File

@@ -0,0 +1,217 @@
name: Create Release Package
on:
push:
tags:
- 'v*'
jobs:
build-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, xml, zip, intl, gettext
tools: composer:v2
- name: Get version from tag
id: version
run: |
VERSION=${GITHUB_REF_NAME#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Building version: $VERSION"
- name: Validate composer.json
run: composer validate --strict
- name: Install Composer dependencies (production)
run: |
composer config platform.php 8.3.0
composer install --no-dev --optimize-autoloader --no-interaction
- name: Install gettext
run: apt-get update && apt-get install -y gettext
- name: Compile translations
run: |
for po in languages/*.po; do
if [ -f "$po" ]; then
mo="${po%.po}.mo"
echo "Compiling $po to $mo"
msgfmt -o "$mo" "$po"
fi
done
- name: Verify plugin version matches tag
run: |
PLUGIN_VERSION=$(grep -oP "Version:\s*\K[0-9]+\.[0-9]+\.[0-9]+" wc-tier-and-package-prices.php | head -1)
TAG_VERSION=${{ steps.version.outputs.version }}
if [ "$PLUGIN_VERSION" != "$TAG_VERSION" ]; then
echo "Error: Plugin version ($PLUGIN_VERSION) does not match tag version ($TAG_VERSION)"
exit 1
fi
echo "Version verified: $PLUGIN_VERSION"
- name: Create release directory
run: mkdir -p releases
- name: Build release package
run: |
VERSION=${{ steps.version.outputs.version }}
PLUGIN_NAME="wc-tier-and-package-prices"
RELEASE_FILE="releases/${PLUGIN_NAME}-${VERSION}.zip"
cd ..
zip -r "${PLUGIN_NAME}/${RELEASE_FILE}" "${PLUGIN_NAME}" \
-x "${PLUGIN_NAME}/.git/*" \
-x "${PLUGIN_NAME}/.gitea/*" \
-x "${PLUGIN_NAME}/.github/*" \
-x "${PLUGIN_NAME}/.vscode/*" \
-x "${PLUGIN_NAME}/.idea/*" \
-x "${PLUGIN_NAME}/.claude/*" \
-x "${PLUGIN_NAME}/CLAUDE.md" \
-x "${PLUGIN_NAME}/wordpress" \
-x "${PLUGIN_NAME}/wordpress/*" \
-x "${PLUGIN_NAME}/core" \
-x "${PLUGIN_NAME}/core/*" \
-x "${PLUGIN_NAME}/wp-core" \
-x "${PLUGIN_NAME}/wp-core/*" \
-x "${PLUGIN_NAME}/releases/*" \
-x "${PLUGIN_NAME}/composer.lock" \
-x "${PLUGIN_NAME}/*.log" \
-x "${PLUGIN_NAME}/logs/*" \
-x "${PLUGIN_NAME}/.gitignore" \
-x "${PLUGIN_NAME}/.gitmodules" \
-x "${PLUGIN_NAME}/.editorconfig" \
-x "${PLUGIN_NAME}/phpcs.xml*" \
-x "${PLUGIN_NAME}/phpunit.xml*" \
-x "${PLUGIN_NAME}/tests/*" \
-x "${PLUGIN_NAME}/templates/cache/*" \
-x "${PLUGIN_NAME}/notes.*" \
-x "${PLUGIN_NAME}/*.po~" \
-x "${PLUGIN_NAME}/*.bak" \
-x "${PLUGIN_NAME}/lib/*/.git/*" \
-x "${PLUGIN_NAME}/lib/*/CLAUDE.md" \
-x "${PLUGIN_NAME}/vendor/magdev/*/.git/*" \
-x "${PLUGIN_NAME}/vendor/magdev/*/CLAUDE.md" \
-x "*.DS_Store" \
-x "*Thumbs.db"
cd "${PLUGIN_NAME}"
echo "Created: ${RELEASE_FILE}"
ls -lh "${RELEASE_FILE}"
- name: Generate checksums
run: |
VERSION=${{ steps.version.outputs.version }}
PLUGIN_NAME="wc-tier-and-package-prices"
RELEASE_FILE="releases/${PLUGIN_NAME}-${VERSION}.zip"
cd releases
sha256sum "${PLUGIN_NAME}-${VERSION}.zip" > "${PLUGIN_NAME}-${VERSION}.zip.sha256"
echo "SHA256:"
cat "${PLUGIN_NAME}-${VERSION}.zip.sha256"
- name: Verify package structure
run: |
set +o pipefail
VERSION=${{ steps.version.outputs.version }}
PLUGIN_NAME="wc-tier-and-package-prices"
echo "Package contents (first 50 entries):"
unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | head -50 || true
# Verify main plugin file exists
if unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -q "${PLUGIN_NAME}/${PLUGIN_NAME}.php"; then
echo "✓ Main plugin file at correct location"
else
echo "✗ Error: Main plugin file not found at ${PLUGIN_NAME}/${PLUGIN_NAME}.php"
exit 1
fi
# Verify vendor directory included
if unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -q "${PLUGIN_NAME}/vendor/"; then
echo "✓ Vendor directory included"
else
echo "✗ Error: Vendor directory not found"
exit 1
fi
# Verify excluded files are not present
if unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -qE "CLAUDE\.md|\.claude/|\.git/|wordpress/"; then
echo "✗ Error: Excluded files found in package"
unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -E "CLAUDE\.md|\.claude/|\.git/|wordpress/"
exit 1
else
echo "✓ No excluded files in package"
fi
- name: Extract changelog for release notes
id: changelog
run: |
VERSION=${{ steps.version.outputs.version }}
# Extract release notes from CHANGELOG.md
NOTES=$(sed -n "/^## \[${VERSION}\]/,/^## \[/p" CHANGELOG.md | sed '$ d' | tail -n +2)
if [ -z "$NOTES" ]; then
NOTES="Release version ${VERSION}"
fi
echo "$NOTES" > release_notes.txt
echo "Release notes extracted"
- name: Create Gitea Release
env:
GITEA_TOKEN: ${{ secrets.SRC_GITEA_TOKEN }}
run: |
VERSION=${{ steps.version.outputs.version }}
TAG_NAME=${{ github.ref_name }}
PLUGIN_NAME="wc-tier-and-package-prices"
# Check if this is a prerelease (contains hyphen like v1.0.0-beta)
PRERELEASE="false"
if [[ "$TAG_NAME" == *-* ]]; then
PRERELEASE="true"
fi
BODY=$(cat release_notes.txt)
# Create release via Gitea API
RELEASE_RESPONSE=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"Release ${VERSION}\", \"body\": $(echo "$BODY" | jq -Rs .), \"draft\": false, \"prerelease\": ${PRERELEASE}}" \
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases")
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | jq -r '.id')
if [ "$RELEASE_ID" == "null" ] || [ -z "$RELEASE_ID" ]; then
echo "Failed to create release:"
echo "$RELEASE_RESPONSE"
exit 1
fi
echo "Created release ID: $RELEASE_ID"
# Upload release assets
for file in "releases/${PLUGIN_NAME}-${VERSION}.zip" "releases/${PLUGIN_NAME}-${VERSION}.zip.sha256"; do
if [ -f "$file" ]; then
FILENAME=$(basename "$file")
echo "Uploading $FILENAME..."
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$file" \
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${FILENAME}"
echo "Uploaded $FILENAME"
fi
done
echo "Release created successfully: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG_NAME}"

6
.gitignore vendored
View File

@@ -37,3 +37,9 @@ wp-core
# Releases (not tracked in git) # Releases (not tracked in git)
/releases/ /releases/
# Claude Code local settings
.claude/settings.json
# Marketing texts (not for public distribution)
MARKETING.md

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "lib/wc-licensed-product-client"]
path = lib/wc-licensed-product-client
url = ../wc-licensed-product-client.git

View File

@@ -5,6 +5,117 @@ 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/), 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). 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
- **Gitea CI/CD Release Pipeline**: Automated release workflow triggered on version tags
- Validates plugin version matches tag version
- Installs production Composer dependencies
- Compiles translation files (.po to .mo)
- Creates release package with proper exclusions
- Generates SHA256 checksum
- Publishes release to Gitea with changelog notes
---
## [1.3.1] - 2026-01-27
### Changed
- **Switched to SecureLicenseClient**: Upgraded from basic `LicenseClient` to `SecureLicenseClient` with HMAC-SHA256 response signature verification for enhanced security against tampering and replay attacks
- **Added Server Secret Configuration**: New "Server Secret" field in License settings for secure communication with the license server
### Added
- **Rate Limit Handling**: Added proper handling of `RateLimitExceededException` with user-friendly messages showing retry wait time
- **Signature Verification Error Handling**: Added dedicated handling for `SignatureException` when response signatures fail verification
- **URL Validation Error Handling**: Added handling for `InvalidArgumentException` from SSRF protection in the license client
### Security
- Response signatures are now verified using HMAC-SHA256 with license-specific derived keys (RFC 5869 HKDF)
- The license client now validates server URLs to prevent SSRF attacks (blocks private IP ranges)
- HTTP connections require HTTPS unless explicitly allowed for localhost testing
### Technical Details
**License Client Upgrade**:
- Changed from `LicenseClient` to `SecureLicenseClient`
- Added `serverSecret` parameter for signature verification
- Library updated from `v0.1.0` to `dev-main` with new security features
**New Exception Handling**:
- `RateLimitExceededException` - shows retry time to user
- `SignatureException` - indicates server secret mismatch
- `InvalidArgumentException` - invalid/blocked URL detected
**New Settings Field**:
- `wc_tpp_license_server_secret` (password type) for the shared secret
---
## [1.3.0] - 2026-01-25 ## [1.3.0] - 2026-01-25
### Breaking Changes ### Breaking Changes

411
CLAUDE.md
View File

@@ -1,7 +1,7 @@
# WooCommerce Tier and Package Prices - AI Context Document # WooCommerce Tier and Package Prices - AI Context Document
**Last Updated:** 2026-01-25 **Last Updated:** 2026-02-03
**Current Version:** 1.3.0 **Current Version:** 1.4.1
**Author:** Marco Graetsch **Author:** Marco Graetsch
**Project Status:** Production-ready WordPress plugin **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. **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.3.1 ### Version 1.4.2
- TBD - No planned features yet
## Technical Stack ## Technical Stack
@@ -40,7 +40,7 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
```json ```json
{ {
"twig/twig": "^3.0", "twig/twig": "^3.0",
"magdev/wc-licensed-product-client": "^0.1", "magdev/wc-licensed-product-client": "dev-main",
"symfony/http-client": "^7.0", "symfony/http-client": "^7.0",
"psr/log": "^3.0", "psr/log": "^3.0",
"psr/cache": "^3.0", "psr/cache": "^3.0",
@@ -55,6 +55,8 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
```txt ```txt
wc-tier-and-package-prices/ wc-tier-and-package-prices/
├── wc-tier-and-package-prices.php # Main plugin file (entry point) ├── wc-tier-and-package-prices.php # Main plugin file (entry point)
├── .gitea/workflows/
│ └── release.yml # CI/CD release pipeline
├── includes/ # PHP classes ├── includes/ # PHP classes
│ ├── class-wc-tpp-admin.php # Admin settings integration │ ├── class-wc-tpp-admin.php # Admin settings integration
│ ├── class-wc-tpp-settings.php # WooCommerce settings page │ ├── class-wc-tpp-settings.php # WooCommerce settings page
@@ -81,7 +83,9 @@ wc-tier-and-package-prices/
│ ├── *.pot # Translation template │ ├── *.pot # Translation template
│ ├── *.po # Translation sources │ ├── *.po # Translation sources
│ └── *.mo # Compiled translations │ └── *.mo # Compiled translations
├── vendor/ # Composer dependencies (included in releases) ├── lib/ # Bundled libraries (git submodules)
│ └── wc-licensed-product-client/ # License client library (v0.2.x)
├── vendor/ # Composer dependencies (generated)
├── releases/ # Release packages (not in git) ├── releases/ # Release packages (not in git)
└── *.md # Documentation files └── *.md # Documentation files
@@ -353,136 +357,133 @@ Symptom: Help icon appearing far from label text at container edge
## Release Process ## Release Process
### Version Bumping ### Automated CI/CD Pipeline (Recommended)
Update version in 3 places: Since v1.4.0, releases are automated via Gitea CI/CD. The pipeline is triggered when a version tag is pushed.
1. `wc-tier-and-package-prices.php` - Plugin header comment (line 7) **Workflow:**
2. `wc-tier-and-package-prices.php` - `WC_TPP_VERSION` constant (line 26)
3. `composer.json` - version field (optional, not critical)
### Creating Release Package
**CRITICAL:** The zip command must be run from the **parent directory** of the plugin folder to create proper archive structure.
```bash ```bash
# From parent directory (/home/magdev/workspaces/php) # 1. Update version in 3 places
cd /home/magdev/workspaces/php # - wc-tier-and-package-prices.php (header comment, line 7)
# - wc-tier-and-package-prices.php (WC_TPP_VERSION constant)
# - composer.json (version field)
# Create zip excluding dev files - note the correct path structure # 2. Update CHANGELOG.md with release notes
zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/ \
-x 'wc-tier-and-package-prices/.git*' \
'wc-tier-and-package-prices/*.log' \
'wc-tier-and-package-prices/.claude/*' \
'wc-tier-and-package-prices/CLAUDE.md' \
'wc-tier-and-package-prices/releases/*' \
'wc-tier-and-package-prices/node_modules/*' \
'wc-tier-and-package-prices/.DS_Store' \
'wc-tier-and-package-prices/Thumbs.db' \
'wc-tier-and-package-prices/.vscode/*' \
'wc-tier-and-package-prices/.idea/*' \
'wc-tier-and-package-prices/*.sublime-*' \
'wc-tier-and-package-prices/notes.*' \
'wc-tier-and-package-prices/logs/*' \
'wc-tier-and-package-prices/templates/cache/*' \
'wc-tier-and-package-prices/composer.lock'
# Return to project directory # 3. Commit and push to dev
cd wc-tier-and-package-prices git add -A && git commit -m "Version X.X.X - [description]
# Generate SHA256 checksum Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
cd releases
sha256sum wc-tier-and-package-prices-X.X.X.zip > wc-tier-and-package-prices-X.X.X.zip.sha256
cd ..
```
**IMPORTANT NOTES:**
- The `vendor/` directory MUST be included in releases (Twig dependency required for runtime)
- Running zip from wrong directory creates empty or malformed archives
- Exclusion patterns must match the relative path structure used in zip command
- Always verify the package with `unzip -l` and test extraction before committing
### Verification Steps
After creating the release package, always verify:
```bash
# Check package size (should be ~400-450KB, NOT 8MB+ or near 0)
ls -lh releases/wc-tier-and-package-prices-X.X.X.zip
# Verify exclusions worked
unzip -l releases/wc-tier-and-package-prices-X.X.X.zip | grep -E "CLAUDE\.md|\.claude/|\.git" && echo "ERROR: Excluded files found!" || echo "OK: No excluded files"
# Test extraction
cd /tmp && rm -rf test-extract && unzip -q /path/to/releases/wc-tier-and-package-prices-X.X.X.zip -d test-extract && ls -la test-extract/wc-tier-and-package-prices/
# Verify version in extracted package
head -30 /tmp/test-extract/wc-tier-and-package-prices/wc-tier-and-package-prices.php | grep -E "Version:|WC_TPP_VERSION"
# Verify template changes (if applicable)
grep 'class="regular"' /tmp/test-extract/wc-tier-and-package-prices/templates/admin/*.twig
```
### Git Workflow for Releases
**Standard workflow:** Work on `dev` branch → merge to `main` → tag → push
```bash
# 1. Ensure you're on dev branch with all changes committed
git checkout dev
git add [files]
git commit -m "Release version X.X.X - [description]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
# 2. Merge dev to main
git checkout main
git merge dev --no-edit # Should be fast-forward
# 3. Create annotated tag
git tag -a vX.X.X -m "Release version X.X.X - [description]"
# 4. Push everything
git push origin main
git push origin vX.X.X
# 5. Update dev and push
git checkout dev
git rebase main # Should be up-to-date already
git push origin dev git push origin dev
# 6. If you have uncommitted local changes (like .claude/settings.local.json) # 4. Merge to main and push
git stash push -m "Local development settings" git checkout main && git merge dev --no-edit && git push origin main
# ... do git operations ...
git stash pop # 5. Create and push tag (triggers CI/CD)
git tag -a vX.X.X -m "Release version X.X.X - [description]"
git push origin vX.X.X
# 6. Switch back to dev
git checkout dev
``` ```
**Important Git Notes:** **What the CI/CD Pipeline Does:**
- Always commit from `dev` branch first 1. Checks out code with git submodules (`submodules: recursive`)
- Tags should use format `vX.X.X` (e.g., `v1.1.22`) 2. Sets up PHP 8.3 with required extensions
- Use annotated tags (`-a`) not lightweight tags 3. Validates `composer.json`
- Commit messages should follow the established format with Claude Code attribution 4. Installs production Composer dependencies (`--no-dev`)
- `.claude/settings.local.json` changes are typically local-only (stash before rebasing) 5. Compiles translation files (.po → .mo)
6. Validates plugin version matches tag version
7. Creates release package with proper exclusions
8. Generates SHA256 checksum
9. Verifies package structure
10. Extracts changelog notes for release description
11. Creates Gitea release with assets
**Pipeline Location:** `.gitea/workflows/release.yml`
**Required Secret:** `SRC_GITEA_TOKEN` - Gitea API token for creating releases
### Git Submodule: wc-licensed-product-client
The `magdev/wc-licensed-product-client` library is bundled as a git submodule in `lib/` to avoid private repository authentication during CI/CD and to prevent conflicts with Composer's `vendor/` directory.
**Location:** `lib/wc-licensed-product-client/`
**Submodule URL:** Relative path `../wc-licensed-product-client.git` (works with Gitea CI/CD)
**Updating the submodule:**
```bash
# Update to a specific tag
cd lib/wc-licensed-product-client
git fetch --tags
git checkout v0.2.3 # or desired version
cd ../..
git add lib/wc-licensed-product-client
git commit -m "Update wc-licensed-product-client to v0.2.3"
```
**Composer Configuration:**
```json
{
"repositories": [
{
"type": "path",
"url": "lib/wc-licensed-product-client"
}
],
"require": {
"magdev/wc-licensed-product-client": "^0.2"
}
}
```
Composer creates a symlink in `vendor/magdev/wc-licensed-product-client` pointing to `lib/wc-licensed-product-client`.
### Manual Release (Legacy)
For manual releases without CI/CD, run from the **parent directory**:
```bash
cd /home/magdev/workspaces/php/wordpress/wp-content/plugins
zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/ \
-x 'wc-tier-and-package-prices/.git/*' \
-x 'wc-tier-and-package-prices/.gitea/*' \
-x 'wc-tier-and-package-prices/.claude/*' \
-x 'wc-tier-and-package-prices/CLAUDE.md' \
-x 'wc-tier-and-package-prices/releases/*' \
-x 'wc-tier-and-package-prices/composer.lock' \
-x 'wc-tier-and-package-prices/*.log' \
-x 'wc-tier-and-package-prices/.gitignore' \
-x 'wc-tier-and-package-prices/.gitmodules' \
-x 'wc-tier-and-package-prices/vendor/*/.git/*' \
-x '*.DS_Store'
cd wc-tier-and-package-prices/releases
sha256sum wc-tier-and-package-prices-X.X.X.zip > wc-tier-and-package-prices-X.X.X.zip.sha256
```
### What Gets Released ### What Gets Released
- All plugin source files - All plugin source files
- Compiled vendor dependencies - Compiled vendor dependencies (including submodule)
- Translation files (.mo compiled from .po) - Translation files (.mo compiled from .po)
- Assets (CSS, JS) - Assets (CSS, JS)
- Documentation (README, CHANGELOG, etc.) - Documentation (README, CHANGELOG, etc.)
### What's Excluded ### What's Excluded
- Git metadata (`.git/`) - Git metadata (`.git/`, `.gitmodules`)
- CI/CD workflows (`.gitea/`)
- Development files (`.vscode/`, `.claude/`, `CLAUDE.md`) - Development files (`.vscode/`, `.claude/`, `CLAUDE.md`)
- Logs and cache files - Logs and cache files
- Previous releases - Previous releases
- `composer.lock` (but `vendor/` is included) - `composer.lock`
## Testing Checklist ## Testing Checklist
@@ -1101,8 +1102,8 @@ if (!empty($tiers)) {
### PHP ### PHP
- Minimum: 7.4 - Minimum: 8.3 (breaking change in v1.3.0)
- Uses modern PHP features (type hints acceptable in new code) - Uses modern PHP features (type hints, named arguments, etc.)
- Composer autoloader handles namespacing - Composer autoloader handles namespacing
### Browsers ### Browsers
@@ -1138,4 +1139,188 @@ The plugin architecture is solid and well-tested. Most bugs arise from:
--- ---
## Session History
### v1.3.0 Release Session (2026-01-25)
**Accomplished:**
1. Fixed known bugs from CLAUDE.md:
- Removed all releases from git tracking (`git rm --cached -r releases/`)
- Deleted all MD5 checksum files, keeping only SHA256
- Updated `.gitignore` to exclude `/releases/`
2. Implemented v1.3.0 features:
- Bumped PHP requirement from 7.4 to 8.3 (breaking change)
- Added PHP version check with admin notice for incompatible servers
- Added `magdev/wc-licensed-product-client` library integration
- Refactored settings page to use WooCommerce modern sub-tabs pattern (`get_own_sections()`)
- Created "General" and "License" sub-tabs
- Implemented license management with AJAX validation/activation
- Added license status caching via WordPress transients
- Added CSS styling for license status display
3. Updated all translation files:
- Added 28 new translation strings for license management
- Updated .pot template and all 7 .po files
- Compiled all .mo files
4. Created release package:
- Package size: 737KB (increased due to new dependencies)
- 642 files included (more due to license client library)
- SHA256 checksum generated
**Key Learnings:**
- WooCommerce settings sub-tabs use `get_own_sections()` and `get_settings_for_{section}_section()` pattern
- License client library `magdev/wc-licensed-product-client` is from private Gitea repo - requires `repositories` config in composer.json
- Package version was `^0.1` not `^1.0` - always check available versions before setting constraint
- Release package size increased from ~430KB to ~737KB due to new Composer dependencies
### v1.3.1 Release Session (2026-01-27)
**Accomplished:**
1. Reviewed server-implementation.md from license client library
2. Upgraded from `LicenseClient` to `SecureLicenseClient`:
- Added "Server Secret" configuration field in License settings tab
- Updated `get_license_client()` to use `SecureLicenseClient` with HMAC-SHA256 signature verification
- Updated JavaScript to pass `server_secret` in AJAX requests
3. Added comprehensive exception handling:
- `RateLimitExceededException` - Shows retry time to user
- `SignatureException` - Reports signature verification failures
- `InvalidArgumentException` - SSRF protection for private IP blocking
4. Updated Composer dependencies:
- Changed `magdev/wc-licensed-product-client` from `^0.1` to `dev-main`
- Library includes new security features (SSRF protection, response signatures)
5. Updated translations:
- Added 7 new translation strings for v1.3.1
- All 7 language files updated and compiled
6. Created release package:
- Package size: 775KB (after proper exclusions)
- 650 files included
- SHA256 checksum generated
7. Updated README.md:
- Documented license management feature
- Updated PHP requirement to 8.3
- Added v1.3.0 and v1.3.1 changelog entries
8. Re-released v1.3.1:
- Fixed release package exclusions (vendor .git dirs were included)
- Added `.claude/settings.json` to .gitignore
**Key Learnings:**
- `SecureLicenseClient` requires `serverSecret` parameter for HMAC verification
- The library's exception classes are in `Magdev\WcLicensedProductClient\Exception\` and `Magdev\WcLicensedProductClient\Security\` namespaces
- `RateLimitExceededException` has a `retryAfter` property (int seconds)
- Always check library documentation in `docs/server-implementation.md` for implementation requirements
**Release Package Learnings (CRITICAL):**
- Vendor directories installed via Composer (dev-main) contain their own `.git/` folders
- Release exclusion patterns must include `'wc-tier-and-package-prices/vendor/*/.git/*'` and `'wc-tier-and-package-prices/vendor/*/CLAUDE.md'`
- Run zip from `/home/magdev/workspaces/php/wordpress/wp-content/plugins/` (NOT `/home/magdev/workspaces/php/`)
- Always verify exclusions with: `unzip -l package.zip | grep -E "\.git/|CLAUDE\.md"`
### v1.4.0 Release Session (2026-01-29)
**Accomplished:**
1. Created Gitea CI/CD release pipeline:
- `.gitea/workflows/release.yml` - Automated release workflow
- Triggers on version tags (`v*`)
- Validates plugin version matches tag
- Installs production Composer dependencies
- Compiles translations (.po → .mo)
- Creates release package with proper exclusions
- Generates SHA256 checksum
- Publishes release to Gitea with changelog notes
2. Added git submodule for license client:
- Initially placed in `vendor/magdev/` - **FAILED** (Composer conflicts)
- Fixed: Moved to `lib/wc-licensed-product-client/` like wp-fedistream
- Uses relative URL `../wc-licensed-product-client.git`
- Pinned to v0.2.2 tag
- Avoids private repository authentication during CI/CD
3. Updated documentation:
- README.md - Added CI/CD section, updated file structure
- CLAUDE.md - Updated release process, added submodule docs
**Key Learnings:**
- **CRITICAL:** Git submodules should be in `lib/` NOT `vendor/` to avoid Composer conflicts
- Composer manages `vendor/` directory - don't put submodules there
- Use relative submodule URL (`../repo.git`) for Gitea CI/CD compatibility
- Use version constraint `^0.2` instead of `@dev` for stability
- Git submodules require `submodules: recursive` in checkout action
- Composer creates symlink from `vendor/` to `lib/` automatically
**CI/CD Pipeline Notes:**
- Pipeline file: `.gitea/workflows/release.yml`
- Required secret: `SRC_GITEA_TOKEN` for Gitea API access
- Uses `shivammathur/setup-php@v2` for PHP setup
- 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! Always refer to this document when starting work on this project. Good luck!

View File

@@ -20,10 +20,11 @@ A powerful WooCommerce plugin that adds tier pricing and package pricing functio
### Admin Features ### Admin Features
- Easy-to-use product meta boxes for adding tiers and packages - Easy-to-use product meta boxes for adding tiers and packages
- Global settings page under WooCommerce menu - Global settings page under WooCommerce menu with sub-tabs
- Configure display position (before/after add to cart, after price) - Configure display position (before/after add to cart, after price)
- Enable/disable tier or package pricing independently - Enable/disable tier or package pricing independently
- Sortable pricing rules - Sortable pricing rules
- License management with secure HMAC signature verification
### Frontend Features ### Frontend Features
- Beautiful pricing tables on product pages - Beautiful pricing tables on product pages
@@ -39,6 +40,20 @@ A powerful WooCommerce plugin that adds tier pricing and package pricing functio
2. Activate the plugin through the 'Plugins' menu in WordPress 2. Activate the plugin through the 'Plugins' menu in WordPress
3. Make sure WooCommerce is installed and activated 3. Make sure WooCommerce is installed and activated
### Automated Releases
This project uses a Gitea CI/CD pipeline for automated releases. When a version tag (e.g., `v1.4.0`) is pushed:
1. Code is checked out with git submodules (dependencies bundled)
2. The pipeline validates the plugin version matches the tag
3. Composer dependencies are installed (production only)
4. Translation files are compiled (.po → .mo)
5. A release package is created with proper exclusions
6. SHA256 checksum is generated
7. Release is published to Gitea with changelog notes extracted from CHANGELOG.md
The `magdev/wc-licensed-product-client` library is bundled as a git submodule to avoid private repository authentication during CI/CD.
## Configuration ## Configuration
### Global Settings ### Global Settings
@@ -110,7 +125,9 @@ When editing a product, scroll to the **Product data** panel:
``` ```
wc-tier-and-package-prices/ wc-tier-and-package-prices/
├── wc-tier-and-package-prices.php # Main plugin file (v1.1.20) ├── wc-tier-and-package-prices.php # Main plugin file (v1.4.0)
├── .gitea/workflows/
│ └── release.yml # CI/CD release pipeline
├── includes/ ├── includes/
│ ├── class-wc-tpp-admin.php # Admin settings integration │ ├── class-wc-tpp-admin.php # Admin settings integration
│ ├── class-wc-tpp-settings.php # WooCommerce settings page │ ├── class-wc-tpp-settings.php # WooCommerce settings page
@@ -137,7 +154,9 @@ wc-tier-and-package-prices/
│ ├── wc-tier-package-prices.pot # Translation template │ ├── wc-tier-package-prices.pot # Translation template
│ ├── wc-tier-package-prices-*.po # Translation sources │ ├── wc-tier-package-prices-*.po # Translation sources
│ └── wc-tier-package-prices-*.mo # Compiled translations │ └── wc-tier-package-prices-*.mo # Compiled translations
├── vendor/ # Composer dependencies (Twig) ├── lib/ # Bundled libraries (git submodules)
│ └── wc-licensed-product-client/ # License client library
├── vendor/ # Composer dependencies (generated)
├── CHANGELOG.md # Complete version history ├── CHANGELOG.md # Complete version history
├── INSTALLATION.md # Installation guide ├── INSTALLATION.md # Installation guide
├── QUICKSTART.md # Quick start guide ├── QUICKSTART.md # Quick start guide
@@ -149,7 +168,7 @@ wc-tier-and-package-prices/
- WordPress 6.0 or higher (tested up to 6.9.x) - WordPress 6.0 or higher (tested up to 6.9.x)
- WooCommerce 8.0 or higher (tested up to 10.x) - WooCommerce 8.0 or higher (tested up to 10.x)
- PHP 7.4 or higher - PHP 8.3 or higher (required since v1.3.0)
### Compatibility ### Compatibility
@@ -183,9 +202,39 @@ This plugin is licensed under the GPL v2 or later.
## Changelog ## Changelog
### Version 1.4.0 - 2026-01-29
__Current Release__ - CI/CD Release Pipeline
- __New__: Gitea CI/CD release pipeline for automated builds and releases
- __New__: Git submodule for `magdev/wc-licensed-product-client` library
- __Changed__: Composer now uses path repository for bundled license client
- __DevOps__: Automated version validation, translation compilation, and release publishing
See [CHANGELOG.md](CHANGELOG.md) for complete details.
### Version 1.3.1 - 2026-01-27
Secure License Client
- __Changed__: Switched to `SecureLicenseClient` with HMAC-SHA256 response signature verification
- __New__: Server Secret configuration field for secure communication with license server
- __Security__: Response signatures verified using HMAC-SHA256 with license-specific derived keys
### Version 1.3.0 - 2026-01-25
__Breaking Changes__ - PHP 8.3+ Required
- __Breaking__: Minimum PHP version increased from 7.4 to 8.3
- __New__: License management via `magdev/wc-licensed-product-client` library
- __New__: Settings page split into "General" and "License" sub-tabs
- __New__: AJAX-based license validation and activation with visual feedback
- __New__: License status caching with daily auto-refresh
- __New__: PHP version check with admin notice for incompatible servers
### Version 1.2.0 - 2025-12-29 ### Version 1.2.0 - 2025-12-29
__Current Release__ - Variable Product Support Variable Product Support
- __New__: Full support for WooCommerce variable products with variation-level pricing - __New__: Full support for WooCommerce variable products with variation-level pricing
- __New__: Each variation can have independent tier and package pricing configuration - __New__: Each variation can have independent tier and package pricing configuration
@@ -194,24 +243,6 @@ __Current Release__ - Variable Product Support
- __Fixed__: Quantity restrictions now work correctly per-variation - __Fixed__: Quantity restrictions now work correctly per-variation
- 100% backward compatible - no breaking changes - 100% backward compatible - no breaking changes
See [CHANGELOG.md](CHANGELOG.md) for complete details.
### Version 1.1.22 - 2025-12-23
- Increased width of label input fields in admin interface
#### Fixed
- **CRITICAL:** WooCommerce Blocks fatal error in mini-cart and cart blocks
- Fixed `woocommerce_store_api_product_quantity_editable` filter signature mismatch
- Filter now correctly accepts `WC_Product` object instead of cart item array
- Resolves "Cannot use object of type WC_Product_Simple as array" fatal error
#### Technical Details
- Updated `block_quantity_editable()` method signature to accept product object
- Changed parameter from `$cart_item` array to `WC_Product $product`
- Uses `$product->get_id()` instead of array access for product ID
- Full compatibility with WooCommerce Store API and block-based cart/checkout
### Recent Major Updates ### Recent Major Updates
#### Version 1.1.7 - Enhanced Tier Pricing #### Version 1.1.7 - Enhanced Tier Pricing

View File

@@ -1,7 +1,6 @@
{ {
"name": "magdev/wc-tier-package-prices", "name": "magdev/wc-tier-package-prices",
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates", "description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
"version": "1.3.0",
"type": "wordpress-plugin", "type": "wordpress-plugin",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"authors": [ "authors": [
@@ -12,18 +11,26 @@
], ],
"repositories": [ "repositories": [
{ {
"type": "vcs", "type": "path",
"url": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client.git" "url": "lib/wc-licensed-product-client"
} }
], ],
"require": { "require": {
"php": ">=8.3", "php": ">=8.3",
"twig/twig": "^3.0", "twig/twig": "^3.0",
"magdev/wc-licensed-product-client": "^0.1" "magdev/wc-licensed-product-client": "^0.2"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [
"includes/" "includes/"
] ]
},
"config": {
"optimize-autoloader": true,
"platform": {
"php": "8.3.0"
} }
},
"minimum-stability": "stable",
"prefer-stable": true
} }

View File

@@ -0,0 +1,333 @@
<?php
/**
* License Checker
*
* Handles license validation with localhost and self-licensing bypass
*
* @package WC_Tier_Package_Prices
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* WC_TPP_License_Checker class
*/
if (!class_exists('WC_TPP_License_Checker')) {
class WC_TPP_License_Checker {
/**
* Singleton instance
*
* @var WC_TPP_License_Checker|null
*/
private static $instance = null;
/**
* Cache key for license status
*/
private const CACHE_KEY = 'wc_tpp_license_status';
/**
* Cache TTL for successful validation (1 hour)
*/
private const CACHE_TTL_SUCCESS = 3600;
/**
* Cache TTL for failed validation (5 minutes)
*/
private const CACHE_TTL_ERROR = 300;
/**
* Localhost patterns
*
* @var array
*/
private const LOCALHOST_HOSTS = ['localhost', '127.0.0.1', '::1'];
/**
* Localhost TLDs
*
* @var array
*/
private const LOCALHOST_TLDS = ['.localhost', '.local', '.test', '.example', '.invalid'];
/**
* Get singleton instance
*
* @return WC_TPP_License_Checker
*/
public static function get_instance(): WC_TPP_License_Checker {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
// Clear cache when license 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_license_server_secret', array($this, 'clear_cache'));
}
/**
* Check if the current environment is localhost
*
* Matches patterns:
* - localhost
* - 127.0.0.1
* - ::1 (IPv6 localhost)
* - *.localhost (subdomains)
* - *.local (subdomains)
* - *.test (RFC 2606)
* - *.example (RFC 2606)
* - *.invalid (RFC 2606)
*
* @return bool
*/
public function is_localhost(): bool {
$domain = $this->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();
}
}
}

View File

@@ -33,6 +33,9 @@ if (!class_exists('WC_TPP_Settings')) {
// Add AJAX handlers for license validation // 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_validate_license', array($this, 'ajax_validate_license'));
add_action('wp_ajax_wc_tpp_activate_license', array($this, 'ajax_activate_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( return array(
'' => __('General', 'wc-tier-package-prices'), '' => __('General', 'wc-tier-package-prices'),
'license' => __('License', 'wc-tier-package-prices'), 'license' => __('License', 'wc-tier-package-prices'),
'updates' => __('Auto-Updates', 'wc-tier-package-prices'),
); );
} }
@@ -154,6 +158,16 @@ if (!class_exists('WC_TPP_Settings')) {
'desc_tip' => true, '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( array(
'title' => __('License Status', 'wc-tier-package-prices'), 'title' => __('License Status', 'wc-tier-package-prices'),
'type' => 'wc_tpp_license_status', 'type' => 'wc_tpp_license_status',
@@ -167,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 * Get cached license status
* *
@@ -188,13 +271,14 @@ if (!class_exists('WC_TPP_Settings')) {
$license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? '')); $license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? ''));
$server_url = esc_url_raw(wp_unslash($_POST['server_url'] ?? '')); $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)) { if (empty($license_key) || empty($server_url) || empty($server_secret)) {
wp_send_json_error(array('message' => __('License key and server URL are required.', 'wc-tier-package-prices'))); wp_send_json_error(array('message' => __('License key, server URL, and server secret are required.', 'wc-tier-package-prices')));
} }
try { try {
$client = $this->get_license_client($server_url); $client = $this->get_license_client($server_url, $server_secret);
$domain = $this->get_current_domain(); $domain = $this->get_current_domain();
$result = $client->validate($license_key, $domain); $result = $client->validate($license_key, $domain);
@@ -212,16 +296,37 @@ if (!class_exists('WC_TPP_Settings')) {
'status' => $this->get_cached_license_status(), '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) { } catch (\Magdev\WcLicensedProductClient\Exception\LicenseException $e) {
delete_transient('wc_tpp_license_status'); delete_transient('wc_tpp_license_status');
wp_send_json_error(array( wp_send_json_error(array(
'message' => $e->getMessage(), 'message' => $e->getMessage(),
'code' => $e->errorCode ?? 'unknown', 'code' => $e->errorCode ?? 'unknown',
)); ));
} catch (\InvalidArgumentException $e) {
wp_send_json_error(array(
'message' => $e->getMessage(),
'code' => 'invalid_url',
));
} catch (\Exception $e) { } catch (\Exception $e) {
delete_transient('wc_tpp_license_status'); delete_transient('wc_tpp_license_status');
wp_send_json_error(array( wp_send_json_error(array(
'message' => $e->getMessage(), 'message' => __('An unexpected error occurred. Please try again.', 'wc-tier-package-prices'),
'code' => 'exception', 'code' => 'exception',
)); ));
} }
@@ -239,13 +344,14 @@ if (!class_exists('WC_TPP_Settings')) {
$license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? '')); $license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? ''));
$server_url = esc_url_raw(wp_unslash($_POST['server_url'] ?? '')); $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)) { if (empty($license_key) || empty($server_url) || empty($server_secret)) {
wp_send_json_error(array('message' => __('License key and server URL are required.', 'wc-tier-package-prices'))); wp_send_json_error(array('message' => __('License key, server URL, and server secret are required.', 'wc-tier-package-prices')));
} }
try { try {
$client = $this->get_license_client($server_url); $client = $this->get_license_client($server_url, $server_secret);
$domain = $this->get_current_domain(); $domain = $this->get_current_domain();
$result = $client->activate($license_key, $domain); $result = $client->activate($license_key, $domain);
@@ -269,14 +375,34 @@ if (!class_exists('WC_TPP_Settings')) {
wp_send_json_error(array('message' => $result->message)); 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) { } catch (\Magdev\WcLicensedProductClient\Exception\LicenseException $e) {
wp_send_json_error(array( wp_send_json_error(array(
'message' => $e->getMessage(), 'message' => $e->getMessage(),
'code' => $e->errorCode ?? 'unknown', 'code' => $e->errorCode ?? 'unknown',
)); ));
} catch (\Exception $e) { } catch (\InvalidArgumentException $e) {
wp_send_json_error(array( wp_send_json_error(array(
'message' => $e->getMessage(), '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', 'code' => 'exception',
)); ));
} }
@@ -285,14 +411,18 @@ if (!class_exists('WC_TPP_Settings')) {
/** /**
* Get license client instance * Get license client instance
* *
* Uses SecureLicenseClient for HMAC signature verification.
*
* @param string $server_url License server URL. * @param string $server_url License server URL.
* @param string $server_secret Shared secret for signature verification.
* @return \Magdev\WcLicensedProductClient\LicenseClientInterface * @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(); $httpClient = \Symfony\Component\HttpClient\HttpClient::create();
return new \Magdev\WcLicensedProductClient\LicenseClient( return new \Magdev\WcLicensedProductClient\SecureLicenseClient(
httpClient: $httpClient, httpClient: $httpClient,
baseUrl: $server_url, baseUrl: $server_url,
serverSecret: $server_secret,
); );
} }
@@ -311,8 +441,9 @@ if (!class_exists('WC_TPP_Settings')) {
public function output() { public function output() {
global $current_section; 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_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(); parent::output();
@@ -320,6 +451,11 @@ if (!class_exists('WC_TPP_Settings')) {
if ('license' === $current_section) { if ('license' === $current_section) {
$this->output_license_scripts(); $this->output_license_scripts();
} }
// Add JavaScript for updates section
if ('updates' === $current_section) {
$this->output_updates_scripts();
}
} }
/** /**
@@ -358,6 +494,20 @@ if (!class_exists('WC_TPP_Settings')) {
* @param array|false $status License status data. * @param array|false $status License status data.
*/ */
private function render_license_status_html($status) { 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 '<span class="wc-tpp-license-active">' . esc_html__('License Active', 'wc-tier-package-prices') . '</span>';
if ('localhost' === $bypass_reason) {
echo '<br><small>' . esc_html__('(Localhost environment - license validation bypassed)', 'wc-tier-package-prices') . '</small>';
} elseif ('self_licensing' === $bypass_reason) {
echo '<br><small>' . esc_html__('(Self-licensing server - license validation bypassed)', 'wc-tier-package-prices') . '</small>';
}
return;
}
if (empty($status)) { if (empty($status)) {
echo '<span class="wc-tpp-license-inactive">' . esc_html__('No license activated', 'wc-tier-package-prices') . '</span>'; echo '<span class="wc-tpp-license-inactive">' . esc_html__('No license activated', 'wc-tier-package-prices') . '</span>';
return; return;
@@ -386,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();
?>
<tr valign="top">
<th scope="row" class="titledesc">
<label><?php esc_html_e('Update Status', 'wc-tier-package-prices'); ?></label>
</th>
<td class="forminp">
<div id="wc-tpp-update-status-container">
<?php $this->render_update_status_html($available_version); ?>
</div>
<p class="description" style="margin-top: 10px;">
<button type="button" class="button" id="wc-tpp-check-updates">
<?php esc_html_e('Check for Updates', 'wc-tier-package-prices'); ?>
</button>
<span class="spinner" id="wc-tpp-update-spinner"></span>
</p>
</td>
</tr>
<?php
}
/**
* Render update status HTML
*
* @param string|null $available_version Available update version.
*/
private function render_update_status_html(?string $available_version) {
echo '<p><strong>' . esc_html__('Current Version:', 'wc-tier-package-prices') . '</strong> ' . esc_html(WC_TPP_VERSION) . '</p>';
if ($available_version) {
echo '<p class="wc-tpp-update-available"><strong>' . esc_html__('Update Available:', 'wc-tier-package-prices') . '</strong> ' . esc_html($available_version) . '</p>';
echo '<p><a href="' . esc_url(admin_url('update-core.php')) . '" class="button button-primary">' . esc_html__('Update Now', 'wc-tier-package-prices') . '</a></p>';
} else {
echo '<p class="wc-tpp-up-to-date">' . esc_html__('You are running the latest version.', 'wc-tier-package-prices') . '</p>';
}
}
/** /**
* Output JavaScript for license management * Output JavaScript for license management
*/ */
@@ -402,6 +596,7 @@ if (!class_exists('WC_TPP_Settings')) {
return { return {
license_key: $('#wc_tpp_license_key').val(), license_key: $('#wc_tpp_license_key').val(),
server_url: $('#wc_tpp_license_server_url').val(), server_url: $('#wc_tpp_license_server_url').val(),
server_secret: $('#wc_tpp_license_server_secret').val(),
nonce: '<?php echo esc_js($nonce); ?>' nonce: '<?php echo esc_js($nonce); ?>'
}; };
} }
@@ -420,8 +615,8 @@ if (!class_exists('WC_TPP_Settings')) {
$validateBtn.on('click', function() { $validateBtn.on('click', function() {
var data = getLicenseData(); var data = getLicenseData();
if (!data.license_key || !data.server_url) { if (!data.license_key || !data.server_url || !data.server_secret) {
alert('<?php echo esc_js(__('Please enter both license server URL and license key.', 'wc-tier-package-prices')); ?>'); alert('<?php echo esc_js(__('Please enter license server URL, license key, and server secret.', 'wc-tier-package-prices')); ?>');
return; return;
} }
@@ -443,8 +638,8 @@ if (!class_exists('WC_TPP_Settings')) {
$activateBtn.on('click', function() { $activateBtn.on('click', function() {
var data = getLicenseData(); var data = getLicenseData();
if (!data.license_key || !data.server_url) { if (!data.license_key || !data.server_url || !data.server_secret) {
alert('<?php echo esc_js(__('Please enter both license server URL and license key.', 'wc-tier-package-prices')); ?>'); alert('<?php echo esc_js(__('Please enter license server URL, license key, and server secret.', 'wc-tier-package-prices')); ?>');
return; return;
} }
@@ -467,5 +662,87 @@ if (!class_exists('WC_TPP_Settings')) {
</script> </script>
<?php <?php
} }
/**
* AJAX handler for checking updates
*/
public function ajax_check_updates() {
check_ajax_referer('wc_tpp_update_nonce', 'nonce');
if (!current_user_can('manage_woocommerce')) {
wp_send_json_error(array('message' => __('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');
?>
<script type="text/javascript">
jQuery(function($) {
var $checkBtn = $('#wc-tpp-check-updates');
var $spinner = $('#wc-tpp-update-spinner');
var $container = $('#wc-tpp-update-status-container');
$checkBtn.on('click', function() {
$spinner.addClass('is-active');
$checkBtn.prop('disabled', true);
$.post(ajaxurl, {
action: 'wc_tpp_check_updates',
nonce: '<?php echo esc_js($nonce); ?>'
})
.done(function(response) {
if (response.success) {
var html = '<p><strong><?php echo esc_js(__('Current Version:', 'wc-tier-package-prices')); ?></strong> ' + response.data.current_version + '</p>';
if (response.data.update_available) {
html += '<p class="wc-tpp-update-available"><strong><?php echo esc_js(__('Update Available:', 'wc-tier-package-prices')); ?></strong> ' + response.data.version + '</p>';
html += '<p><a href="<?php echo esc_url(admin_url('update-core.php')); ?>" class="button button-primary"><?php echo esc_js(__('Update Now', 'wc-tier-package-prices')); ?></a></p>';
} else {
html += '<p class="wc-tpp-up-to-date"><?php echo esc_js(__('You are running the latest version.', 'wc-tier-package-prices')); ?></p>';
}
$container.html(html);
} else {
alert(response.data.message || '<?php echo esc_js(__('Failed to check for updates.', 'wc-tier-package-prices')); ?>');
}
})
.fail(function() {
alert('<?php echo esc_js(__('Request failed. Please try again.', 'wc-tier-package-prices')); ?>');
})
.always(function() {
$spinner.removeClass('is-active');
$checkBtn.prop('disabled', false);
});
});
});
</script>
<?php
}
} }
} }

View File

@@ -0,0 +1,468 @@
<?php
/**
* Update Checker
*
* Handles WordPress plugin updates from the license server
*
* @package WC_Tier_Package_Prices
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* WC_TPP_Update_Checker class
*/
if (!class_exists('WC_TPP_Update_Checker')) {
class WC_TPP_Update_Checker {
/**
* Singleton instance
*
* @var WC_TPP_Update_Checker|null
*/
private static $instance = null;
/**
* Plugin slug
*/
private const PLUGIN_SLUG = 'wc-tier-and-package-prices';
/**
* Update check cache key
*/
private const CACHE_KEY = 'wc_tpp_update_info';
/**
* Default check frequency in hours
*/
private const DEFAULT_CHECK_FREQUENCY = 12;
/**
* Get singleton instance
*
* @return WC_TPP_Update_Checker
*/
public static function get_instance(): WC_TPP_Update_Checker {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
// Only register if updates are not disabled
if ($this->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'] ?? '<a href="https://src.bundespruefstelle.ch/magdev">Marco Graetsch</a>';
$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();
}
}
}

View File

@@ -1,12 +1,12 @@
# German (Switzerland) translation for WooCommerce Tier and Package Prices # 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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: German (Switzerland)\n" "Language-Team: German (Switzerland)\n"
"Language: de_CH\n" "Language: de_CH\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert." msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "Lizenzschlüssel und Server-URL sind erforderlich." msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php #: 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 "Bitte geben Sie sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "Aktivierung fehlgeschlagen."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "Anfrage fehlgeschlagen. Bitte versuchen Sie es erneut." 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)"

View File

@@ -1,12 +1,12 @@
# German (Switzerland, Informal) translation for WooCommerce Tier and Package Prices # German (Switzerland, Informal) 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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: German (Switzerland)\n" "Language-Team: German (Switzerland)\n"
"Language: de_CH_informal\n" "Language: de_CH_informal\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert." msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "Lizenzschlüssel und Server-URL sind erforderlich." msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php #: 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 "Bitte gib sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "Aktivierung fehlgeschlagen."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "Anfrage fehlgeschlagen. Bitte versuche es erneut." msgstr "Anfrage fehlgeschlagen. Bitte versuche 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 "Konfiguriere 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 "Du verwendest 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)"

View File

@@ -1,12 +1,12 @@
# German (Germany) translation for WooCommerce Tier and Package Prices # 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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: German\n" "Language-Team: German\n"
"Language: de_DE\n" "Language: de_DE\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert." msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "Lizenzschlüssel und Server-URL sind erforderlich." msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php #: 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 "Bitte geben Sie sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "Aktivierung fehlgeschlagen."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "Anfrage fehlgeschlagen. Bitte versuchen Sie es erneut." 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)"

View File

@@ -1,12 +1,12 @@
# German (Germany, Informal) translation for WooCommerce Tier and Package Prices # German (Germany, Informal) 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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: German (Germany)\n" "Language-Team: German (Germany)\n"
"Language: de_DE_informal\n" "Language: de_DE_informal\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Zugriff verweigert." msgstr "Zugriff verweigert."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "Lizenzschlüssel und Server-URL sind erforderlich." msgstr "Server-Geheimnis"
#: includes/class-wc-tpp-settings.php #: 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 "Bitte gib sowohl die Lizenzserver-URL als auch den Lizenzschlüssel ein." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "Aktivierung fehlgeschlagen."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "Anfrage fehlgeschlagen. Bitte versuche es erneut." msgstr "Anfrage fehlgeschlagen. Bitte versuche 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 "Konfiguriere 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 "Du verwendest 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)"

View File

@@ -1,12 +1,12 @@
# English (US) translation for WooCommerce Tier and Package Prices # 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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: English\n" "Language-Team: English\n"
"Language: en_US\n" "Language: en_US\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Permission denied." msgstr "Permission denied."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "License key and server URL are required." msgstr "Server Secret"
#: includes/class-wc-tpp-settings.php #: 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 "Please enter both license server URL and license key." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "Activation failed."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "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)"

View File

@@ -1,12 +1,12 @@
# French (Switzerland) translation for WooCommerce Tier and Package Prices # French (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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: French (Switzerland)\n" "Language-Team: French (Switzerland)\n"
"Language: fr_CH\n" "Language: fr_CH\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Accès refusé." msgstr "Accès refusé."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "La clé de licence et l'URL du serveur sont requises." msgstr "Secret serveur"
#: includes/class-wc-tpp-settings.php #: 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 "Veuillez entrer l'URL du serveur de licence et la clé de licence." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "L'activation a échoué."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "La requête a échoué. Veuillez réessayer." msgstr "La requête a échoué. Veuillez réessayer."
#. v1.4.1 - Auto-Updates and License Bypass
#: includes/class-wc-tpp-settings.php
msgid "Auto-Updates"
msgstr "Mises à jour automatiques"
#: includes/class-wc-tpp-settings.php
msgid "Auto-Update Settings"
msgstr "Paramètres de mise à jour automatique"
#: includes/class-wc-tpp-settings.php
msgid "Configure automatic plugin updates from the license server."
msgstr "Configurez les mises à jour automatiques du plugin depuis le serveur de licence."
#: includes/class-wc-tpp-settings.php
msgid "Enable Update Notifications"
msgstr "Activer les notifications de mise à jour"
#: includes/class-wc-tpp-settings.php
msgid "Check for available plugin updates."
msgstr "Rechercher les mises à jour disponibles du plugin."
#: 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 "Lorsque cette option est activée, le plugin vérifiera les mises à jour depuis le serveur de licence et affichera des notifications dans l'administration WordPress."
#: includes/class-wc-tpp-settings.php
msgid "Automatically Install Updates"
msgstr "Installer automatiquement les mises à jour"
#: includes/class-wc-tpp-settings.php
msgid "Automatically install updates when available."
msgstr "Installer automatiquement les mises à jour lorsqu'elles sont disponibles."
#: includes/class-wc-tpp-settings.php
msgid "(Requires a valid license)"
msgstr "(Nécessite une licence valide)"
#: includes/class-wc-tpp-settings.php
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
msgstr "Lorsque cette option est activée, les mises à jour seront automatiquement installées lors des mises à jour en arrière-plan de WordPress."
#: includes/class-wc-tpp-settings.php
msgid "Check Frequency (Hours)"
msgstr "Fréquence de vérification (heures)"
#: includes/class-wc-tpp-settings.php
msgid "How often to check for updates."
msgstr "Fréquence de recherche des mises à jour."
#: includes/class-wc-tpp-settings.php
msgid "Number of hours between update checks. Default is 12 hours."
msgstr "Nombre d'heures entre les vérifications de mise à jour. Par défaut: 12 heures."
#: includes/class-wc-tpp-settings.php
msgid "Update Status"
msgstr "Statut de mise à jour"
#: includes/class-wc-tpp-settings.php
msgid "Check for Updates"
msgstr "Rechercher des mises à jour"
#: includes/class-wc-tpp-settings.php
msgid "Current Version:"
msgstr "Version actuelle:"
#: includes/class-wc-tpp-settings.php
msgid "Update Available:"
msgstr "Mise à jour disponible:"
#: includes/class-wc-tpp-settings.php
msgid "You are running the latest version."
msgstr "Vous utilisez la dernière version."
#: includes/class-wc-tpp-settings.php
msgid "Update Now"
msgstr "Mettre à jour maintenant"
#: includes/class-wc-tpp-settings.php
#. translators: %s: Version number
msgid "Update available: version %s"
msgstr "Mise à jour disponible: version %s"
#: includes/class-wc-tpp-settings.php
msgid "Failed to check for updates."
msgstr "Échec de la recherche de mises à jour."
#: includes/class-wc-tpp-settings.php
msgid "(Localhost environment - license validation bypassed)"
msgstr "(Environnement localhost - validation de licence ignorée)"
#: includes/class-wc-tpp-settings.php
msgid "(Self-licensing server - license validation bypassed)"
msgstr "(Serveur auto-licencié - validation de licence ignorée)"

View File

@@ -1,12 +1,12 @@
# Italian (Switzerland) translation for WooCommerce Tier and Package Prices # Italian (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. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"PO-Revision-Date: 2025-12-30 00:00+0000\n" "PO-Revision-Date: 2026-02-03 00:00+0000\n"
"Last-Translator: Marco Graetsch\n" "Last-Translator: Marco Graetsch\n"
"Language-Team: Italian (Switzerland)\n" "Language-Team: Italian (Switzerland)\n"
"Language: it_CH\n" "Language: it_CH\n"
@@ -359,12 +359,33 @@ msgid "Permission denied."
msgstr "Accesso negato." msgstr "Accesso negato."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "La chiave di licenza e l'URL del server sono obbligatori." msgstr "Segreto del server"
#: includes/class-wc-tpp-settings.php #: 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 "Inserisci sia l'URL del server di licenza che la chiave di licenza." 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 #: includes/class-wc-tpp-settings.php
msgid "Validation failed." msgid "Validation failed."
@@ -377,3 +398,98 @@ msgstr "Attivazione fallita."
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "Richiesta fallita. Per favore riprova." msgstr "Richiesta fallita. Per favore riprova."
#. v1.4.1 - Auto-Updates and License Bypass
#: includes/class-wc-tpp-settings.php
msgid "Auto-Updates"
msgstr "Aggiornamenti automatici"
#: includes/class-wc-tpp-settings.php
msgid "Auto-Update Settings"
msgstr "Impostazioni aggiornamento automatico"
#: includes/class-wc-tpp-settings.php
msgid "Configure automatic plugin updates from the license server."
msgstr "Configura gli aggiornamenti automatici del plugin dal server di licenza."
#: includes/class-wc-tpp-settings.php
msgid "Enable Update Notifications"
msgstr "Abilita notifiche di aggiornamento"
#: includes/class-wc-tpp-settings.php
msgid "Check for available plugin updates."
msgstr "Verifica la disponibilità di aggiornamenti del plugin."
#: 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 "Se abilitato, il plugin verificherà la presenza di aggiornamenti dal server di licenza e mostrerà le notifiche nell'amministrazione WordPress."
#: includes/class-wc-tpp-settings.php
msgid "Automatically Install Updates"
msgstr "Installa automaticamente gli aggiornamenti"
#: includes/class-wc-tpp-settings.php
msgid "Automatically install updates when available."
msgstr "Installa automaticamente gli aggiornamenti quando disponibili."
#: includes/class-wc-tpp-settings.php
msgid "(Requires a valid license)"
msgstr "(Richiede una licenza valida)"
#: includes/class-wc-tpp-settings.php
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
msgstr "Se abilitato, gli aggiornamenti verranno installati automaticamente quando WordPress esegue gli aggiornamenti in background."
#: includes/class-wc-tpp-settings.php
msgid "Check Frequency (Hours)"
msgstr "Frequenza di controllo (ore)"
#: includes/class-wc-tpp-settings.php
msgid "How often to check for updates."
msgstr "Con quale frequenza controllare gli aggiornamenti."
#: includes/class-wc-tpp-settings.php
msgid "Number of hours between update checks. Default is 12 hours."
msgstr "Numero di ore tra i controlli degli aggiornamenti. Predefinito: 12 ore."
#: includes/class-wc-tpp-settings.php
msgid "Update Status"
msgstr "Stato aggiornamento"
#: includes/class-wc-tpp-settings.php
msgid "Check for Updates"
msgstr "Verifica aggiornamenti"
#: includes/class-wc-tpp-settings.php
msgid "Current Version:"
msgstr "Versione attuale:"
#: includes/class-wc-tpp-settings.php
msgid "Update Available:"
msgstr "Aggiornamento disponibile:"
#: includes/class-wc-tpp-settings.php
msgid "You are running the latest version."
msgstr "Stai utilizzando l'ultima versione."
#: includes/class-wc-tpp-settings.php
msgid "Update Now"
msgstr "Aggiorna ora"
#: includes/class-wc-tpp-settings.php
#. translators: %s: Version number
msgid "Update available: version %s"
msgstr "Aggiornamento disponibile: versione %s"
#: includes/class-wc-tpp-settings.php
msgid "Failed to check for updates."
msgstr "Impossibile verificare la presenza di aggiornamenti."
#: includes/class-wc-tpp-settings.php
msgid "(Localhost environment - license validation bypassed)"
msgstr "(Ambiente localhost - verifica della licenza saltata)"
#: includes/class-wc-tpp-settings.php
msgid "(Self-licensing server - license validation bypassed)"
msgstr "(Server auto-licenziato - verifica della licenza saltata)"

View File

@@ -1,10 +1,10 @@
# Copyright (C) 2025 Marco Graetsch # Copyright (C) 2026 Marco Graetsch
# This file is distributed under the GPL v2 or later. # This file is distributed under the GPL v2 or later.
msgid "" msgid ""
msgstr "" 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" "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-02-03 00:00+0000\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -332,11 +332,32 @@ msgid "Permission denied."
msgstr "" msgstr ""
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "License key and server URL are required." msgid "Server Secret"
msgstr "" msgstr ""
#: includes/class-wc-tpp-settings.php #: 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 "" msgstr ""
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
@@ -350,3 +371,98 @@ msgstr ""
#: includes/class-wc-tpp-settings.php #: includes/class-wc-tpp-settings.php
msgid "Request failed. Please try again." msgid "Request failed. Please try again."
msgstr "" msgstr ""
#. v1.4.1 - Auto-Updates and License Bypass
#: includes/class-wc-tpp-settings.php
msgid "Auto-Updates"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Auto-Update Settings"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Configure automatic plugin updates from the license server."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Enable Update Notifications"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Check for available plugin updates."
msgstr ""
#: 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 ""
#: includes/class-wc-tpp-settings.php
msgid "Automatically Install Updates"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Automatically install updates when available."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "(Requires a valid license)"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Check Frequency (Hours)"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "How often to check for updates."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Number of hours between update checks. Default is 12 hours."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Update Status"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Check for Updates"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Current Version:"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Update Available:"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "You are running the latest version."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Update Now"
msgstr ""
#: includes/class-wc-tpp-settings.php
#. translators: %s: Version number
msgid "Update available: version %s"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "Failed to check for updates."
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "(Localhost environment - license validation bypassed)"
msgstr ""
#: includes/class-wc-tpp-settings.php
msgid "(Self-licensing server - license validation bypassed)"
msgstr ""

View File

@@ -4,7 +4,7 @@
* Plugin Name: WooCommerce Tier and Package Prices * Plugin Name: WooCommerce Tier and Package Prices
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-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 * Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices
* Version: 1.3.0 * Version: 1.4.1
* Author: Marco Graetsch * Author: Marco Graetsch
* Author URI: https://src.bundespruefstelle.ch/magdev * Author URI: https://src.bundespruefstelle.ch/magdev
* Text Domain: wc-tier-package-prices * Text Domain: wc-tier-package-prices
@@ -46,7 +46,7 @@ if (!function_exists('wc_tpp_php_version_notice')) {
// Define plugin constants // Define plugin constants
if (!defined('WC_TPP_VERSION')) { if (!defined('WC_TPP_VERSION')) {
define('WC_TPP_VERSION', '1.3.0'); define('WC_TPP_VERSION', '1.4.1');
} }
if (!defined('WC_TPP_PLUGIN_DIR')) { if (!defined('WC_TPP_PLUGIN_DIR')) {
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));
@@ -111,10 +111,18 @@ if (!class_exists('WC_Tier_Package_Prices')) {
private function includes() { private function includes() {
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-template-loader.php'; require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-template-loader.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-license-checker.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-update-checker.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-admin.php'; require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-admin.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-product-meta.php'; require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-product-meta.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-frontend.php'; require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-frontend.php';
require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-cart.php'; require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-cart.php';
// Initialize license checker (singleton)
WC_TPP_License_Checker::get_instance();
// Initialize update checker (singleton)
WC_TPP_Update_Checker::get_instance();
} }
public function declare_hpos_compatibility() { public function declare_hpos_compatibility() {
@@ -132,6 +140,12 @@ if (!class_exists('WC_Tier_Package_Prices')) {
add_option('wc_tpp_enable_tier_pricing', 'yes'); add_option('wc_tpp_enable_tier_pricing', 'yes');
add_option('wc_tpp_enable_package_pricing', 'yes'); add_option('wc_tpp_enable_package_pricing', 'yes');
add_option('wc_tpp_display_table', 'yes'); add_option('wc_tpp_display_table', 'yes');
// Add default auto-update options
add_option('wc_tpp_update_notification_enabled', 'yes');
add_option('wc_tpp_auto_install_enabled', 'no');
add_option('wc_tpp_update_check_frequency', '12');
flush_rewrite_rules(); flush_rewrite_rules();
} }