import axios from 'axios' export interface MarketplaceValidationResult { isValid: boolean message: string data?: { sellerId?: string sellerName?: string tradeMark?: string [key: string]: unknown } } export interface WildberriesSellerInfo { id: number name: string inn: string kpp?: string } export interface OzonSellerInfo { id: number name: string status: string } export class MarketplaceService { private wbApiUrl: string private ozonApiUrl: string constructor() { this.wbApiUrl = process.env.WILDBERRIES_API_URL || 'https://common-api.wildberries.ru' this.ozonApiUrl = process.env.OZON_API_URL || 'https://api-seller.ozon.ru' } /** * Валидирует API ключ Wildberries */ async validateWildberriesApiKey(apiKey: string): Promise { try { console.warn('🔵 Starting Wildberries validation for key:', apiKey.substring(0, 20) + '...') // Сначала проверяем валидность ключа через ping (быстрее) console.warn('📡 Making ping request to:', `${this.wbApiUrl}/ping`) const pingResponse = await axios.get(`${this.wbApiUrl}/ping`, { headers: { Authorization: `Bearer ${apiKey}`, }, timeout: 5000, }) console.warn('📡 Ping response:', { status: pingResponse.status, data: pingResponse.data, }) if (pingResponse.status !== 200 || pingResponse.data?.Status !== 'OK') { return { isValid: false, message: 'API ключ Wildberries невалиден', } } // Если ping прошёл, получаем информацию о продавце const response = await axios.get(`${this.wbApiUrl}/api/v1/seller-info`, { headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, timeout: 10000, }) if (response.status === 200 && response.data) { const sellerData = response.data return { isValid: true, message: 'API ключ Wildberries валиден', data: { sellerId: sellerData.sid, // sid - это уникальный ID продавца sellerName: sellerData.name, // обычное наименование продавца tradeMark: sellerData.tradeMark, // торговое наименование продавца }, } } return { isValid: false, message: 'Не удалось получить информацию о продавце Wildberries', } } catch (error) { console.error('🔴 Wildberries API validation error:', error) if (axios.isAxiosError(error)) { console.warn('🔴 Axios error details:', { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, message: error.message, code: error.code, }) if (error.response?.status === 401) { return { isValid: false, message: 'Неверный API ключ Wildberries', } } if (error.response?.status === 403) { return { isValid: false, message: 'Доступ запрещён. Проверьте права API ключа Wildberries', } } if (error.response?.status === 429) { return { isValid: false, message: 'Слишком много запросов к Wildberries API. Попробуйте позже', } } if (error.code === 'ECONNABORTED') { return { isValid: false, message: 'Превышено время ожидания ответа от Wildberries API', } } } return { isValid: false, message: 'Ошибка при проверке API ключа Wildberries', } } } /** * Валидирует API ключ Ozon */ async validateOzonApiKey(apiKey: string, clientId?: string): Promise { try { // Для Ozon нужен Client-Id if (!clientId) { return { isValid: false, message: 'Для Ozon API требуется Client-Id', } } // Пытаемся получить информацию о продавце const response = await axios.post( `${this.ozonApiUrl}/v1/seller/info`, {}, { headers: { 'Api-Key': apiKey, 'Client-Id': clientId, 'Content-Type': 'application/json', }, timeout: 10000, }, ) if (response.status === 200 && response.data?.result) { const sellerData = response.data.result as OzonSellerInfo return { isValid: true, message: 'API ключ Ozon валиден', data: { sellerId: sellerData.id?.toString(), sellerName: sellerData.name, status: sellerData.status, }, } } return { isValid: false, message: 'Не удалось получить информацию о продавце Ozon', } } catch (error) { console.error('Ozon API validation error:', error) if (axios.isAxiosError(error)) { if (error.response?.status === 401) { return { isValid: false, message: 'Неверный API ключ или Client-Id для Ozon', } } if (error.response?.status === 403) { return { isValid: false, message: 'Доступ запрещён. Проверьте права API ключа Ozon', } } if (error.code === 'ECONNABORTED') { return { isValid: false, message: 'Превышено время ожидания ответа от Ozon API', } } } return { isValid: false, message: 'Ошибка при проверке API ключа Ozon', } } } /** * Общий метод валидации API ключа по типу маркетплейса */ async validateApiKey( marketplace: 'WILDBERRIES' | 'OZON', apiKey: string, clientId?: string, ): Promise { switch (marketplace) { case 'WILDBERRIES': return this.validateWildberriesApiKey(apiKey) case 'OZON': return this.validateOzonApiKey(apiKey, clientId) default: return { isValid: false, message: 'Неподдерживаемый тип маркетплейса', } } } /** * Проверяет формат API ключа перед отправкой запроса */ validateApiKeyFormat(marketplace: 'WILDBERRIES' | 'OZON', apiKey: string): boolean { if (!apiKey || typeof apiKey !== 'string') { return false } switch (marketplace) { case 'WILDBERRIES': // Wildberries API ключи (JWT токены) содержат буквы, цифры, дефисы, подчёркивания и точки return /^[a-zA-Z0-9\-_.]{10,}$/.test(apiKey) case 'OZON': // Ozon API ключи обычно содержат буквы, цифры и дефисы return /^[a-zA-Z0-9\-_]{10,}$/.test(apiKey) default: return false } } }