You've already forked wc-licensed-product
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:
271
src/Api/RestApiController.php
Normal file
271
src/Api/RestApiController.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API Controller
|
||||
*
|
||||
* @package Jeremias\WcLicensedProduct\Api
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jeremias\WcLicensedProduct\Api;
|
||||
|
||||
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
use WP_REST_Server;
|
||||
|
||||
/**
|
||||
* Handles REST API endpoints for license validation
|
||||
*/
|
||||
final class RestApiController
|
||||
{
|
||||
private const NAMESPACE = 'wc-licensed-product/v1';
|
||||
|
||||
private LicenseManager $licenseManager;
|
||||
|
||||
public function __construct(LicenseManager $licenseManager)
|
||||
{
|
||||
$this->licenseManager = $licenseManager;
|
||||
$this->registerHooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register WordPress hooks
|
||||
*/
|
||||
private function registerHooks(): void
|
||||
{
|
||||
add_action('rest_api_init', [$this, 'registerRoutes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes
|
||||
*/
|
||||
public function registerRoutes(): void
|
||||
{
|
||||
// Validate license endpoint (public)
|
||||
register_rest_route(self::NAMESPACE, '/validate', [
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'validateLicense'],
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => [
|
||||
'license_key' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'validate_callback' => function ($value): bool {
|
||||
return !empty($value) && strlen($value) <= 64;
|
||||
},
|
||||
],
|
||||
'domain' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'validate_callback' => function ($value): bool {
|
||||
return !empty($value) && strlen($value) <= 255;
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Check license status endpoint (public)
|
||||
register_rest_route(self::NAMESPACE, '/status', [
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'checkStatus'],
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => [
|
||||
'license_key' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Activate license on domain endpoint (public)
|
||||
register_rest_route(self::NAMESPACE, '/activate', [
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'activateLicense'],
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => [
|
||||
'license_key' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
],
|
||||
'domain' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Deactivate license endpoint (public)
|
||||
register_rest_route(self::NAMESPACE, '/deactivate', [
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => [$this, 'deactivateLicense'],
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => [
|
||||
'license_key' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
],
|
||||
'domain' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate license endpoint
|
||||
*/
|
||||
public function validateLicense(WP_REST_Request $request): WP_REST_Response
|
||||
{
|
||||
$licenseKey = $request->get_param('license_key');
|
||||
$domain = $request->get_param('domain');
|
||||
|
||||
$result = $this->licenseManager->validateLicense($licenseKey, $domain);
|
||||
|
||||
$statusCode = $result['valid'] ? 200 : 403;
|
||||
|
||||
return new WP_REST_Response($result, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check license status endpoint
|
||||
*/
|
||||
public function checkStatus(WP_REST_Request $request): WP_REST_Response
|
||||
{
|
||||
$licenseKey = $request->get_param('license_key');
|
||||
$license = $this->licenseManager->getLicenseByKey($licenseKey);
|
||||
|
||||
if (!$license) {
|
||||
return new WP_REST_Response([
|
||||
'valid' => false,
|
||||
'error' => 'license_not_found',
|
||||
'message' => __('License key not found.', 'wc-licensed-product'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
return new WP_REST_Response([
|
||||
'valid' => $license->isValid(),
|
||||
'status' => $license->getStatus(),
|
||||
'domain' => $license->getDomain(),
|
||||
'expires_at' => $license->getExpiresAt()?->format('Y-m-d'),
|
||||
'activations_count' => $license->getActivationsCount(),
|
||||
'max_activations' => $license->getMaxActivations(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate license on domain endpoint
|
||||
*/
|
||||
public function activateLicense(WP_REST_Request $request): WP_REST_Response
|
||||
{
|
||||
$licenseKey = $request->get_param('license_key');
|
||||
$domain = $request->get_param('domain');
|
||||
|
||||
$license = $this->licenseManager->getLicenseByKey($licenseKey);
|
||||
|
||||
if (!$license) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'license_not_found',
|
||||
'message' => __('License key not found.', 'wc-licensed-product'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
if (!$license->isValid()) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'license_invalid',
|
||||
'message' => __('This license is not valid.', 'wc-licensed-product'),
|
||||
], 403);
|
||||
}
|
||||
|
||||
$normalizedDomain = $this->licenseManager->normalizeDomain($domain);
|
||||
|
||||
// Check if already activated on this domain
|
||||
if ($license->getDomain() === $normalizedDomain) {
|
||||
return new WP_REST_Response([
|
||||
'success' => true,
|
||||
'message' => __('License is already activated for this domain.', 'wc-licensed-product'),
|
||||
]);
|
||||
}
|
||||
|
||||
// Check if can activate on another domain
|
||||
if (!$license->canActivate()) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'max_activations_reached',
|
||||
'message' => __('Maximum number of activations reached.', 'wc-licensed-product'),
|
||||
], 403);
|
||||
}
|
||||
|
||||
// Update domain (in this simple implementation, we replace the domain)
|
||||
$success = $this->licenseManager->updateLicenseDomain($license->getId(), $domain);
|
||||
|
||||
if (!$success) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'activation_failed',
|
||||
'message' => __('Failed to activate license.', 'wc-licensed-product'),
|
||||
], 500);
|
||||
}
|
||||
|
||||
return new WP_REST_Response([
|
||||
'success' => true,
|
||||
'message' => __('License activated successfully.', 'wc-licensed-product'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate license endpoint
|
||||
*/
|
||||
public function deactivateLicense(WP_REST_Request $request): WP_REST_Response
|
||||
{
|
||||
$licenseKey = $request->get_param('license_key');
|
||||
$domain = $request->get_param('domain');
|
||||
|
||||
$license = $this->licenseManager->getLicenseByKey($licenseKey);
|
||||
|
||||
if (!$license) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'license_not_found',
|
||||
'message' => __('License key not found.', 'wc-licensed-product'),
|
||||
], 404);
|
||||
}
|
||||
|
||||
$normalizedDomain = $this->licenseManager->normalizeDomain($domain);
|
||||
|
||||
// Verify domain matches
|
||||
if ($license->getDomain() !== $normalizedDomain) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'domain_mismatch',
|
||||
'message' => __('License is not activated for this domain.', 'wc-licensed-product'),
|
||||
], 403);
|
||||
}
|
||||
|
||||
// Set status to inactive
|
||||
$success = $this->licenseManager->updateLicenseStatus($license->getId(), 'inactive');
|
||||
|
||||
if (!$success) {
|
||||
return new WP_REST_Response([
|
||||
'success' => false,
|
||||
'error' => 'deactivation_failed',
|
||||
'message' => __('Failed to deactivate license.', 'wc-licensed-product'),
|
||||
], 500);
|
||||
}
|
||||
|
||||
return new WP_REST_Response([
|
||||
'success' => true,
|
||||
'message' => __('License deactivated successfully.', 'wc-licensed-product'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user