Files
sfera/src/services/marketplace-service.ts

265 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<MarketplaceValidationResult> {
try {
console.log('🔵 Starting Wildberries validation for key:', apiKey.substring(0, 20) + '...');
// Сначала проверяем валидность ключа через ping (быстрее)
console.log('📡 Making ping request to:', `${this.wbApiUrl}/ping`);
const pingResponse = await axios.get(
`${this.wbApiUrl}/ping`,
{
headers: {
'Authorization': `Bearer ${apiKey}`
},
timeout: 5000
}
)
console.log('📡 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.log('🔴 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<MarketplaceValidationResult> {
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<MarketplaceValidationResult> {
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
}
}
}