Implement version 0.0.1 - Licensed Product type for WooCommerce

Add complete plugin infrastructure for selling software with license keys:

- New "Licensed Product" WooCommerce product type
- License key generation (XXXX-XXXX-XXXX-XXXX format) on order completion
- Domain-based license validation system
- REST API endpoints (validate, status, activate, deactivate)
- Customer My Account "Licenses" page
- Admin license management under WooCommerce > Licenses
- Checkout domain field for licensed products
- Custom database tables for licenses and product versions
- Twig template engine integration
- Full i18n support with German (de_CH) translation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 18:55:18 +01:00
parent 8a4802248c
commit 404083f023
22 changed files with 3746 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
<?php
/**
* Checkout Controller
*
* @package Jeremias\WcLicensedProduct\Checkout
*/
declare(strict_types=1);
namespace Jeremias\WcLicensedProduct\Checkout;
use Jeremias\WcLicensedProduct\License\LicenseManager;
/**
* Handles checkout modifications for licensed products
*/
final class CheckoutController
{
private LicenseManager $licenseManager;
public function __construct(LicenseManager $licenseManager)
{
$this->licenseManager = $licenseManager;
$this->registerHooks();
}
/**
* Register WordPress hooks
*/
private function registerHooks(): void
{
// Add domain field to checkout
add_action('woocommerce_after_order_notes', [$this, 'addDomainField']);
// Validate domain field
add_action('woocommerce_checkout_process', [$this, 'validateDomainField']);
// Save domain field to order meta
add_action('woocommerce_checkout_update_order_meta', [$this, 'saveDomainField']);
// Display domain in order details (admin)
add_action('woocommerce_admin_order_data_after_billing_address', [$this, 'displayDomainInAdmin']);
// Display domain in order email
add_action('woocommerce_email_after_order_table', [$this, 'displayDomainInEmail'], 10, 3);
}
/**
* Check if cart contains licensed products
*/
private function cartHasLicensedProducts(): bool
{
if (!WC()->cart) {
return false;
}
foreach (WC()->cart->get_cart() as $cartItem) {
$product = $cartItem['data'];
if ($product && $product->is_type('licensed')) {
return true;
}
}
return false;
}
/**
* Add domain field to checkout form
*/
public function addDomainField(): void
{
if (!$this->cartHasLicensedProducts()) {
return;
}
?>
<div id="licensed-product-domain-field">
<h3><?php esc_html_e('License Domain', 'wc-licensed-product'); ?></h3>
<p class="form-row form-row-wide">
<label for="licensed_product_domain">
<?php esc_html_e('Domain for License Activation', 'wc-licensed-product'); ?>
<abbr class="required" title="<?php esc_attr_e('required', 'wc-licensed-product'); ?>">*</abbr>
</label>
<input
type="text"
class="input-text"
name="licensed_product_domain"
id="licensed_product_domain"
placeholder="<?php esc_attr_e('example.com', 'wc-licensed-product'); ?>"
value="<?php echo esc_attr(WC()->checkout->get_value('licensed_product_domain')); ?>"
/>
<span class="description">
<?php esc_html_e('Enter the domain where you will use this license (without http:// or www).', 'wc-licensed-product'); ?>
</span>
</p>
</div>
<?php
}
/**
* Validate domain field during checkout
*/
public function validateDomainField(): void
{
if (!$this->cartHasLicensedProducts()) {
return;
}
$domain = isset($_POST['licensed_product_domain'])
? sanitize_text_field($_POST['licensed_product_domain'])
: '';
if (empty($domain)) {
wc_add_notice(
__('Please enter a domain for your license activation.', 'wc-licensed-product'),
'error'
);
return;
}
// Validate domain format
$normalizedDomain = $this->licenseManager->normalizeDomain($domain);
if (!$this->isValidDomain($normalizedDomain)) {
wc_add_notice(
__('Please enter a valid domain name.', 'wc-licensed-product'),
'error'
);
}
}
/**
* Save domain field to order meta
*/
public function saveDomainField(int $orderId): void
{
if (!$this->cartHasLicensedProducts()) {
return;
}
if (isset($_POST['licensed_product_domain']) && !empty($_POST['licensed_product_domain'])) {
$domain = sanitize_text_field($_POST['licensed_product_domain']);
$normalizedDomain = $this->licenseManager->normalizeDomain($domain);
$order = wc_get_order($orderId);
if ($order) {
$order->update_meta_data('_licensed_product_domain', $normalizedDomain);
$order->save();
}
}
}
/**
* Display domain in admin order view
*/
public function displayDomainInAdmin(\WC_Order $order): void
{
$domain = $order->get_meta('_licensed_product_domain');
if (!$domain) {
return;
}
?>
<p>
<strong><?php esc_html_e('License Domain:', 'wc-licensed-product'); ?></strong>
<?php echo esc_html($domain); ?>
</p>
<?php
}
/**
* Display domain in order emails
*/
public function displayDomainInEmail(\WC_Order $order, bool $sentToAdmin, bool $plainText): void
{
$domain = $order->get_meta('_licensed_product_domain');
if (!$domain) {
return;
}
if ($plainText) {
echo "\n" . esc_html__('License Domain:', 'wc-licensed-product') . ' ' . esc_html($domain) . "\n";
} else {
?>
<p>
<strong><?php esc_html_e('License Domain:', 'wc-licensed-product'); ?></strong>
<?php echo esc_html($domain); ?>
</p>
<?php
}
}
/**
* Validate domain format
*/
private function isValidDomain(string $domain): bool
{
// Basic domain validation
if (strlen($domain) > 255) {
return false;
}
// Check for valid domain pattern
$pattern = '/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/';
return (bool) preg_match($pattern, $domain);
}
}