Last active
June 2, 2022 04:01
-
-
Save wmakeev/b8b8db87f8a03cc760204e936f2e3f24 to your computer and use it in GitHub Desktop.
[OZON API wrapper] #ozon #api #marketplace
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# CHANGELOG: | |
- 2022-06-02 initial | |
*/ | |
import type { fetch, FetchOptions } from "../tools/fetch-adapter"; | |
export interface OzonOptions { | |
clientId: string; | |
apiKey: string; | |
endpoint?: string; | |
fetch: typeof fetch; | |
} | |
const OZON_API_ENDPOINT = "api-seller.ozon.ru"; | |
export interface OzonCategory { | |
category_id: number; | |
title: string; | |
children: OzonCategory[]; | |
} | |
export interface OzonCategoryTreeResponse { | |
result: OzonCategory[]; | |
} | |
export interface OzonProductInfo { | |
id: number; | |
name: string; | |
offer_id: string; | |
barcode: string; | |
buybox_price: string; | |
category_id: number; | |
created_at: string; | |
images: string[]; | |
marketing_price: string; | |
min_ozon_price: string; | |
old_price: string; | |
premium_price: string; | |
price: string; | |
recommended_price: string; | |
min_price: string; | |
sources: { | |
is_enabled: boolean; | |
sku: number; | |
source: "fbo" | "fbs"; | |
}[]; | |
stocks: { | |
coming: number; | |
present: number; | |
reserved: number; | |
}; | |
errors: []; | |
vat: string; | |
visible: boolean; | |
visibility_details: { | |
has_price: boolean; | |
has_stock: boolean; | |
active_product: boolean; | |
reasons: {}; | |
}; | |
price_index: string; | |
images360: string[]; | |
color_image: string; | |
primary_image: string; | |
status: { | |
state: "price_sent"; | |
state_failed: string; | |
moderate_status: "approved"; | |
decline_reasons: []; | |
validation_state: "success"; | |
state_name: "Продается"; | |
state_description: string; | |
is_failed: boolean; | |
is_created: boolean; | |
state_tooltip: string; | |
item_errors: []; | |
state_updated_at: string; | |
}; | |
state: string; | |
service_type: "IS_CODE_SERVICE"; | |
fbo_sku: number; | |
fbs_sku: number; | |
} | |
export const ProductVisibility = { | |
/** все товары. */ | |
ALL: "ALL", | |
/** товары, которые видны покупателям. */ | |
VISIBLE: "VISIBLE", | |
/** товары, которые по какой-то из причин не видны покупателям. */ | |
INVISIBLE: "INVISIBLE", | |
/** товары, у которых не указано наличие. */ | |
EMPTY_STOCK: "EMPTY_STOCK", | |
/** товары, которые не прошли модерацию. */ | |
NOT_MODERATED: "NOT_MODERATED", | |
/** товары, которые прошли модерацию. */ | |
MODERATED: "MODERATED", | |
/** товары, которые видны покупателям, но недоступны к покупке. */ | |
DISABLED: "DISABLED", | |
/** товары, создание которых завершилось ошибĸой. */ | |
STATE_FAILED: "STATE_FAILED", | |
/** товары, готовые к поставке. */ | |
READY_TO_SUPPLY: "READY_TO_SUPPLY", | |
/** товары, которые проходят проверку на премодерации (валидатором). */ | |
VALIDATION_STATE_PENDING: "VALIDATION_STATE_PENDING", | |
/** товары, которые не прошли проверку на премодерации (валидатором). */ | |
VALIDATION_STATE_FAIL: "VALIDATION_STATE_FAIL", | |
/** товары, которые прошли проверку на премодерации (валидатором). */ | |
VALIDATION_STATE_SUCCESS: "VALIDATION_STATE_SUCCESS", | |
/** товары, готовые к продаже. */ | |
TO_SUPPLY: "TO_SUPPLY", | |
/** товары в продаже. */ | |
IN_SALE: "IN_SALE", | |
/** товары, скрытые от покупателей. */ | |
REMOVED_FROM_SALE: "REMOVED_FROM_SALE", | |
/** заблокированные товары. */ | |
BANNED: "BANNED", | |
/** товары с завышенной ценой. */ | |
OVERPRICED: "OVERPRICED", | |
/** товары со слишком завышенной ценой. */ | |
CRITICALLY_OVERPRICED: "CRITICALLY_OVERPRICED", | |
/** товары без штрихкода. */ | |
EMPTY_BARCODE: "EMPTY_BARCODE", | |
/** товары со штрихкодом. */ | |
BARCODE_EXISTS: "BARCODE_EXISTS", | |
/** товары на карантине после изменения цены более чем на 50%. */ | |
QUARANTINE: "QUARANTINE", | |
/** товары в архиве. */ | |
ARCHIVED: "ARCHIVED", | |
/** товары в продаже со стоимостью выше, чем у конкурентов. */ | |
OVERPRICED_WITH_STOCK: "OVERPRICED_WITH_STOCK", | |
/** товары в продаже с пустым или неполным описанием. */ | |
PARTIAL_APPROVED: "PARTIAL_APPROVED", | |
/** товары без изображений. */ | |
IMAGE_ABSENT: "IMAGE_ABSENT", | |
/** товары, для которых заблокирована модерация. */ | |
MODERATION_BLOCK: "MODERATION_BLOCK", | |
}; | |
export interface ProductV2GetProductListResponseItem { | |
offer_id: string; | |
product_id: number; | |
} | |
export interface ProductV2GetProductListResponseResult { | |
items: ProductV2GetProductListResponseItem[]; | |
last_id: string; | |
total: number; | |
} | |
export const CategoryAttributesRequestAttributeType = { | |
ALL: "ALL", | |
REQUIRED: "REQUIRED", | |
OPTIONAL: "OPTIONAL", | |
}; | |
export const Language = { | |
DEFAULT: "DEFAULT", | |
RU: "RU", | |
EN: "EN", | |
}; | |
export interface Attribute { | |
/** Идентификатор справочника */ | |
id: number; | |
/** Название */ | |
name: string; | |
/** Тип характеристики */ | |
type: string; | |
/** Описание характеристики. */ | |
description: string; | |
/** Признак, что характеристика — набор значений */ | |
is_collection: boolean; | |
/** Признак обязательной характеристики */ | |
is_required: boolean; | |
dictionary_id: number; | |
/** Идентификатор группы характеристик */ | |
group_id: number; | |
/** Название группы характеристик */ | |
group_name: string; | |
} | |
export interface CategoryAttributes { | |
category_id: number; | |
attributes: Attribute[]; | |
} | |
export interface CategoryAttributesResponse { | |
result: CategoryAttributes[]; | |
} | |
export interface DictionaryValue { | |
id: number; | |
info: string; | |
picture: string; | |
value: string; | |
} | |
export class Ozon { | |
constructor(private options: OzonOptions) {} | |
async fetchApi( | |
method: FetchOptions["method"], | |
path: string, | |
// params: Record<string, string>, | |
body: FetchOptions["body"] | |
): Promise<unknown> { | |
const { apiKey, clientId, fetch, endpoint } = this.options; | |
const safePath = path[0] !== "/" ? `/${path}` : path; | |
const url = `https://${endpoint ?? OZON_API_ENDPOINT}${safePath}`; | |
const resp = await fetch(url, { | |
method, | |
headers: { | |
"Client-Id": clientId, | |
"Api-Key": apiKey, | |
}, | |
body, | |
}); | |
const data = await resp.json(); | |
if (resp.status !== 200) { | |
throw new Error(data.message || `Ошибка запроса ${resp.status}`); | |
} | |
return data; | |
} | |
async POST(path: string, body: FetchOptions["body"]) { | |
return await this.fetchApi("POST", path, body); | |
} | |
async categoryTreeV1(params?: { language: "RU" }) { | |
const resp = (await this.POST( | |
"/v1/category/tree", | |
JSON.stringify( | |
params ?? { | |
language: "RU", | |
} | |
) | |
)) as OzonCategoryTreeResponse; | |
return resp; | |
} | |
/** | |
* Список товаров | |
* @link https://docs.ozon.ru/api/seller/#operation/ProductAPI_GetProductList | |
*/ | |
async getProductListV2(params: { | |
filter?: { | |
offer_id?: string[]; | |
product_id?: number[]; | |
visibility?: keyof typeof ProductVisibility; | |
}; | |
last_id?: string; | |
limit?: number; | |
}) { | |
const resp = (await this.POST( | |
"/v2/product/list", | |
JSON.stringify(params) | |
)) as { | |
result: ProductV2GetProductListResponseResult; | |
}; | |
return resp.result; | |
} | |
/** | |
* Получить список товаров по идентификаторам | |
* @link https://docs.ozon.ru/api/seller/#operation/ProductAPI_GetProductInfoListV2 | |
*/ | |
async getProductInfoListV2(params?: { | |
offer_id?: string[]; | |
product_id?: number[]; | |
sku?: number[]; | |
}) { | |
const resp = (await this.POST( | |
"/v2/product/info/list", | |
JSON.stringify(params) | |
)) as { | |
result: { | |
items: OzonProductInfo[]; | |
}; | |
}; | |
return resp.result; | |
} | |
async getCategoryAttributesV3(params?: { | |
/** default: `ALL` */ | |
attribute_type?: keyof typeof CategoryAttributesRequestAttributeType; | |
category_id?: number[]; | |
language?: keyof typeof Language; | |
}) { | |
const resp = (await this.POST( | |
"/v3/category/attribute", | |
JSON.stringify(params) | |
)) as CategoryAttributesResponse; | |
return resp.result; | |
} | |
async getCategoryAttributeValuesV2(params?: { | |
attribute_id: number; | |
category_id: number; | |
language?: keyof typeof Language; | |
last_value_id?: number; | |
limit?: number; | |
}) { | |
const resp = (await this.POST( | |
"/v2/category/attribute/values", | |
JSON.stringify(params) | |
)) as { | |
has_next: boolean; | |
result: DictionaryValue[]; | |
}; | |
return resp; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment