Подключение магазина к ByMe Seller API

Практический маршрут для ERP, PIM, WMS и других систем продавца: машинный доступ, каталог, остатки и статусы доставки.

Перед началом. Для обмена данными нужны активная организация продавца, подключённая интеграция и доступ для выбранной среды. Известные ограничения перечислены ниже. Значения в угловых скобках — заполнители, не реальные идентификаторы или ключи.

Как проверять запросы. Выполняйте примеры из сервера своей интеграции. API Reference служит справочником: сессия кабинета продавца не переносится в документацию. Для ERP нужен отдельный машинный токен с DPoP или mTLS; приватные ключи и токены остаются в вашей системе.

1. Подготовьте организацию

Действие администратора продавца

Нужны организация продавца со статусом active, право управлять её интеграциями, ID магазина, актуальная категория товара и ID склада. Проверьте статус организации через getSellerOrganization. Пока организация проходит регистрацию (onboarding), можно подготовить подключение и публичный ключ; обмен данными начнётся после завершения проверки магазина. Создание интеграции и учётных данных выполняет уполномоченный продавец со своей seller-сессией; выпуск учётных данных требует подтверждения владельца ключом доступа. Машинный токен принадлежит одной организации; он не заменяет административную сессию.

Базовый адрес запросов продавца — https://api.byme.my/seller/v1. Для каждого запроса передавайте X-Request-Id и W3C traceparent. Для записи используйте новый Idempotency-Key на новое намерение и тот же ключ при точном повторе тела. Ответы об ошибках разбирайте как application/problem+json, сохраняя request_id.

2. Создайте и активируйте интеграцию

Действие администратора продавца

Создайте интеграцию операцией createSellerIntegration (POST /organizations/{organization_id}/integrations). Запрашивайте только нужные возможности. callback_origin должен быть HTTPS origin под вашим контролем. Проверьте полученный integration_id и состояние через getSellerIntegration, затем вызовите activateSellerIntegration с причиной. Активация включает выдачу машинных токенов только после подтверждения issuer.

POST /seller/v1/organizations/<organization_id>/integrations
Authorization: Bearer <seller-role-access-token>
Idempotency-Key: <unique-key>
Content-Type: application/json

{
  "type": "erp",
  "environment": "production",
  "callback_origin": "https://merchant.example",
  "capabilities": ["catalog_read", "catalog_write", "inventory_write", "fulfillment_read", "fulfillment_write"]
}

POST /seller/v1/organizations/<organization_id>/integrations/<integration_id>/activation
Authorization: Bearer <seller-role-access-token>
Idempotency-Key: <another-unique-key>
Content-Type: application/json

{"reason":"Merchant integration ready"}

Получение интеграции возвращает сильный ETag. Если меняете её параметры через PATCH, отправляйте именно этот ETag в If-Match; при 409 или 412 сначала перечитайте состояние.

3. Зарегистрируйте публичный ключ

Действие администратора продавца и интегратора

Сгенерируйте отдельную пару P-256 в вашей системе. Приватный ключ никогда не передавайте ByMe. Операция createSellerIntegrationCredential принимает только публичные координаты JWK для DPoP. resource_scope.organization_id должен совпадать с путём и интеграцией. Сейчас машинный доступ активируется только для всей организации: массивы store_ids, warehouse_ids, market_codes должны быть пустыми, а field_allowlist — ["/"]. Подмножество возможностей учётных данных не может расширять возможности интеграции.

POST /seller/v1/organizations/<organization_id>/integrations/<integration_id>/credentials
Authorization: Bearer <seller-role-access-token>
Idempotency-Key: <unique-key>
Content-Type: application/json

{
  "environment": "production",
  "sender_constraint": "dpop",
  "expires_at": "<future-rfc3339-time>",
  "resource_scope": {
    "organization_id": "<organization_id>",
    "store_ids": [], "warehouse_ids": [], "market_codes": [],
    "capabilities": ["catalog_read", "catalog_write", "inventory_write", "fulfillment_read", "fulfillment_write"],
    "field_allowlist": ["/"]
  },
  "public_key_jwk": {"kty":"EC", "crv":"P-256", "x":"<base64url-x>", "y":"<base64url-y>"}
}

Для mTLS вместо DPoP отправьте публичный сертификат в client_certificate_pem и используйте https://identity-mtls.byme.my/oauth2/token для токена и отдельные catalog-mtls.byme.my и fulfillment-mtls.byme.my для соответствующих ресурсов. Клиентский приватный ключ остаётся у интегратора. Не смешивайте оба режима в одних учётных данных.

4. Получите короткий машинный токен

Действие интегратора

Для DPoP отправьте client_credentials на POST https://api.byme.my/oauth2/token с зарегистрированным client_id, подписанным ES256 private_key_jwt assertion и свежим DPoP proof для метода POST и именно этого публичного URL. В assertion iss и sub равны credential ID, aud — полному URL токена, jti уникален, а iat/exp ограничены пятью минутами. scope содержит только нужные разрешённые seller scopes. Токен живёт не более 900 секунд, refresh token не выдаётся.

Соответствие возможностей и scopes: catalog_read → seller:read-seller-shop-items, catalog_write → seller:upsert-seller-shop-items, inventory_write → seller:replace-seller-shop-inventory, fulfillment_read → seller:read-seller-shop-fulfillment-events, fulfillment_write → seller:create-seller-shop-fulfillment-events. Перечислите выбранные scopes через пробел, затем URL-кодируйте значение формы.

Ниже приведены примеры для сервера интеграции. Файлы с токенами, assertion и proof создавайте с закрытыми правами; команды сохраняют ответы в файлы и не печатают секретные значения.

# DPoP: assertion и proof уже подписаны вашим сервисом; каждый proof одноразовый.
set -euo pipefail
umask 077
RUN_DIR="$(mktemp -d)"
trap 'rm -rf "$RUN_DIR"' EXIT
TOKEN_RESPONSE_FILE="$RUN_DIR/token-response.json"
ACCESS_TOKEN_FILE="$RUN_DIR/access-token"

printf 'DPoP: %s\n' "$(cat "$DPoP_TOKEN_PROOF_FILE")" > "$RUN_DIR/token.headers"

curl --fail-with-body --silent --show-error \
  --request POST 'https://api.byme.my/oauth2/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --header "@$RUN_DIR/token.headers" \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode "client_id=$CREDENTIAL_ID" \
  --data-urlencode "scope=$SELLER_SCOPES" \
  --data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
  --data-urlencode "client_assertion@${CLIENT_ASSERTION_FILE}" \
  --output "$TOKEN_RESPONSE_FILE"
jq -er '.access_token' "$TOKEN_RESPONSE_FILE" > "$ACCESS_TOKEN_FILE"

# Создайте новый proof для этого метода и URL с ath полученного токена.
printf 'Authorization: DPoP %s\nDPoP: %s\n' \
  "$(cat "$ACCESS_TOKEN_FILE")" "$(cat "$DPoP_RESOURCE_PROOF_FILE")" > "$RUN_DIR/resource.headers"
curl --fail-with-body --silent --show-error \
  --request GET 'https://api.byme.my/seller/v1/organizations/<organization_id>/shop-integration/items' \
  --header "@$RUN_DIR/resource.headers" \
  --output "$RUN_DIR/items.json"
# mTLS: certificate binding is proved by the TLS connection.
set -euo pipefail
umask 077
RUN_DIR="$(mktemp -d)"
trap 'rm -rf "$RUN_DIR"' EXIT
TOKEN_RESPONSE_FILE="$RUN_DIR/token-response.json"
ACCESS_TOKEN_FILE="$RUN_DIR/access-token"

curl --fail-with-body --silent --show-error \
  --cert "$CLIENT_CERT_FILE" --key "$CLIENT_KEY_FILE" \
  --request POST 'https://identity-mtls.byme.my/oauth2/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode "client_id=$CREDENTIAL_ID" \
  --data-urlencode "scope=$SELLER_SCOPES" \
  --output "$TOKEN_RESPONSE_FILE"
jq -er '.access_token' "$TOKEN_RESPONSE_FILE" > "$ACCESS_TOKEN_FILE"

# Для товаров используйте catalog-mtls; DPoP и client_assertion здесь не нужны.
printf 'Authorization: Bearer %s\n' "$(cat "$ACCESS_TOKEN_FILE")" > "$RUN_DIR/resource.headers"
curl --fail-with-body --silent --show-error \
  --cert "$CLIENT_CERT_FILE" --key "$CLIENT_KEY_FILE" \
  --request GET 'https://catalog-mtls.byme.my/seller/v1/organizations/<organization_id>/shop-integration/items' \
  --header "@$RUN_DIR/resource.headers" \
  --output "$RUN_DIR/items.json"

Для каждого DPoP-запроса создавайте новый proof с фактическими методом и публичным URL и с ath от access token. Для mTLS используйте тот же клиентский сертификат и ключ на token endpoint и на ресурсном host; не добавляйте заголовок DPoP или форму client_assertion. Повтор одного proof отвергается. Не записывайте токены, assertions и proofs в логи. Правила proof и binding — RFC 9449; формат client assertion — RFC 7523.

5. Синхронизируйте товары

Действие интегратора

upsertSellerShopItems принимает пакет товаров по external_item_id в рамках организации. SKU — бизнес-атрибут, не ключ идемпотентности. Укажите действующую категорию, цену в младших единицах строкой, валюту и фискальный код. Начинайте с draft и проверяйте результат каждого элемента; активный статус входящего товара не обходит модерацию и не делает карточку покупаемой.

PUT /seller/v1/organizations/<organization_id>/shop-integration/items
Authorization: DPoP <access-token>
DPoP: <fresh-resource-proof>
Idempotency-Key: <stable-key-for-this-batch>
Content-Type: application/json

{"items":[{
  "external_item_id":"item-001", "seller_sku":"SKU-001",
  "title":"Пример товара", "category_id":"<approved-category-id>",
  "store_id":"<store-id>", "status":"draft",
  "price_minor":"10000", "currency":"RUB", "tax_code":"vat20"
}]}

Сверьте результат через GET /organizations/{organization_id}/shop-integration/items/{external_item_id} или список с cursor pagination. Публикация listing требует отдельных реальных условий готовности, включая модерацию и данные продавца.

6. Передайте остатки

Действие интегратора

replaceSellerShopInventory устанавливает абсолютное доступное количество для пары товар/склад. Передайте время фактического состояния: более старые события игнорируются. Инкрементальный режим обновляет только указанные пары. Полный snapshot обнуляет отсутствующие пары только на последней странице и только если все строки приняты и expected_item_count совпадает с числом различных пар.

PUT /seller/v1/organizations/<organization_id>/shop-integration/inventory
Authorization: DPoP <access-token>
DPoP: <fresh-resource-proof>
Idempotency-Key: <stable-key-for-this-batch>
Content-Type: application/json

{"mode":"incremental","items":[{
  "external_item_id":"item-001", "warehouse_id":"<warehouse-id>",
  "quantity_available":"0", "occurred_at":"<current-rfc3339-time>"
}]}

Нулевой остаток не скрывает карточку, но делает предложение недоступным к покупке. Точный повтор должен использовать тот же ключ и то же тело; конфликт fingerprint — терминальная ошибка, а не повод послать новую запись без чтения состояния.

Читайте заказы и возвраты в ERP

Для чтения заказов явно включите order_read и fulfillment_read в интеграцию и новые учётные данные. Запрашивайте оба scope: seller:read-seller-shop-orders и seller:read-seller-shop-fulfillment-events. Для возвратов нужны отдельная capability return_read и scope seller:read-seller-shop-returns. Существующие ключи не получают новые права автоматически; общий seller:read эти операции не разрешает.

GET /seller/v1/organizations/<organization_id>/shop-integration/orders?limit=50
GET /seller/v1/organizations/<organization_id>/shop-integration/returns?limit=50

Заказы содержат коммерческий status, информационный payment_state и отдельно фактические сведения о выполнении. confirmed означает подтверждение заказа продавцом. Пока событие выполнения не получено, fulfillment_status и fulfillment_version отсутствуют: не подставляйте paid или версию 1. Возвраты содержат собственный статус процесса возврата и seller_order_id; значения returned в статусах выполнения нет. Контакты покупателя, адреса, суммы и свободные заметки не экспортируются.

Передавайте page.next_cursor без изменения, пока page.has_more истинно. Заказы упорядочены по времени обновления, возвраты — по requested_at. Для повторной сверки возврата используйте его ID или seller_order_id: время создания не меняется при дальнейшем движении возврата. snapshot_at обозначает время наблюдения страницы, а не гарантию свежести всех проекций. Параметр seller_order_id для точечного чтения заказа не совмещается с cursor.

Проверяйте доступность этих новых операций в выбранной среде после выкладки. Для DPoP используется https://api.byme.my/seller/v1. Отдельные mTLS-маршруты заказов и возвратов требуют активации DNS, сертификатов и listener; адреса catalog-mtls.byme.my и fulfillment-mtls.byme.my эти операции не обслуживают.

7. Сообщайте о выполнении оплаченного заказа

Действие интегратора

После получения настоящего оплаченного seller_order_id, подтверждённого платёжной системой, передайте событие collecting через createSellerShopFulfillmentEvents. Это подтверждает выполнение продавцом и не является способом выставить или переопределить платёжный статус: paid и completed выставляет система на основании платёжной истины. Далее используйте только разрешённые переходы для выбранной модели доставки и необходимые поля доказательств. Время occurred_at сохраняется как сообщённое время; сроки и переходы рассчитывает сервер. Статус returned принадлежит процессу возврата и появляется после его авторитетного результата.

POST /seller/v1/organizations/<organization_id>/shop-integration/fulfillment-events
Authorization: DPoP <access-token>
DPoP: <fresh-resource-proof>
Idempotency-Key: <stable-key-for-this-batch>
Content-Type: application/json

{"events":[{
  "event_id":"<unique-event-id>", "seller_order_id":"<paid-seller-order-id>",
  "status":"collecting", "occurred_at":"<current-rfc3339-time>"
}]}

Проверяйте результат через GET /organizations/{organization_id}/shop-integration/fulfillment-events?seller_order_id=.... Повтор event_id с теми же данными не создаёт новую запись; повтор с другим содержимым отклоняется. Отмена оплаченного заказа запускает полный возврат покупателю и не должна использоваться как тестовое событие.

8. Обрабатывайте сбои и жизненный цикл доступа

ОтветДействие
401/403Проверьте срок токена, DPoP proof, организацию, capability и состояние интеграции. Не повторяйте старый proof.
409/412При fingerprint conflict не меняйте тело под прежним ключом; при устаревшем ETag перечитайте ресурс.
429Учитывайте Retry-After; повторяйте ту же операцию с прежним ключом и телом, затем сверяйте авторитетное состояние.
501/503Сверьте доступность операции ниже. Не повторяйте неподдерживаемую возможность в цикле; при временном 503 следуйте Retry-After, если он есть.

При смене ключа используйте credential rotation с ограниченным временем перекрытия. При компрометации отзовите credential или интеграцию. Отзыв интеграции инвалидирует все её машинные токены. Для разбора ошибки передавайте поддержке продавца request_id и время, но не токен, приватный ключ или персональные данные.

Требования KYB

В текущем контракте required_actions ответов getSellerKybStatus, updateSellerKybProfile и createSellerKybSubmission содержит SellerKybRequiredAction: action_id, label, required. Это указания заполнить KYB или фискальный профиль; срок временного auth challenge к ним не применяется. После выполнения перечитайте статус. Изменение схемы одобрено владельцем API 28 сентября 2026; проверка новой версии в production ещё требуется.

Подписка в ответе организации

В текущем контракте business_subscription сохраняет ветку действительной подписки и допускает явное отсутствие данных: {"status":"unavailable","reason_code":"subscription_not_configured","retryable":false}. В этой ветке нет тарифа, периода, лимитов или прав подписки. Сначала проверьте состояние; не подставляйте бесплатный тариф или пробный период. Создание и чтение организации остаются доступны. Изменение одобрено владельцем API 28 сентября 2026; production-проверка новой проекции ещё требуется.

Вариант товара

getSellerProductVariant читает вариант рабочей ревизии PIM по организации и variant_id. Ответ содержит сохранённые sku_identifier_id, seller_sku, типизированные attributes, состояние и ревизию товара. gtin не означает проверку реестром. Предложения читайте отдельно. Неполные старые данные возвращают product_variant_metadata_incomplete.

Правила и цены предложений

  1. С seller-сессией прочитайте GET /organizations/{organization_id}/pricing/production-workspace. Ответ включает default workspace, rules, automation и offer_prices на snapshot_at. Валюта каждой цены сохраняется отдельно.
  2. Создайте черновое правило через POST /organizations/{organization_id}/pricing/production-workspace/rules с target, adjustment, floor (Money), priority и schedule. Передайте Idempotency-Key; точный повтор не создаёт второе правило. predicate удалён из одобренной схемы.
  3. Для проверки и активации используйте стандартные операции pricing workspace и его workspace_id. Черновое правило не меняет действующие цены. Активация повторно проверяет актуальные данные и ограничения.
  4. Для промокода перед активацией получите новый receipt через simulateSellerPromotion для конкретного promotion_id. Проверьте пустой guardrail_rejections и передайте его simulation_digest. Общий workspace digest не является receipt промокода.

Эти исправления проверены локально, включая PostgreSQL. Доступность новой версии в production проверяется отдельно после CI и выкладки. Машинные shop scopes не предоставляют права управления pricing или промо.

Текущие ограничения

Проверенные production-сценарии: upsert и чтение shop items, inventory batch и snapshot, события выполнения оплаченного заказа, DPoP/mTLS выпуск токена, replay и отзыв доступа. Offer contract, eligibility и tax quote также проверены реальными запросами после выкладки. Stock item detail проверен на QA SKU после incremental inventory feed с нулевым остатком: ответ 200 соответствует схеме. Warehouse detail вернул HTTP 200, но последующая проверка выявила несовпадение тела с контрактом; исправление требует новой выкладки. Налоговая политика проверена только на изолированной QA-организации; это не подтверждает ставки для реальных продавцов. Авторизованный проход всех 144 GET-операций и последующие проверки выявили следующие ещё недоступные возможности общего Seller OpenAPI:

ОтветОперацииКак действовать
501Discount allocation.Проверяйте фактическую доступность перед подключением. Не считайте описание в OpenAPI подтверждением запуска.
503Catalog product aggregate (ожидает runtime-проверки), product variant (новая проекция ожидает выкладки), media, brand claim, certificate, generation job, authenticity policy; stock item detail для старых записей без SKU; ads entities; creator applications и catalog; business subscription и invoices; demand forecast; first-party finance.Эти пути пока не подходят для обязательного сценария интеграции. Stock item требует сохранённого SKU: текущая цепочка catalog → inventory проверена, но старые записи без идентификатора SKU возвращают 503. Повторяйте только подтверждённый временный сбой.

Список описывает состояние последней production-проверки, а не бессрочное обещание. Сверяйте доступность нужной операции перед внедрением. Нормативные схемы и точные статусы смотрите в Seller OpenAPI и Core OpenAPI.