0) { $key .= '-'; } for ($j = 0; $j < 4; $j++) { $key .= $chars[random_int(0, strlen($chars) - 1)]; } } return $key; } /** * Generate a license for a completed order */ public function generateLicense( int $orderId, int $productId, int $customerId, string $domain ): ?License { global $wpdb; // Check if license already exists for this order and product $existing = $this->getLicenseByOrderAndProduct($orderId, $productId); if ($existing) { return $existing; } $product = wc_get_product($productId); if (!$product instanceof LicensedProduct) { return null; } // Generate unique license key $licenseKey = $this->generateLicenseKey(); while ($this->getLicenseByKey($licenseKey)) { $licenseKey = $this->generateLicenseKey(); } // Calculate expiration date $expiresAt = null; $validityDays = $product->get_validity_days(); if ($validityDays !== null && $validityDays > 0) { $expiresAt = (new \DateTimeImmutable())->modify("+{$validityDays} days")->format('Y-m-d H:i:s'); } // Determine version ID if bound to version $versionId = null; if ($product->is_bound_to_version()) { $versionId = $this->getCurrentVersionId($productId); } $tableName = Installer::getLicensesTable(); $result = $wpdb->insert( $tableName, [ 'license_key' => $licenseKey, 'order_id' => $orderId, 'product_id' => $productId, 'customer_id' => $customerId, 'domain' => $this->normalizeDomain($domain), 'version_id' => $versionId, 'status' => License::STATUS_ACTIVE, 'activations_count' => 1, 'max_activations' => $product->get_max_activations(), 'expires_at' => $expiresAt, ], ['%s', '%d', '%d', '%d', '%s', '%d', '%s', '%d', '%d', '%s'] ); if ($result === false) { return null; } return $this->getLicenseById((int) $wpdb->insert_id); } /** * Get license by ID */ public function getLicenseById(int $id): ?License { global $wpdb; $tableName = Installer::getLicensesTable(); $row = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$tableName} WHERE id = %d", $id), ARRAY_A ); return $row ? License::fromArray($row) : null; } /** * Get license by license key */ public function getLicenseByKey(string $licenseKey): ?License { global $wpdb; $tableName = Installer::getLicensesTable(); $row = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$tableName} WHERE license_key = %s", $licenseKey), ARRAY_A ); return $row ? License::fromArray($row) : null; } /** * Get license by order and product */ public function getLicenseByOrderAndProduct(int $orderId, int $productId): ?License { global $wpdb; $tableName = Installer::getLicensesTable(); $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$tableName} WHERE order_id = %d AND product_id = %d", $orderId, $productId ), ARRAY_A ); return $row ? License::fromArray($row) : null; } /** * Get all licenses for a customer */ public function getLicensesByCustomer(int $customerId): array { global $wpdb; $tableName = Installer::getLicensesTable(); $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$tableName} WHERE customer_id = %d ORDER BY created_at DESC", $customerId ), ARRAY_A ); return array_map(fn(array $row) => License::fromArray($row), $rows ?: []); } /** * Get all licenses (for admin) */ public function getAllLicenses(int $page = 1, int $perPage = 20): array { global $wpdb; $tableName = Installer::getLicensesTable(); $offset = ($page - 1) * $perPage; $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$tableName} ORDER BY created_at DESC LIMIT %d OFFSET %d", $perPage, $offset ), ARRAY_A ); return array_map(fn(array $row) => License::fromArray($row), $rows ?: []); } /** * Get total license count */ public function getLicenseCount(): int { global $wpdb; $tableName = Installer::getLicensesTable(); return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tableName}"); } /** * Validate a license key for a domain */ public function validateLicense(string $licenseKey, string $domain): array { $license = $this->getLicenseByKey($licenseKey); if (!$license) { return [ 'valid' => false, 'error' => 'license_not_found', 'message' => __('License key not found.', 'wc-licensed-product'), ]; } // Check license status if ($license->getStatus() === License::STATUS_REVOKED) { return [ 'valid' => false, 'error' => 'license_revoked', 'message' => __('This license has been revoked.', 'wc-licensed-product'), ]; } // Check expiration if ($license->isExpired()) { $this->updateLicenseStatus($license->getId(), License::STATUS_EXPIRED); return [ 'valid' => false, 'error' => 'license_expired', 'message' => __('This license has expired.', 'wc-licensed-product'), ]; } if ($license->getStatus() === License::STATUS_INACTIVE) { return [ 'valid' => false, 'error' => 'license_inactive', 'message' => __('This license is inactive.', 'wc-licensed-product'), ]; } // Check domain $normalizedDomain = $this->normalizeDomain($domain); if ($license->getDomain() !== $normalizedDomain) { return [ 'valid' => false, 'error' => 'domain_mismatch', 'message' => __('This license is not valid for this domain.', 'wc-licensed-product'), ]; } return [ 'valid' => true, 'license' => [ 'product_id' => $license->getProductId(), 'expires_at' => $license->getExpiresAt()?->format('Y-m-d'), 'version_id' => $license->getVersionId(), ], ]; } /** * Update license status */ public function updateLicenseStatus(int $licenseId, string $status): bool { global $wpdb; $tableName = Installer::getLicensesTable(); $result = $wpdb->update( $tableName, ['status' => $status], ['id' => $licenseId], ['%s'], ['%d'] ); return $result !== false; } /** * Update license domain */ public function updateLicenseDomain(int $licenseId, string $domain): bool { global $wpdb; $tableName = Installer::getLicensesTable(); $result = $wpdb->update( $tableName, ['domain' => $this->normalizeDomain($domain)], ['id' => $licenseId], ['%s'], ['%d'] ); return $result !== false; } /** * Delete a license */ public function deleteLicense(int $licenseId): bool { global $wpdb; $tableName = Installer::getLicensesTable(); $result = $wpdb->delete( $tableName, ['id' => $licenseId], ['%d'] ); return $result !== false; } /** * Normalize domain name */ public function normalizeDomain(string $domain): string { // Remove protocol $domain = preg_replace('#^https?://#', '', $domain); // Remove trailing slash and path $domain = preg_replace('#/.*$#', '', $domain); // Remove www prefix $domain = preg_replace('#^www\.#', '', $domain); // Lowercase return strtolower(trim($domain)); } /** * Get current version ID for a product */ private function getCurrentVersionId(int $productId): ?int { global $wpdb; $tableName = Installer::getVersionsTable(); $versionId = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$tableName} WHERE product_id = %d AND is_active = 1 ORDER BY released_at DESC LIMIT 1", $productId ) ); return $versionId ? (int) $versionId : null; } }