feat: Add licensing module backend

- LicensingSettings.php with key format, activation limits, expiry settings
- LicenseManager.php with key generation, activation/deactivation, validation
- LicensingModule.php with WooCommerce product meta integration
- LicensesController.php with admin, customer, and public API endpoints
- Database tables: woonoow_licenses, woonoow_license_activations
- has_settings enabled in ModuleRegistry
This commit is contained in:
Dwindi Ramadhana
2026-01-05 16:20:32 +07:00
parent 663e6c13e6
commit b367c1fcf8
7 changed files with 1005 additions and 0 deletions

View File

@@ -0,0 +1,291 @@
<?php
/**
* Licenses API Controller
*
* REST API endpoints for license management.
*
* @package WooNooW\Api
*/
namespace WooNooW\Api;
if (!defined('ABSPATH')) exit;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use WooNooW\Core\ModuleRegistry;
use WooNooW\Modules\Licensing\LicenseManager;
class LicensesController {
/**
* Register REST routes
*/
public static function register_routes() {
// Check if module is enabled
if (!ModuleRegistry::is_enabled('licensing')) {
return;
}
// Admin routes
register_rest_route('woonoow/v1', '/licenses', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_licenses'],
'permission_callback' => function() {
return current_user_can('manage_woocommerce');
},
]);
register_rest_route('woonoow/v1', '/licenses/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_license'],
'permission_callback' => function() {
return current_user_can('manage_woocommerce');
},
]);
register_rest_route('woonoow/v1', '/licenses/(?P<id>\d+)', [
'methods' => 'DELETE',
'callback' => [__CLASS__, 'revoke_license'],
'permission_callback' => function() {
return current_user_can('manage_woocommerce');
},
]);
register_rest_route('woonoow/v1', '/licenses/(?P<id>\d+)/activations', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_activations'],
'permission_callback' => function() {
return current_user_can('manage_woocommerce');
},
]);
// Customer routes
register_rest_route('woonoow/v1', '/account/licenses', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_customer_licenses'],
'permission_callback' => function() {
return is_user_logged_in();
},
]);
register_rest_route('woonoow/v1', '/account/licenses/(?P<id>\d+)/deactivate', [
'methods' => 'POST',
'callback' => [__CLASS__, 'customer_deactivate'],
'permission_callback' => function() {
return is_user_logged_in();
},
]);
// Public API routes (for software validation)
register_rest_route('woonoow/v1', '/licenses/validate', [
'methods' => 'POST',
'callback' => [__CLASS__, 'validate_license'],
'permission_callback' => '__return_true',
]);
register_rest_route('woonoow/v1', '/licenses/activate', [
'methods' => 'POST',
'callback' => [__CLASS__, 'activate_license'],
'permission_callback' => '__return_true',
]);
register_rest_route('woonoow/v1', '/licenses/deactivate', [
'methods' => 'POST',
'callback' => [__CLASS__, 'deactivate_license'],
'permission_callback' => '__return_true',
]);
}
/**
* Get all licenses (admin)
*/
public static function get_licenses(WP_REST_Request $request) {
$args = [
'search' => $request->get_param('search'),
'status' => $request->get_param('status'),
'product_id' => $request->get_param('product_id'),
'user_id' => $request->get_param('user_id'),
'limit' => $request->get_param('per_page') ?: 50,
'offset' => (($request->get_param('page') ?: 1) - 1) * ($request->get_param('per_page') ?: 50),
];
$result = LicenseManager::get_all_licenses($args);
// Enrich with product and user info
foreach ($result['licenses'] as &$license) {
$license = self::enrich_license($license);
}
return new WP_REST_Response([
'licenses' => $result['licenses'],
'total' => $result['total'],
'page' => $request->get_param('page') ?: 1,
'per_page' => $args['limit'],
]);
}
/**
* Get single license (admin)
*/
public static function get_license(WP_REST_Request $request) {
$license = LicenseManager::get_license($request->get_param('id'));
if (!$license) {
return new WP_Error('not_found', __('License not found', 'woonoow'), ['status' => 404]);
}
$license = self::enrich_license($license);
$license['activations'] = LicenseManager::get_activations($license['id']);
return new WP_REST_Response($license);
}
/**
* Revoke license (admin)
*/
public static function revoke_license(WP_REST_Request $request) {
$result = LicenseManager::revoke($request->get_param('id'));
if (!$result) {
return new WP_Error('revoke_failed', __('Failed to revoke license', 'woonoow'), ['status' => 500]);
}
return new WP_REST_Response(['success' => true]);
}
/**
* Get activations for license (admin)
*/
public static function get_activations(WP_REST_Request $request) {
$activations = LicenseManager::get_activations($request->get_param('id'));
return new WP_REST_Response($activations);
}
/**
* Get customer's licenses
*/
public static function get_customer_licenses(WP_REST_Request $request) {
$user_id = get_current_user_id();
$licenses = LicenseManager::get_user_licenses($user_id);
// Enrich each license
foreach ($licenses as &$license) {
$license = self::enrich_license($license);
$license['activations'] = LicenseManager::get_activations($license['id']);
}
return new WP_REST_Response($licenses);
}
/**
* Customer deactivate their own activation
*/
public static function customer_deactivate(WP_REST_Request $request) {
$user_id = get_current_user_id();
$license = LicenseManager::get_license($request->get_param('id'));
if (!$license || $license['user_id'] != $user_id) {
return new WP_Error('not_found', __('License not found', 'woonoow'), ['status' => 404]);
}
$data = $request->get_json_params();
$result = LicenseManager::deactivate(
$license['license_key'],
$data['activation_id'] ?? null,
$data['machine_id'] ?? null
);
if (is_wp_error($result)) {
return $result;
}
return new WP_REST_Response($result);
}
/**
* Validate license (public API)
*/
public static function validate_license(WP_REST_Request $request) {
$data = $request->get_json_params();
if (empty($data['license_key'])) {
return new WP_Error('missing_key', __('License key is required', 'woonoow'), ['status' => 400]);
}
$result = LicenseManager::validate($data['license_key']);
return new WP_REST_Response($result);
}
/**
* Activate license (public API)
*/
public static function activate_license(WP_REST_Request $request) {
$data = $request->get_json_params();
if (empty($data['license_key'])) {
return new WP_Error('missing_key', __('License key is required', 'woonoow'), ['status' => 400]);
}
$activation_data = [
'domain' => $data['domain'] ?? null,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
'machine_id' => $data['machine_id'] ?? null,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
];
$result = LicenseManager::activate($data['license_key'], $activation_data);
if (is_wp_error($result)) {
return $result;
}
return new WP_REST_Response($result);
}
/**
* Deactivate license (public API)
*/
public static function deactivate_license(WP_REST_Request $request) {
$data = $request->get_json_params();
if (empty($data['license_key'])) {
return new WP_Error('missing_key', __('License key is required', 'woonoow'), ['status' => 400]);
}
$result = LicenseManager::deactivate(
$data['license_key'],
$data['activation_id'] ?? null,
$data['machine_id'] ?? null
);
if (is_wp_error($result)) {
return $result;
}
return new WP_REST_Response($result);
}
/**
* Enrich license with product and user info
*/
private static function enrich_license($license) {
// Add product info
$product = wc_get_product($license['product_id']);
$license['product_name'] = $product ? $product->get_name() : __('Unknown Product', 'woonoow');
// Add user info
$user = get_userdata($license['user_id']);
$license['user_email'] = $user ? $user->user_email : '';
$license['user_name'] = $user ? $user->display_name : __('Unknown User', 'woonoow');
// Add computed fields
$license['is_expired'] = $license['expires_at'] && strtotime($license['expires_at']) < time();
$license['activations_remaining'] = $license['activation_limit'] > 0
? max(0, $license['activation_limit'] - $license['activation_count'])
: -1;
return $license;
}
}