You've already forked wc-licensed-product
- Add file attachment support for product versions (Media Library) - Add version auto-detection from uploaded filenames - Implement secure customer downloads with hash verification - Add license key copy-to-clipboard functionality - Redesign customer licenses page with card-based UI - Fix product versions meta box visibility for non-licensed types - Add DownloadController for secure file delivery - Update CLAUDE.md roadmap and session history Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
319 lines
13 KiB
Markdown
319 lines
13 KiB
Markdown
# WooCommerce Plugin for licensed Products
|
|
|
|
**Author:** Marco Graetsch
|
|
**Author URL:** <https://src.bundespruefstelle.ch/magdev>
|
|
**Author Email:** <magdev3.0@gmail.com>
|
|
**Repository URL:** <https://src.bundespruefstelle.ch/magdev/wc-licensed-product>
|
|
**Issues URL:** <https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues>
|
|
|
|
## Project Overview
|
|
|
|
This plugin adds a new product type named `licensed` to sell software products using license keys. It implements an endpoint to check valid licenses against the WooCommerce payment database. License keys are generated are bounded to a specific domain, which must be defined within the checkout process, and will be available as soon as an order is marked as paid. License keys can optionally be bound to major software versions. Customer can view their purchased license keys in their customer profile.
|
|
|
|
### Features
|
|
|
|
- Add a new product type named `licensed`
|
|
- Generate license keys based on purchased releases, bounded to a specific domain, if the related payment is marked as completed
|
|
|
|
#### Frontend
|
|
|
|
- Customers can view their purchased licenses in the customer area
|
|
|
|
#### Administration
|
|
|
|
- Add CRUD for license keys
|
|
- Manage different versions of the same licensed product
|
|
|
|
### Key Fact: 100% AI-Generated
|
|
|
|
This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance.
|
|
|
|
## Temporary Roadmap
|
|
|
|
**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.
|
|
|
|
### Known Bugs
|
|
|
|
_No known bugs at this time._
|
|
|
|
### Version 0.0.4 (Next)
|
|
|
|
- Consider adding license usage statistics/analytics
|
|
- Consider adding bulk license operations in admin
|
|
- Consider adding license renewal/extension functionality
|
|
|
|
## Technical Stack
|
|
|
|
- **Language:** PHP 8.3.x
|
|
- **Framework:** Latest WordPress Plugin API
|
|
- **E-commerce:** WooCommerce 10.0+
|
|
- **Template Engine:** Twig 3.0 (via Composer)
|
|
- **Wordpress Base Theme** twentytwentyfive
|
|
- **Frontend:** Vanilla JavaScript
|
|
- **Styling:** Custom CSS
|
|
- **Dependency Management:** Composer
|
|
- **Internationalization:** WordPress i18n (.pot/.po/.mo files)
|
|
- **Canonical Plugin Name:** `wc-licensed-product`
|
|
|
|
### Security Best Practices
|
|
|
|
- All user inputs are sanitized (integers for quantities/prices)
|
|
- Nonce verification on form submissions
|
|
- Output escaping in templates (`esc_attr`, `esc_html`, `esc_js`)
|
|
- Direct file access prevention via `ABSPATH` check
|
|
|
|
### Translation Ready
|
|
|
|
All user-facing strings use:
|
|
|
|
```php
|
|
__('Text to translate', 'wc-licensed-product')
|
|
_e('Text to translate', 'wc-licensed-product')
|
|
```
|
|
|
|
Text domain: `wc-licensed-product`
|
|
|
|
#### Translation Template
|
|
|
|
- Base `.pot` file created: `languages/wc-licensed-product.pot`
|
|
- Ready for translation to any locale
|
|
- All translatable strings properly marked with text domain
|
|
|
|
#### Available Translations
|
|
|
|
- `en_US` - English (United States) [base language - .pot template]
|
|
- `de_CH` - German (Switzerland, formal)
|
|
|
|
To compile translations to .mo files for production:
|
|
|
|
```bash
|
|
for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
|
|
```
|
|
|
|
### Create releases
|
|
|
|
- The `vendor/` directory MUST be included in releases (Dependencies required for runtime)
|
|
- **Don't create any release files until version 0.1.x and up!**
|
|
- **CRITICAL**: Build `vendor/` for the MINIMUM supported PHP version, not the development version
|
|
- Use `composer config platform.php 8.3.0` before building release packages
|
|
- Run `composer update --no-dev --optimize-autoloader` to rebuild dependencies
|
|
- **CRITICAL**: WordPress requires plugins in a subdirectory structure
|
|
- Run zip from the `plugins/` parent directory, NOT from within the plugin directory
|
|
- Package must extract to `wc-licensed-product/` subdirectory with main file at `wc-licensed-product/wc-licensed-product.php`
|
|
- Correct command: `cd /wp-content/plugins/ && zip -r wc-licensed-product/releases/wc-licensed-product-x.x.x.zip wc-licensed-product ...`
|
|
- Wrong: Running zip from inside the plugin directory creates files at root level
|
|
- **CRITICAL**: Exclude symlinks explicitly - zip follows symlinks by default
|
|
- Always use `-x "wc-licensed-product/wp-core" -x "wc-licensed-product/wp-core/*" -x "wc-licensed-product/wp-plugins" -x "wc-licensed-product/wp-plugins/*"` to exclude development symlinks
|
|
- Otherwise the entire linked directory contents will be included in the package
|
|
- Exclusion patterns must match the relative path structure used in zip command
|
|
- Always verify the package structure with `unzip -l` before distribution
|
|
- Check all files are prefixed with `wc-licensed-product/`
|
|
- Verify main file is at `wc-licensed-product/wc-licensed-product.php`
|
|
- Check for duplicate entries (indicates multiple builds in same archive)
|
|
- Test installation on the minimum supported PHP version before final deployment
|
|
- Releases are stored in `releases/` including checksums
|
|
- Track release changes in a single `CHANGELOG.md` file
|
|
- Bump the version number to either bugfix release versions or on new features minor release versions
|
|
- **CRITICAL**: WordPress reads version from TWO places - BOTH must be updated:
|
|
1. Plugin header comment `Version: x.x.x` (line ~6 in wc-licensed-product.php) - WordPress uses THIS for admin display
|
|
2. PHP constant `WC_LICENSED_PRODUCT_VERSION` (line ~28) - Used internally by the plugin
|
|
- If only the constant is updated, WordPress will show the old version in Plugins list
|
|
|
|
**Important Git Notes:**
|
|
|
|
- Default branch while development is `dev`
|
|
- Create releases from branch `main` after merging branch `dev`
|
|
- Tags should use format `vX.X.X` (e.g., `v1.1.22`), start with v0.1.0
|
|
- Use annotated tags (`-a`) not lightweight tags
|
|
- Commit messages should follow the established format with Claude Code attribution
|
|
- `.claude/settings.local.json` changes are typically local-only (stash before rebasing)
|
|
|
|
#### What Gets Released
|
|
|
|
- All plugin source files
|
|
- Compiled vendor dependencies
|
|
- Translation files (.mo compiled from .po)
|
|
- Assets (CSS, JS)
|
|
- Documentation (README, CHANGELOG, etc.)
|
|
|
|
#### What's Excluded
|
|
|
|
- Git metadata (`.git/`)
|
|
- Development files (`.vscode/`, `.claude/`, `CLAUDE.md`, `wp-core`, `wp-plugins`)
|
|
- Logs and cache files
|
|
- Previous releases
|
|
- `composer.lock` (but `vendor/` is included)
|
|
|
|
---
|
|
|
|
**For AI Assistants:**
|
|
|
|
When starting a new session on this project:
|
|
|
|
1. Read this CLAUDE.md file first
|
|
2. Semantic versioning follows the `MAJOR.MINOR.BUGFIX` pattern
|
|
3. Check git log for recent changes
|
|
4. Verify you're on the `dev` branch before making changes
|
|
5. Run `composer install` if vendor/ is missing
|
|
6. Test changes before committing
|
|
7. Follow commit message format with Claude Code attribution
|
|
8. Update this session history section with learnings
|
|
9. Never commit backup files (`*.po~`, `*.bak`, etc.) - check `git status` before committing
|
|
10. Follow markdown linting rules (see below)
|
|
|
|
Always refer to this document when starting work on this project.
|
|
|
|
### Markdown Linting Rules
|
|
|
|
When editing CLAUDE.md or other markdown files, follow these rules to avoid linting errors:
|
|
|
|
1. **MD031 - Blank lines around fenced code blocks**: Always add a blank line before and after fenced code blocks, even when they follow list items. Example of correct format:
|
|
|
|
- **Item label**:
|
|
|
|
(blank line here)
|
|
\`\`\`php
|
|
code example
|
|
\`\`\`
|
|
(blank line here)
|
|
|
|
2. **MD056 - Table column count**: Table separators must have matching column counts and proper spacing. Use consistent dash lengths that match column header widths.
|
|
|
|
3. **MD009 - No trailing spaces**: Remove trailing whitespace from lines
|
|
|
|
4. **MD012 - No multiple consecutive blank lines**: Use only single blank lines between sections
|
|
|
|
5. **MD040 - Fenced code blocks should have a language specified**: Always add a language identifier to code blocks (e.g., `txt`, `bash`, `php`). For shortcode examples, use `txt`.
|
|
|
|
6. **MD032 - Lists should be surrounded by blank lines**: Add a blank line before AND after list blocks, including after bold labels like `**Attributes:**`.
|
|
|
|
7. **MD034 - Bare URLs**: Wrap URLs in angle brackets (e.g., `<https://example.com>`) or use markdown link syntax `[text](url)`.
|
|
|
|
8. **Author section formatting**: Use a heading (`### Name`) instead of bold (`**Name**`) for the author name to maintain consistent document structure.
|
|
|
|
## Project Architecture
|
|
|
|
### Directory Structure
|
|
|
|
```txt
|
|
wc-licensed-product/
|
|
├── assets/
|
|
│ └── css/ # Frontend and admin stylesheets
|
|
├── languages/ # Translation files (.pot, .po, .mo)
|
|
├── releases/ # Release packages (version 0.1.0+)
|
|
├── src/
|
|
│ ├── Admin/ # AdminController - license management UI
|
|
│ ├── Api/ # RestApiController - license validation endpoints
|
|
│ ├── Checkout/ # CheckoutController - domain field at checkout
|
|
│ ├── Frontend/ # AccountController - customer licenses page
|
|
│ ├── License/ # License model and LicenseManager
|
|
│ └── Product/ # LicensedProduct type and LicensedProductType
|
|
├── templates/
|
|
│ ├── admin/ # Twig templates for admin views
|
|
│ └── frontend/ # Twig templates for customer views
|
|
└── vendor/ # Composer dependencies (Twig)
|
|
```
|
|
|
|
### Database Tables
|
|
|
|
Created on plugin activation via `Installer::createTables()`:
|
|
|
|
- `{prefix}_wc_licensed_product_licenses` - License keys with domain binding
|
|
- `{prefix}_wc_licensed_product_versions` - Product version tracking
|
|
|
|
### REST API Endpoints
|
|
|
|
Base: `/wp-json/wc-licensed-product/v1/`
|
|
|
|
| Endpoint | Method | Description |
|
|
| -------------- | ------ | -------------------------------- |
|
|
| `/validate` | POST | Validate license key for domain |
|
|
| `/status` | POST | Get license status |
|
|
| `/activate` | POST | Activate license on domain |
|
|
| `/deactivate` | POST | Deactivate license |
|
|
|
|
### Key Classes
|
|
|
|
- `Plugin` (singleton) - Main controller, initializes all components
|
|
- `Installer` - Database setup, activation/deactivation hooks
|
|
- `LicenseManager` - License CRUD, validation, key generation
|
|
- `License` - Entity model with status constants
|
|
- `LicensedProduct` - Extends WC_Product for licensed products
|
|
- `LicensedProductType` - Registers product type with WooCommerce
|
|
|
|
---
|
|
|
|
## Session History
|
|
|
|
### 2026-01-21 - Initial Implementation (v0.0.1)
|
|
|
|
**Implemented:**
|
|
|
|
- Created basic WordPress/WooCommerce plugin structure
|
|
- Implemented domain-bound license management via REST API
|
|
- Added "Licensed Product" WooCommerce product type
|
|
- License generation on order completion (XXXX-XXXX-XXXX-XXXX format)
|
|
- Customer account page for viewing licenses
|
|
- Admin CRUD interface for license management
|
|
- Checkout domain field for licensed products
|
|
- German (de_CH) translation
|
|
|
|
**Architecture decisions:**
|
|
|
|
- Used Twig 3.0 for templating with fallback PHP templates
|
|
- License keys use format XXXX-XXXX-XXXX-XXXX (alphanumeric)
|
|
- Licenses bound to normalized domains (no protocol, no www, lowercase)
|
|
- REST API at `/wp-json/wc-licensed-product/v1/`
|
|
- Custom database tables for licenses and product versions
|
|
- WooCommerce HPOS compatibility included
|
|
|
|
### 2026-01-21 - Version 0.0.2 Features
|
|
|
|
**Implemented:**
|
|
|
|
- Product version management UI (meta box on product edit page)
|
|
- AJAX-based version CRUD operations
|
|
- Email notifications with license keys on order completion
|
|
- REST API rate limiting (30 requests/minute per IP)
|
|
|
|
**New classes:**
|
|
|
|
- `ProductVersion` - Version entity model
|
|
- `VersionManager` - Version CRUD operations
|
|
- `VersionAdminController` - Admin UI for versions
|
|
- `LicenseEmailController` - Email integration
|
|
|
|
**Technical notes:**
|
|
|
|
- Rate limiting uses WordPress transients
|
|
- IP detection supports Cloudflare and proxies
|
|
- Version management uses AJAX for smooth UX
|
|
|
|
### 2026-01-21 - Version 0.0.3 Features
|
|
|
|
**Implemented:**
|
|
|
|
- File attachment support for product versions (WordPress Media Library integration)
|
|
- Version auto-detection from uploaded filenames (e.g., `plugin-v1.2.3.zip`)
|
|
- Customer download page for purchased licenses with secure authenticated downloads
|
|
- License key copy-to-clipboard functionality on account page
|
|
- New card-based UI for customer licenses page with download section
|
|
- Product versions meta box visibility fix (now hidden for non-licensed product types)
|
|
|
|
**New classes:**
|
|
|
|
- `DownloadController` - Secure file delivery with hash-based URL verification
|
|
|
|
**Technical notes:**
|
|
|
|
- Secure download URLs use hash verification (license_id-version_id-hash format)
|
|
- Database schema updated: `attachment_id` column added to versions table
|
|
- ProductVersion model extended with `getEffectiveDownloadUrl()` and `getDownloadFilename()`
|
|
- Media uploader filters for zip files only
|
|
- Clipboard API with fallback for older browsers
|
|
- Card-based responsive UI design for licenses page
|
|
|
|
**Bug fixes:**
|
|
|
|
- Fixed product versions meta box visibility for non-licensed product types (targets `#wc_licensed_product_versions` container)
|