# WC Licensed Product

A WooCommerce plugin to sell software products using license keys with domain-based validation.

## Description

WC Licensed Product adds a new product type "Licensed Product" to WooCommerce, enabling you to sell software with automatically generated license keys. Licenses are bound to specific domains and can be validated through a REST API.

## Features

### Core Features

- **Licensed Product Type**: New WooCommerce product type for software sales
- **Variable Licensed Products**: Create product variations with different license durations (monthly, yearly, lifetime)
- **Automatic License Generation**: License keys generated on order completion (format: XXXX-XXXX-XXXX-XXXX)
- **Domain Binding**: Licenses are bound to customer-specified domains
- **Multi-Domain Licensing**: Customers can purchase multiple licenses for different domains in a single order
- **REST API**: Public endpoints for license validation and management
- **Response Signing**: Optional HMAC-SHA256 cryptographic signatures for API responses
- **Per-License Secrets**: Each customer receives a unique verification secret for their license
- **Version Binding**: Optional binding to major software versions
- **Expiration Support**: Set license validity periods or lifetime licenses
- **Rate Limiting**: API endpoints protected with configurable rate limiting (default: 30 requests/minute)
- **Frontend Rate Limiting**: Transfer requests (5/hour) and downloads (30/hour) protected against abuse
- **Trusted Proxy Support**: Configurable trusted proxies for accurate rate limiting behind CDNs
- **Checkout Blocks**: Full support for WooCommerce Checkout Blocks (default since WC 8.3+)
- **Self-Licensing**: The plugin can validate its own license (for commercial distribution)
- **WordPress Auto-Updates**: Receive plugin updates through WordPress's native update system
- **Automated Releases**: CI/CD pipeline for consistent release packaging

### Customer Features

- **My Account Licenses**: Customers can view their licenses in My Account (grouped by product)
- **License Transfers**: Customers can transfer licenses to new domains
- **Secure Downloads**: Download purchased software versions with license verification
- **Version History**: Access to older versions with collapsible download section
- **Copy to Clipboard**: Easy license key copying
- **API Verification Secret**: Per-license secret displayed for secure API integration

### Admin Features

- **License Management**: Full CRUD interface for license management
- **License Dashboard**: Statistics and analytics (WooCommerce > Reports > Licenses)
- **Dashboard Widgets**: License statistics and download statistics on WordPress admin dashboard
- **Search & Filtering**: Search by license key, domain, status, or product
- **Live Search**: AJAX-powered instant search results
- **Inline Editing**: Edit license status, expiry, and domain directly in the list
- **Bulk Operations**: Activate, deactivate, revoke, extend, or delete multiple licenses
- **License Transfer**: Transfer licenses to new domains
- **CSV Export/Import**: Export and import licenses via CSV
- **Order Integration**: View and manage licenses directly from order pages
- **Generate Licenses**: Manually generate licenses for admin-created orders
- **Expiration Warnings**: Automatic email notifications before license expiration
- **Auto-Expire**: Daily cron job automatically expires licenses past their expiration date
- **License Testing**: Test licenses against the API directly from admin interface
- **Version Management**: Manage multiple versions per product with file attachments
- **Download Tracking**: Track download counts per version with statistics widget
- **SHA256 Checksums**: File integrity verification with SHA256 hash display
- **Global Settings**: Default license settings via WooCommerce settings tab
- **WooCommerce HPOS**: Compatible with High-Performance Order Storage

## Requirements

- WordPress 6.0 or higher
- WooCommerce 10.0 or higher
- PHP 8.3 or higher

## Installation

1. Upload the `wc-licensed-product` folder to `/wp-content/plugins/`
2. Activate the plugin through the 'Plugins' menu in WordPress
3. The plugin will create necessary database tables on activation

## Usage

### Creating a Licensed Product

1. Go to Products > Add New
2. Select "Licensed Product" from the product type dropdown (or "Licensed Variable Product" for different license durations)
3. Configure the product price in the General tab
4. Set license options in the "License Settings" tab:
   - **Max Activations**: Number of domains allowed per license
   - **License Validity**: Days until expiration (empty = lifetime)
   - **Bind to Major Version**: Lock license to current major version

### Creating Variable Licensed Products

For selling licenses with different durations (monthly, yearly, lifetime):

1. Go to Products > Add New
2. Select "Licensed Variable Product" from the product type dropdown
3. Create variations as you would for any variable product (e.g., by "License Duration")
4. For each variation, set:
   - **Variation Price**: Different prices for different durations
   - **License Duration (Days)**: Days until expiration (0 = lifetime)
   - **Max Activations**: Override parent product setting if needed

Duration labels (Monthly, Yearly, Lifetime) are automatically displayed at checkout.

### Managing Product Versions

1. Edit a Licensed Product
2. Use the "Product Versions" meta box to add versions
3. Upload version files via WordPress Media Library
4. Version numbers are auto-detected from filenames (e.g., `plugin-v1.2.3.zip`)

### Global Default Settings

1. Go to WooCommerce > Settings > Licensed Products
2. Set default values for Max Activations, License Validity, and Version Binding
3. Enable Multi-Domain Licensing to allow multiple licenses per cart item
4. Per-product settings override these defaults

### Customer Checkout

When a customer purchases a licensed product, they must enter the domain where they will use the license during checkout.

### Viewing Licenses

- **Customers**: My Account > Licenses
- **Administrators**: WooCommerce > Licenses
- **Dashboard**: WooCommerce > Reports > Licenses (for statistics)

### Exporting & Importing Licenses

**Export:**

1. Go to WooCommerce > Licenses
2. Click "Export CSV" to download all licenses

**Import:**

1. Go to WooCommerce > Licenses
2. Click "Import CSV"
3. Upload a CSV file (supports exported format or simplified format)
4. Choose options: skip header row, update existing licenses

**Import Limits (Security):**

- Maximum file size: 2MB
- Maximum rows per import: 1000
- Cooldown between imports: 5 minutes

## Security

The plugin implements several security best practices:

- **Input Sanitization**: All user inputs are sanitized using WordPress functions
- **Output Escaping**: All output is escaped to prevent XSS attacks
- **XSS-Safe DOM Construction**: JavaScript uses `createElement()` and `textContent` instead of `innerHTML`
- **CSRF Protection**: Nonce verification on all forms and AJAX requests
- **SQL Injection Prevention**: All database queries use prepared statements
- **Capability Checks**: Admin functions require `manage_woocommerce` capability
- **Secure Downloads**: File downloads use hash-verified URLs with user authentication
- **Response Signing**: Optional HMAC-SHA256 signatures for API tamper protection
- **Rate Limiting**: API and frontend operations protected against abuse
- **Import Limits**: CSV imports limited by file size, row count, and cooldown period

### Trusted Proxy Configuration

If your server is behind a load balancer, reverse proxy, or CDN (like Cloudflare), you need to configure trusted proxies for accurate rate limiting. Without this, the rate limiter uses the direct connection IP which may be your proxy's IP.

**Configuration (wp-config.php):**

```php
// For Cloudflare (includes all Cloudflare IP ranges)
define('WC_LICENSE_TRUSTED_PROXIES', 'CLOUDFLARE');

// For specific proxy IPs
define('WC_LICENSE_TRUSTED_PROXIES', '10.0.0.1,10.0.0.2');

// For CIDR ranges
define('WC_LICENSE_TRUSTED_PROXIES', '10.0.0.0/8,192.168.1.0/24');

// Combine multiple methods
define('WC_LICENSE_TRUSTED_PROXIES', 'CLOUDFLARE,10.0.0.1');
```

**Note**: Only configure trusted proxies if you actually use them. Without this configuration, rate limiting is more secure against IP spoofing attacks.

### Configurable Rate Limiting

The default rate limit is 30 requests per 60 seconds. You can customize this:

```php
// Requests allowed per window (default: 30)
define('WC_LICENSE_RATE_LIMIT', 60);

// Window duration in seconds (default: 60)
define('WC_LICENSE_RATE_WINDOW', 120);
```

## REST API

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

### Response Signing (Optional)

When the server is configured with a shared secret, all API responses include cryptographic signatures for tamper protection:

**Configuration (wp-config.php):**

```php
define('WC_LICENSE_SERVER_SECRET', 'your-secure-random-string-min-32-chars');
```

Generate a secure secret using:

```bash
openssl rand -hex 32
```

**Response Headers:**

| Header | Description |
| ------ | ----------- |
| `X-License-Signature` | HMAC-SHA256 signature of the response body |
| `X-License-Timestamp` | Unix timestamp when the response was generated |

The signature prevents man-in-the-middle attacks and ensures response integrity. Use the `magdev/wc-licensed-product-client` Composer package with the `SecureLicenseClient` class to automatically verify signatures.

**Per-License Customer Secrets**: Each customer receives a unique verification secret derived from their license key. This secret is displayed in their account page under "API Verification Secret" and can be used with the client library instead of sharing the master server secret.

### Client Libraries & Examples

**PHP (Recommended):** Install the official client library via Composer:

```bash
composer require magdev/wc-licensed-product-client
```

The library provides:

- `LicenseClient` - Standard client for API calls
- `SecureLicenseClient` - Client with automatic response signature verification

**Example clients** for other languages are available in `docs/client-examples/`:

- **cURL** - Shell script examples ([curl.sh](docs/client-examples/curl.sh))
- **PHP** - Standalone client example ([php-client.php](docs/client-examples/php-client.php))
- **Python** - Client class with dataclasses ([python-client.py](docs/client-examples/python-client.py))
- **JavaScript** - Browser and Node.js client ([javascript-client.js](docs/client-examples/javascript-client.js))
- **C#** - Async client with System.Text.Json ([csharp-client.cs](docs/client-examples/csharp-client.cs))

All examples include rate limit handling (HTTP 429) and demonstrate the validate, status, and activate endpoints.

### Validate License

Validate a license key for a specific domain.

```http
POST /wp-json/wc-licensed-product/v1/validate
Content-Type: application/json

{
  "license_key": "XXXX-XXXX-XXXX-XXXX",
  "domain": "example.com"
}
```

**Success Response (200):**

```json
{
  "valid": true,
  "license": {
    "product_id": 123,
    "expires_at": "2027-01-21",
    "version_id": 5
  }
}
```

**Error Response (403):**

```json
{
  "valid": false,
  "error": "domain_mismatch",
  "message": "This license is not valid for this domain."
}
```

### Check Status

Get detailed license status information.

```http
POST /wp-json/wc-licensed-product/v1/status
Content-Type: application/json

{
  "license_key": "XXXX-XXXX-XXXX-XXXX"
}
```

**Response (200):**

```json
{
  "valid": true,
  "status": "active",
  "domain": "example.com",
  "expires_at": "2027-01-21",
  "activations_count": 1,
  "max_activations": 3
}
```

### Activate License

Activate a license on a domain.

```http
POST /wp-json/wc-licensed-product/v1/activate
Content-Type: application/json

{
  "license_key": "XXXX-XXXX-XXXX-XXXX",
  "domain": "newdomain.com"
}
```

**Response (200):**

```json
{
  "success": true,
  "message": "License activated successfully."
}
```

### Error Codes

| Code | Description |
| ---- | ----------- |
| `license_not_found` | License key does not exist |
| `license_revoked` | License has been revoked |
| `license_expired` | License has expired |
| `license_inactive` | License is inactive |
| `domain_mismatch` | License not valid for this domain |
| `max_activations_reached` | Maximum activations reached |
| `rate_limit_exceeded` | Too many requests (wait and retry) |

## Auto-Updates

Licensed plugins can receive updates through WordPress's native plugin update system. When properly configured, WordPress will check the license server for updates and display them in the Plugins page.

### Configuration

In WooCommerce > Settings > Licensed Products > Auto-Updates:

- **Enable Update Notifications**: Show available updates in WordPress admin
- **Automatically Install Updates**: Let WordPress install updates automatically
- **Update Check Frequency**: How often to check for updates (1-168 hours)

### How It Works

1. The plugin periodically checks the configured license server for updates
2. If a newer version is available and the license is valid, WordPress shows the update
3. Updates can be installed manually or automatically (if enabled)
4. Downloads are authenticated using the license key

### API Endpoint

The update check uses the `/update-check` REST API endpoint:

```http
POST /wp-json/wc-licensed-product/v1/update-check
Content-Type: application/json

{
  "license_key": "XXXX-XXXX-XXXX-XXXX",
  "domain": "example.com",
  "plugin_slug": "my-plugin",
  "current_version": "1.0.0"
}
```

## License Statuses

- **Active**: License is valid and usable
- **Inactive**: License has been deactivated
- **Expired**: License validity period has ended
- **Revoked**: License has been manually revoked by admin

## Email Notifications

The plugin sends automatic email notifications (configurable via WooCommerce > Settings > Emails):

- **Order Completion**: License keys included in order confirmation emails
- **Expiration Warning (7 days)**: Reminder sent 7 days before expiration
- **Expiration Warning (1 day)**: Urgent reminder sent 1 day before expiration
- **License Expired**: Notification when a license auto-expires

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for version history and changes.

## Support

For issues and feature requests, please visit:
<https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues>

## Author

Marco Graetsch

## Development

### Setup

After cloning the repository, initialize the git submodule and install dependencies:

```bash
git clone https://src.bundespruefstelle.ch/magdev/wc-licensed-product.git
cd wc-licensed-product
git submodule update --init --recursive
composer install
```

### Project Structure

- `src/` - PHP source files (PSR-4 autoloaded)
- `assets/` - CSS and JavaScript files
- `templates/` - Twig templates for admin and frontend views
- `languages/` - Translation files (.pot, .po, .mo)
- `lib/` - Git submodule for the client library
- `docs/` - API documentation and client examples

### Creating Releases

Releases are automatically created by the Gitea CI/CD pipeline when a version tag is pushed:

```bash
# Update version in wc-licensed-product.php (both header and constant)
# Update CHANGELOG.md with release notes
git add -A && git commit -m "Release v0.7.3"
git tag -a v0.7.3 -m "Release v0.7.3"
git push origin main --tags
```

The pipeline will:

1. Build production dependencies
2. Compile translations
3. Create the release package with proper WordPress structure
4. Generate SHA256 checksum
5. Publish to Gitea releases

### Translations

To add or update translations:

```bash
# Extract strings to .pot template
# (Use a tool like wp-cli or poedit)

# Compile .po files to .mo for production
for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
```

## License

GPL-2.0-or-later
