array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'email' => $email, 'password' => $password, 'domain' => $domain, ) ), 'timeout' => 30, 'sslverify' => true, ) ); // Handle network errors if ( is_wp_error( $response ) ) { return array( 'success' => false, 'message' => 'Connection error: ' . $response->get_error_message(), ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); // Handle shop already connected error (409 Conflict) if ( $status_code === 409 && isset( $data['code'] ) && $data['code'] === 'SHOP_ALREADY_CONNECTED' ) { return array( 'success' => false, 'code' => 'SHOP_ALREADY_CONNECTED', 'message' => $data['error'] ?? 'Account already connected to another shop', 'existing_shop' => $data['existing_shop'] ?? null, ); } // Handle API errors if ( $status_code !== 200 ) { $error_msg = isset( $data['error'] ) ? $data['error'] : 'Invalid credentials'; return array( 'success' => false, 'message' => $error_msg, ); } // Extract data from response if ( ! isset( $data['data'] ) ) { return array( 'success' => false, 'message' => 'Invalid API response format', ); } $api_data = $data['data']; $token = $api_data['token'] ?? null; $shop_id = $api_data['shop_id'] ?? null; if ( ! $token || ! $shop_id ) { return array( 'success' => false, 'message' => 'Invalid API response: missing token or shop_id', ); } // Save non-expiring JWT token and shop_id to database Scan_Show_Database::save_jwt( $token, $shop_id ); return array( 'success' => true, 'data' => array( 'token' => $token, 'shop_id' => $shop_id, ), ); } public static function is_connected() { $jwt = Scan_Show_Database::get_jwt(); return ! empty( $jwt ); } public static function get_user_data() { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'error' => 'Not connected' ); } $url = self::get_backend_url() . 'v1/user'; $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { return array( 'error' => isset( $data['error'] ) ? $data['error'] : 'Failed to fetch user data' ); } return isset( $data['data']['user'] ) ? $data['data']['user'] : array( 'error' => 'Invalid response format' ); } public static function get_connection_status() { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'connected' => false, 'message' => 'Not connected to STW Platform', ); } $user_data = self::get_user_data(); if ( isset( $user_data['error'] ) ) { return array( 'connected' => false, 'message' => $user_data['error'], ); } return array( 'connected' => true, 'user_email' => isset( $user_data['email_address'] ) ? $user_data['email_address'] : '', 'username' => isset( $user_data['username'] ) ? $user_data['username'] : '', ); } public static function get_subscription() { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'error' => 'Not connected' ); } $url = self::get_backend_url() . 'v1/user/subscription'; $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { return array( 'error' => isset( $data['error'] ) ? $data['error'] : 'Failed to fetch subscription data' ); } return isset( $data['data']['subscription'] ) ? $data['data']['subscription'] : array( 'error' => 'Invalid response format' ); } public static function get_quota() { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'error' => 'Not connected' ); } // Fetch quota data $quota_url = self::get_backend_url() . 'v1/user/quotas'; $quota_response = wp_remote_get( $quota_url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $quota_response ) ) { return array( 'error' => 'Connection error: ' . $quota_response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $quota_response ); $body = wp_remote_retrieve_body( $quota_response ); $quota_data = json_decode( $body, true ); if ( $status_code !== 200 ) { return array( 'error' => isset( $quota_data['error'] ) ? $quota_data['error'] : 'Failed to fetch quota data' ); } $quotas = isset( $quota_data['data']['quotas'] ) ? $quota_data['data']['quotas'] : array(); // Fetch subscription data $subscription = self::get_subscription(); // Fetch user data $user = self::get_user_data(); // Combine all data return array( 'quotas' => $quotas, 'subscription' => $subscription, 'user' => $user, ); } public static function get_woocommerce_products() { if ( ! class_exists( 'WooCommerce' ) ) { return array( 'error' => 'WooCommerce is not active' ); } $query = new WC_Product_Query( array( 'limit' => -1, 'status' => array( 'publish', 'draft', 'private' ), ) ); $products = array(); foreach ( $query->get_products() as $product ) { $thumbnail_id = $product->get_image_id(); $image_url = $thumbnail_id ? wp_get_attachment_image_url( $thumbnail_id, 'thumbnail' ) : ''; // Get product categories $category_ids = $product->get_category_ids(); $categories = array(); foreach ( $category_ids as $cat_id ) { $term = get_term( $cat_id, 'product_cat' ); if ( $term && ! is_wp_error( $term ) ) { $categories[] = array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, ); } } $products[] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price' => $product->get_price(), 'status' => get_post_status( $product->get_id() ), 'image' => $image_url, 'categories' => $categories, ); } return $products; } public static function get_woocommerce_categories() { if ( ! class_exists( 'WooCommerce' ) ) { return array( 'error' => 'WooCommerce is not active' ); } $terms = get_terms( array( 'taxonomy' => 'product_cat', 'hide_empty' => false, ) ); if ( is_wp_error( $terms ) ) { return array( 'error' => $terms->get_error_message() ); } $categories = array(); foreach ( $terms as $term ) { $categories[] = array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, 'count' => $term->count, ); } return $categories; } /** * Get linked items from backend API * GET /v1/wordpress/:shopId/items */ public static function get_linked_items() { $jwt = Scan_Show_Database::get_jwt(); $shop_id = Scan_Show_Database::get_shop_id(); if ( ! $jwt || ! $shop_id ) { return array( 'error' => 'Not connected' ); } $url = self::get_backend_url() . 'v1/wordpress/' . $shop_id . '/items'; $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { return array( 'error' => isset( $data['error'] ) ? $data['error'] : 'Failed to fetch linked items' ); } return isset( $data['items'] ) ? $data['items'] : array(); } /** * Check if a product is linked to an STW item * GET /v1/wordpress/item/:shopId/:productId * * @param string $product_id WordPress product ID * @return array|false Returns item data if linked, false if not linked or error */ public static function get_product_item( $product_id ) { $jwt = Scan_Show_Database::get_jwt(); $shop_id = Scan_Show_Database::get_shop_id(); if ( ! $jwt || ! $shop_id ) { return false; } $url = self::get_backend_url() . 'v1/wordpress/item/' . $shop_id . '/' . $product_id; $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return false; } $status_code = wp_remote_retrieve_response_code( $response ); // If product is not linked, API returns error status if ( $status_code !== 200 ) { return false; } $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); // Return item data if found return isset( $data['data']['item'] ) ? $data['data']['item'] : false; } /** * Construct iframe URL for 3D viewer * Only returns URL if product is actually linked to an STW item * Format: /item/wordpress/[shopId]/[productId] */ public static function get_iframe_url( $product_id ) { $shop_id = Scan_Show_Database::get_shop_id(); if ( ! $shop_id ) { return null; } // Check if product is linked before constructing URL $item = self::get_product_item( $product_id ); if ( ! $item ) { return null; } $frontend_url = self::get_frontend_url(); return $frontend_url . 'item/wordpress/' . $shop_id . '/' . $product_id; } /** * Get iframe URL for a page (not product) * Format: /item/[itemId]/embed or /scene/[roomId]/embed * * @param int $page_id WordPress page/post ID * @return string|null Iframe URL or null if not linked */ public static function get_page_iframe_url( $page_id ) { // Get linked data from database $link_data = Scan_Show_Database::get_page_link_data( $page_id ); if ( ! $link_data || ! isset( $link_data['entity_id'] ) ) { return null; } // Construct embed URL based on type $frontend_url = self::get_frontend_url(); $type = isset( $link_data['entity_type'] ) ? $link_data['entity_type'] : 'item'; if ( $type === 'room' ) { // Rooms use /scene/ path for embed URLs return rtrim( $frontend_url, '/' ) . '/scene/' . $link_data['entity_id'] . '/embed'; } else { return rtrim( $frontend_url, '/' ) . '/item/' . $link_data['entity_id'] . '/embed'; } } /** * Get rooms from backend API * GET /v1/rooms?userId={userId}&isPublishedOnly=true&page=1&pageSize=100 * * @param int $page Page number (default 1) * @param int $page_size Number of items per page (default 100) * @return array List of rooms or error */ public static function get_rooms( $page = 1, $page_size = 100 ) { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'error' => 'Not connected' ); } // Get user data to retrieve user ID $user_data = self::get_user_data(); if ( isset( $user_data['error'] ) || ! isset( $user_data['id'] ) ) { return array( 'error' => 'Unable to retrieve user information' ); } $user_id = $user_data['id']; // Construct URL with query parameters - only get published rooms $url = self::get_backend_url() . 'v1/rooms?' . http_build_query( array( 'userId' => $user_id, 'isPublishedOnly' => 'true', 'page' => $page, 'pageSize' => $page_size, ) ); $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { return array( 'error' => isset( $data['error'] ) ? $data['error'] : 'Failed to fetch rooms' ); } // Return rooms array from data return isset( $data['data']['rooms'] ) ? $data['data']['rooms'] : array(); } /** * Enable embed for an item * POST /v1/item/{id}/embed * * @param string $item_id STW item ID * @return array Success or error response */ public static function enable_item_embed( $item_id ) { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'success' => false, 'error' => 'Not connected' ); } $url = self::get_backend_url() . 'v1/item/' . urlencode( $item_id ) . '/embed'; $response = wp_remote_post( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'body' => wp_json_encode( array() ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'success' => false, 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { $error_msg = isset( $data['error'] ) ? $data['error'] : 'Failed to enable embed'; return array( 'success' => false, 'error' => $error_msg, 'quota_exceeded' => $status_code === 403 ); } return array( 'success' => true, 'data' => $data ); } /** * Enable embed for a room * POST /v1/room/{id}/embed * * @param string $room_id STW room ID * @return array Success or error response */ public static function enable_room_embed( $room_id ) { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'success' => false, 'error' => 'Not connected' ); } $url = self::get_backend_url() . 'v1/room/' . urlencode( $room_id ) . '/embed'; $response = wp_remote_post( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'body' => wp_json_encode( array() ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'success' => false, 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { $error_msg = isset( $data['error'] ) ? $data['error'] : 'Failed to enable embed'; return array( 'success' => false, 'error' => $error_msg, 'quota_exceeded' => $status_code === 403 ); } return array( 'success' => true, 'data' => $data ); } /** * Get all items (not just embed-enabled) * GET /v1/items?userId={userId}&isPublishedOnly=true * * @return array List of items or error */ public static function get_all_items() { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { return array( 'error' => 'Not connected' ); } // Get user data to retrieve user ID $user_data = self::get_user_data(); if ( isset( $user_data['error'] ) || ! isset( $user_data['id'] ) ) { return array( 'error' => 'Unable to retrieve user information' ); } $user_id = $user_data['id']; // Construct URL with query parameters - only get published items $url = self::get_backend_url() . 'v1/items?' . http_build_query( array( 'userId' => $user_id, 'isPublishedOnly' => 'true', ) ); $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, 'Content-Type' => 'application/json', ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( $status_code !== 200 ) { return array( 'error' => isset( $data['error'] ) ? $data['error'] : 'Failed to fetch items' ); } // Return items array from data return isset( $data['data']['items'] ) ? $data['data']['items'] : array(); } /** * Remove shop connection from STW platform * DELETE /v1/wordpress/install * * @return array Success or error response */ public static function remove_shop_connection() { $jwt = Scan_Show_Database::get_jwt(); if ( ! $jwt ) { // If not connected, no need to call API. return array( 'success' => true ); } $url = self::get_backend_url() . 'v1/wordpress/install'; $response = wp_remote_request( $url, array( 'method' => 'DELETE', 'headers' => array( 'Authorization' => 'Bearer ' . $jwt, ), 'timeout' => 30, ) ); if ( is_wp_error( $response ) ) { return array( 'success' => false, 'error' => 'Connection error: ' . $response->get_error_message() ); } $status_code = wp_remote_retrieve_response_code( $response ); if ( $status_code !== 200 ) { $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); $error_msg = isset( $data['error'] ) ? $data['error'] : 'Failed to uninstall'; return array( 'success' => false, 'error' => $error_msg ); } return array( 'success' => true ); } }