licenseManager = $licenseManager; $this->versionManager = $versionManager; $this->registerHooks(); } /** * Register WordPress hooks */ private function registerHooks(): void { // Add download endpoint add_action('init', [$this, 'addDownloadEndpoint']); // Handle download requests add_action('template_redirect', [$this, 'handleDownloadRequest']); } /** * Add download endpoint */ public function addDownloadEndpoint(): void { add_rewrite_endpoint('license-download', EP_ROOT | EP_PAGES); } /** * Handle download request */ public function handleDownloadRequest(): void { global $wp_query; if (!isset($wp_query->query_vars['license-download'])) { return; } $downloadKey = sanitize_text_field($wp_query->query_vars['license-download']); if (empty($downloadKey)) { wp_die( __('Invalid download link.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } // Parse download key: format is "license_id-version_id-hash" $parts = explode('-', $downloadKey); if (count($parts) < 3) { wp_die( __('Invalid download link format.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } $licenseId = absint($parts[0]); $versionId = absint($parts[1]); $hash = $parts[2]; // Verify hash $expectedHash = $this->generateDownloadHash($licenseId, $versionId); if (!hash_equals($expectedHash, $hash)) { wp_die( __('Invalid download link.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } // Check user authentication if (!is_user_logged_in()) { wp_redirect(wp_login_url(home_url('license-download/' . $downloadKey))); exit; } // Get license $license = $this->licenseManager->getLicenseById($licenseId); if (!$license) { wp_die( __('License not found.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 404] ); } // Verify user owns the license $currentUserId = get_current_user_id(); if ($license->getCustomerId() !== $currentUserId) { wp_die( __('You do not have permission to download this file.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } // Check license status if ($license->getStatus() !== 'active') { wp_die( __('Your license is not active. Please contact support.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } // Get version $version = $this->versionManager->getVersionById($versionId); if (!$version) { wp_die( __('Version not found.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 404] ); } // Verify version belongs to licensed product if ($version->getProductId() !== $license->getProductId()) { wp_die( __('Version does not match your licensed product.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } // Check if version is active if (!$version->isActive()) { wp_die( __('This version is no longer available for download.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 403] ); } // Get download file $attachmentId = $version->getAttachmentId(); $downloadUrl = $version->getDownloadUrl(); if ($attachmentId) { $this->serveAttachment($attachmentId, $version->getVersion()); } elseif ($downloadUrl) { // Redirect to external URL wp_redirect($downloadUrl); exit; } else { wp_die( __('No download file available for this version.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 404] ); } } /** * Serve attachment file for download */ private function serveAttachment(int $attachmentId, string $version): void { $filePath = get_attached_file($attachmentId); if (!$filePath || !file_exists($filePath)) { wp_die( __('Download file not found.', 'wc-licensed-product'), __('Download Error', 'wc-licensed-product'), ['response' => 404] ); } $filename = wp_basename($filePath); $mimeType = mime_content_type($filePath) ?: 'application/octet-stream'; $fileSize = filesize($filePath); // Prevent caching nocache_headers(); // Set headers for download header('Content-Type: ' . $mimeType); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Length: ' . $fileSize); header('Content-Transfer-Encoding: binary'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Expires: 0'); header('Pragma: public'); // Clear output buffer if (ob_get_level()) { ob_end_clean(); } // Read file and output readfile($filePath); exit; } /** * Generate download hash for security */ public function generateDownloadHash(int $licenseId, int $versionId): string { $data = $licenseId . '-' . $versionId . '-' . wp_salt('auth'); return substr(hash('sha256', $data), 0, 16); } /** * Generate download URL for a license and version */ public function generateDownloadUrl(int $licenseId, int $versionId): string { $hash = $this->generateDownloadHash($licenseId, $versionId); $downloadKey = $licenseId . '-' . $versionId . '-' . $hash; return home_url('license-download/' . $downloadKey); } }