# wb-api-client > Автоматически сгенерированные клиенты Wildberries Seller API для Python, TypeScript, Go, Java, PHP, OneScript, C# и Rust. Одна версия на все восемь экосистем, bearer-токен маскируется по умолчанию. Любая страница сайта доступна в Markdown по её адресу с суффиксом `.md` — например `guides/quickstart.md`. Это относится и к {apiPages} справочника API, которые ниже не перечислены поштучно: страница модуля перечисляет все свои операции. --- title: "SDK для Wildberries Seller API" description: "Автоматически сгенерированные SDK для Python, TypeScript, Go, Java, PHP, OneScript (1С), C# и Rust. Одна версия для всех экосистем. Bearer-токен маскируется по умолчанию." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/ --- # wb-api-client — Wildberries API на Вашем языке. Автоматически сгенерированные SDK для Python, TypeScript, Go, Java, PHP, OneScript (1С), C# и Rust. Одна версия для всех экосистем. Bearer-токен маскируется по умолчанию. - **13 категорий API, один пакет** — Все категории — товары, заказы (FBS / DBW / FBW / DBS), продвижение, аналитика, финансы и остальные — поставляются как изолированные под-модули внутри одного пакета на язык. Никаких коллизий имён между категориями. - **Маскирование секретов из коробки** — Ваш bearer-токен обёрнут в языко-специфичный SecretString, поэтому он не попадает в логи, print, дампы отладчика и Sentry, пока вы явно не запросите значение. - **Ежедневная синхронизация с апстримом** — CI-задача каждый день скачивает спецификации WB. Если что-то изменилось, автоматически публикуется новый релиз на PyPI, npm, Go, Maven Central, Packagist, hub.oscript.io, NuGet и crates.io — с соответствующим git-тегом. - **Одна версия, восемь экосистем** — 1.YYYYMMDD.N — валидный стабильный semver в каждом языке. pip / npm / go get / mvn / composer / opm / dotnet add / cargo add подхватывают последнюю версию без прелиз-плясок. - **Bearer-авторизация везде** — Апстрим-YAML путает apiKey-схему с bearer JWT. Пост-обработка убирает дублирование, и в каждом сгенерированном клиенте остаётся ровно один способ авторизации. - **Детерминированная генерация** — Зафиксированные версии openapi-generator и форматтеров, скрытые метки времени. Проверка на PR перегенерирует всё с нуля и падает, если результат отличается. wb-api-client — клиентские библиотеки для Wildberries Seller API: 13 категорий (товары, заказы FBS / DBW / FBW / DBS, продвижение, аналитика, финансы и остальные) на восьми языках, с единой версией и ежедневной пересборкой по официальным спецификациям WB. ## Установка **Python** ```bash pip install valeryverkhoturov-wb-api-client ``` **TypeScript** ```bash npm install @valeryverkhoturov/wb-api-client ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` **Java (Maven)** ```xml io.github.valeryverkhoturov wb-api-client LATEST ``` **PHP** ```bash composer require valeryverkhoturov/wb-api-client ``` **OneScript** ```bash opm install wb-api-client ``` **C#** ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` **Rust** ```bash cargo add wb-api-client ``` ## Первый вызов **Python** ```python from wb_api_client.items import Configuration, ApiClient from wb_api_client.items.api import ItemsApi cfg = Configuration(access_token="<ваш JWT WB>") api = ItemsApi(ApiClient(cfg)) ``` **TypeScript** ```ts import { Configuration, ItemsApi } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({}); cfg.setAccessToken("<ваш JWT WB>"); const api = new ItemsApi(cfg); ``` **Go** ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" cfg := wbitems.NewConfiguration() cfg.SetAccessToken("<ваш JWT WB>") client := wbitems.NewAPIClient(cfg) ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ApiClient client = new ApiClient(); client.setAccessToken("<ваш JWT WB>"); ItemsApi api = new ItemsApi(client); ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi; use GuzzleHttp\Client; $config = (new Configuration()) ->setAccessToken('<ваш JWT WB>'); $api = new ItemsApi(new Client(), $config); ``` **OneScript** ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен("<ваш JWT WB>"); Клиент = Новый ItemsApi(Настройки); ``` **C#** ```csharp using ValeryVerkhoturov.WbApiClient.Items.Api; using ValeryVerkhoturov.WbApiClient.Items.Client; var config = new Configuration(); config.SetAccessToken("<ваш JWT WB>"); var api = new ItemsApi(config); ``` **Rust** ```rust use wb_api_client::items::apis::{configuration::Configuration, items_api}; let mut cfg = Configuration::new(); cfg.set_access_token("<ваш JWT WB>"); ``` Подробно по каждому языку и всем 13 под-модулям: [Python](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/python) · [TypeScript](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/typescript) · [Go](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/go) · [Java](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/java) · [PHP](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/php) · [OneScript](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/onescript) · [C#](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/csharp) · [Rust](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/rust). Для LLM-агентов: [llms.txt](https://valeryverkhoturov.github.io/wb-api-client-docs/llms.txt) — указатель всех страниц сайта, [llms-full.txt](https://valeryverkhoturov.github.io/wb-api-client-docs/llms-full.txt) — вся документация одним файлом.

wb-api-client mascot

--- title: "Быстрый старт" description: "Пять минут: получить токен, установить клиент для своего языка, сделать первый вызов." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/quickstart --- # Быстрый старт Пять минут: получить токен, установить клиент для своего языка, сделать первый вызов. ## 1. Получите API-токен WB Клиентские библиотеки — это только транспорт; вам всё равно нужен аккаунт продавца Wildberries и персональный API-токен. 1. Войдите на [seller.wildberries.ru](https://seller.wildberries.ru) и откройте раздел [Интеграции по API](https://seller.wildberries.ru/api-integrations). 2. Нажмите **+ Создать токен** и выберите вкладку **Для интеграции вручную**. 3. Выберите тип токена — для обычной работы подойдёт **персональный** или **базовый**, для проверки в песочнице — **тестовый**. 4. Заполните название, выберите категории методов и уровень доступа (**Чтение и запись** или **Только чтение**). Для первого теста достаточно категории **Контент** — её использует под-модуль `items`. 5. Для персонального токена отметьте чекбокс **Я понимаю, что не следует передавать токен третьим лицам** и нажмите **Создать**. 6. Нажмите **Скопировать и закрыть** — токен окажется в буфере обмена. После этого посмотреть его в кабинете уже не получится. Выбирайте только те категории, с которыми планируете работать. Если нужно вызывать несколько категорий API (`items`, `orders_fbs`, `analytics`, …), создайте токен с объединением категорий — или по токену на каждую категорию, если хотите уменьшить радиус поражения при утечке. ## 2. Установка Один пакет на язык, внутри которого 13 категорий API как отдельные под-модули. **Python** ```bash pip install valeryverkhoturov-wb-api-client ``` **TypeScript** ```bash npm install @valeryverkhoturov/wb-api-client ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` **Java (Maven)** ```xml io.github.valeryverkhoturov wb-api-client LATEST ``` **PHP** ```bash composer require valeryverkhoturov/wb-api-client ``` **OneScript** ```bash opm install wb-api-client ``` **C#** ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` **Rust** ```bash cargo add wb-api-client ``` `LATEST` для Java: посмотрите текущую версию `1.YYYYMMDD.N` на [странице релизов](https://github.com/ValeryVerkhoturov/wb-api-client/releases) и подставьте её. ## 3. Первый вызов — данные о продавце В под-модуле `general` есть эндпоинт `getV1SellerInfo` — он дёшев и подтверждает, что и токен, и сетевой путь работают. **Python** ```python from wb_api_client.general import Configuration, ApiClient from wb_api_client.general.api import GeneralApi cfg = Configuration(access_token="<ваш JWT WB>") api = GeneralApi(ApiClient(cfg)) info = api.get_v1_seller_info() print(info) ``` **TypeScript** ```ts import { Configuration, GeneralApi, } from "@valeryverkhoturov/wb-api-client/general"; const cfg = new Configuration({}); cfg.setAccessToken("<ваш JWT WB>"); const api = new GeneralApi(cfg); const info = await api.getV1SellerInfo(); console.log(info.data); ``` **Go** ```go package main import ( "context" "fmt" wbgeneral "github.com/ValeryVerkhoturov/wb-api-client-go/general" ) func main() { cfg := wbgeneral.NewConfiguration() cfg.SetAccessToken("<ваш JWT WB>") client := wbgeneral.NewAPIClient(cfg) info, _, err := client.GeneralAPI.GetV1SellerInfo(context.Background()).Execute() if err != nil { panic(err) } fmt.Printf("%+v\n", info) } ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.general.ApiClient; import io.github.valeryverkhoturov.wbapi.general.api.GeneralApi; ApiClient client = new ApiClient(); client.setAccessToken("<ваш JWT WB>"); GeneralApi api = new GeneralApi(client); System.out.println(api.getV1SellerInfo()); ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\General\Configuration; use ValeryVerkhoturov\WbApiClient\General\Api\GeneralApi; use GuzzleHttp\Client; $config = (new Configuration()) ->setAccessToken('<ваш JWT WB>'); $api = new GeneralApi(new Client(), $config); print_r($api->getV1SellerInfo()); ``` **OneScript** ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен("<ваш JWT WB>"); Клиент = Новый GeneralApi(Настройки); Сообщить(Клиент.GetV1SellerInfo().Тело); ``` **C#** ```csharp using ValeryVerkhoturov.WbApiClient.General.Api; using ValeryVerkhoturov.WbApiClient.General.Client; var config = new Configuration(); config.SetAccessToken("<ваш JWT WB>"); var api = new GeneralApi(config); Console.WriteLine(api.GetV1SellerInfo()); ``` **Rust** ```rust use wb_api_client::general::apis::{configuration::Configuration, general_api}; #[tokio::main] async fn main() { let mut cfg = Configuration::new(); cfg.set_access_token("<ваш JWT WB>"); let info = general_api::get_v1_seller_info(&cfg).await; println!("{:#?}", info); } ``` Если вернулись данные о вашем продавце — всё подключено. ## 4. Дальше - **[Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication)** — как устроен SecretString, зачем не выводить токен и как получить сырое значение, когда оно действительно нужно. - **[Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling)** — коды ответов WB, стратегия ретраев на 429/5xx и как ошибки выглядят в каждом языке. - **[Страницы языков](https://valeryverkhoturov.github.io/wb-api-client-docs/languages/python)** — все под-модули, сниппеты установки и форма каждого класса `Api`. ## Что делать, если первый вызов не проходит | Симптом | Вероятная причина | Что делать | | ------------------------------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | Токен неверен / просрочен / без нужных категорий | Перевыпустите в кабинете продавца; убедитесь, что категории токена подходят под модуль (например, «Контент» для `items`) | | `403 Forbidden` | Токен валиден, но категорий недостаточно | Добавьте токену нужную категорию или создайте новый | | `429 Too Many Requests` | Упёрлись в лимит WB | Подождите `X-Ratelimit-Retry` секунд из ответа и откладывайте повторные запросы; SDK не ретраит сам | | Зависает соединение | Корпоративный прокси / MITM TLS | Задайте переменную `HTTPS_PROXY`; большинство клиентов её учитывает | | `ImportError` / `Cannot find module` | Установлено не то имя пакета | Правильное имя — `valeryverkhoturov-wb-api-client` / `@valeryverkhoturov/wb-api-client`. Пакет с «голым» именем `wb-api-client` — это другой проект |

wb-api-client mascot

--- title: "Аутентификация" description: "Каждый вызов Wildberries Seller API требует bearer JWT в заголовке Authorization. Клиенты сами подставляют заголовок; вы передаёте только токен." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication --- # Аутентификация Каждый вызов Wildberries Seller API требует **bearer JWT** в заголовке `Authorization`. Клиенты сами подставляют заголовок; вы передаёте только токен. ## Откуда берётся токен Токены выпускаются в кабинете продавца: 1. [seller.wildberries.ru](https://seller.wildberries.ru) → раздел [Интеграции по API](https://seller.wildberries.ru/api-integrations). 2. **+ Создать токен** → вкладка **Для интеграции вручную** → выберите тип токена: **персональный**, **базовый** или **тестовый** для песочницы. 3. Заполните название, выберите категории методов (Контент, Маркетплейс, Аналитика, …) и уровень доступа — **Чтение и запись** или **Только чтение**. 4. **Создать** → **Скопировать и закрыть**. Токен показывают один раз — сохраните его сразу. Каждой выбранной категории примерно соответствует один или несколько под-модулей в клиенте. Если пришёл `403` — токен валиден, но прав не хватает; перевыпустите с нужной категорией. Какой тип токена выбрать: - **Персональный** — для собственных программ и on-premise систем (включая локальные версии 1С). Даёт доступ к чувствительным данным, поэтому его нельзя передавать третьим лицам и использовать в облачных сервисах. - **Базовый** — доступ к ограниченному набору данных. Подходит для проверки интеграции на реальных данных и для случаев, когда персональный использовать нельзя. - **Тестовый** — работает только с [песочницей WB API](https://dev.wildberries.ru/openapi/sandbox) и сгенерированными тестовыми данными; доступа к реальным данным продавца нет. - **Сервисный** — создаётся для конкретного облачного сервиса из каталога [«Решения для бизнеса»](https://seller.wildberries.ru/auth-services); категории и уровни доступа заполняются автоматически. Любой токен живёт 180 дней с момента создания, после чего нужно выпустить новый. Проверить валидность токена и его категории можно, декодировав JWT — это строка по [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519). Не вставляйте токен в онлайн-декодеры: так его может перехватить кто угодно. ## Обёртки SecretString Получив токен, не носите его по коду как обычную строку. Каждый клиент оборачивает значение в языко-специфичный «секретный» тип — он маскирует токен в логах, `print`-выводе, отладчике и дампах ошибок, пока вы явно не запросите значение. | Язык | Обёртка | Сеттер | Маскируется в | Явное значение | | ---------- | -------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------- | --------------------------- | | Python | [`pydantic.SecretStr`](https://docs.pydantic.dev/latest/api/types/#pydantic.types.SecretStr) | `Configuration(access_token=…)` | `print(cfg.access_token)` → `**********` | `.get_secret_value()` | | TypeScript | встроенный класс `SecretString` | `Configuration.setAccessToken(…)` | `console.log(new SecretString("…"))` → `` | `.exposeSecret()` | | Go | [`secrecy.SecretString`](https://github.com/negrel/secrecy) | `Configuration.SetAccessToken(…)` | `fmt.Printf("%v", cfg.AccessToken)` → `` | `.ExposeSecret()` | | Java | `SecretString` в каждом под-пакете | `ApiClient.setAccessToken(String)` | `System.out.println(s)` → `` | `.exposeSecret()` | | PHP | `SecretString` в каждом под-пространстве | `Configuration::setAccessToken(string)` | `var_dump($config)` → `''` | `$config->getAccessToken()` | | C# | `SecretString` в каждом пространстве имён | `Configuration.SetAccessToken(…)` | `Console.WriteLine(new SecretString("…"))` → `` | `.ExposeSecret()` | | OneScript | класс `СекретнаяСтрока` | `Конфигурация.УстановитьТокен(…)` | `Сообщить(Настройки.Токен())` → `СекретнаяСтрока` | `.Раскрыть()` | | Rust | `SecretString` в каждом модуле | `Configuration::set_access_token(…)` | `println!("{s:?}")` / `{}` → `` | `.expose_secret()` | ### Зачем это нужно Реальные ситуации, ради которых это сделано: - Логгер-middleware, который в трейсе ошибки дампит всю конфигурацию запроса. - `print(config)`, оставленный в коде после отладки. - Исключения, сериализованные в Sentry / трекер вместе с полным контекстом HTTP-клиента. - Отладчик IDE, показывающий поля объекта на демо-встрече по шаринг-скрину. Обёртка превращает каждый из этих сценариев из утечки токена в безобидную метку ``. Цена — одна строка кода в точке ввода. ### Как это выглядит на практике **Python** ```python from wb_api_client.items import Configuration cfg = Configuration(access_token="eyJhbGciOi...") print(cfg.access_token) # SecretStr('**********') print(cfg.access_token.get_secret_value()) # eyJhbGciOi... (по запросу) ``` **TypeScript** ```ts import { Configuration } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({}); cfg.setAccessToken("eyJhbGciOi..."); console.log(cfg.accessToken); // SecretString { } console.log(cfg.accessToken.exposeSecret()); // eyJhbGciOi... (по запросу) ``` **Go** ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" cfg := wbitems.NewConfiguration() cfg.SetAccessToken("eyJhbGciOi...") fmt.Printf("%v\n", cfg.AccessToken) // fmt.Println(cfg.AccessToken.ExposeSecret()) // eyJhbGciOi... (по запросу) ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.items.SecretString; SecretString s = new SecretString("eyJhbGciOi..."); System.out.println(s); // System.out.println(s.exposeSecret()); // eyJhbGciOi... (по запросу) ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\Items\SecretString; $s = new SecretString('eyJhbGciOi...'); var_dump($s); // object(...) { ['value']=> '' } echo $s->exposeSecret(); // eyJhbGciOi... (по запросу) ``` **OneScript** ```bsl С = Новый СекретнаяСтрока("eyJhbGciOi..."); Сообщить(С); // СекретнаяСтрока Сообщить(С.Раскрыть()); // eyJhbGciOi... (по запросу) ``` **C#** ```csharp var s = new SecretString("eyJhbGciOi..."); Console.WriteLine($"{s}"); // Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (по запросу) ``` **Rust** ```rust use wb_api_client::items::apis::configuration::SecretString; let s = SecretString::new("eyJhbGciOi..."); println!("{s:?}"); // println!("{}", s.expose_secret()); // eyJhbGciOi... (по запросу) ``` ## Один токен на несколько под-модулей Если в процессе вы используете сразу несколько под-модулей, у каждого — свой `Configuration` / `ApiClient`. Так задумано: openapi-generator создаёт полностью изолированные SDK на каждую спецификацию. Переиспользуйте строку токена, но конструируйте конфиг на каждый модуль. **Python** ```python from wb_api_client.items import Configuration as ItemsCfg, ApiClient as ItemsClient from wb_api_client.orders_fbs import Configuration as OrdCfg, ApiClient as OrdClient TOKEN = "<ваш JWT WB>" items_api = ItemsClient(ItemsCfg(access_token=TOKEN)) orders_api = OrdClient(OrdCfg(access_token=TOKEN)) ``` **TypeScript** ```ts import { Configuration as ItemsCfg } from "@valeryverkhoturov/wb-api-client/items"; import { Configuration as OrdCfg } from "@valeryverkhoturov/wb-api-client/orders_fbs"; const TOKEN = "<ваш JWT WB>"; const itemsCfg = new ItemsCfg({}); itemsCfg.setAccessToken(TOKEN); const ordCfg = new OrdCfg({}); ordCfg.setAccessToken(TOKEN); ``` ## Ротация токенов Токены WB не ротируются сами и живут максимум 180 дней — их нужно заменять вручную. Хорошая практика: 1. Держите токен в секрет-менеджере (SSM, Vault, GitHub Actions secret) — не в исходниках. 2. Читайте в переменную окружения при старте процесса и конструируйте конфиг один раз. 3. На ротации: убейте процесс (или пул соединений) и запустите заново с новым токеном. Клиент кэширует токен на уровне экземпляра Configuration. ## Типичные ошибки авторизации | Ответ | Значение | | ----------------------------------------- | ---------------------------------------------------------------------------------- | | `401 Unauthorized` — `token expired` | Срок токена истёк; выпустите новый | | `401 Unauthorized` — `invalid signature` | Неверный формат заголовка Authorization или токен от другой среды (sandbox / prod) | | `403 Forbidden` — `no access to resource` | Токен валиден, прав не хватает — перевыпустите с правильной галочкой | | `429 Too Many Requests` | Rate-limit; сам токен в порядке | Ретраи и работа с ошибками — в [руководстве по обработке ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling).

wb-api-client mascot

--- title: "Обработка ошибок" description: "WB возвращает стандартные HTTP-коды и JSON в теле. Клиенты превращают их в языко-нативные исключения — сырой Response вы получите только если явно об этом…" lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling --- # Обработка ошибок WB возвращает стандартные HTTP-коды и JSON в теле. Клиенты превращают их в языко-нативные исключения — сырой `Response` вы получите только если явно об этом попросите. ## Коды, за которыми стоит следить | Код | Значение | Правильная реакция | | ----------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `200`–`204` | Успех | Разобрать тело (или ничего не делать при 204) | | `400` | Некорректный запрос | Починить запрос; ретрай бесполезен | | `401` | Нет токена / плохой / просрочен | Обновить токен; НЕ ретраить с тем же | | `402` | У сервиса недостаточно средств на балансе | Возвращается только сервисам из каталога «Решения для бизнеса» — пополнить баланс | | `403` | Токену не хватает прав на эндпоинт | Проверить: категория токена подходит методу, метод разрешён типу токена, токен не создан удалённым пользователем. Перевыпустить при необходимости | | `404` | Ресурса нет | Проверить URL; бизнес-логика — не всегда ошибка. Полезное — в поле `detail` тела | | `409` | Конфликт (напр. поставка уже доставлена) | Бизнес-логика; у WB своя схема на каждый эндпоинт | | `413` | Тело запроса больше лимита | Уменьшить число объектов в запросе | | `422` | Ошибка обработки параметров | Проверить данные запроса — они не должны противоречить друг другу | | `429` | Rate-limit | Подождать `X-Ratelimit-Retry` секунд из ответа, повторить | | `498` | Anti-bot WBAAS | С этими клиентами не должно возникать (корректный UA); если увидели — что-то проксирует/переписывает запрос | | `5xx` | Проблема на стороне WB | Ретрай с экспоненциальной задержкой, лимит по попыткам | ## Паттерн ретраев Клиенты сами не ретраят — это осознанное решение (политика ретраев — дело приложения). Минимальный хендлер для ретраибельных ошибок: **Python** ```python import time from wb_api_client.items.exceptions import ApiException def with_retry(fn, *, max_attempts=5, base=1.0): for attempt in range(1, max_attempts + 1): try: return fn() except ApiException as e: if e.status not in (429, 500, 502, 503, 504) or attempt == max_attempts: raise retry_after = float( e.headers.get("X-Ratelimit-Retry") or e.headers.get("Retry-After") or 0 ) or base * 2 ** attempt time.sleep(retry_after) ``` **TypeScript** ```ts import { AxiosError } from "axios"; async function withRetry( fn: () => Promise, { maxAttempts = 5, base = 1000 } = {}, ): Promise { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (err) { const e = err as AxiosError; const status = e.response?.status ?? 0; if (![429, 500, 502, 503, 504].includes(status) || attempt === maxAttempts) throw err; const retryAfterHeader = e.response?.headers?.["x-ratelimit-retry"] ?? e.response?.headers?.["retry-after"]; const delay = retryAfterHeader ? Number(retryAfterHeader) * 1000 : base * 2 ** attempt; await new Promise((r) => setTimeout(r, delay)); } } throw new Error("unreachable"); } ``` **Go** ```go import ( "context" "errors" "net/http" "time" ) func withRetry[T any](ctx context.Context, fn func() (T, *http.Response, error)) (T, error) { var zero T for attempt := 1; attempt <= 5; attempt++ { v, resp, err := fn() if err == nil { return v, nil } status := 0 if resp != nil { status = resp.StatusCode } if status != 429 && (status < 500 || status > 504) || attempt == 5 { return zero, err } delay := time.Duration(1< wb-api-client mascot

--- title: "Свои заголовки" description: "Иногда к каждому запросу нужно добавить заголовок, которого нет в спецификации WB: креденшелы шлюза или корпоративного прокси, стоящего перед API,…" lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/custom-headers --- # Свои заголовки Иногда к каждому запросу нужно добавить заголовок, которого нет в спецификации WB: креденшелы шлюза или корпоративного прокси, стоящего перед API, идентификатор трассировки, метка окружения. Каждый клиент умеет добавлять такие заголовки — механизм свой в каждом языке, и ниже разобраны все восемь. ## Что сюда не относится Две вещи стоит отделить сразу, иначе легко добавить заголовок, который клиент и так проставляет. **Токен WB.** Заголовок `Authorization` клиент формирует сам из токена — см. [Аутентификацию](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication). Не собирайте его руками через механизмы этой страницы: в части языков (Go) это приведёт к двум заголовкам `Authorization` в одном запросе. **Заголовки, описанные в спецификации.** Если заголовок объявлен в спецификации операции, генератор уже превратил его в обычный аргумент метода. В WB такие есть: `X-Nm-Id` и `X-Photo-Number` у `postV3MediaFile` в под-модуле `items`. Передавайте их аргументами, а не как «свои». Остаётся всё остальное — заголовки, о которых спецификация не знает. Их и добавляем. ## Заголовки на весь клиент Самый частый случай: заголовок нужен всем запросам. Задаётся один раз при сборке клиента. **Python** ```python from wb_api_client.items import Configuration, ApiClient from wb_api_client.items.api import ItemsApi client = ApiClient(Configuration(access_token="<ваш JWT WB>")) client.set_default_header("X-Gateway-Token", "<креденшелы шлюза>") client.set_default_header("X-Request-Source", "erp-sync") api = ItemsApi(client) ``` **TypeScript** ```ts import { Configuration, ItemsApi, } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({ baseOptions: { headers: { "X-Gateway-Token": "<креденшелы шлюза>", "X-Request-Source": "erp-sync", }, }, }); cfg.setAccessToken("<ваш JWT WB>"); const api = new ItemsApi(cfg); ``` **Go** ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" cfg := wbitems.NewConfiguration() cfg.SetAccessToken("<ваш JWT WB>") cfg.AddDefaultHeader("X-Gateway-Token", "<креденшелы шлюза>") cfg.AddDefaultHeader("X-Request-Source", "erp-sync") client := wbitems.NewAPIClient(cfg) ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ApiClient client = new ApiClient(); client.setAccessToken("<ваш JWT WB>"); client.addDefaultHeader("X-Gateway-Token", "<креденшелы шлюза>"); client.addDefaultHeader("X-Request-Source", "erp-sync"); ItemsApi api = new ItemsApi(client); ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi; use GuzzleHttp\Client; $config = (new Configuration()) ->setAccessToken('<ваш JWT WB>'); // У Configuration в PHP нет реестра заголовков — их держит Guzzle-клиент. $http = new Client([ 'headers' => [ 'X-Gateway-Token' => '<креденшелы шлюза>', 'X-Request-Source' => 'erp-sync', ], ]); $api = new ItemsApi($http, $config); ``` **OneScript** ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен("<ваш JWT WB>"); Настройки.ДополнительныеЗаголовки.Вставить("X-Gateway-Token", "<креденшелы шлюза>"); Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Source", "erp-sync"); Клиент = Новый ItemsApi(Настройки); ``` **C#** ```csharp using ValeryVerkhoturov.WbApiClient.Items.Api; using ValeryVerkhoturov.WbApiClient.Items.Client; var config = new Configuration(); config.SetAccessToken("<ваш JWT WB>"); config.DefaultHeaders["X-Gateway-Token"] = "<креденшелы шлюза>"; config.DefaultHeaders["X-Request-Source"] = "erp-sync"; var api = new ItemsApi(config); ``` **Rust** ```rust use wb_api_client::items::apis::configuration::Configuration; // У Configuration в Rust нет реестра заголовков — их держит reqwest-клиент. let mut headers = reqwest::header::HeaderMap::new(); headers.insert("X-Gateway-Token", "<креденшелы шлюза>".parse().unwrap()); headers.insert("X-Request-Source", "erp-sync".parse().unwrap()); let mut cfg = Configuration::new(); cfg.set_access_token("<ваш JWT WB>"); cfg.client = reqwest::Client::builder() .default_headers(headers) .build() .unwrap(); ``` Заголовки живут на экземпляре `Configuration` / `ApiClient` — а он свой у каждого под-модуля. Используете `items` и `orders_fbs` — задайте заголовки обоим; подробнее про это в разделе [«Один токен на несколько под-модулей»](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication#один-токен-на-несколько-под-модулеи). ## Заголовки на один запрос Отдельный заголовок для одного вызова умеют не все клиенты: это зависит от того, что сгенерировал openapi-generator для конкретного языка. | Язык | Поддержка на уровне вызова | | ---------- | ------------------------------------------------------ | | Python | да — именованный параметр `_headers=` у каждого метода | | TypeScript | да — последний аргумент `RawAxiosRequestConfig` | | Go | нет — методы не принимают заголовки | | Java | нет | | PHP | нет | | C# | нет | | OneScript | нет — `Заголовки` собираются внутри метода | | Rust | нет — методы принимают только `&Configuration` | **Python** ```python # Внимание: заголовки по умолчанию перекрывают _headers при совпадении имени. tags = api.get_v2_tags(_headers={"X-Correlation-Id": "9f1c-…"}) ``` **TypeScript** ```ts // Здесь наоборот: заголовок вызова перекрывает и baseOptions, и Authorization. const tags = await api.getV2Tags({ headers: { "X-Correlation-Id": "9f1c-…" }, }); ``` В остальных шести языках, если заголовок нужен только части запросов, соберите второй экземпляр клиента со своим набором заголовков и ходите через него. ## Что чем перекрывается Порядок слияния у каждого генератора свой, и он не везде интуитивен. Проверено по сгенерированному коду: | Язык | Порядок (побеждает правый) | Особенность | | ---------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | Python | `_headers` → заголовки по умолчанию | **Умолчания побеждают**: `default_headers` применяются поверх `_headers` | | TypeScript | авторизация → `baseOptions.headers` → заголовки вызова | заголовком конфигурации можно перебить и `Authorization` | | Go | — | используется `Header.Add`, то есть **добавление, а не замена**: дубль имени даст два заголовка | | Java | `defaultHeaderMap` поверх заголовков операции | замена по имени | | PHP | заголовки запроса → дефолты Guzzle | клиентские дефолты Guzzle применяются, только если заголовка ещё нет | | C# | `DefaultHeaders` → заголовки операции | замена по имени | | OneScript | `Accept`/`User-Agent`/`Authorization` → `ДополнительныеЗаголовки` → заголовки операции | `Соответствие.Вставить` заменяет значение | | Rust | `default_headers` клиента → заголовки операции | заголовок, который ставит операция, перекрывает дефолт reqwest-клиента | Практический вывод один: не используйте механизмы этой страницы, чтобы переопределить заголовок, который клиент уже ставит сам. Для `User-Agent` есть штатная настройка в каждом языке, для `Authorization` — сеттер токена. ## Секреты в заголовках `SecretString` оборачивает **только** токен WB. Креденшелы шлюза или партнёрского сервиса — обычные строки, и клиент их не маскирует: в логе HTTP-клиента или в дампе конфигурации они будут видны целиком. Поэтому к ним применимы те же правила, что и к токену WB: 1. Держите значение в секрет-менеджере (Vault, SSM, GitHub Actions secret), а не в исходниках. 2. Читайте в переменную окружения на старте процесса и собирайте клиент один раз. 3. Проверьте, что ваш логгер запросов не пишет заголовки целиком — либо внесите свои имена заголовков в список маскируемых. Если вы логируете запросы через middleware, добавьте имена этих заголовков в тот же фильтр, где у вас уже лежит `Authorization`.

wb-api-client mascot

--- title: "Python" description: "Python-клиент Wildberries Seller API: пакет valeryverkhoturov-wb-api-client с PyPI, 13 категорий API, маскирование токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/python --- # Python Пакет: [`valeryverkhoturov-wb-api-client`](https://pypi.org/project/valeryverkhoturov-wb-api-client/) на PyPI. - **Python:** 3.9+ - **HTTP:** `urllib3` - **Модели:** `pydantic` v2 - **Обёртка секретов:** `pydantic.SecretStr` ## Установка ```bash pip install valeryverkhoturov-wb-api-client ``` Для проектов на Poetry / uv / PDM добавляется как обычная зависимость: **pyproject.toml** ```toml [project] dependencies = [ "valeryverkhoturov-wb-api-client", ] ``` **uv** ```bash uv add valeryverkhoturov-wb-api-client ``` **poetry** ```bash poetry add valeryverkhoturov-wb-api-client ``` ## Форма импортов Дистрибутив ставит один пакет `wb_api_client` с 13 под-модулями — по одному на категорию WB API. Каждый под-модуль — самостоятельный SDK со своими `Configuration`, `ApiClient` и ровно одним API-классом, названным по модулю (`GeneralApi`, `ItemsApi`, `OrdersFbsApi` и т.д.). ```python from wb_api_client. import Configuration, ApiClient from wb_api_client..api import Api ``` ## Под-модули | Слаг | Категория | | ----------------- | -------------------------------------------------------- | | `general` | Общее — ping, инфо о продавце, управление пользователями | | `items` | Работа с товарами (Контент) | | `orders_fbs` | Заказы FBS | | `orders_dbw` | Заказы DBW | | `dbs` | DBS | | `in_store_pickup` | Самовывоз | | `orders_fbw` | Поставки FBW | | `promotion` | Маркетинг и продвижение | | `communications` | Общение с покупателями | | `rates` | Тарифы | | `analytics` | Аналитика и данные | | `reports` | Отчёты | | `finances` | Документы и бухгалтерия | В каждом под-модуле ровно один класс API, названный по модулю: `general` → `GeneralApi`, `items` → `ItemsApi`, `orders_fbs` → `OrdersFbsApi` и так далее. Полный список — в README, который перегенерируется каждый релиз: [clients/python/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/python/README.md). ## Авторизация ```python from wb_api_client.items import Configuration, ApiClient from wb_api_client.items.api import ItemsApi cfg = Configuration(access_token="<ваш JWT WB>") # оборачивается в SecretStr client = ApiClient(cfg) api = ItemsApi(client) ``` Обычная строка тоже подойдёт — она автоматически оборачивается. Чтобы прочитать сырое значение (например, для диагностики): ```python cfg.access_token # SecretStr(**********) cfg.access_token.get_secret_value() # "<ваш JWT WB>" ``` ## Async / sync Сгенерированный клиент — **синхронный** (urllib3). Если нужна асинхронность — запускайте вызовы в тред-пуле: ```python import asyncio result = await asyncio.to_thread(api.get_v1_seller_info) ``` Нативно-асинхронный вариант в планах нет — asynс-python-шаблон openapi-generator отстаёт от синхронного. ## Типизация Каждая модель — pydantic v2. IDE и статические анализаторы видят полные типы: ```python from wb_api_client.items.models import Product # pydantic-модель, dataclass-like p: Product = Product.model_validate(some_dict) ``` ## Тестирование против клиента У `Configuration` есть параметр `host` — можно направить на мок-сервер: ```python cfg = Configuration( access_token="test-token", host="http://localhost:8080", ) ``` ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/python/README.md)

wb-api-client mascot

--- title: "TypeScript" description: "TypeScript-клиент Wildberries Seller API: пакет @valeryverkhoturov/wb-api-client с npm, типы .d.ts, axios, 13 категорий API." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/typescript --- # TypeScript Пакет: [`@valeryverkhoturov/wb-api-client`](https://www.npmjs.com/package/@valeryverkhoturov/wb-api-client) на npm. - **Node:** 18+ - **HTTP:** `axios` - **Типы:** first-class TypeScript, поставляются `.d.ts` - **Обёртка секретов:** встроенный класс `SecretString` ## Установка **npm** ```bash npm install @valeryverkhoturov/wb-api-client ``` **pnpm** ```bash pnpm add @valeryverkhoturov/wb-api-client ``` **yarn** ```bash yarn add @valeryverkhoturov/wb-api-client ``` **bun** ```bash bun add @valeryverkhoturov/wb-api-client ``` ## Форма импортов Каждый под-модуль — отдельный subpath-экспорт. Импортируйте из `@valeryverkhoturov/wb-api-client/`: ```ts import { Configuration, ItemsApi, } from "@valeryverkhoturov/wb-api-client/items"; ``` Импорт с корня `@valeryverkhoturov/wb-api-client` не поддерживается — barrel-экспорта нет специально, чтобы не тянуть в бандл все 13 SDK, когда нужен один. ## Под-модули По под-модулю на категорию: `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Полный список классов `Api` на каждый модуль — в per-release README: [clients/typescript/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/typescript/README.md). ## Авторизация ```ts import { Configuration, ItemsApi } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({}); cfg.setAccessToken("<ваш JWT WB>"); const api = new ItemsApi(cfg); const res = await api.someEndpoint(); console.log(res.data); ``` `setAccessToken` внутри оборачивает значение в `SecretString`. Прямое присваивание (`cfg.accessToken = "..."`) не поддерживается — всегда используйте сеттер, чтобы гарантировать маскирование. Получить сырое значение обратно (например, для диагностического лога): ```ts cfg.accessToken.exposeSecret(); ``` ## User-Agent Клиент отправляет `ValeryVerkhoturov/wb-api-client/typescript` в каждом запросе — WB использует это как грубый сигнал маршрутизации. Чтобы добавить свой идентификатор приложения, не убирая базовый: ```ts const cfg = new Configuration({}); cfg.setAccessToken(TOKEN); cfg.baseOptions = { ...cfg.baseOptions, headers: { ...cfg.baseOptions.headers, "User-Agent": "MyApp/1.2.3 (+https://myapp.example) ValeryVerkhoturov/wb-api-client/typescript", }, }; ``` ## ESM / CJS Пакет публикует dual entry points — `import` из ESM-кода, `require` из CommonJS. В `tsconfig.json` `"module": "NodeNext"` или `"module": "bundler"` выбирают правильный автоматически. ## Поддержка бандлеров Subpath-экспорты описаны в `package.json` через `exports`. Современные бандлеры (Vite, Rollup, esbuild, Webpack 5+, Turbopack) их уважают из коробки. Webpack 4 — нет; обновляйте или пропишите алиас руками. ## Тестирование Направьте на мок-сервер через `basePath`: ```ts const cfg = new Configuration({ basePath: "http://localhost:8080" }); ``` ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/typescript/README.md)

wb-api-client mascot

--- title: "Go" description: "Go-клиент Wildberries Seller API: модуль github.com/ValeryVerkhoturov/wb-api-client-go, 13 категорий API, маскирование bearer-токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/go --- # Go Модуль: `github.com/ValeryVerkhoturov/wb-api-client-go` — резолвится из git-тегов [отдельного репозитория](https://github.com/ValeryVerkhoturov/wb-api-client-go). - **Go:** 1.22+ - **HTTP:** `net/http` (стандартная библиотека) - **Обёртка секретов:** [`github.com/negrel/secrecy`](https://github.com/negrel/secrecy) ## Установка ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` Зафиксировать конкретную версию по тегу: ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@v1.20260926.0 ``` `proxy.golang.org` берёт код прямо из пушнутого тега — отдельный реестр не нужен. ## Почему модуль живёт в отдельном репозитории В Go принято «один модуль на репозиторий», поэтому клиент лежит в [wb-api-client-go](https://github.com/ValeryVerkhoturov/wb-api-client-go) с `go.mod` в корне. Релизные теги (`vX.Y.Z`) пушатся в этот репозиторий тем же пайплайном, что и в основной — Go подхватывает нужный тег. Поскольку `MAJOR = 1` зафиксирован (см. [версионирование](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/versioning)), суффикса `/v2` не будет ни сейчас, ни в будущем. ## Форма импортов По одному Go-пакету на категорию WB API, у каждого свои `Configuration`, `APIClient` и типы `*API`. ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" ``` Давайте каждому под-модулю алиас (`wbitems`, `wbfbs`, `wbanalytics`, …) — иначе имена конфликтуют: во всех модулях экспортируются одни и те же идентификаторы. ## Под-модули `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Структуры `*API` и методы эндпоинтов на модуль перечислены в per-release README: [wb-api-client-go/README.md](https://github.com/ValeryVerkhoturov/wb-api-client-go#readme). ## Авторизация ```go package main import ( "context" "fmt" wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" ) func main() { cfg := wbitems.NewConfiguration() cfg.SetAccessToken("<ваш JWT WB>") // оборачивается в *secrecy.SecretString client := wbitems.NewAPIClient(cfg) v, _, err := client.DefaultAPI.SomeEndpoint(context.Background()).Execute() if err != nil { panic(err) } fmt.Printf("%+v\n", v) } ``` `SetAccessToken` — единственный путь: стандартный для openapi-generator канал `ContextAccessToken` через `context.Value` вырезан, случайно обойти обёртку не получится. Получить сырое значение обратно: ```go cfg.AccessToken.ExposeSecret() ``` ## Контексты Каждый вызов принимает `context.Context`. Прокидывайте дедлайн и отмену из своего приложения — клиент их уважает: ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() v, _, err := client.DefaultAPI.SomeEndpoint(ctx).Execute() ``` ## Свой HTTP-клиент Подставьте `*http.Client` для прокси, кастомных таймаутов или транспорт-обёртки: ```go cfg := wbitems.NewConfiguration() cfg.HTTPClient = &http.Client{ Timeout: 60 * time.Second, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, }, } ``` ## Тестирование Переопределите хост при создании Configuration: ```go cfg := wbitems.NewConfiguration() cfg.Host = "localhost:8080" cfg.Scheme = "http" ``` ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client-go#readme)

wb-api-client mascot

--- title: "Java" description: "Java-клиент Wildberries Seller API: координаты io.github.valeryverkhoturov:wb-api-client с Maven Central, 13 категорий API, маскирование токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/java --- # Java Координаты: `io.github.valeryverkhoturov:wb-api-client` на [Maven Central](https://central.sonatype.com/artifact/io.github.valeryverkhoturov/wb-api-client). - **Java:** 8+ (компилируется с `-source 1.8 -target 1.8`) - **HTTP:** OkHttp 4 - **JSON:** Gson + gson-fire - **Обёртка секретов:** `SecretString` в каждом под-пакете ## Установка **Maven** ```xml io.github.valeryverkhoturov wb-api-client LATEST ``` **Gradle (Kotlin)** ```kts dependencies { implementation("io.github.valeryverkhoturov:wb-api-client:LATEST") } ``` **Gradle (Groovy)** ```groovy dependencies { implementation 'io.github.valeryverkhoturov:wb-api-client:LATEST' } ``` **sbt** ```scala libraryDependencies += "io.github.valeryverkhoturov" % "wb-api-client" % "LATEST" ``` Замените `LATEST` на актуальную версию `1.YYYYMMDD.N` со [страницы релизов](https://github.com/ValeryVerkhoturov/wb-api-client/releases). ## Раскладка пакетов Базовый пакет: `io.github.valeryverkhoturov.wbapi`. Каждая категория WB — свой под-пакет со своим `ApiClient`, моделями и классами `api`. ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ``` ## Под-пакеты `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Полный список API-классов на модуль: [clients/java/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/java/README.md). ## Авторизация ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ApiClient client = new ApiClient(); client.setAccessToken("<ваш JWT WB>"); ItemsApi api = new ItemsApi(client); ``` `setAccessToken` принимает обычную `String` и оборачивает её в `SecretString` внутри клиента — конструировать обёртку руками не нужно, а в логах и дампах токен остаётся замаскированным. ## Один `SecretString` на под-пакет — почему Обёртка генерируется в каждый под-пакет, а не расшарена на всю библиотеку, потому что openapi-generator создаёт полностью изолированные SDK на каждую спецификацию — общего «core»-модуля нет. Объединение потребовало бы дополнительного прохода генерации с межмодульными зависимостями. Цена — один класс на под-пакет; плюс в том, что клиент остаётся чистым выхлопом openapi-generator с минимальными точечными правками. На практике это незаметно: `setAccessToken` оборачивает строку в `SecretString` своего под-пакета сам, поэтому импортировать и конструировать обёртку не требуется. ## Свой OkHttp-клиент Подставьте свой экземпляр для прокси, интерсепторов или кастомных таймаутов: ```java import okhttp3.OkHttpClient; import java.time.Duration; OkHttpClient http = new OkHttpClient.Builder() .connectTimeout(Duration.ofSeconds(5)) .readTimeout(Duration.ofSeconds(120)) .build(); ApiClient client = new ApiClient(http); client.setAccessToken(TOKEN); ``` ## Java 8 vs новее Генератор выдаёт Java-8-совместимый код с `threetenbp` для дат и времени. На Java 11+ это по-прежнему работает — «современную» зависимость дополнительно ставить не нужно. Если хочется работать с `java.time` напрямую, модели предоставляют доступ к исходным строковым значениям через методы `set*ToString` / `get*ToString`, где это применимо. ## Тестирование Переопределите base path: ```java ApiClient client = new ApiClient(); client.setBasePath("http://localhost:8080"); ``` ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/java/README.md)

wb-api-client mascot

--- title: "PHP" description: "PHP-клиент Wildberries Seller API: пакет valeryverkhoturov/wb-api-client с Packagist, Guzzle, 13 категорий API, маскирование токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/php --- # PHP Пакет: [`valeryverkhoturov/wb-api-client`](https://packagist.org/packages/valeryverkhoturov/wb-api-client) на Packagist. - **PHP:** 8.1+ - **HTTP:** [Guzzle 7](https://docs.guzzlephp.org/en/stable/) - **Автозагрузка:** PSR-4, корневое пространство `ValeryVerkhoturov\WbApiClient\` - **Обёртка секретов:** `SecretString` в каждом под-пространстве ## Установка ```bash composer require valeryverkhoturov/wb-api-client ``` ## Где лежит исходник PHP здесь — отдельный случай: Packagist требует, чтобы `composer.json` был в **корне** обходимого репозитория, поэтому PHP-клиент живёт в отдельном репозитории — [`ValeryVerkhoturov/wb-api-client-php`](https://github.com/ValeryVerkhoturov/wb-api-client-php) — а не под `clients/php` в моно-репозитории. Соседний репозиторий подключён как git-submodule по пути `clients/php` в основном репозитории, так что ежедневный CI-прогон генерирует все восемь языков в одном месте, а затем коммитит и тегает оба репозитория синхронно. Для конечного потребителя через Composer это прозрачно — просто `composer require` и работайте. ## Форма пространств имён Корневое пространство: `ValeryVerkhoturov\WbApiClient\`. Каждая категория WB — PascalCase-подпространство: ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi; ``` ## Под-пространства `General`, `Items`, `OrdersFbs`, `OrdersDbw`, `Dbs`, `InStorePickup`, `OrdersFbw`, `Promotion`, `Communications`, `Rates`, `Analytics`, `Reports`, `Finances`. Полный список API на под-пространство: [README проекта wb-api-client-php](https://github.com/ValeryVerkhoturov/wb-api-client-php#readme). ## Авторизация ```php setAccessToken('<ваш JWT WB>'); $api = new ItemsApi(new Client(), $config); $result = $api->someEndpoint(); ``` `setAccessToken` принимает обычную строку и хранит её внутри как `SecretString`: `var_dump($config)` покажет токен замаскированным, а не сырым. Получить сырое значение: ```php $raw = $config->getAccessToken(); // string|null ``` ## User-Agent Каждый запрос отправляет `ValeryVerkhoturov/wb-api-client/php` (не дефолт openapi-generator). Чтобы добавить свой идентификатор приложения: ```php $config = (new Configuration()) ->setAccessToken(TOKEN) ->setUserAgent('MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/php'); ``` ## Свой Guzzle-клиент Собственный клиент для прокси, middleware, кастомных таймаутов: ```php use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; $stack = HandlerStack::create(); // $stack->push(...); // ваш middleware $http = new Client([ 'timeout' => 60, 'handler' => $stack, 'proxy' => getenv('HTTPS_PROXY') ?: null, ]); $api = new ItemsApi($http, $config); ``` ## Тестирование Переопределите host на Configuration: ```php $config = (new Configuration()) ->setHost('http://localhost:8080') ->setAccessToken('test-token'); ``` ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [README соседнего репозитория](https://github.com/ValeryVerkhoturov/wb-api-client-php#readme)

wb-api-client mascot

--- title: "OneScript (1С)" description: "OneScript-клиент (1С) Wildberries Seller API: пакет wb-api-client с hub.oscript.io, 13 категорий API, маскирование bearer-токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/onescript --- # OneScript (1С) Пакет: [`wb-api-client`](https://hub.oscript.io/pools/default/packages/wb-api-client) на hub.oscript.io. - **OneScript:** 2.0.0+ - **HTTP:** [1connector](https://hub.oscript.io/pools/default/packages/1connector) 2.3.3 - **Зависимости:** [1connector](https://hub.oscript.io/pools/default/packages/1connector) 2.3.3 — транспорт, [jason](https://hub.oscript.io/pools/default/packages/jason) 0.6.0 — сериализация моделей - **Обёртка секретов:** класс `СекретнаяСтрока` ## Установка ```bash opm install wb-api-client ``` Либо подключите каталог пакета напрямую — например, склонировав соседний репозиторий на нужном теге: ```bash git clone --branch v1.20260926.0 https://github.com/ValeryVerkhoturov/wb-api-client-1c.git ``` ```bsl #Использовать "путь/к/wb-api-client-1c" ``` ## Где лежит исходник Как и PHP, OneScript живёт в отдельном репозитории — [`ValeryVerkhoturov/wb-api-client-1c`](https://github.com/ValeryVerkhoturov/wb-api-client-1c), подключённом к моно-репозиторию как git-submodule по пути `clients/onescript`. Так пользователи 1С могут склонировать только клиент, не утаскивая весь пайплайн, и поставить его из тега. Ежедневный CI-прогон генерирует все восемь языков в одном месте, затем коммитит и тегает оба соседних репозитория одним и тем же тегом `v`. ## Как разделены категории В OneScript нет пространств имён: имя класса глобально. Поэтому категории разделены **именами**, а не путями импорта: - **Классы API** названы по категории — одна категория, один класс: `GeneralApi`, `ItemsApi`, `OrdersFbsApi`, `FinancesApi`. Имена уникальны во всех 13 спецификациях, поэтому префикс им не нужен. - **Классы моделей** несут категорию префиксом: `ItemsResponse4XX`, `OrdersFbsResponse4XX`. Без этого они бы столкнулись — одно только имя `Response4XX` встречается в 12 спецификациях из 13. Файлы разложены по каталогам `src/Классы/<Категория>/` и `src/Модели/<Категория>/`, но каталоги — только для чтения человеком: классы резолвит `lib.config`. Один `#Использовать` подключает сразу все категории. ## Авторизация ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен("<ваш JWT WB>"); Клиент = Новый GeneralApi(Настройки); Ответ = Клиент.GetPing(); Сообщить(Ответ.КодСостояния); Сообщить(Ответ.Данные["Status"]); ``` `УстановитьТокен` принимает и строку, и готовую `СекретнаяСтрока`. Токен не раскрывается при выводе: у объекта нет экспортируемого свойства со значением, а `Строка(Объект)` в OneScript возвращает только имя класса. ```bsl Сообщить(Настройки.Токен()); // СекретнаяСтрока Сообщить(Настройки.Токен().Раскрыть()); // eyJhbGciOi... (по запросу) ``` ## Форма методов Обязательные параметры передаются позиционно, необязательные — одной коллекцией последним аргументом: ```bsl Клиент = Новый ItemsApi(Настройки); // Только тело запроса Ответ = Клиент.PostV2CardsDeleteTrash(Тело); // Тело + необязательные параметры строки запроса Ответ = Клиент.PostV2CardsErrorList(Тело, Новый Структура("locale", "ru")); ``` Ключи в `ДопПараметры` совпадают с именами из спецификации. Если имя не является допустимым идентификатором `Структуры` (например, содержит дефис — как заголовок `X-Trace`), передайте `Соответствие`: ```bsl ДопПараметры = Новый Соответствие; ДопПараметры.Вставить("X-Trace", "trace-1"); ``` ## Ответ Каждый метод возвращает `ОтветAPI`: | Свойство / метод | Тип | Описание | | ---------------- | ------------ | ---------------------------------- | | `КодСостояния` | Число | код состояния HTTP | | `Данные` | Произвольный | тело, разобранное из JSON | | `Тело` | Строка | тело ответа как есть | | `Заголовки` | Соответствие | заголовки ответа | | `Успешно()` | Булево | код 2xx | | `ЭтоОшибка()` | Булево | код 4xx или 5xx | | `Заголовок(Имя)` | Строка | поиск заголовка без учёта регистра | Если тело не JSON (`text/plain`, `application/zip`), `Данные` будет `Неопределено`, а содержимое останется в `Тело`. ## Обработка ошибок По умолчанию ответы 4xx и 5xx приводят к исключению с текстом вида `HTTP 404 — GET /api/v3/orders: <тело>`. `ВызватьИсключение` в OneScript не сохраняет объект для перехватчика — `ОписаниеОшибки()` вернул бы только имя класса, — поэтому отдельного класса исключения здесь нет: вся информация в тексте. Чтобы разобрать ошибку по полям, отключите выброс: ```bsl Настройки.ВыбрасыватьИсключениеПриОшибке = Ложь; Ответ = Клиент.GetPing(); Если Ответ.ЭтоОшибка() Тогда Сообщить(Ответ.КодСостояния); Сообщить(Ответ.Данные["detail"]); КонецЕсли; ``` ## Адрес сервиса У каждой категории WB свой хост, и он зашит в операцию. `БазовыйURL` по умолчанию пустой — это значит «брать адрес из спецификации». Заполните свойство, чтобы направить **все** запросы на другой хост, например на песочницу или тестовый стенд: ```bsl Настройки.БазовыйURL = "https://marketplace-api-sandbox.wildberries.ru"; ``` ## User-Agent и заголовки Каждый запрос отправляет `ValeryVerkhoturov/wb-api-client/onescript`. Заменить или дополнить: ```bsl Настройки.ПользовательскийАгент = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/onescript"; Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Id", "…"); ``` > **Заголовки только ASCII** > > Значения заголовков должны состоять из символов ASCII — это ограничение HTTP-стека .NET, на котором работает OneScript. Кириллица в параметрах строки запроса, сегментах пути и теле запроса работает: они кодируются отдельно. ## Таймаут и прокси ```bsl Настройки.Таймаут = 120; // секунды Настройки.Прокси = Новый ИнтернетПрокси(Истина); ``` ## Модели Модели — классы со свойствами и парой методов преобразования. Незаполненные свойства в тело запроса не попадают, поэтому там не окажется `null` вместо отсутствующего поля. ```bsl Товар = Новый ItemsItem(); Товар.nmID = 123; Товар.price = 1000; Данные = Товар.Данные(); // Соответствие с именами из спецификации // Обратно из ответа Восстановленный = Новый ItemsItem(Ответ.Данные); ``` Тело запроса можно передавать и как модель, и как обычное `Соответствие` / `Структуру` — транспорт разложит и то, и другое. ## Чем сгенерировано В openapi-generator нет генератора OneScript, поэтому он написан отдельно: [`ValeryVerkhoturov/onescript-openapi-generator`](https://github.com/ValeryVerkhoturov/onescript-openapi-generator) — плагин, наследующий `DefaultCodegen` и подключаемый рядом с `openapi-generator-cli.jar` через `META-INF/services`. В основном репозитории он закреплён по коммиту, как и версия образа openapi-generator для остальных языков. ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [README соседнего репозитория](https://github.com/ValeryVerkhoturov/wb-api-client-1c#readme)

wb-api-client mascot

--- title: "C#" description: "C#-клиент Wildberries Seller API: пакет ValeryVerkhoturov.WbApiClient с NuGet, 13 категорий API, маскирование токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/csharp --- # C# Пакет: [`ValeryVerkhoturov.WbApiClient`](https://www.nuget.org/packages/ValeryVerkhoturov.WbApiClient) на NuGet. - **Целевая платформа:** .NET 8.0 - **HTTP:** [RestSharp](https://restsharp.dev/) - **Пространство имён:** `ValeryVerkhoturov.WbApiClient.<Категория>` - **Обёртка секретов:** `SecretString` в каждом пространстве имён ## Установка ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` Или в `.csproj`: ```xml ``` ## Форма пространств имён Корень — `ValeryVerkhoturov.WbApiClient`, каждая категория WB это PascalCase-подпространство. Внутри него генератор раскладывает код по трём частям: ```csharp using ValeryVerkhoturov.WbApiClient.Items.Api; // классы Api using ValeryVerkhoturov.WbApiClient.Items.Client; // Configuration, SecretString, ApiException using ValeryVerkhoturov.WbApiClient.Items.Model; // модели запросов и ответов ``` Категории изолированы пространствами имён, поэтому одноимённые модели из разных разделов не конфликтуют: `Items.Model.Error` и `Finances.Model.Error` сосуществуют. ## Под-пространства `General`, `Items`, `OrdersFbs`, `OrdersDbw`, `Dbs`, `InStorePickup`, `OrdersFbw`, `Promotion`, `Communications`, `Rates`, `Analytics`, `Reports`, `Finances`. ## Авторизация ```csharp using ValeryVerkhoturov.WbApiClient.General.Api; using ValeryVerkhoturov.WbApiClient.General.Client; var config = new Configuration(); config.SetAccessToken("<ваш JWT WB>"); var api = new GeneralApi(config); var ping = api.GetPing(); Console.WriteLine(ping.Status); ``` `SetAccessToken` принимает обычную строку и хранит её внутри как `SecretString`. Геттера для токена у `Configuration` нет специально: сырое значение читают только сгенерированные классы Api — через `IReadableConfiguration`. Токен не раскрывается при выводе: `ToString()` у `SecretString` всегда возвращает ``, а через него проходят и интерполяция строк, и `Console.WriteLine`, и большинство логгеров. ```csharp var s = new SecretString("eyJhbGciOi..."); Console.WriteLine($"{s}"); // Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (по запросу) ``` ## Асинхронные вызовы У каждого метода есть асинхронный вариант с суффиксом `Async`, принимающий `CancellationToken`: ```csharp var ping = await api.GetPingAsync(cancellationToken: token); ``` ## Настройки клиента ```csharp var config = new Configuration { Timeout = TimeSpan.FromSeconds(60), // по умолчанию 100 секунд UserAgent = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/csharp", }; config.SetAccessToken(token); config.DefaultHeaders.Add("X-Request-Id", requestId); ``` Каждый запрос по умолчанию отправляет `ValeryVerkhoturov/wb-api-client/csharp`. ### Адрес сервиса У каждой категории WB свой хост, и он подставлен в клиент из спецификации. Чтобы направить запросы на другой хост — например на песочницу — задайте `BasePath`: ```csharp config.BasePath = "https://marketplace-api-sandbox.wildberries.ru"; ``` ## Обработка ошибок Ответы 4xx и 5xx приводят к `ApiException`: ```csharp using ValeryVerkhoturov.WbApiClient.OrdersFbs.Api; using ValeryVerkhoturov.WbApiClient.OrdersFbs.Client; var fbsConfig = new Configuration(); fbsConfig.SetAccessToken("<ваш JWT WB>"); var fbs = new OrdersFbsApi(fbsConfig); try { var orders = fbs.GetV3Orders(limit: 10, next: 0); } catch (ApiException e) { Console.WriteLine(e.ErrorCode); // код состояния HTTP Console.WriteLine(e.Message); Console.WriteLine(e.ErrorContent); // тело ответа } ``` > **Configuration своя в каждом пространстве имён** > > Генератор выпускает отдельный `Configuration` (и `SecretString`, и > `ApiException`) в каждой категории, поэтому `OrdersFbs.Client.Configuration` — > это не `General.Client.Configuration`. Один экземпляр на все категории > переиспользовать не получится: каждый класс Api принимает `Configuration` > своего пространства имён. Токен при этом один и тот же. ## Внедрение зависимостей Клиенты Api — обычные классы с конструктором от `Configuration`, поэтому регистрируются как есть: ```csharp services.AddSingleton(_ => { var config = new Configuration(); config.SetAccessToken(builder.Configuration["Wb:Token"]); return config; }); services.AddTransient(); ``` Отдельного пакета для DI не требуется: генератор использует библиотеку RestSharp, а не `IHttpClientFactory`. ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [Пакет на NuGet](https://www.nuget.org/packages/ValeryVerkhoturov.WbApiClient)

wb-api-client mascot

--- title: "Rust" description: "Rust-клиент Wildberries Seller API: крейт wb-api-client на crates.io, 13 категорий API, маскирование bearer-токена." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/languages/rust --- # Rust Крейт: [`wb-api-client`](https://crates.io/crates/wb-api-client) на crates.io — исходники живут в [отдельном репозитории](https://github.com/ValeryVerkhoturov/wb-api-client-rust), подключённом к основному как submodule `clients/rust`. - **Rust:** edition 2021 (пайплайн публикует с toolchain 1.98) - **HTTP:** `reqwest` с `rustls-tls` — системный OpenSSL не нужен - **Обёртка секретов:** домашний `SecretString` в каждом модуле ## Установка ```bash cargo add wb-api-client ``` Зафиксировать конкретную версию по тегу git-зависимостью: ```toml wb-api-client = { git = "https://github.com/ValeryVerkhoturov/wb-api-client-rust", tag = "v1.20261022.0" } ``` ## Почему крейт живёт в отдельном репозитории crates.io связывает крейт с выделенным репозиторием, а git-зависимости резолвятся от корня репозитория — поэтому клиент лежит в [wb-api-client-rust](https://github.com/ValeryVerkhoturov/wb-api-client-rust) с `Cargo.toml` в корне. Релизные теги (`vX.Y.Z`) пушатся в этот репозиторий тем же пайплайном, что и в основной. ## Форма импортов Один крейт, 13 модулей-категорий под `wb_api_client::`, у каждого — под-модули `apis` и `models`: ```rust use wb_api_client::items::apis::{configuration::Configuration, items_api}; ``` Методы эндпоинтов — свободные `async fn` внутри `<категория>_api` и принимают `&Configuration`, поэтому нужен async-рантайм (в примерах — `tokio`). ## Под-модули `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Функции и модели на модуль перечислены в per-release README: [wb-api-client-rust/README.md](https://github.com/ValeryVerkhoturov/wb-api-client-rust#readme). ## Авторизация ```rust use wb_api_client::items::apis::{configuration::Configuration, items_api}; #[tokio::main] async fn main() { let mut cfg = Configuration::new(); cfg.set_access_token("<ваш JWT WB>"); // оборачивается в SecretString let result = items_api::some_endpoint(&cfg).await; println!("{:#?}", result); } ``` `set_access_token` — единственный путь: токен хранится в домашнем `SecretString`, у которого и `Debug`, и `Display` печатают `` — случайно слить его в лог через `println!("{cfg:?}")` не получится. Получить сырое значение обратно — только явно: ```rust use wb_api_client::items::apis::configuration::SecretString; let s = SecretString::new("<ваш JWT WB>"); println!("{}", s.expose_secret()); ``` ## Свой HTTP-клиент Подставьте свой `reqwest::Client` — для прокси, кастомных таймаутов или заголовков по умолчанию: ```rust let mut cfg = Configuration::new(); cfg.client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(60)) .build() .unwrap(); ``` ## Тестирование Переопределите базовый URL при создании Configuration: ```rust let mut cfg = Configuration::new(); cfg.base_path = "http://localhost:8080".to_owned(); ``` ## См. также - [Аутентификация](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/authentication) - [Обработка ошибок](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/error-handling) - [Полный справочник по модулям на GitHub](https://github.com/ValeryVerkhoturov/wb-api-client-rust#readme)

wb-api-client mascot

--- title: "Версионирование" description: "Все восемь языковых пакетов делят одну версионную строку на релиз: 1.YYYYMMDD.N." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/versioning --- # Версионирование Все восемь языковых пакетов делят одну версионную строку на релиз: **`1.YYYYMMDD.N`**. ## Формат | Часть | Значение | Почему | | ------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MAJOR` | `1` — зафиксирован | Позволяет пути Go-модуля обходиться без суффикса `/vN`, который требовался бы при `MAJOR ≥ 2`. Любое действительно ломающее изменение пайплайна отражается мажором _репозитория кода_, а не новым путём модуля. | | `MINOR` | `YYYYMMDD` (UTC-дата релиза) | Монотонно растёт по дням — `20260919 < 20260920 < 20261001`. Сортируется как целое по правилам semver. | | `PATCH` | `N` — счётчик за день | `0` для первого релиза за день, далее `1`, `2`, … (редко). | Пример: `1.20260921.0` — первый релиз 21 сентября 2026 (UTC). ## Как считается номер Прямо в workflow daily-check: ```bash today="$(date -u +%Y%m%d)" n="$(git tag --list "v1.${today}.*" | wc -l | tr -d ' ')" version="1.${today}.${n}" ``` Полученная строка вставляется в каждый языковой манифест — `pyproject.toml`, `package.json`, тег `go.mod`, `pom.xml`, `composer.json`, `Cargo.toml` — как есть. Никакой язык не видит переведённой формы. ## Что резолвится как «последняя» Все релизы — **стабильные** (не prerelease), поэтому `@latest` / `LATEST` / `^1.0.0` берут максимальную опубликованную: **Python** ```bash pip install valeryverkhoturov-wb-api-client ``` **TypeScript** ```bash npm install @valeryverkhoturov/wb-api-client ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` **Java** ```xml [1.0.0,) ``` **PHP** ```bash composer require valeryverkhoturov/wb-api-client ``` **OneScript** ```bash opm install wb-api-client ``` **C#** ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` **Rust** ```bash cargo add wb-api-client ``` ## Фиксация версии Фиксируйте точный `1.YYYYMMDD.N`, когда важна воспроизводимость (CI, production): **Python** ```txt valeryverkhoturov-wb-api-client==1.20260926.0 ``` **TypeScript** ```json "@valeryverkhoturov/wb-api-client": "1.20260926.0" ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@v1.20260926.0 ``` **Java** ```xml 1.20260926.0 ``` **PHP** ```json "valeryverkhoturov/wb-api-client": "1.20260926.0" ``` **OneScript** ```bsl .ЗависитОт("wb-api-client", "1.20260926.0") ``` **C#** ```xml ``` **Rust** ```toml wb-api-client = "=1.20260926.0" ``` ## Каденция релизов Новые версии выпускаются только когда апстрим-спецификации реально меняются. Технически: `daily-check.yml` запускается в 06:15 UTC, сверяет `swaggers/checksums.txt` с предыдущим прогоном и перегенерирует + тегает только если что-то изменилось. Спокойная неделя = ноль релизов; выкатка портала WB обычно даёт один релиз следующим утром. Ручной форс-релиз (например, чтобы забрать client-side фикс): `Actions → Daily upstream check → Run workflow` с `force: true`. ## Депрекации WB убирает поля по собственному графику. Когда это случается: 1. Из спецификации пропадает поле. 2. На следующем прогоне daily-check перегенерирует без поля. 3. Публикуется новая версия без поля. 4. Клиенты на предыдущей версии продолжают работать — в их локальных моделях поле есть, просто приходят null'ы. Формального окна депрекации нет — источник истины — контракт апстрима. Если нужно время на миграцию, фиксируйте старую версию. ## Расхождение версий между языками — невозможно по построению Одна и та же строка `1.YYYYMMDD.N` вставляется в манифест каждого языка в одном и том же вызове generate-clients.py, и все языки перегенерируются в каждом релизном прогоне — поэтому получить «Python 1.20260921.0, а Go 1.20260919.0» из одного апстрим-состояния невозможно. Единственный способ уплыть: сломается публикация одного языка (например, Maven Central отклонит подпись). Тег и исходник уже в GitHub; ручной перезапуск конкретного publish-job для этого языка чинит ситуацию.

wb-api-client logo

--- title: "Архитектура" description: "Один экран о том, что реально лежит в основном репозитории, чтобы про сгенерированные клиенты ничего не казалось магией." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/architecture --- # Архитектура Один экран о том, что реально лежит в [основном репозитории](https://github.com/ValeryVerkhoturov/wb-api-client), чтобы про сгенерированные клиенты ничего не казалось магией. ## Пайплайн ``` download-specs.py → swaggers/*.yaml (сырой апстрим, checksummed) process-specs.py → swaggers/processed/ (13 проходов) generate-clients.py → clients//… (openapi-generator-cli, 8 языков) ├── inject-secrets.py (SecretString-обёртка на каждый язык) ├── {black|prettier|gofmt|spotless|php-cs-fixer|rustfmt} (канонизация форматирования) └── generate-readmes.py (README.md на каждый язык) ↓ коммитится тегом v, один тег покрывает все 8 языков publish.yml → PyPI / npm / Go tag / Maven Central / Packagist / hub.oscript.io / NuGet / crates.io generate-docs.py → wb-api-client-docs (справка по API: страницы операций, сниппеты и структуры данных на 8 языках, RU/EN) ``` Каждый шаг — обычный Python-файл, ничего не скрыто за непрозрачным тулингом. ## Зачем генерировать WB выпускает OpenAPI-спецификации. Написать 13 клиентов × 8 языков вручную — уплывут за неделю. Плюс генерации из спецификации: каждое поле, каждый enum, каждая обёртка ответа — ровно то, что задокументировал WB. И повторная синхронизация стоит нуля усилий после сборки пайплайна. Минус — выхлоп генератора остаётся выхлопом генератора: местами уродливым, местами со следами кривостей спецификации. Именно поэтому есть слой пост-обработки. ## process-specs.py — 13 проходов 1. **`inject_bearer_auth`** — удаляет схему `HeaderApiKey` (дублирует реальный bearer JWT) и ставит одну `BearerAuth`. Каждый сгенерированный клиент получает ровно один путь авторизации. 2. **`fix_untyped_arrays`** — добавляет `items: {type: string}` массивам без указания внутреннего типа (иначе Go / TS сгенерируют `[]any`). 3. **`widen_integer_formats`** — ставит `format: int64` целым полям без формата и с форматами `uint32`/`uint64`/Unix-timestamp: WB использует их для ID и миллисекундных меток времени, которые переполняют знаковый 32-битный int. Явные `int32` и коды валют не трогаются. 4. **`inline_top_level_arrays`** — инлайнит все `$ref` на компоненты с `type: array` и схлопывает обёртки `allOf: [array, description-only]` (openapi-generator плохо работает с массивом на корне). Гоняется до неподвижной точки, потому что заинлайненные поддеревья могут содержать другие ссылки. 5. **`rename_digit_prefixed_schemas`** — `409SupplyDeliverError` → `Http409SupplyDeliverError` (иначе невалидный Go-идентификатор). 6. **`name_inline_response_schemas`** — поднимает анонимные inline-тела 4xx/5xx в схемы `Response` (снова ради валидных Go-идентификаторов). 7. **`drop_response_required`** — WB помечает `required` почти каждое поле ответа, но живой API опускает поля когда хочет, и тогда каждый язык падает на десериализации. Проход удаляет `required` у всех схем, достижимых из ответов; у тел запросов `required` сохраняется, так что валидация ввода не страдает. 8. **`sanitize_non_ascii_enums`** — добавляет `x-enum-varnames` для русских значений вроде `"Склад WB"` (иначе — пустые идентификаторы в JS/Go/Java). 9. **`rename_tags_to_module`** — теги WB русские (`Финансовые отчёты`), openapi-generator зачищает их до пустого имени класса, и все языки получают `Api`/`DefaultApi`. Проход переписывает теги каждой операции на имя модуля (`Finances`, `Reports`, …), поэтому у модуля один нормально названный класс (`FinancesApi`, …), а исходные теги сохраняются в `x-original-tags` для генератора документации. 10. **`drop_date_formats`** — WB возвращает `""` для неустановленных дат (`fixTariffDateFrom`, `utdUcdDate`), что не парсится ни одним строгим date-парсером. Проход снимает `format: date` / `format: date-time` со строковых схем, и клиенты типизируют их как обычные строки. 11. **`htmlize_descriptions_to_markdown`** — каждое поле `description` прогоняется через `markdownify`, чтобы HTML в докстрингах рендерился как Markdown. 12. **`absolutize_description_links`** — к относительным ссылкам `[…](https://valeryverkhoturov.github.io/wb-api-client-docs/openapi/…)` добавляется `https://dev.wildberries.ru`, чтобы ссылки в сгенерированных докстрингах открывались. 13. **`add_docs_links`** — добавляет строку `Library doc: ` в `description` каждой операции со ссылкой на страницу операции на этом сайте. Генераторы копируют `description` в докстринги/Javadoc, поэтому каждый метод клиента ссылается на свою страницу документации. `generate-docs.py` вырезает эту строку обратно, так что сам сайт не затрагивается. ## generate-clients.py - Гоняет `openapi-generator-cli` (закреплённый Docker-образ) для каждой спецификации и каждого языка во временные каталоги. - Склеивает пер-спецификационные SDK в единое дерево на язык — Python-пакеты под `wb_api_client.`, TypeScript subpath-экспорты, Go под-пакеты, Java под-пакеты, PHP под-пространства, OneScript — по префиксу имени класса (пространств имён в языке нет), C# — пространства имён, Rust — модули одного крейта (`wb_api_client::`). - Запускает `inject-secrets.py`, чтобы патчить каждый сгенерированный `Configuration` / `ApiClient` соответствующей языковой обёрткой секрета. - Запускает закреплённый форматтер: `black`, `prettier`, `gofmt`, spotless (google-java-format), `php-cs-fixer`, `dotnet format`, `cargo fmt` (rustfmt). Всё внутри Docker — без языковых рантаймов на хосте. - Подставляет `__VERSION__` в топ-уровневые манифесты (`pyproject.toml`, `package.json`, `go.mod`, `pom.xml`, `composer.json`, `Cargo.toml`). - Запускает `generate-readmes.py`, чтобы выпустить README на каждый язык. Итог: `clients/{python,typescript,go,java,php,onescript,csharp,rust}/` — восемь пакетов, готовых к публикации. ## Детерминизм Ежедневная задача постоянно бы «дрожала», не будь генерация детерминированной. Что для этого сделано: - Docker-тег `openapi-generator-cli` закреплён. - В конфиге openapi-generator для каждого языка выставлено `hideGenerationTimestamp: true`. - Все форматтеры закреплены на конкретных версиях. - PR-check-workflow перегенерирует всё с нуля и падает, если `git diff swaggers/ clients/` непустой. Это ловит любое изменение, из-за которого следующие прогоны разъедутся. ## Публикация `daily-check.yml` считает версию, регенерирует, коммитит, тегает. Затем шлёт 8 событий `workflow_dispatch` в `publish.yml` — по одному на язык — с тегом в качестве входа `ref`. Каждая языковая ветка `publish.yml`: - Python — OIDC → PyPI trusted publishing (`pypa/gh-action-pypi-publish`). - TypeScript — OIDC → npm trusted publishing (`npm publish --provenance`). - Go — тег пушится в [`wb-api-client-go`](https://github.com/ValeryVerkhoturov/wb-api-client-go) (см. ниже), сама джоба — no-op: `proxy.golang.org` тянет прямо из тега. - Java — подписанный GPG-деплой в Maven Central через `central-publishing-maven-plugin`. Maven закреплён на 3.9.x (`stCarolas/setup-maven`): плагин собирает bundle через Maven-2 stager, и Resolver из Maven 3.10 роняет туда служебный `maven-metadata-local.xml`, из-за чего Central отклоняет bundle («Bundle has content that does NOT have a .pom file»). - PHP — пинг API `packagist.org/api/update-package`. - OneScript — `opm build` и `opm push` в hub.oscript.io. - C# — `dotnet pack` и `dotnet nuget push` в NuGet. - Rust — `cargo publish` в crates.io (GitHub-окружение `crates-io`, toolchain закреплён на 1.98). Отдельно daily-check перегенерирует справку по API (`generate-docs.py` — страницы операций, сниппеты вызова и структуры входящих/исходящих данных на каждом языке, ссылки на спецификацию WB) и пушит её в `wb-api-client-docs`, откуда сайт деплоится на GitHub Pages. Reusable-воркфлоу (`workflow_call`) был бы аккуратнее, но PyPI и npm trusted publishing их не поддерживают: и OIDC-claim `job_workflow_ref` (callee), и Sigstore-attestation `workflow_ref` (caller) должны указывать на один и тот же файл, что с `workflow_call` невозможно. Поэтому — `workflow_dispatch`. ## Соседние репозитории Четыре языка живут в своих репозиториях, подключённых сюда как git-submodule: - [`ValeryVerkhoturov/wb-api-client-php`](https://github.com/ValeryVerkhoturov/wb-api-client-php) → `clients/php`. Причина: Packagist требует `composer.json` в КОРНЕ обходимого репозитория. - [`ValeryVerkhoturov/wb-api-client-1c`](https://github.com/ValeryVerkhoturov/wb-api-client-1c) → `clients/onescript`. Так пользователи 1С могут склонировать только клиент и поставить его из тега. - [`ValeryVerkhoturov/wb-api-client-go`](https://github.com/ValeryVerkhoturov/wb-api-client-go) → `clients/go`. Причина: Go версионирует модули тегами репозитория, поэтому модуль обязан лежать в корне репо (`go get github.com/ValeryVerkhoturov/wb-api-client-go`) — модуль в подкаталоге потребовал бы тегов вида `clients/go/v…` и тянул бы весь монорепозиторий. - [`ValeryVerkhoturov/wb-api-client-rust`](https://github.com/ValeryVerkhoturov/wb-api-client-rust) → `clients/rust`. Причина: crates.io связывает крейт с выделенным репозиторием, а git-зависимости (`wb-api-client = { git = "…", tag = "vX.Y.Z" }`) резолвятся от корня репозитория. Ежедневный прогон коммитит и тегает все четыре соседних репозитория ДО основного — иначе записанные указатели сабмодулей ссылались бы на коммиты, которых на их удалённых ещё нет, и свежий `git clone --recurse-submodules` падал бы. Пятый сосед — [`ValeryVerkhoturov/wb-api-client-docs`](https://github.com/ValeryVerkhoturov/wb-api-client-docs), этот сайт. Он не сабмодуль: daily-check клонирует его, запускает `generate-docs.py` и пушит результат обратно. ## Генератор OneScript В openapi-generator нет генератора OneScript, поэтому он вынесен в отдельный репозиторий — [`ValeryVerkhoturov/onescript-openapi-generator`](https://github.com/ValeryVerkhoturov/onescript-openapi-generator). Это обычный плагин: класс наследует `DefaultCodegen`, регистрируется через `META-INF/services` и подключается рядом с `openapi-generator-cli.jar`, так что `-g onescript` работает как встроенный генератор. В основном репозитории он закреплён по коммиту — это аналог закреплённого тега Docker-образа для остальных языков. ## Что НЕ генерируется Ручные части основного репозитория: - `scripts/` — сам пайплайн, разложенный по доменным контекстам в `scripts/pipeline/`: `core/` (общее ядро: пути репозитория, именование спецификаций/модулей/языков, YAML IO, запуск внешних инструментов (subprocess, docker, git, python, macOS codesign)), `specs/` (download, postprocess), `generation/` (generate, secrets), `onescript/` (toolchain, manifests, verify), `docs/` (reference, readmes). Плоские `scripts/*.py` — тонкие CLI-обёртки над этими модулями, названные по производимому результату: `download-specs`, `process-specs`, `generate-clients`, `generate-readmes`, `generate-docs`, `generate-onescript-manifests`, `inject-secrets`, `verify-onescript`. Домашнее правило: ни `#`-комментариев, ни докстрингов под `scripts/` — проверяется в `make scripts-check` и в PR-check. - `generator-configs/*.yaml` — по одному конфигу openapi-generator на язык. - `templates/*` — топ-уровневые манифесты с плейсхолдерами `__VERSION__`. - `.github/workflows/*` — daily-check, per-language publish, PR drift check. - `Makefile`, `README.md`, `CLAUDE.md`. Всё под `clients/` — сгенерированный выхлоп, руками не редактируется. Перегенерируйте. ## Полное описание пайплайна Файл [`CLAUDE.md`](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/CLAUDE.md) в основном репозитории — рабочая, машиночитаемая версия того же содержимого, поддерживается в актуальном состоянии.

wb-api-client mascot

--- title: "Участие в разработке" description: "Баги, странности спецификации, кривости генератора — заводите issue в основном репозитории кода: ValeryVerkhoturov/wb-api-client/issues." lang: ru-RU source: https://valeryverkhoturov.github.io/wb-api-client-docs/guides/contributing --- # Участие в разработке ## Сообщения об ошибках Баги, странности спецификации, кривости генератора — заводите issue в основном репозитории кода: [`ValeryVerkhoturov/wb-api-client/issues`](https://github.com/ValeryVerkhoturov/wb-api-client/issues). Что приложить: - Какой языковой клиент (Python / TypeScript / Go / Java / PHP / OneScript / C# / Rust) - Какой под-модуль (`items`, `orders_fbs`, `analytics`, …) - Версию из манифеста (`1.YYYYMMDD.N`) - Минимальный воспроизводимый пример — один вызов API, одно исключение, ожидание против факта Проблемы сайта документации (опечатки, недостающее, битые примеры) — в репозиторий документации: [`ValeryVerkhoturov/wb-api-client-docs/issues`](https://github.com/ValeryVerkhoturov/wb-api-client-docs/issues). ## Сгенерированный код руками НЕ редактируется Каждый файл под `clients/` в основном репозитории — сгенерирован. Правка там переписывается на следующем прогоне daily-check — PR-check тоже отметит правку до мерджа. Если нашли ошибку в сгенерированном выходе, чинить нужно в одном из: - **`scripts/pipeline/specs/postprocess.py`** — новый проход, если правка применяется к спецификации до генерации. - **`scripts/pipeline/generation/secrets.py`** — если правка внутри сгенерированного `Configuration` / `ApiClient`. - **`generator-configs/*.yaml`** — если это флаг openapi-generator. - **`templates/{lang}/*`** — если это топ-уровневый манифест. Карта пайплайна — в [справочнике по архитектуре](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/architecture). ## Локальная разработка ```bash git clone --recurse-submodules https://github.com/ValeryVerkhoturov/wb-api-client cd wb-api-client pip install -r scripts/requirements.txt # ruamel.yaml, markdownify, requests, black, flask ./scripts/download-specs.py # тянет апстрим (локально может отдать 498) python scripts/process-specs.py # -> swaggers/processed/ ./scripts/generate-clients.py 1.20260920.0 # -> clients/{python,typescript,go,java,php,onescript,csharp,rust} make verify # сборка + проверка форматирования на каждом языке ``` `generate-clients.py` всегда регенерирует все восемь языков. Для одноязычной итерации при отладке комментируйте остальные вызовы `queue_generation`, а не добавляйте флаг. Переменная окружения `OPENAPI_GENERATOR_THREADS` управляет параллелизмом генерации (по умолчанию 2). Форматирование гоняется в Docker, поэтому языковые рантаймы на хосте не нужны. `make verify-` и `make {black,prettier,gofmt,spotless,php-cs-fixer,dotnet-format}` — отдельные цели. У OneScript форматтера нет, поэтому `make verify-onescript` вместо него компилирует каждый модуль через `oscript -check` и загружает пакет целиком. `make help` перечисляет всё. Сам пайплайн лежит в `scripts/pipeline/` по доменным контекстам (см. [архитектуру](https://valeryverkhoturov.github.io/wb-api-client-docs/guides/architecture)); плоские `scripts/*.py` — лишь CLI-обёртки. Правки в `scripts/` дополнительно проверяются `make scripts-check` (smoke-проверки общего ядра, запрет `#`-комментариев и докстрингов), а black гоняется и по `scripts/`, не только по `clients/python`. ## Flow с PR 1. Форкните основной репозиторий. 2. Внесите изменение в `scripts/`, `templates/`, `generator-configs/` или воркфлоу. 3. Перегенерируйте локально, чтобы `clients/` совпало. 4. Закоммитьте `swaggers/`, `clients/` и свои правки исходников одним коммитом. 5. Откройте PR. PR-check-workflow перегенерирует всё с нуля и упадёт, если `git diff` непустой относительно вашего коммита — это ловит недетерминированность. ## Участие в документации Этот сайт живёт в [`ValeryVerkhoturov/wb-api-client-docs`](https://github.com/ValeryVerkhoturov/wb-api-client-docs). ```bash git clone https://github.com/ValeryVerkhoturov/wb-api-client-docs cd wb-api-client-docs npm install npm run docs:dev # → http://localhost:5173/wb-api-client-docs/ ``` При пуше в `main` workflow деплоя сам собирает и публикует на GitHub Pages. ## Лицензия Оба репозитория — Apache 2.0. Ваши правки считаются переданными под той же лицензией.

wb-api-client mascot