Files
wc-licensed-product/CLAUDE.md
magdev 8420734f37 Update CLAUDE.md with v0.1.0 session history
- Removed completed 0.1.0 roadmap items
- Added comprehensive session history for v0.1.0 release
- Documented code review findings and bug fixes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 12:00:01 +01:00

27 KiB

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 the moment

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:

__('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:

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

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

Full API documentation available in openapi.json (OpenAPI 3.1 specification).

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)

2026-01-21 - Version 0.0.4 Features

Implemented:

  • WooCommerce settings tab "Licensed Products" for default license settings
  • Default settings for Max Activations, License Validity, and Bind to Major Version
  • Per-product settings override global defaults (empty = use default)
  • Settings link in product edit page pointing to WooCommerce settings

New classes:

  • SettingsController - WooCommerce settings tab integration

Technical notes:

  • Settings stored using WooCommerce options API
  • LicensedProduct model falls back to global defaults when product settings are empty
  • Added has_custom_* methods to check if product has overridden defaults
  • Product edit panel shows placeholders with default values

2026-01-21 - Version 0.0.5 Features

Implemented:

  • Bulk license operations in admin (activate, deactivate, revoke, extend, delete)
  • License renewal/extension functionality (extend by 30/90/365 days)
  • Set license to lifetime (remove expiration)
  • Quick action buttons per license row (+30d, ∞, Revoke, Delete)
  • Checkbox selection with select-all for bulk operations
  • Improved admin notices for bulk operation results

New methods in LicenseManager:

  • extendLicense() - Extend license by specified days
  • setLicenseLifetime() - Remove expiration (set to lifetime)
  • bulkUpdateStatus() - Bulk update license status
  • bulkDelete() - Bulk delete licenses
  • bulkExtend() - Bulk extend licenses

Technical notes:

  • Admin page redesigned with WordPress list table styling
  • Twig template functions for generating action URLs with nonces
  • JavaScript for checkbox synchronization and bulk action handling
  • Row-actions pattern for cleaner per-license action UI

2026-01-21 - Version 0.0.6 Features

Implemented:

  • License usage statistics/analytics dashboard (WooCommerce > License Dashboard)
  • License transfer functionality (change domain from admin)
  • Export licenses to CSV with all related data
  • OpenAPI 3.1 specification for REST API (openapi.json)
  • Removed /deactivate API endpoint (admin-only operation now)

New methods in LicenseManager:

  • transferLicense() - Transfer license to new domain
  • getStatistics() - Get comprehensive license statistics
  • exportLicensesForCsv() - Export all licenses for CSV download

New files:

  • templates/admin/dashboard.html.twig - Dashboard template
  • openapi.json - OpenAPI 3.1 specification

Technical notes:

  • Dashboard shows status cards, monthly chart, top products/domains
  • Transfer uses modal dialog with form submission
  • CSV export includes BOM for UTF-8 Excel compatibility
  • OpenAPI spec documents all endpoints with examples and error responses
  • Statistics query uses SQL aggregation for performance

2026-01-21 - Version 0.0.7 Features

Implemented:

  • License Dashboard moved to WooCommerce Reports section (Reports > Licenses tab)
  • License search and filtering in admin (by key, domain, status, product)
  • Customer-facing license transfer request with AJAX modal
  • Email notifications for license expiration warnings (7 days and 1 day before)
  • Bulk import licenses from CSV with validation and options

New methods in LicenseManager:

  • getLicensesExpiringSoon() - Get licenses expiring within specified days
  • markExpirationNotified() - Track expiration notification state
  • wasExpirationNotified() - Check if notification already sent
  • importLicense() - Create license from imported data

Updated controllers:

  • AdminController - Added Reports integration, search/filter handling, CSV import
  • AccountController - Added customer transfer AJAX handler with domain validation
  • LicenseEmailController - Added cron scheduling and expiration warning emails
  • Installer - Clears cron events on deactivation

New UI features:

  • Search box and filter dropdowns on licenses page
  • Transfer button and modal on customer licenses page
  • Import CSV page with format documentation
  • Pagination preserves filter state

Technical notes:

  • WooCommerce Reports integration uses woocommerce_admin_reports filter
  • Customer transfer uses AJAX with nonce verification
  • Expiration warnings use WordPress cron with daily schedule
  • CSV import supports both exported format and simplified format
  • User meta tracks expiration notifications to prevent duplicates

2026-01-21 - Version 0.0.8 Features

Implemented:

  • Removed "Current Version" field from product license settings
  • Current version now automatically derived from latest product version in database
  • Refactored email system to use WooCommerce transactional email system
  • License Expiration Warning email configurable via WooCommerce > Settings > Emails
  • Uses WooCommerce's email header/footer templates for consistent styling
  • Configurable warning days for expiration notifications (first and second warning)

New classes:

  • LicenseExpirationEmail - WooCommerce WC_Email subclass for license expiration warnings

Modified classes:

  • LicensedProduct - get_current_version() and get_major_version() now query VersionManager
  • LicensedProductType - Removed Current Version field from product data panel
  • VersionAdminController - Removed automatic update of _licensed_current_version meta
  • LicenseEmailController - Registers WooCommerce email class, uses WC email system for sending
  • SettingsController - Updated email section to link to WooCommerce email settings

Technical notes:

  • Email uses WooCommerce's emails/email-header.php and emails/email-footer.php templates
  • Email content is rendered inline, inheriting WooCommerce's styling and customizations
  • Admins can customize subject, heading, additional content, and enable/disable via WC settings
  • Expiration warning schedule (days before) remains in plugin settings
  • Email enable/disable is controlled through WooCommerce email settings

2026-01-21 - Version 0.0.9 Features

Implemented:

  • Created API client examples for multiple programming languages
  • Documentation for REST API usage with code examples

New files:

  • docs/client-examples/README.md - API documentation and overview
  • docs/client-examples/curl.sh - cURL command examples
  • docs/client-examples/php-client.php - PHP client class with examples
  • docs/client-examples/python-client.py - Python client class with examples
  • docs/client-examples/javascript-client.js - JavaScript/Node.js client class
  • docs/client-examples/csharp-client.cs - C# client class with examples

Technical notes:

  • All client examples include error handling for rate limiting (HTTP 429)
  • Examples demonstrate all three API endpoints: validate, status, activate
  • JavaScript client works in both browser and Node.js environments
  • Python client uses dataclasses for type-safe responses
  • C# client uses async/await patterns and System.Text.Json

2026-01-21 - Bug Fixes (pre-v0.0.10)

Fixed bugs:

  • Fixed: No licenses generated when order is marked as done
  • Fixed: No domain field in WooCommerce Checkout Blocks

New files:

  • src/Checkout/CheckoutBlocksIntegration.php - WooCommerce Blocks checkout integration
  • src/Checkout/StoreApiExtension.php - Store API extension for domain field handling
  • assets/js/checkout-blocks.js - Frontend JavaScript for checkout block domain field

Modified files:

  • src/Plugin.php - Added checkout blocks registration and multiple order status hooks
  • src/License/LicenseManager.php - Fixed product type check in generateLicense()

Technical notes:

  • Added support for WooCommerce Checkout Blocks (default since WC 8.3+)
  • CheckoutBlocksIntegration implements IntegrationInterface for block checkout
  • StoreApiExtension handles server-side domain data via Store API
  • License generation now triggers on completed, processing, and payment_complete hooks
  • Fixed LicenseManager::generateLicense() to properly check product type with is_type()
  • Classic checkout (woocommerce_after_order_notes) still works for stores using classic checkout

Additional bug fix:

  • Fixed: Version uploads not appearing in list after creation

Modified files:

  • src/Product/VersionManager.php - Fixed null attachment_id handling in createVersion()
  • src/Admin/VersionAdminController.php - Added product validation and error logging

Technical notes:

  • $wpdb->insert() now only includes attachment_id field when it has a valid value
  • Added error logging for version creation failures to aid debugging
  • Improved meta box visibility logic for new products

2026-01-21 - Version 0.0.10 Features

Implemented:

  • License meta box on WooCommerce order edit pages
  • Editable order domain field with AJAX-based inline editing
  • Editable license domains directly from the order page
  • Licenses table showing all licenses associated with an order
  • Support for both classic orders and HPOS (High-Performance Order Storage)

New files:

  • src/Admin/OrderLicenseController.php - Order page license integration
  • assets/js/order-licenses.js - JavaScript for inline domain editing

New methods in LicenseManager:

  • getLicensesByOrder() - Get all licenses for a specific order

Technical notes:

  • Meta box automatically detects HPOS vs classic order storage
  • AJAX handlers for updating both order domain and individual license domains
  • Domain validation with normalization (strips protocol, www prefix)
  • Inline edit UI with save/cancel for license domains
  • Links to full licenses management page for advanced actions

Additional features added in this session:

  • Inline editing for license fields (status, expiry date, domain) in admin licenses overview
  • Copy license key button with clipboard API and fallback for older browsers
  • Live search for licenses in admin overview with AJAX-powered results dropdown
  • Settings link added to plugin action links in Plugins list
  • Fixed 404 error on licenses menu item in customer account
  • Fixed Twig template cache issues with auto_reload option

New files:

  • assets/js/admin-licenses.js - JavaScript for live search, inline editing, and copy functionality

New methods in LicenseManager:

  • updateLicenseExpiry() - Update license expiry date with auto-reactivation for expired licenses

AJAX handlers added to AdminController:

  • handleAjaxStatusUpdate() - Update license status via AJAX
  • handleAjaxExpiryUpdate() - Update license expiry date via AJAX
  • handleAjaxDomainUpdate() - Update license domain via AJAX
  • handleAjaxRevoke() - Revoke license via AJAX

Technical notes:

  • Live search uses 300ms debounce and keyboard navigation (arrows, enter, escape)
  • Inline editing shows edit icons on hover, supports enter to save and escape to cancel
  • Copy button uses Clipboard API with textarea fallback for older browsers
  • All AJAX handlers use nonce verification (wclp_inline_edit nonce)
  • Twig configured with auto_reload => true for development to always check template changes

Bug fix:

  • Fixed: Licenses menu item in customer account page resulted in 404 error
  • Root cause: WooCommerce My Account endpoints require both add_rewrite_endpoint() AND registration with woocommerce_get_query_vars filter
  • Fix: Added addLicensesQueryVar() method to register the endpoint query var with WooCommerce

Release v0.0.10:

  • Created release package: releases/wc-licensed-product-0.0.10.zip (472 KB)
  • SHA256: 3f4a093f6d4d02389082c3a88c00542f477ab3ad4d4a0c65079e524ef0739620
  • Tagged as v0.0.10 and pushed to main branch

2026-01-22 - Version 0.0.11 Features

Implemented:

  • Created date column added to admin license overview showing when each license was generated

Modified files:

  • templates/admin/licenses.html.twig - Added "Created" column to table header and data cells
  • src/Admin/AdminController.php - Added "Created" column to PHP fallback rendering
  • src/Plugin.php - Added getInstance() alias for singleton access

Technical notes:

  • New column displays license creation date in Y-m-d format
  • Both Twig template and PHP fallback updated for consistency
  • WooCommerce Analytics integration was attempted but removed due to WordPress permission issues with submenu pages

Release v0.0.11:

  • Created release package: releases/wc-licensed-product-0.0.11.zip (473 KB)
  • SHA256: c3f66c4ac54741053f87ce1a63b4ddb49ad9707d5c194a271311bb95518ab13c
  • Tagged as v0.0.11 and pushed to main branch

2026-01-22 - Version 0.1.0 - First Stable Minor Release

Overview:

First stable minor release after comprehensive code review for WordPress/WooCommerce best practices and security.

Code Review Findings:

Security practices verified:

  • Input sanitization with sanitize_text_field(), absint(), esc_attr(), esc_html(), esc_url()
  • Nonce verification on all forms and AJAX handlers
  • Capability checks with current_user_can('manage_woocommerce')
  • SQL injection prevention using $wpdb->prepare() throughout
  • Secure download URLs with hash verification using hash_equals()
  • Rate limiting on REST API (30 requests/minute)
  • Cryptographically secure license key generation with random_int()

Bug Fixes:

  • Fixed VersionManager::updateVersion() null format handling for attachment ID updates
  • Improved input sanitization in AdminController::enqueueAdminAssets() for page context checks

Documentation Updates:

  • Updated README.md with complete feature documentation
  • Added new features: Live Search, Inline Editing, Order Integration, WooCommerce HPOS compatibility, Checkout Blocks support
  • Removed outdated "Current Version" field from usage instructions

Translation Updates:

  • Regenerated .pot template with all current strings
  • Updated German (de_CH) translation with new strings
  • Compiled .mo file for production use

Modified files:

  • src/Product/VersionManager.php - Fixed null format handling in attachment update
  • src/Admin/AdminController.php - Improved $_GET sanitization for page context
  • README.md - Updated feature documentation
  • CHANGELOG.md - Added 0.1.0 release notes
  • wc-licensed-product.php - Version bump to 0.1.0
  • languages/* - Updated all translation files

Release v0.1.0:

  • Created release package: releases/wc-licensed-product-0.1.0.zip (478 KB)
  • SHA256: 62638e240315107098be4cb40faff8395e9e1b719d79b73d80e69d680b305e87
  • Tagged as v0.1.0 and pushed to main branch