licenseManager = $licenseManager; $this->registerHooks(); } /** * Register Store API hooks */ private function registerHooks(): void { add_action('woocommerce_blocks_loaded', [$this, 'registerStoreApiExtension']); } /** * Register the Store API extension */ public function registerStoreApiExtension(): void { if (!class_exists('Automattic\WooCommerce\StoreApi\StoreApi')) { return; } // Register endpoint data extension woocommerce_store_api_register_endpoint_data([ 'endpoint' => CheckoutSchema::IDENTIFIER, 'namespace' => self::IDENTIFIER, 'data_callback' => [$this, 'getExtensionData'], 'schema_callback' => [$this, 'getExtensionSchema'], 'schema_type' => ARRAY_A, ]); // Register update callback for the domain field woocommerce_store_api_register_update_callback([ 'namespace' => self::IDENTIFIER, 'callback' => [$this, 'handleExtensionUpdate'], ]); // Hook into checkout order processing add_action('woocommerce_store_api_checkout_order_processed', [$this, 'processCheckoutOrder']); } /** * Get extension data for the checkout endpoint */ public function getExtensionData(): array { if (SettingsController::isMultiDomainEnabled()) { return [ 'licensed_product_domains' => WC()->session ? WC()->session->get('licensed_product_domains', []) : [], ]; } return [ 'licensed_product_domain' => WC()->session ? WC()->session->get('licensed_product_domain', '') : '', ]; } /** * Get extension schema */ public function getExtensionSchema(): array { if (SettingsController::isMultiDomainEnabled()) { return [ 'licensed_product_domains' => [ 'description' => __('Domains for license activation by product', 'wc-licensed-product'), 'type' => 'array', 'context' => ['view', 'edit'], 'readonly' => false, 'items' => [ 'type' => 'object', 'properties' => [ 'product_id' => [ 'type' => 'integer', ], 'variation_id' => [ 'type' => 'integer', ], 'domains' => [ 'type' => 'array', 'items' => [ 'type' => 'string', ], ], ], ], ], ]; } return [ 'licensed_product_domain' => [ 'description' => __('Domain for license activation', 'wc-licensed-product'), 'type' => 'string', 'context' => ['view', 'edit'], 'readonly' => false, ], ]; } /** * Handle extension data updates from the frontend */ public function handleExtensionUpdate(array $data): void { if (SettingsController::isMultiDomainEnabled()) { // Multi-domain mode if (isset($data['licensed_product_domains']) && is_array($data['licensed_product_domains'])) { $normalizedData = $this->normalizeDomainsData($data['licensed_product_domains']); if (WC()->session) { WC()->session->set('licensed_product_domains', $normalizedData); } } } else { // Single domain mode if (isset($data['licensed_product_domain'])) { $sanitized = sanitize_text_field($data['licensed_product_domain']); $normalized = $this->licenseManager->normalizeDomain($sanitized); if (WC()->session) { WC()->session->set('licensed_product_domain', $normalized); } } } } /** * Normalize domains data from frontend */ private function normalizeDomainsData(array $domainsData): array { $normalized = []; foreach ($domainsData as $item) { if (!isset($item['product_id']) || !isset($item['domains']) || !is_array($item['domains'])) { continue; } $productId = (int) $item['product_id']; $variationId = isset($item['variation_id']) ? (int) $item['variation_id'] : 0; $domains = []; foreach ($item['domains'] as $domain) { $sanitized = sanitize_text_field($domain); if (!empty($sanitized)) { $domains[] = $this->licenseManager->normalizeDomain($sanitized); } } if (!empty($domains)) { $entry = [ 'product_id' => $productId, 'domains' => $domains, ]; // Include variation_id if present if ($variationId > 0) { $entry['variation_id'] = $variationId; } $normalized[] = $entry; } } return $normalized; } /** * Process the checkout order - save domains to order meta */ public function processCheckoutOrder(\WC_Order $order): void { $requestData = json_decode(file_get_contents('php://input'), true); // Handle JSON decode errors gracefully if (json_last_error() !== JSON_ERROR_NONE) { $requestData = null; } if (SettingsController::isMultiDomainEnabled()) { $this->processMultiDomainOrder($order, $requestData); } else { $this->processSingleDomainOrder($order, $requestData); } } /** * Process order in single domain mode (legacy) */ private function processSingleDomainOrder(\WC_Order $order, ?array $requestData): void { $domain = ''; // Check session first if (WC()->session) { $domain = WC()->session->get('licensed_product_domain', ''); } // Check in the request data for block checkout (extension data) if (empty($domain) && isset($requestData['extensions'][self::IDENTIFIER]['licensed_product_domain'])) { $sanitized = sanitize_text_field($requestData['extensions'][self::IDENTIFIER]['licensed_product_domain']); $domain = $this->licenseManager->normalizeDomain($sanitized); } // Check for wclp_license_domain (from our hidden input) if (empty($domain) && isset($requestData['wclp_license_domain'])) { $sanitized = sanitize_text_field($requestData['wclp_license_domain']); $domain = $this->licenseManager->normalizeDomain($sanitized); } // Check for additional_fields (WC Blocks API) if (empty($domain) && isset($requestData['additional_fields']['wc-licensed-product/domain'])) { $sanitized = sanitize_text_field($requestData['additional_fields']['wc-licensed-product/domain']); $domain = $this->licenseManager->normalizeDomain($sanitized); } if (!empty($domain)) { $order->update_meta_data('_licensed_product_domain', $domain); $order->save(); // Clear session data if (WC()->session) { WC()->session->set('licensed_product_domain', ''); } } } /** * Process order in multi-domain mode */ private function processMultiDomainOrder(\WC_Order $order, ?array $requestData): void { $domainData = []; // Check session first if (WC()->session) { $domainData = WC()->session->get('licensed_product_domains', []); } // Check in the request data for block checkout (extension data) if (empty($domainData) && isset($requestData['extensions'][self::IDENTIFIER]['licensed_product_domains'])) { $domainData = $this->normalizeDomainsData( $requestData['extensions'][self::IDENTIFIER]['licensed_product_domains'] ); } // Check for wclp_license_domains (from our hidden input - JSON string) if (empty($domainData) && isset($requestData['wclp_license_domains'])) { $parsed = json_decode($requestData['wclp_license_domains'], true); if (json_last_error() === JSON_ERROR_NONE && is_array($parsed)) { $domainData = $this->normalizeDomainsData($parsed); } } // Check for licensed_domains in classic format (from DOM injection) if (empty($domainData) && isset($requestData['licensed_domains']) && is_array($requestData['licensed_domains'])) { $domainData = []; $variationIds = $requestData['licensed_variation_ids'] ?? []; foreach ($requestData['licensed_domains'] as $key => $domains) { if (!is_array($domains)) { continue; } // Parse key - could be "productId" or "productId_variationId" $parts = explode('_', (string) $key); $productId = (int) $parts[0]; $variationId = isset($parts[1]) ? (int) $parts[1] : 0; // Also check for hidden variation ID field if ($variationId === 0 && isset($variationIds[$key])) { $variationId = (int) $variationIds[$key]; } $normalizedDomains = []; foreach ($domains as $domain) { $sanitized = sanitize_text_field($domain); if (!empty($sanitized)) { $normalizedDomains[] = $this->licenseManager->normalizeDomain($sanitized); } } if (!empty($normalizedDomains)) { $entry = [ 'product_id' => $productId, 'domains' => $normalizedDomains, ]; if ($variationId > 0) { $entry['variation_id'] = $variationId; } $domainData[] = $entry; } } } if (!empty($domainData)) { $order->update_meta_data('_licensed_product_domains', $domainData); $order->save(); // Clear session data if (WC()->session) { WC()->session->set('licensed_product_domains', []); } } } }