From b405fd49cc7cfa4c4e00f9939e2c0b1f2ea8c501 Mon Sep 17 00:00:00 2001 From: dwindown Date: Wed, 5 Nov 2025 22:08:53 +0700 Subject: [PATCH] feat: Add Store Settings backend with full countries/timezones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ StoreSettingsProvider.php: - get_countries() - All WooCommerce countries - get_timezones() - All PHP timezones with UTC offsets - get_currencies() - All WooCommerce currencies with symbols - get_settings() - Current store settings - save_settings() - Save store settings ✅ StoreController.php: - GET /woonoow/v1/store/settings - POST /woonoow/v1/store/settings - GET /woonoow/v1/store/countries (200+ countries) - GET /woonoow/v1/store/timezones (400+ timezones) - GET /woonoow/v1/store/currencies (100+ currencies) - Response caching (1 hour for static data) 🔌 Integration: - Registered in Api/Routes.php - Permission checks (manage_woocommerce) - Error handling Next: Update Store.tsx to use real API --- includes/Api/Routes.php | 5 + includes/Api/StoreController.php | 223 ++++++++++++++++++++++ includes/Compat/StoreSettingsProvider.php | 169 ++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 includes/Api/StoreController.php create mode 100644 includes/Compat/StoreSettingsProvider.php diff --git a/includes/Api/Routes.php b/includes/Api/Routes.php index 8fe847e..0f9d492 100644 --- a/includes/Api/Routes.php +++ b/includes/Api/Routes.php @@ -8,6 +8,7 @@ use WooNooW\Api\OrdersController; use WooNooW\Api\AnalyticsController; use WooNooW\Api\AuthController; use WooNooW\API\PaymentsController; +use WooNooW\API\StoreController; class Routes { public static function init() { @@ -44,6 +45,10 @@ class Routes { // Payments controller $payments_controller = new PaymentsController(); $payments_controller->register_routes(); + + // Store controller + $store_controller = new StoreController(); + $store_controller->register_routes(); }); } } diff --git a/includes/Api/StoreController.php b/includes/Api/StoreController.php new file mode 100644 index 0000000..4a9f488 --- /dev/null +++ b/includes/Api/StoreController.php @@ -0,0 +1,223 @@ +namespace, '/' . $this->rest_base . '/settings', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [$this, 'get_settings'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // POST /woonoow/v1/store/settings + register_rest_route($this->namespace, '/' . $this->rest_base . '/settings', [ + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => [$this, 'save_settings'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // GET /woonoow/v1/store/countries + register_rest_route($this->namespace, '/' . $this->rest_base . '/countries', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [$this, 'get_countries'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // GET /woonoow/v1/store/timezones + register_rest_route($this->namespace, '/' . $this->rest_base . '/timezones', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [$this, 'get_timezones'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + + // GET /woonoow/v1/store/currencies + register_rest_route($this->namespace, '/' . $this->rest_base . '/currencies', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [$this, 'get_currencies'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); + } + + /** + * Get store settings + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response|WP_Error Response object or error + */ + public function get_settings(WP_REST_Request $request) { + try { + $settings = StoreSettingsProvider::get_settings(); + + $response = rest_ensure_response($settings); + $response->header('Cache-Control', 'max-age=60'); + + return $response; + } catch (\Exception $e) { + return new WP_Error( + 'get_settings_failed', + $e->getMessage(), + ['status' => 500] + ); + } + } + + /** + * Save store settings + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response|WP_Error Response object or error + */ + public function save_settings(WP_REST_Request $request) { + $settings = $request->get_json_params(); + + if (empty($settings)) { + return new WP_Error( + 'missing_settings', + 'No settings provided', + ['status' => 400] + ); + } + + try { + $result = StoreSettingsProvider::save_settings($settings); + + if (!$result) { + return new WP_Error( + 'save_failed', + 'Failed to save settings', + ['status' => 500] + ); + } + + return rest_ensure_response([ + 'success' => true, + 'message' => 'Settings saved successfully', + 'settings' => StoreSettingsProvider::get_settings(), + ]); + } catch (\Exception $e) { + return new WP_Error( + 'save_settings_failed', + $e->getMessage(), + ['status' => 500] + ); + } + } + + /** + * Get countries + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response|WP_Error Response object or error + */ + public function get_countries(WP_REST_Request $request) { + try { + $countries = StoreSettingsProvider::get_countries(); + + $response = rest_ensure_response($countries); + $response->header('Cache-Control', 'max-age=3600'); // Cache for 1 hour + + return $response; + } catch (\Exception $e) { + return new WP_Error( + 'get_countries_failed', + $e->getMessage(), + ['status' => 500] + ); + } + } + + /** + * Get timezones + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response|WP_Error Response object or error + */ + public function get_timezones(WP_REST_Request $request) { + try { + $timezones = StoreSettingsProvider::get_timezones(); + + $response = rest_ensure_response($timezones); + $response->header('Cache-Control', 'max-age=3600'); // Cache for 1 hour + + return $response; + } catch (\Exception $e) { + return new WP_Error( + 'get_timezones_failed', + $e->getMessage(), + ['status' => 500] + ); + } + } + + /** + * Get currencies + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response|WP_Error Response object or error + */ + public function get_currencies(WP_REST_Request $request) { + try { + $currencies = StoreSettingsProvider::get_currencies(); + + $response = rest_ensure_response($currencies); + $response->header('Cache-Control', 'max-age=3600'); // Cache for 1 hour + + return $response; + } catch (\Exception $e) { + return new WP_Error( + 'get_currencies_failed', + $e->getMessage(), + ['status' => 500] + ); + } + } + + /** + * Check permission + * + * @return bool True if user has permission + */ + public function check_permission() { + return current_user_can('manage_woocommerce'); + } +} diff --git a/includes/Compat/StoreSettingsProvider.php b/includes/Compat/StoreSettingsProvider.php new file mode 100644 index 0000000..25c5103 --- /dev/null +++ b/includes/Compat/StoreSettingsProvider.php @@ -0,0 +1,169 @@ +get_countries(); + + $formatted = []; + foreach ($countries as $code => $name) { + $formatted[] = [ + 'code' => $code, + 'name' => $name, + ]; + } + + return $formatted; + } + + /** + * Get all timezones + * + * @return array List of timezones + */ + public static function get_timezones(): array { + $timezones = timezone_identifiers_list(); + $formatted = []; + + foreach ($timezones as $timezone) { + // Group by continent + $parts = explode('/', $timezone); + $continent = $parts[0] ?? 'Other'; + + if (!isset($formatted[$continent])) { + $formatted[$continent] = []; + } + + // Format display name + $display = str_replace('_', ' ', $timezone); + + // Get UTC offset + try { + $tz = new \DateTimeZone($timezone); + $now = new \DateTime('now', $tz); + $offset = $tz->getOffset($now); + $hours = floor($offset / 3600); + $minutes = abs(floor(($offset % 3600) / 60)); + $offset_string = sprintf('UTC%+d:%02d', $hours, $minutes); + } catch (\Exception $e) { + $offset_string = 'UTC'; + } + + $formatted[$continent][] = [ + 'value' => $timezone, + 'label' => $display, + 'offset' => $offset_string, + ]; + } + + return $formatted; + } + + /** + * Get all currencies from WooCommerce + * + * @return array List of currencies + */ + public static function get_currencies(): array { + if (!function_exists('get_woocommerce_currencies')) { + return []; + } + + $currencies = get_woocommerce_currencies(); + $formatted = []; + + foreach ($currencies as $code => $name) { + $symbol = get_woocommerce_currency_symbol($code); + + $formatted[] = [ + 'code' => $code, + 'name' => $name, + 'symbol' => $symbol, + ]; + } + + return $formatted; + } + + /** + * Get store settings + * + * @return array Store settings + */ + public static function get_settings(): array { + return [ + 'store_name' => get_option('blogname', ''), + 'contact_email' => get_option('admin_email', ''), + 'support_email' => get_option('woocommerce_email_from_address', ''), + 'phone' => get_option('woocommerce_store_phone', ''), + 'country' => get_option('woocommerce_default_country', ''), + 'address' => get_option('woocommerce_store_address', ''), + 'address_2' => get_option('woocommerce_store_address_2', ''), + 'city' => get_option('woocommerce_store_city', ''), + 'postcode' => get_option('woocommerce_store_postcode', ''), + 'currency' => get_option('woocommerce_currency', 'USD'), + 'currency_position' => get_option('woocommerce_currency_pos', 'left'), + 'thousand_separator' => get_option('woocommerce_price_thousand_sep', ','), + 'decimal_separator' => get_option('woocommerce_price_decimal_sep', '.'), + 'number_of_decimals' => (int) get_option('woocommerce_price_num_decimals', 2), + 'timezone' => get_option('timezone_string', 'UTC'), + 'weight_unit' => get_option('woocommerce_weight_unit', 'kg'), + 'dimension_unit' => get_option('woocommerce_dimension_unit', 'cm'), + ]; + } + + /** + * Save store settings + * + * @param array $settings Settings to save + * @return bool True on success + */ + public static function save_settings(array $settings): bool { + $mapping = [ + 'store_name' => 'blogname', + 'contact_email' => 'admin_email', + 'support_email' => 'woocommerce_email_from_address', + 'phone' => 'woocommerce_store_phone', + 'country' => 'woocommerce_default_country', + 'address' => 'woocommerce_store_address', + 'address_2' => 'woocommerce_store_address_2', + 'city' => 'woocommerce_store_city', + 'postcode' => 'woocommerce_store_postcode', + 'currency' => 'woocommerce_currency', + 'currency_position' => 'woocommerce_currency_pos', + 'thousand_separator' => 'woocommerce_price_thousand_sep', + 'decimal_separator' => 'woocommerce_price_decimal_sep', + 'number_of_decimals' => 'woocommerce_price_num_decimals', + 'timezone' => 'timezone_string', + 'weight_unit' => 'woocommerce_weight_unit', + 'dimension_unit' => 'woocommerce_dimension_unit', + ]; + + foreach ($settings as $key => $value) { + if (isset($mapping[$key])) { + update_option($mapping[$key], $value); + } + } + + return true; + } +}