""" Сервис для работы с Recommerce API. API документация: - Запросы отправляются на домен магазина (store_url) - Авторизация через заголовок x-auth-token - POST данные передаются как form-data - Ответы в JSON """ import requests from typing import Tuple class RecommerceService: """Сервис для работы с Recommerce API""" def __init__(self, integration): """ Args: integration: RecommerceIntegration instance """ self.integration = integration def _get_headers(self) -> dict: """Заголовки для API запросов""" token = self.integration.api_token or '' # HTTP заголовки должны быть ASCII-совместимыми return { 'x-auth-token': token.encode('ascii', 'ignore').decode('ascii'), 'Accept': 'application/json', } def _get_url(self, path: str) -> str: """Полный URL для API endpoint""" base = self.integration.store_url.rstrip('/') return f"{base}/api/v1/{path.lstrip('/')}" def _request(self, method: str, path: str, **kwargs) -> Tuple[bool, dict, str]: """ HTTP запрос к API. Returns: (success, data, error_message) """ url = self._get_url(path) try: response = requests.request( method, url, headers=self._get_headers(), timeout=15, **kwargs ) if response.status_code in [200, 201, 204]: if response.text: return True, response.json(), '' return True, {}, '' else: return False, {}, f"HTTP {response.status_code}: {response.text[:200]}" except requests.exceptions.Timeout: return False, {}, 'Таймаут соединения (15 сек)' except requests.exceptions.ConnectionError: return False, {}, 'Не удалось подключиться к серверу' except Exception as e: return False, {}, str(e) def test_connection(self) -> Tuple[bool, str]: """ Проверить соединение с Recommerce API. Returns: (success, message) """ if not self.integration.store_url: return False, 'Не указан URL магазина' if not self.integration.api_token: return False, 'Не указан API токен' url = self.integration.store_url.rstrip('/') + '/api/v1/' try: response = requests.get( url, headers=self._get_headers(), timeout=15 ) if response.status_code == 401: return False, 'Неверный API токен' if response.status_code == 403: return False, 'Доступ запрещён' # Любой ответ от сервера = соединение работает return True, 'Соединение установлено' except requests.exceptions.Timeout: return False, 'Таймаут соединения (15 сек)' except requests.exceptions.ConnectionError: return False, 'Не удалось подключиться к серверу' except Exception as e: return False, str(e) # ========== Categories ========== def get_category(self, sku: str) -> Tuple[bool, dict, str]: """Получить категорию по SKU""" return self._request('GET', f'catalog/categories/{sku}') def create_category(self, data: dict) -> Tuple[bool, dict, str]: """ Создать категорию. Обязательные поля в data: - sku: артикул категории - title: название категории """ return self._request('POST', 'catalog/categories', data=data) def update_category(self, sku: str, data: dict) -> Tuple[bool, dict, str]: """Обновить категорию (только переданные поля)""" return self._request('POST', f'catalog/categories/{sku}', data=data) def delete_category(self, sku: str) -> Tuple[bool, dict, str]: """Удалить категорию""" return self._request('DELETE', f'catalog/categories/{sku}') # ========== Products ========== def get_product(self, sku: str) -> Tuple[bool, dict, str]: """Получить товар по SKU""" return self._request('GET', f'catalog/products/{sku}') def create_product(self, data: dict) -> Tuple[bool, dict, str]: """ Создать товар. Обязательные поля в data: - name: название товара - sku: артикул товара - parent_category_sku: артикул категории - price[amount]: цена - price[currency]: валюта (BYN|RUB|USD|EUR|KZT) """ return self._request('POST', 'catalog/products', data=data) def update_product(self, sku: str, data: dict) -> Tuple[bool, dict, str]: """Обновить товар (только переданные поля)""" return self._request('POST', f'catalog/products/{sku}', data=data) def delete_product(self, sku: str) -> Tuple[bool, dict, str]: """Удалить товар""" return self._request('DELETE', f'catalog/products/{sku}') # ========== Orders ========== def get_orders(self, page: int = 1, updated_after: str = None) -> Tuple[bool, dict, str]: """ Получить список заказов. Args: page: номер страницы (по 100 заказов) updated_after: фильтр 'Y-m-d-H-i-s' - только заказы после даты """ params = {'page': page} if updated_after: params['updated_after'] = updated_after return self._request('GET', 'orders', params=params)