You've already forked wc-licensed-product
Implement multi-domain licensing for v0.5.0
- Add multi-domain checkout support for WooCommerce Blocks - Fix domain field rendering using ExperimentalOrderMeta slot - Add DOM injection fallback for checkout field rendering - Update translations with new multi-domain strings (de_CH) - Update email templates for grouped license display - Refactor account page to group licenses by product/order Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,8 +94,10 @@ final class OrderLicenseController
|
||||
return;
|
||||
}
|
||||
|
||||
// Get order domain
|
||||
$orderDomain = $order->get_meta('_licensed_product_domain');
|
||||
// Check for multi-domain format first, then fall back to legacy single domain
|
||||
$multiDomainData = $order->get_meta('_licensed_product_domains');
|
||||
$legacyDomain = $order->get_meta('_licensed_product_domain');
|
||||
$hasMultiDomain = !empty($multiDomainData) && is_array($multiDomainData);
|
||||
|
||||
// Get licenses for this order
|
||||
$licenses = $this->licenseManager->getLicensesByOrder($order->get_id());
|
||||
@@ -104,23 +106,42 @@ final class OrderLicenseController
|
||||
?>
|
||||
<div class="wclp-order-licenses">
|
||||
<div class="wclp-order-domain-section">
|
||||
<h4><?php esc_html_e('Order Domain', 'wc-licensed-product'); ?></h4>
|
||||
<p class="description">
|
||||
<?php esc_html_e('The domain specified during checkout. Changing this will not automatically update existing license domains.', 'wc-licensed-product'); ?>
|
||||
</p>
|
||||
<div class="wclp-inline-edit">
|
||||
<input type="text"
|
||||
id="wclp-order-domain"
|
||||
class="regular-text"
|
||||
value="<?php echo esc_attr($orderDomain); ?>"
|
||||
data-order-id="<?php echo esc_attr($order->get_id()); ?>"
|
||||
placeholder="<?php esc_attr_e('example.com', 'wc-licensed-product'); ?>" />
|
||||
<button type="button" class="button" id="wclp-save-order-domain">
|
||||
<?php esc_html_e('Save', 'wc-licensed-product'); ?>
|
||||
</button>
|
||||
<span class="spinner"></span>
|
||||
<span class="wclp-status-message"></span>
|
||||
</div>
|
||||
<h4><?php esc_html_e('Order Domains', 'wc-licensed-product'); ?></h4>
|
||||
|
||||
<?php if ($hasMultiDomain): ?>
|
||||
<p class="description">
|
||||
<?php esc_html_e('Domains specified during checkout (multi-domain order).', 'wc-licensed-product'); ?>
|
||||
</p>
|
||||
<div class="wclp-multi-domain-display" style="margin-top: 10px;">
|
||||
<?php foreach ($multiDomainData as $item): ?>
|
||||
<?php
|
||||
$product = wc_get_product($item['product_id']);
|
||||
$productName = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product');
|
||||
?>
|
||||
<div class="wclp-product-domains-item" style="margin-bottom: 10px; padding: 10px; background: #f8f8f8; border-radius: 4px;">
|
||||
<strong><?php echo esc_html($productName); ?>:</strong><br>
|
||||
<code><?php echo esc_html(implode(', ', $item['domains'])); ?></code>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="description">
|
||||
<?php esc_html_e('The domain specified during checkout. Changing this will not automatically update existing license domains.', 'wc-licensed-product'); ?>
|
||||
</p>
|
||||
<div class="wclp-inline-edit">
|
||||
<input type="text"
|
||||
id="wclp-order-domain"
|
||||
class="regular-text"
|
||||
value="<?php echo esc_attr($legacyDomain); ?>"
|
||||
data-order-id="<?php echo esc_attr($order->get_id()); ?>"
|
||||
placeholder="<?php esc_attr_e('example.com', 'wc-licensed-product'); ?>" />
|
||||
<button type="button" class="button" id="wclp-save-order-domain">
|
||||
<?php esc_html_e('Save', 'wc-licensed-product'); ?>
|
||||
</button>
|
||||
<span class="spinner"></span>
|
||||
<span class="wclp-status-message"></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
@@ -128,15 +149,26 @@ final class OrderLicenseController
|
||||
<h4><?php esc_html_e('Licenses', 'wc-licensed-product'); ?></h4>
|
||||
|
||||
<?php
|
||||
// Count licensed products to check if all have licenses
|
||||
$licensedProductCount = 0;
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if ($product && $product->is_type('licensed')) {
|
||||
$licensedProductCount++;
|
||||
// Count expected licenses based on domain data
|
||||
$expectedLicenses = 0;
|
||||
if ($hasMultiDomain) {
|
||||
// Multi-domain: count total domains across all products
|
||||
foreach ($multiDomainData as $item) {
|
||||
if (isset($item['domains']) && is_array($item['domains'])) {
|
||||
$expectedLicenses += count($item['domains']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Legacy: one license per licensed product
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if ($product && $product->is_type('licensed')) {
|
||||
$expectedLicenses++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$missingLicenses = $licensedProductCount - count($licenses);
|
||||
$missingLicenses = $expectedLicenses - count($licenses);
|
||||
$hasDomainData = $hasMultiDomain || !empty($legacyDomain);
|
||||
?>
|
||||
|
||||
<?php if (empty($licenses)): ?>
|
||||
@@ -150,7 +182,7 @@ final class OrderLicenseController
|
||||
<em><?php esc_html_e('Licenses will be generated when the order is marked as paid/completed.', 'wc-licensed-product'); ?></em>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<?php if ($orderDomain && $order->is_paid()): ?>
|
||||
<?php if ($hasDomainData && $order->is_paid()): ?>
|
||||
<p style="margin-top: 10px;">
|
||||
<button type="button" class="button button-primary" id="wclp-generate-licenses" data-order-id="<?php echo esc_attr($order->get_id()); ?>">
|
||||
<?php esc_html_e('Generate Licenses', 'wc-licensed-product'); ?>
|
||||
@@ -158,7 +190,7 @@ final class OrderLicenseController
|
||||
<span class="spinner" style="float: none; margin-top: 4px;"></span>
|
||||
<span class="wclp-generate-status"></span>
|
||||
</p>
|
||||
<?php elseif (!$orderDomain): ?>
|
||||
<?php elseif (!$hasDomainData): ?>
|
||||
<p class="description" style="margin-top: 10px; color: #d63638;">
|
||||
<span class="dashicons dashicons-warning"></span>
|
||||
<?php esc_html_e('Please set the order domain above before generating licenses.', 'wc-licensed-product'); ?>
|
||||
@@ -251,7 +283,7 @@ final class OrderLicenseController
|
||||
?>
|
||||
</p>
|
||||
|
||||
<?php if ($missingLicenses > 0 && $orderDomain && $order->is_paid()): ?>
|
||||
<?php if ($missingLicenses > 0 && $hasDomainData && $order->is_paid()): ?>
|
||||
<p style="margin-top: 10px;">
|
||||
<span class="dashicons dashicons-warning" style="color: #dba617;"></span>
|
||||
<?php
|
||||
@@ -474,68 +506,138 @@ final class OrderLicenseController
|
||||
wp_send_json_error(['message' => __('Order must be paid before licenses can be generated.', 'wc-licensed-product')]);
|
||||
}
|
||||
|
||||
// Get domain
|
||||
$domain = $order->get_meta('_licensed_product_domain');
|
||||
if (empty($domain)) {
|
||||
// Check for multi-domain format first
|
||||
$multiDomainData = $order->get_meta('_licensed_product_domains');
|
||||
$legacyDomain = $order->get_meta('_licensed_product_domain');
|
||||
|
||||
if (!empty($multiDomainData) && is_array($multiDomainData)) {
|
||||
// Multi-domain format
|
||||
$result = $this->generateMultiDomainLicenses($order, $multiDomainData);
|
||||
} elseif (!empty($legacyDomain)) {
|
||||
// Legacy single domain format
|
||||
$result = $this->generateLegacyLicenses($order, $legacyDomain);
|
||||
} else {
|
||||
wp_send_json_error(['message' => __('Please set the order domain before generating licenses.', 'wc-licensed-product')]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate licenses for each licensed product
|
||||
$generated = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if ($product && $product->is_type('licensed')) {
|
||||
$license = $this->licenseManager->generateLicense(
|
||||
$orderId,
|
||||
$product->get_id(),
|
||||
$order->get_customer_id(),
|
||||
$domain
|
||||
);
|
||||
|
||||
if ($license) {
|
||||
// Check if this is a new license or existing
|
||||
$existingLicenses = $this->licenseManager->getLicensesByOrder($orderId);
|
||||
$isNew = true;
|
||||
foreach ($existingLicenses as $existing) {
|
||||
if ($existing->getProductId() === $product->get_id() && $existing->getId() !== $license->getId()) {
|
||||
$isNew = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($isNew) {
|
||||
$generated++;
|
||||
} else {
|
||||
$skipped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($generated > 0) {
|
||||
if ($result['generated'] > 0) {
|
||||
wp_send_json_success([
|
||||
'message' => sprintf(
|
||||
/* translators: %d: Number of licenses generated */
|
||||
_n(
|
||||
'%d license generated successfully.',
|
||||
'%d licenses generated successfully.',
|
||||
$generated,
|
||||
$result['generated'],
|
||||
'wc-licensed-product'
|
||||
),
|
||||
$generated
|
||||
$result['generated']
|
||||
),
|
||||
'generated' => $generated,
|
||||
'skipped' => $skipped,
|
||||
'generated' => $result['generated'],
|
||||
'skipped' => $result['skipped'],
|
||||
'reload' => true,
|
||||
]);
|
||||
} else {
|
||||
wp_send_json_success([
|
||||
'message' => __('All licenses already exist for this order.', 'wc-licensed-product'),
|
||||
'generated' => 0,
|
||||
'skipped' => $skipped,
|
||||
'skipped' => $result['skipped'],
|
||||
'reload' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate licenses for multi-domain format
|
||||
*/
|
||||
private function generateMultiDomainLicenses(\WC_Order $order, array $domainData): array
|
||||
{
|
||||
$generated = 0;
|
||||
$skipped = 0;
|
||||
$orderId = $order->get_id();
|
||||
$customerId = $order->get_customer_id();
|
||||
|
||||
// Index domains by product ID
|
||||
$domainsByProduct = [];
|
||||
foreach ($domainData as $item) {
|
||||
if (isset($item['product_id']) && isset($item['domains']) && is_array($item['domains'])) {
|
||||
$domainsByProduct[(int) $item['product_id']] = $item['domains'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if (!$product || !$product->is_type('licensed')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$productId = $product->get_id();
|
||||
$domains = $domainsByProduct[$productId] ?? [];
|
||||
|
||||
// Get existing licenses for this product
|
||||
$existingLicenses = $this->licenseManager->getLicensesByOrderAndProduct($orderId, $productId);
|
||||
$existingDomains = array_map(fn($l) => $l->getDomain(), $existingLicenses);
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
$normalizedDomain = $this->licenseManager->normalizeDomain($domain);
|
||||
|
||||
// Skip if license already exists for this domain
|
||||
if (in_array($normalizedDomain, $existingDomains, true)) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$license = $this->licenseManager->generateLicense(
|
||||
$orderId,
|
||||
$productId,
|
||||
$customerId,
|
||||
$normalizedDomain
|
||||
);
|
||||
|
||||
if ($license) {
|
||||
$generated++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['generated' => $generated, 'skipped' => $skipped];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate licenses for legacy single domain format
|
||||
*/
|
||||
private function generateLegacyLicenses(\WC_Order $order, string $domain): array
|
||||
{
|
||||
$generated = 0;
|
||||
$skipped = 0;
|
||||
$orderId = $order->get_id();
|
||||
$customerId = $order->get_customer_id();
|
||||
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if (!$product || !$product->is_type('licensed')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if license already exists
|
||||
$existing = $this->licenseManager->getLicenseByOrderAndProduct($orderId, $product->get_id());
|
||||
if ($existing) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$license = $this->licenseManager->generateLicense(
|
||||
$orderId,
|
||||
$product->get_id(),
|
||||
$customerId,
|
||||
$domain
|
||||
);
|
||||
|
||||
if ($license) {
|
||||
$generated++;
|
||||
}
|
||||
}
|
||||
|
||||
return ['generated' => $generated, 'skipped' => $skipped];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user