Поделиться через


API отправки Microsoft Store для приложения MSI или EXE

Используйте API отправки Microsoft Store для MSI или EXE-приложения для программного запроса и создания отправки приложений MSI или EXE для учетной записи Центра партнеров вашей организации. Этот API полезен, если ваша учетная запись управляет множеством приложений, и вы хотите автоматизировать и оптимизировать процесс отправки для этих ресурсов. Этот API использует Azure Active Directory (Azure AD) для проверки подлинности вызовов из приложения или службы.

Следующие шаги описывают комплексный процесс использования API отправки Microsoft Store:

  1. Убедитесь, что вы выполнили все предварительные требования.
  2. Перед вызовом метода в API отправки Microsoft Store получите маркер доступа Azure AD. После получения маркера у вас есть 60 минут для использования этого маркера в вызовах API отправки Microsoft Store до истечения срока действия маркера. После истечения срока действия маркера можно создать новый маркер.
  3. Вызовите API отправки Microsoft Store для приложения MSI или EXE.

Шаг 1. Выполнение предварительных требований для использования API отправки в Microsoft Store

Прежде чем приступить к написанию кода для вызова API отправки Microsoft Store для приложения MSI или EXE, убедитесь, что выполнены следующие предварительные требования.

  • У вас (или вашей организации) должен иметься каталог Azure AD, а также у вас должен быть доступ уровня глобального администратора к этому каталогу. Если вы уже используете Microsoft 365 или другие бизнес-службы Microsoft, у вас уже есть каталог Azure AD. В противном случае вы можете создать Azure AD в Центре партнеров без дополнительной платы.
  • Необходимо связать приложение Azure AD с учетной записью Центра партнеров и получить идентификатор арендатора, идентификатор и ключ клиента. Эти значения необходимы для получения маркера доступа Azure AD, который будет использоваться в вызовах к API отправки Microsoft Store.
  • Подготовьте приложение для использования с API отправки в Microsoft Store:
    • Если приложение еще не существует в Центре партнеров, необходимо создать приложение, зарезервировав его имя в Центре партнеров. Api отправки Microsoft Store нельзя использовать для создания приложения в Центре партнеров; Вы должны работать в Центре партнеров, чтобы создать его, а затем после этого можно использовать API для доступа к приложению и программно создавать для него отправки.
    • Прежде чем создать отправку для данного приложения с помощью этого API, необходимо сначала создать одну отправку для приложения в Центре партнеров, включая ответ на анкету о возрастных оценках . После этого вы сможете программно создавать новые отправки для этого приложения с помощью API.
    • Если вы создаете или обновляете отправку приложения и необходимо включить новый пакет, подготовьте сведения о пакете.
    • Если вы создаете или обновляете отправку приложения и вам нужно включить снимки экрана или изображения для описания Магазина, подготовьте снимки экрана и изображения приложения.

Порядок связывания приложения Azure AD с учетной записью Центра партнеров

Прежде чем использовать API отправки Microsoft Store для ПРИЛОЖЕНИЯ MSI или EXE, необходимо связать приложение Azure AD с учетной записью Центра партнеров, получить идентификатор клиента и идентификатор клиента для приложения и создать ключ. Приложение Azure AD представляет приложение или службу, из которой требуется вызвать API отправки Microsoft Store. Для получения маркера доступа Azure AD, передаваемого в API, необходимы идентификатор арендатора, идентификатор и ключ клиента.

Примечание.

Эту задачу нужно выполнить только один раз. После получения идентификатора арендатора, идентификатора и ключа клиента их можно повторно использовать в любой момент, когда потребуется создать новый маркер доступа Azure AD.

  1. В Центре партнеров свяжите учетную запись Центра партнеров своей организации с каталогом Azure AD организации.
  2. Затем на странице "Пользователи" в разделе параметров учетной записи Центра партнеров добавьте приложение Azure AD, представляющее приложение или службу, которую вы будете использовать для доступа к отправке для учетной записи Центра партнеров. Убедитесь, что этому приложению назначена роль Менеджер. Если приложение еще не существует в каталоге Azure AD, можно создать новое приложение Azure AD в Центре партнеров.
  3. Вернитесь на страницу Пользователи, щелкните имя приложения Azure AD, чтобы перейти к параметрам приложения, и скопируйте идентификатор арендатора и идентификатор клиента.
  4. Чтобы добавить новый ключ или секрет клиента, см. следующие инструкции или инструкции по регистрации приложения на портале Azure.

Чтобы зарегистрировать приложение, выполните приведенные действия.

  1. Войдите на портал Azure.

  2. Если у вас есть доступ к нескольким арендаторам, в верхнем меню используйте фильтр Directories + subscriptions (Каталоги и подписки) , чтобы выбрать арендатор, в котором следует зарегистрировать приложение.

  3. Найдите и выберите Azure Active Directory.

  4. В разделе "Управление" выберите Регистрация приложений > Выберите приложение.

  5. Выберите сертификаты и секреты > > секретов клиента New client secret.

  6. Добавьте описание секрета клиента.

  7. Выберите срок действия секрета или укажите настраиваемое время существования.

  8. Время существования секрета клиента ограничено двумя годами (24 месяца) или меньше. Для настраиваемого времени существования нельзя задать значение, превышающее 24 месяца.

    Примечание.

    Корпорация Майкрософт рекомендует задать значение срока действия менее 12 месяцев.

  9. Выберите Добавить.

  10. Запишите значение секрета, чтобы затем использовать его в коде клиентского приложения. Это значение секрета больше нигде не отображается после закрытия страницы.

Шаг 2. Получение маркера доступа Azure AD

Перед вызовом любого из методов в API отправки Microsoft Store для MSI или EXE необходимо сначала получить маркер доступа Azure AD, который передается в заголовок авторизации каждого метода в API. После получения маркера доступа у вас будет 60 минут, чтобы использовать его до истечения срока действия. После истечения срока действия маркера можно обновить маркер, чтобы продолжить использовать его в дальнейших вызовах API.

Чтобы получить маркер доступа, следуйте инструкциям из статьи [Service to Service to Service Calls Using Client Credentials]/azure/active-directory/azuread-dev/v1-oauth2-client-creds-grant-flow) для отправки HTTP POST в https://login.microsoftonline.com/<конечную точку tenant_id>/oauth2/token. Ниже приведен пример запроса.

POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8

grant_type=client_credentials
&client_id=<your_client_id>
&client_secret=<your_client_secret>
&scope=https://api.store.microsoft.com/.default

tenant_id Для значения в URI POST и client_id client_secret параметрах укажите идентификатор клиента, идентификатор клиента и ключ приложения, полученный из Центра партнеров в предыдущем разделе. Для параметра области необходимо указать https://api.store.microsoft.com/.default.

После истечения срока действия маркера доступа его можно обновить, следуя инструкциям ниже.

Примеры, демонстрирующие получение маркера доступа с помощью C# или Node.js, см . в примерах кода ДЛЯ API отправки Microsoft Store для приложения MSI или EXE.

Шаг 3. Использование API отправки в Microsoft Store

После получения маркера доступа Azure AD можно вызвать методы в API отправки Microsoft Store для MSI или EXE-приложения. API включает множество методов, сгруппированных в сценарии для приложений. Для создания или обновления отправки обычно вызывается несколько методов в определенном порядке. Сведения о каждом сценарии и синтаксисе каждого метода см. в следующих разделах:

Примечание.

После получения маркера доступа у вас есть 60 минут для вызова методов в API отправки Microsoft Store для MSI или EXE-приложения до истечения срока действия маркера.

Базовый URL-адрес

Базовый URL-адрес API отправки Microsoft Store для EXE или MSI-приложения: https://api.store.microsoft.com

Контракты API

Получение API метаданных текущей отправки черновика

Извлекает метаданные в каждом модуле (перечисления, свойства или доступность) в рамках текущей отправки черновика.

Путь [все модули]: /submission/v1/product/{productId}/metadata?languages={languages}&includelanguagelist={true/false}
Путь [один модуль]: /submission/v1/product/{productId}/metadata/{moduleName}?languages={languages}&includelanguagelist={true/false}
Метод: GET

Параметры пути

Параметр Описание
productId Идентификатор центра партнеров продукта
moduleName Модуль Центра партнеров— перечисления, свойства или доступность

Параметры запроса

Параметр Описание
languages Необязательный фильтр языков перечисления как разделенная запятыми строка [ограничение до 200 языков].

Если нет, извлекаются первые 200 доступных метаданных языков перечисления. [ например, "en-us, en-gb"].
includelanguagelist Необязательный логический код — если true, возвращает список добавленных языков перечисления и их состояние полноты.

Обязательные заголовки

Верхний колонтитул Значение
Authorization: Bearer <Token> Идентификатор приложения Azure AD, зарегистрированный в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Значение
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
специальные возможностиSupport Логический
additionalLicenseTerms Строка
доступность Object Данные модуля доступности
Категория Строка См. список категорий ниже
certificationNotes Строка
кодом Строка Код ошибки сообщения
contactInfo Строка
copyright Строка
dependsOnDriversOrNT Логический
описание Строка
developedBy Строка
возможность обнаружения Строка [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
freeTrial Строка [NO_FREE_TRIAL, FREE_TRIAL]
hardwareItemType Строка
isPrivacyPolicyRequired Логический
isRecommended Логический
isRequired Логический
isSuccess Логический
isSystemFeatureRequired Массив объектов
язык Строка См. список языков ниже
Предложения Массив объектов Перечисление данных модуля для каждого языка
рынки Массив строк См. список рынков ниже
message Строка Описание ошибки
минимальное программное обеспечениеHardware Строка
minimumRequirement Строка
penAndInkSupport Логический
цены Строка [БЕСПЛАТНЫЙ, FREEMIUM, ПОДПИСКА, ПЛАТНАЯ]
privacyPolicyUrl Строка
productDeclarations Object
productFeatures Массив строк
свойства Object Данные модуля свойств
рекомендуемое программное обеспечениеHardware Строка
рекомендуетсяRequirement Строка
responseData Object Содержит фактические полезные данные ответа для запроса
к системе SAP Массив объектов
searchTerms Массив строк
shortDescription Строка
подкатегория Строка Список подкатегорий ниже
supportContactInfo Строка
systemRequirementDetails Массив объектов
целевой объект Строка Сущность, из которой возникла ошибка
website Строка
whatsNew Строка

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "availability":{
            "markets": ["US"],
            "discoverability": "DISCOVERABLE",
            "enableInFutureMarkets": true,
            "pricing": "PAID",
            "freeTrial": "NO_FREE_TRIAL"
        },
        "properties":{
            "isPrivacyPolicyRequired": true,
            "privacyPolicyUrl": "http://contoso.com",
            "website": "http://contoso.com",
            "supportContactInfo": "http://contoso.com",
            "certificationNotes": "Certification Notes",
            "category": "DeveloperTools",
            "subcategory": "Database",
            "productDeclarations": {
                "dependsOnDriversOrNT": false,
                "accessibilitySupport": false,
                "penAndInkSupport": false
            },
            "isSystemFeatureRequired": [
                {
                    "isRequired": true,
                    "isRecommended": false,
                    "hardwareItemType": "Touch"
                },
                {
                    "isRequired": true,
                    "isRecommended": false,
                    "hardwareItemType": "Keyboard"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "Mouse"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "Camera"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "NFC_HCE"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "NFC_Proximity"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "Bluetooth_LE"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "Telephony"
                },
                {
                    "isRequired": false,
                    "isRecommended": false,
                    "hardwareItemType": "Microphone"
                }
            ],
            "systemRequirementDetails": [
                {
                    "minimumRequirement": "1GB",
                    "recommendedRequirement": "4GB",
                    "hardwareItemType": "Memory"
                },
                {
                    "minimumRequirement": "",
                    "recommendedRequirement": "",
                    "hardwareItemType": "DirectX"
                },
                {
                    "minimumRequirement": "",
                    "recommendedRequirement": "",
                    "hardwareItemType": "Video_Memory"
                },
                {
                    "minimumRequirement": "",
                    "recommendedRequirement": "",
                    "hardwareItemType": "Processor"
                },
                {
                    "minimumRequirement": "",
                    "recommendedRequirement": "",
                    "hardwareItemType": "Graphics"
                }
            ]
        },
        "listings":[{
            "language": "en-us",
            "description": "Description",
            "whatsNew": "What's New",
            "productFeatures": ["Feature 1"],
            "shortDescription": "Short Description",
            "searchTerms": ["Search Ter 1"],
            "additionalLicenseTerms": "License Terms",
            "copyright": "Copyright Information",
            "developedBy": "Developer Details",
            "sortTitle": "Product 101",
            "requirements": [
                {
                    "minimumHardware": "Pentium4",
                    "recommendedHardware": "Corei9"
                }
            ],
            "contactInfo": "contactus@contoso.com"               
        }],      
        "listingLanguages": [{"language":"en-us", "isComplete": true}]
    }
}

Обновление API метаданных текущей отправки черновика

Обновляет метаданные в каждом модуле в разделе отправки черновика. Проверки API

  • Для активной отправки. Если существует, сбой с сообщением об ошибке.
  • Если все модули находятся в состоянии готовности, чтобы разрешить операцию "Сохранить черновик".
  • Каждое поле в отправке проверяется в соответствии с требованиями Магазина
  • Правила проверки сведений о системе:
    • Допустимые значения в hardwareItemType = память: 300 МБ, 750 МБ, 1 ГБ, 2 ГБ, 4 ГБ, 6 ГБ, 8 ГБ, 12 ГБ, 16 ГБ, 20 ГБ
    • Допустимые значения в hardwareItemType = DirectX: DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12
    • Допустимые значения в hardwareItemType = Video_Memory: 1 ГБ, 2 ГБ, 4 ГБ, 6 ГБ

Путь [полное обновление модуля]: /submission/v1/product/{productId}/метаданные
Метод: PUT

Путь [обновление исправления модуля]: /submission/v1/product/{productId}/metadata
Метод: PATCH

Поведение API

В случае API полного обновления модуля — все данные модуля должны присутствовать в запросе на полное обновление каждого поля. Любое поле, которое отсутствует в запросе, его значение по умолчанию используется для перезаписи текущего значения для этого конкретного модуля.
В случае API обновления модуля исправлений — в запросе должны присутствовать только поля, которые необходимо обновить. Эти значения полей из request перезаписывают существующие значения, сохраняя все остальные поля, которые не присутствуют в запросе, так же, как и текущий для этого конкретного модуля.

Параметры пути

Параметр Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Значение
Authorization: Bearer <Token> Идентификатор приложения Azure AD, зарегистрированный в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Параметры запроса

Имя. Тип Описание
доступность Object Объект для хранения метаданных модуля доступности
рынки Массив строк Обязательный список рынков ниже
возможность обнаружения Строка Обязательный [ DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Логический Обязательный
цены Строка Обязательный [ БЕСПЛАТНЫЙ, FREEMIUM, ПОДПИСКА, ПЛАТНАЯ]
freeTrial Строка Требуется, если цена платна или подписка [NO_FREE_TRIAL, FREE_TRIAL]
свойства Object Объект для хранения метаданных модуля свойств
isPrivacyPolicyRequired Логический Обязательный
privacyPolicyUrl Строка Обязательный, если isPrivacyPolicyRequired = true , должен быть допустимым URL-адресом
website Строка Должен быть допустимым URL-адресом
supportContactInfo Строка Должен быть допустимым URL-адресом или адресом электронной почты
certificationNotes Строка Рекомендуемое ограничение символов = 2000
Категория Строка Обязательный список категорий ниже
подкатегория Строка Обязательный список подкатегорий ниже
productDeclarations Object Обязательный
isSystemFeatureRequired Массив объектов [Сенсорный, клавиатура, мышь, камера, NFC_HCE, NFC_Proximity, Bluetooth_LE, телефония, микрофон]
isRequired Логический Обязательный
isRecommended Логический Обязательный
hardwareItemType Строка Обязательный
systemRequirementDetails Массив объектов [Процессор, графика, память, DirectX, Video_Memory]
minimumRequirement Строка Обязательный для systemRequirementsText, MaxLength = 200

Допустимые значения в hardwareItemType = память: [300 МБ, 750 МБ, 1 ГБ, 2 ГБ, 4 ГБ, 6 ГБ, 8 ГБ, 12 ГБ, 16 ГБ, 20 ГБ]

Допустимые значения в hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Допустимые значения в hardwareItemType = Video_Memory: [1 ГБ, 2 ГБ, 4 ГБ, 6 ГБ]
рекомендуетсяRequirement Строка Обязательный для systemRequirementsText, MaxLength = 200

Допустимые значения в hardwareItemType = память: [300 МБ, 750 МБ, 1 ГБ, 2 ГБ, 4 ГБ, 6 ГБ, 8 ГБ, 12 ГБ, 16 ГБ, 20 ГБ]

Допустимые значения в hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Допустимые значения в hardwareItemType = Video_Memory: [1 ГБ, 2 ГБ, 4 ГБ, 6 ГБ]
dependsOnDriversOrNT Логический Обязательный
специальные возможностиSupport Логический Обязательный
penAndInkSupport Логический Обязательный
Предложения Object Объект для перечисления данных модуля для одного языка
язык Строка Обязательный список языков ниже
описание Строка Обязательный предел символа = 10000
whatsNew Строка Ограничение символов = 1500
productFeatures Массив строк 200 символов на каждую функцию; До 20 функций
shortDescription Строка Ограничение символов = 1000
searchTerms Массив строк 30 символов на термин поиска; До 7 терминов поиска

21 уникальных слов TOTAL во всех терминах поиска
additionalLicenseTerms Строка Обязательный предел символа = 10000
copyright Строка Ограничение символов = 200
developedBy Строка Ограничение символов = 255
к системе SAP Массив объектов 200 символов на элемент; До 11 элементов в диапазоне от минимального и рекомендуемого]
минимальное программное обеспечениеHardware Строка Ограничение символов = 200
рекомендуемое программное обеспечениеHardware Строка Ограничение символов = 200
contactInfo Строка Ограничение символов = 200
листингиToAdd Массив строк См. список языков ниже
listingsToRemove Массив строк См. список языков ниже

Рынки

Рынок Аббревиатура
Афганистан AF
Албания AL
Алжир DZ
Американское Самоа AS
Андорра AD
Ангола AO
Ангилья ИИ
Антарктика AQ
Антигуа и Барбуда ГД
Аргентина расчеты с клиентами
Армения 4 млн
Аруба AW
Австралия AU
Австрия AT
Азербайджан AZ
Багамские о-ва BS
Бахрейн BH
Бангладеш BD
Барбадос BB
Беларусь BY
Бельгия BE
Белиз BZ
Бенин BJ
Бермудские о-ва BM
Бутан BT
Венесуэла (Боливарианская Республика) VE
Боливия 80
Бонэйр BQ
Босния и Герцеговина BA
Ботсвана BW
Остров Буве BV
Бразилия BR
Британская территория в Индийском океане IO
Виргинские о-ва (Великобритания) VG
Бруней-Даруссалам BN
Болгария BG
Буркина-Фасо BF
Бурунди служба
Камбоджа KH
Камерун ТМ
Канада Целостности и доступности
Кабо-Верде C
Острова Кайман Кентукки
Центрально-Африканская Республика CF
Чад TD
Чили CL
Китай CN
Остров Рождества CX
Кокосовые о-ва CC
Колумбия CO
Коморы КМ
Конго CG
Демократическая Республика Конго CD
Острова Кука OK
Коста-Рика CR
Хорватия HR
Кюрасао CW
Кипр CY
Чешская республика CZ
Кот-д'Ивуар CI
Дания DK
Джибути DJ
Доминика DM
Доминиканская Республика DO
Эквадор EC
Египет EG
Эль-Сальвадор SV
Экваториальная Гвинея GQ
Эритрея ER
Эстония EE
Эфиопия ET
Фолклендские о-ва FK
Фарерские о-ва FO
Фиджи FJ
Финляндия FI
Франция FR
Французская Гвиана GH
Французская Полинезия ТФ
Французские южные и Антарктические земли TF
Габон Общедоступная версия
Гамбия GM
Грузия GE
Германия DE
Гана GH
Гибралтар Отпуск товаров
Греция GR
Гренландия GL
Гренада GD
Гваделупа GP
Гуам GU
Гватемала GT
Гернси GG
Гвинея GN
Гвинея-Бисау GW
Гайана GY
Гаити HT
Остров Херд и острова Макдональд HM
Ватикан VA
Гондурас HN
Гонконг, САР HK
Венгрия HU
Исландия IS
Индия В
Индонезия ID
Ирак IQ
Ирландия IE
Израиль IL
Италия IT
Ямайка JM
Япония JP
Джерси JE
Иордания JO
Казахстан KZ
Кения KE
Кирибати KI
Республика Корея KR
Кувейт KW
Киргизстан KG
Лаос ЛАТИНСКАЯ АМЕРИКА
Латвия Lv
Ливан LB
Лесото LS
Либерия LR
Ливия LY
Лихтенштейн LI
Литва LT
Люксембург LU
Макао (САР) МО
Северная Македония MK
Мадагаскар MG
Малави MW
Малайзия MY
Мальдивы MV
Мали ML
Мальта MT
Остров Мэн "IM" (Обмен мгновенными сообщениями);
Маршалловы о-ва MH
Мартиника MQ
Мавритания МИ
Маврикий MU
Майотта YT
Мексика MX
Федеративные Штаты Микронезии FM
Молдова MD
Монако MC
Монголия MN
Монтенегр - Я
Монтсеррат MS
Марокко MA
Мозамбик MZ
Мьянма MM
Намибия Неприменимо
Науру NR
Непал NP
Нидерланды NL
Новая Каледония NC
Новая Зеландия Новая Зеландия
Никарагуа NI
Нигер NE
Нигерия NG
Ниуэ NU
О-в Норфолк NF
Северные Марианские о-ва MP
Норвегия Нет
Оман 0 млн
Пакистан PK
Палау PW
Палестинская Автономия PS
Панама Пенсильвания
Папуа — Новая Гвинея Р
Парагвай PY
Перу PE
Филиппины PH
О-ва Питкэрн PN
Польша PL
Португалия PT
Катар Контроль качества
Реюньон RE
Румыния 0
Россия ЕЗ
Руанда RW
Сен-Бартельми BL
Острова Св. Елены, Вознесения и Тристан-да-Кунья SH
Сент-Киттс и Невис KN
Сент-Люсия LC
Сен-Мартин (французская часть) MF
Сен-Пьер и Микелон PM
Сент-Винсент и Гренадины VC
Самоа WS
Сан-Марино SM
Саудовская Аравия SA
Сенегал SN
Сербия RS
Сейшельские Острова SC
Сьерра-Леоне SL
Сингапур ПЗ
Синт Маартен (голландская часть) SX
Словакия 5 тыс.
Словения SI
Соломоновы Острова SB
Сомали SO
ЮАР ZA
Южная Георгия и Южные Сандвичевы о-ва GS
Испания ES
Шри-Ланка LK
Суринам SR
Шпицберген и Ян-Майен SJ
Свазиленд SZ
Швеция SE
Швейцария CH
Сан-Томе и Принсипи ST
Тайвань TW
Таджикистан TJ
Танзания TZ
Таиланд TH
Тимор-Лесте TL
Tog - TG
Токелау TK
Тонга TO
Тринидад и Томаг - TT
Тунис ИО
Турция TR
Туркменистан TM
Острова Теркс и Кайкос TC
Тувалу TV
Американские незначительные аутлийские острова МЕРА
США (США) VI
Уганда UG
Украина UA
ОАЭ AE
Соединенное Королевство ГБ
Соединенные Штаты US
Уругвай UY
Узбекистан UZ
Вануату VU
Вьетнам VN
Уоллис и Футуна WF
Йемен YE
Замбия ZM
Зимбабве ZW
Аландские острова AX

Категории и подкатегории

Категория Подкатегории
BookAndReference EReader, Фантастика, Nonfiction, Reference
Триггер AccountingAndfinance, Collaboration, CRM, DataAndAnalytics, FileManagement, InventoryAndlogistics, LegalAndHR, ProjectManagement, RemoteDesktop, SalesAndMarketing, TimeAndExpenses
DeveloperTools База данных, designTools, DevelopmentKits, Networking, ReferenceAndTraining, Серверы, Служебные программы, WebHosting
Образование EducationBooksAndReference, EarlyLearning, Инструкции, Язык, StudyAids
Развлечения (Нет)
FoodAndDining (Нет)
GovernmentAndPolitics (Нет)
HealthAndFitness (Нет)
KidsAndFamily KidsAndFamilyBooksAndReference, KidsAndFamilyEntertainment, ХоббиAndToys, SportsAndActivities, KidsAndFamilyTravel
Образ жизни Automotive, DYI, HomeAndGarden, Relationships, SpecialInterest, StyleAndFashion
Медицинское обслуживание (Нет)
МультимедиаDesign ИллюстрацияAndGraphicDesign, MusicProduction, PhotoAndVideoProduction
Музыка (Нет)
NavigationAndMaps (Нет)
NewsAndWeather Новости, погода
PersonalFinance BankingAndInvestments, BudgetingAndTaxes
Personalization RingtonesAndSounds, темы, ОбоиAndLockScreens
PhotoAndVideo (Нет)
Продуктивность (Нет)
Безопасность PCProtection, PersonalSecurity
Покупки (Нет)
Соцсети (Нет)
Спорт (Нет)
Поездка CityGuides, Отели
UtilitiesAndTools BackupAndManage, FileManager

Языки

Имя языка Коды поддерживаемых языков
Африкаанс af, af-za
Албанский sq, sq-al
Амхарский am, am-et
Армянский hy, hy-am
Ассамский as, as-in
Азербайджанский az-arab, az-arab-az, az-cyrl, az-cyrl-az, az-latn, az-latn-az
Баскский eu, eu-es
Белорусский be, be-by
Бенгальский bn, bn-bd, bn-in
Боснийский bs, bs-cyrl, bs-cyrl-ba, bs-latn, bs-latn-ba
Болгарский bg, bg-bg
Каталанский ca, ca-es, ca-es-валенсия
Чероки chr-cher, chr-cher-us, chr-latn
Китайский (упрощенное письмо) zh-Hans, zh-cn, zh-hans-cn, zh-sg, zh-hans-sg
Китайский, традиционное письмо zh-Hant, zh-hk, zh-mo, zh-tw, zh-hant-hk, zh-hant-mo, zh-hant-tw, zh-mo, zh-tw, zh-hant-hk, zh-hant-mo, zh-hant-tw
Хорватский hr, hr-hr, hr-ba
чешский cs, cs-cz
датский da, da-dk
Дари prs, prs-af, prs-arab
Голландский nl, nl-nl, nl-be
Английский en-au, en-ca, en-gb, en-ie, en-in, en-nz, en-sg, en-us, en-ru, en-bz, en-hk, en-hk, en-jm, en-jm, en-mt, en-my, en-ph, en-pk, en-tt, en-vn, en-zw
Эстонский et, et-ee
Filipin - fil, fil-latn, fil-ph
Финский fi, fi-fi
Французский fr, fr-be, fr-ca, fr-ch, fr-fr, fr-lu, fr-cd, fr-ci, fr-cm, fr-ht, fr-ma, fr-mc, fr-ml, fr-re, frc-latn, frp-latn
Галисийский gl, gl-es
Грузинский ka, ka-ge
Немецкий de, de-at, de-ch, de-de, de-lu, de-li
Греческий el, el-gr
Гуджарати gu, gu-in
Хауса ha, ha-latn, ha-latn-ng
Иврит он, he-il
Хинди привет, привет, привет
Венгерский hu, hu-hu
Исландский is, is-is
Igb - ig-latn, ig-ng
Индонезийский id, id-id
Inuktitut (латиница) iu-cans, iu-latn, iu-latn-ca
Ирландский ga, ga-ie
Коса xh, xh-za
Зулу zu, zu-za
Итальянский it-it, it-ch
Японский ja, ja-jp
Каннада kn, kn-in
Казахский kk, kk-kz
Кхмерский km, km-kh
K'iche' quc-latn, qut-gt, qut-latn
Киньяруанда rw, rw-rw
Суахили sw, sw-ke
Конкани кок, кок-ин
Корейский ko, ko-kr
Курдский ku-arab, ku-arab-iq
Киргизский ky-kg, ky-cyrl
Лаосский lo, lo-la
Латышский lv, lv-lv
Литовский lt, lt-lt
Люксембургский lb, lb-lu
Macedonian mk, mk-mk
Малайский ms,ms-bn, ms-my
Малаялам ml, ml-in
Мальтийский mt, mt-mt
Маори mi, mi-latn, mi-nz
Маратхи mr, mr-in
Монгольский (кириллица) mn-cyrl, mn-mong, mn-mn, mn-phag
Непальский ne, ne-np
Норвежский nb, nb-no, nn, nn-no, нет, нет
Ория или или в
Персидский fa, fa-ir
Польский pl, pl-pl
Португальский (Бразилия) pt-br
Португальский (Португалия) pt, pt-pt
Панджаби pa, pa-arab, pa-arab-pk, pa-deva, pa-in
Кечуа quz, quz-bo, quz-ec, quz-pe
Румынский ro, ro-ro
русский ru, ru-ru
Гэльский gd-gb, gd-latn
Сербский (латиница) sr-Latn, sr-latn-cs, sr-latn-ba, sr-latn-me, sr-latn-rs
Сербский (кириллица) sr-cyrl, sr-cyrl-ba, sr-cyrl-cs, sr-cyrl-me, sr-cyrl-rs
Северный сото nso, nso-za
Тсвана tn, tn-bw, tn-za
Синдхи sd-arab, sd-arab-pk, sd-deva
Сингальский si, si-lk
Словацкий sk, sk-sk
Словенский sl, sl-si
Испанский es, es-cl, es-co, es-es, es-mx, es-ar, es-bo, es-cr, es-do, es-ec, es-es-hn, es-ni, es-pa, es-pe, es-pr, es-py, es-sv, es-us, es-uy, es-ve
Шведский sv, sv-se, sv-fi
Таджикский (кириллица) tg-arab, tg-cyrl, tg-cyrl-tj, tg-latn
Тамильский ta, ta-in
Татарский tt-arab, tt-cyrl, tt-latn, tt-ru
Телугу te, te-in
Тайский th, th-th
Тигринья ti, ti-et
Турецкий tr, tr-tr
Туркменский tk-cyrl, tk-latn, tk-tm, tk-latn-tr, tk-cyrl-tr
Украинский uk, uk-ua
Урду your, your-pk
Уйгурский ug-arab, ug-cn, ug-cyrl, ug-latn
Узбекский (латиница) uz, uz-cyrl, uz-latn, uz-latn-uz
Вьетнамский vi, vi-vn
Валлийский cy, cy-gb
Волоф wo, wo-sn
Йоруба yo-latn, yo-ng

Пример запроса

{
    "availability":{
        "markets": ["US"],
        "discoverability": "DISCOVERABLE",
        "enableInFutureMarkets": true,
        "pricing": "PAID",
        "freeTrial": "NO_FREE_TRIAL"
    },
    "properties":{
        "isPrivacyPolicyRequired": true,
        "privacyPolicyUrl": "http://contoso.com",
        "website": "http://contoso.com",
        "supportContactInfo": "http://contoso.com",
        "certificationNotes": "Certification Notes",
        "category": "DeveloperTools",
        "subcategory": "Database",
        "productDeclarations": {
            "dependsOnDriversOrNT": false,
            "accessibilitySupport": false,
            "penAndInkSupport": false
        },
        "isSystemFeatureRequired": [
        {
            "isRequired": true,
                "isRecommended": false,
                "hardwareItemType": "Touch"
            },
            {
                "isRequired": true,
                "isRecommended": false,
                "hardwareItemType": "Keyboard"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Mouse"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Camera"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "NFC_HCE"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "NFC_Proximity"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Bluetooth_LE"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Telephony"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Microphone"
            }
        ],
        "systemRequirementDetails": [
            {
                "minimumRequirement": "1GB",
                "recommendedRequirement": "4GB",
                "hardwareItemType": "Memory"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "DirectX"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "Video_Memory"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "Processor"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "Graphics"
            }
        ]
    },
    "listings":{
        "language": "en-us",
        "description": "Description",
        "whatsNew": "What's New",
        "productFeatures": ["Feature 1"],
        "shortDescription": "Short Description",
        "searchTerms": ["Search Ter 1"],
        "additionalLicenseTerms": "License Terms",
        "copyright": "Copyright Information",
        "developedBy": "Developer Details",
        "sortTitle": "Product 101",
        "requirements": [
            {
                "minimumHardware": "Pentium4",
                "recommendedHardware": "Corei9"
            }
        ],
        "contactInfo": "contactus@contoso.com"               
    },      
    "listingsToAdd": ["en-au"],
    "listingsToRemove": ["en-gb"]
}

Заголовки ответов

Верхний колонтитул Значение
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем снова вызывать API из-за ограничения скорости

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object Содержит фактические полезные данные ответа для запроса
pollingUrl Строка URL-адрес опроса для получения состояния любой выполняющейся отправки
ongoingSubmissionId Строка Идентификатор отправки любой уже запущенной отправки

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    } 
}

Получение API текущих черновика пакетов

Извлекает сведения о пакете в соответствии с текущей отправкой черновика.

Путь [все пакеты]: /submission/v1/product/{productId}/packages
Метод: GET

Путь [один пакет]: /submission/v1/product/{productId}/packages/{packageId}
Метод: GET

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта
packageId Уникальный идентификатор пакета для получения

Обязательные заголовки

Верхний колонтитул Значение
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Значение
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список ошибок или предупреждений, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
служб Массив объектов Объект для хранения данных модуля пакета
packageId Строка
packageUrl Строка
languages Массив строк
architectures Массив строк [Нейтральный, X86, X64, Arm, Arm64]
isSilentInstall Логический Это должно быть отмечено как true, если установщик работает в автоматическом режиме, не требуя переключений или других значений false.
InstallerParameters Строка
genericDocUrl Строка
errorDetails Массив объектов
errorScenario Строка
errorScenarioDetails Массив объектов
errorValue Строка
errorUrl Строка
packageType Строка

Пример ответа

{   
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
    }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData":{
        "packages":[{
            "packageId": "pack0832",
            "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
            "languages": ["en-us"],
            "architectures": ["X86"],
            "isSilentInstall": true,
            "installerParameters": "/s",
            "genericDocUrl": "https://docs.contoso.com/doclink",
            "errorDetails": [{
                "errorScenario": "rebootRequired",
                "errorScenarioDetails": [{
                    "errorValue": "ERR001001",
                    "errorUrl": "https://errors.contoso.com/errors/ERR001001"
                }]
            }],
            "packageType": "exe",
        }]
    }
}

Обновление API текущих черновика пакетов

Обновляет сведения о пакете в соответствии с текущей отправкой черновика.

Путь [полное обновление модуля]: /submission/v1/product/{productId}/packages
Метод: PUT

Путь [обновление исправлений для одного пакета]: /submission/v1/product/{productId}/packages/{packageId}
Метод: PATCH

Поведение API

В случае API полного обновления модуля — все данные пакетов должны присутствовать в запросе на полное обновление каждого поля. Любое поле, которое отсутствует в запросе, его значение по умолчанию используется для перезаписи текущего значения для этого конкретного модуля. Это приводит к перезаписи всех существующих пакетов с новым набором пакетов из запроса. Это приведет к повторному восстановлению идентификаторов пакетов и пользователю следует вызвать API пакетов GET для последних идентификаторов пакетов.

В случае API обновления исправлений для одного пакета — только поля, которые необходимо обновить для данного пакета, должны присутствовать в запросе. Эти значения полей из запроса перезаписывают существующие значения, сохраняя все остальные поля, которые не присутствуют в запросе, так же, как и текущие для этого конкретного пакета. Другие пакеты в наборе остаются как есть.

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта
packageId Уникальный идентификатор пакета

Обязательные заголовки

Верхний колонтитул Значение
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Параметры запроса

Имя. Тип Описание
служб Массив объектов Объект для хранения данных модуля пакета [требуется только для полного обновления модуля]
packageUrl Строка Обязательный
languages Массив строк Обязательный
architectures Массив строк Обязательный элемент должен содержать одну архитектуру : Нейтральный, X86, X64, Arm, Arm64
isSilentInstall Логический Обязательный параметр должен быть помечен как true, если установщик работает в автоматическом режиме без необходимости переключений или других значений false.
InstallerParameters Строка Обязательный, если isSilentInstall имеет значение false
genericDocUrl Строка Обязательный, если packageType является exe Link to document, содержащий сведения о пользовательских кодах ошибок для установщика типов EXE
errorDetails Массив объектов Метаданные для хранения пользовательских кодов ошибок и сведений для установщиков типов EXE.
errorScenario Строка Определите конкретный сценарий ошибки. [installationCancelledByUser, applicationAlreadyExists, installationAlreadyInProgress, diskSpaceIsFull, rebootRequired, networkFailure, packageRejectedDuringInstallation, installationSuccessful, miscellaneous]
errorScenarioDetails Массив объектов
errorValue Строка Код ошибки, который может присутствовать во время установки
errorUrl Строка URL-адрес для получения сведений об ошибке
packageType Строка Обязательный [exe, msi]

Пример запроса [полное обновление модуля]

{
    "packages":[{
        "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
        "languages": ["en-us"],
        "architectures": ["X86"],
        "isSilentInstall": true,
        "installerParameters": "/s",
        "genericDocUrl": "https://docs.contoso.com/doclink",
        "errorDetails": [{
            "errorScenario": "rebootRequired",
            "errorScenarioDetails": [{
                "errorValue": "ERR001001",
                "errorUrl": "https://errors.contoso.com/errors/ERR001001"
            }]
        }],
        "packageType": "exe",
    }]
}

Пример запроса [обновление исправлений для одного пакета]

{
    "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
    "languages": ["en-us"],
    "architectures": ["X86"],
    "isSilentInstall": true,
    "installerParameters": "/s",
    "genericDocUrl": "https://docs.contoso.com/doclink",
    "errorDetails": [{
        "errorScenario": "rebootRequired",
        "errorScenarioDetails": [{
            "errorValue": "ERR001001",
            "errorUrl": "https://errors.contoso.com/errors/ERR001001"
        }]
    }],
    "packageType": "exe",
}

Заголовки ответов

Верхний колонтитул Значение
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов [Список сообщений об ошибках или предупреждениях, если таковые есть]
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
pollingUrl Строка [URL-адрес опроса для получения состояния отправки в случае любой уже запущенной отправки]
ongoingSubmissionId Строка [Идентификатор отправки для любой уже запущенной отправки]

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    } 
}

API фиксации пакетов

Фиксирует новый набор пакетов, обновленный с помощью API обновления пакетов в рамках текущей отправки черновика. Этот API возвращает URL-адрес опроса для отслеживания отправки пакета.

Путь: /submission/v1/product/{productId}/packages/commit
Метод: POST

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Значение
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Значение
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов [Список сообщений об ошибках или предупреждениях, если таковые есть]
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
pollingUrl Строка [URL-адрес опроса для получения состояния отправки или отправки пакета в случае любой уже запущенной отправки]
ongoingSubmissionId Строка [Идентификатор отправки для любой уже запущенной отправки]

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/status",
        "ongoingSubmissionId": ""
    } 
}

Получение API перечисления текущих проектов активов

Извлекает сведения о ресурсах в соответствии с текущей отправкой черновика.

Путь: /submission/v1/product/{productId}/listings/assets?languages={languages}
Метод: GET

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Параметры запроса

Имя Описание
languages [Необязательно] Список языков фильтруется как разделенная запятыми строка [ограничение до 200 языков]. Если нет, извлекаются первые 200 доступные данные о ресурсах языка перечисления. (например, "en-us, en-gb")

Обязательные заголовки

Верхний колонтитул Значение
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Значение
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
перечислениеAssets Массив объектов Перечисление сведений о ресурсах для каждого языка
язык Строка
storeLogos Массив объектов
снимки экрана Массив объектов
id Строка
assetUrl Строка Должен быть допустимым URL-адресом
imageSize Object
width Целое
высота Целое

Пример ответа

{   
"isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData":{
        "listingAssets": [{
            "language": "en-us",
            "storeLogos": [
                {
                    "id": "1234567890abcdefgh",
                    "assetUrl": "https://contoso.com/blob=1234567890abcdefgh",
                    "imageSize": {
                        "width": 2160,
                        "height": 2160
                    }
                }
            ],
            "screenshots": [
                {
                    "id": "1234567891abcdefgh",
                    "assetUrl": "https://contoso.com/blob=1234567891abcdefgh",
                    "imageSize": {
                        "width": 2160,
                        "height": 2160
                    }
                }
            ]
        }]
    }
}

Создание API перечисления активов

Создает новый список отправки активов в рамках текущей отправки черновика.

Обновление ресурсов перечисления

API отправки Microsoft Store для EXE или MSI приложения используют URL-адреса SAS, созданные средой выполнения, в хранилищах BLOB-объектов для каждого отдельного ресурса образа, а также вызов API фиксации после успешной отправки. Чтобы иметь возможность обновлять ресурсы списка, и, в свою очередь, иметь возможность добавлять и удалять языковые стандарта в модуле перечисления, можно использовать следующий подход:

  1. Используйте API создания списка активов для отправки запросов относительно отправки ресурсов вместе с языком, типом и количеством ресурсов.
  2. На основе количества запрошенных ресурсов идентификаторы активов создаются по запросу и будут создавать краткосрочный URL-адрес SAS и отправлять его обратно в текст ответа в соответствии с типом активов. Этот URL-адрес можно использовать для отправки ресурсов изображений определенного типа с помощью HTTP-клиентов [Put BLOB-объект (REST API) — служба хранилища Azure | Документация Майкрософт].
  3. После отправки вы можете использовать API "Зафиксировать активы" для отправки новых сведений об идентификаторе ресурса, полученных ранее из предыдущего вызова API. Один API будет внутренне фиксировать данные о ресурсах перечисления после проверки.
  4. Этот подход эффективно перезаписывает весь набор предыдущих образов типа ресурса в определенном языке, который отправляется в запросе. Следовательно, ранее загруженные ресурсы будут удалены.

Путь: /submission/v1/product/{productId}/listings/assets/create
Метод: POST

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Description
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Параметры запроса

Имя. Тип Описание
язык Строка Обязательный
createAssetRequest Object Обязательный
Снимок экрана Целое Требуется, если isV необходимо обновить снимки экрана или добавить новый язык перечисления [1 – 10]
Логотип Целое Требуется, если isV необходимо обновить логотипы или добавить новый язык описания [1 или 2]

Заголовки ответов

Верхний колонтитул Description
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
перечислениеAssets Object Объект, содержащий подробные сведения о StoreLogos и снимках экрана для отправки
язык Строка
storeLogos Массив объектов
снимки экрана Массив объектов
id Строка
primaryAssetUploadUrl Строка Первичный URL-адрес для отправки ресурса перечисления с помощью REST API BLOB-объектов Azure
secondaryAssetUploadUrl Строка Дополнительный URL-адрес для отправки ресурса перечисления с помощью REST API BLOB-объектов Azure
httpMethod Метод HTTP Метод HTTP, необходимый для отправки ресурсов с помощью URL-адресов отправки ресурсов — основной или вторичной
httpHeaders Object Объект с ключами в качестве обязательных заголовков, которые должны присутствовать в вызове API отправки в URL-адреса отправки активов. Если значение не является пустым, заголовки должны иметь определенные значения. Кроме того, значения вычисляются во время вызова API.

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "listingAssets": {
            "language": "en-us",
            "storeLogos":[{
                "id": "1234567890abcdefgh",
                "primaryAssetUploadUrl": "https://contoso.com/upload?blob=1234567890abcdefgh&sig=12345",
                "secondaryAssetUploadUrl": "https://contoso.com/upload?blob=0987654321abcdfger&sig=54326",
                "httpMethod": "PUT",
                "httpHeaders": {"Required Header Name": "Header Value"}
            }],
            "screenshots":[{
                "id": "0987654321abcdfger",
                "primaryAssetUploadUrl": "https://contoso.com/upload?blob=0987654321abcdfger&sig=54321",
                "secondaryAssetUploadUrl": "https://contoso.com/upload?blob=0987654321abcdfger&sig=54322",
                "httpMethod": "PUT",
                "httpHeaders": {"Required Header Name": "Header Value"}

            }]
        }
    } 
}

API перечисления активов фиксации

Фиксирует новый ресурс перечисления, отправленный с помощью сведений из API создания активов в рамках текущей отправки черновика.

Путь: /submission/v1/product/{productId}/listings/assets/commit
Метод: PUT

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Description
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Параметры запроса

Имя. Тип Описание
перечислениеAssets Object
язык Строка
storeLogos Массив объектов
снимки экрана Массив объектов
id Строка Должен быть существующий идентификатор, который пользователь хочет сохранить из API получения текущих активов или нового идентификатора, в котором был отправлен новый ресурс в API создания активов.
assetUrl Строка Должен быть URL-адрес существующего ресурса, который пользователь хочет сохранить из API получения текущих активов листинга, или URL-адрес отправки — основной или вторичный, с помощью которого новый ресурс был отправлен в API создания активов списка. Должен быть допустимым URL-адресом

Пример запроса

{
    "listingAssets": { 
        "language": "en-us",    
        "storeLogos": [
            {
                "id": "1234567890abcdefgh",
                "assetUrl": "https://contoso.com/blob=1234567890abcdefgh",
            }
        ],
        "screenshots": [
            {
                "id": "1234567891abcdefgh",
                "assetUrl": "https://contoso.com/blob=1234567891abcdefgh",
            }
        ]
    }
}

Заголовки ответов

Верхний колонтитул Description
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
pollingUrl Строка URL-адрес опроса для получения состояния любой выполняющейся отправки
ongoingSubmissionId Строка Идентификатор отправки уже выполняющейся отправки

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    } 
}

API опроса состояния модуля

API для проверки готовности модуля перед созданием отправки. Также проверяет состояние отправки пакета.

Путь: /submission/v1/product/{productId}/status
Метод: GET

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Description
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Description
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
isReady Логический Указывает, находятся ли все модули в состоянии готовности, включая отправку пакета
ongoingSubmissionId Строка Идентификатор отправки уже выполняющейся отправки

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "isReady": true,
        "ongoingSubmissionId": ""
    }
}

Создание API отправки

Создает отправку из текущего черновика для приложения MSI или EXE. Проверка API:

  • для активной отправки и завершается сбоем с сообщением об ошибке, если существует активная отправка.
  • Если все модули находятся в состоянии готовности к созданию отправки.
  • каждое поле в отправке проверяется в соответствии с требованиями Магазина

Path:/submit/v1/product/{productId}/submit
Метод: POST

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Description
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Description
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
pollingUrl Строка URL-адрес опроса для получения состояния готовности модуля, включая отправку пакета для отправки
submissionId Строка Идентификатор только что созданной отправки
ongoingSubmissionId Строка Идентификатор отправки уже выполняющейся отправки

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "submissionId": "1234567890", 
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    }
}

API опроса состояния отправки

API для проверки состояния отправки.

Путь: /submission/v1/product/{productId}/submission/{submissionId}/status
Метод: GET

Параметры пути

Имя Описание
productId Идентификатор центра партнеров продукта

Обязательные заголовки

Верхний колонтитул Description
Authorization: Bearer <Token> Использование идентификатора приложения Azure AD, зарегистрированного в учетной записи Центра партнеров
X-Seller-Account-Id Идентификатор продавца учетной записи Центра партнеров

Заголовки ответов

Верхний колонтитул Description
X-Correlation-ID Уникальный идентификатор GUID для каждого запроса. Это можно предоставить группе поддержки для анализа любой проблемы.
Retry-After Время в секундах, в течение которого клиент должен ждать, прежде чем вызывать API снова из-за ограничения скорости.

Параметры ответа

Имя. Тип Описание
isSuccess Логический
ошибки Массив объектов Список сообщений об ошибках или предупреждениях, если таковые есть
кодом Строка Код ошибки сообщения
message Строка Описание ошибки
целевой объект Строка Сущность, из которой возникла ошибка
responseData Object
publishingStatus Строка Состояние публикации отправки — [INPROGRESSS, PUBLISHED, FAILED, UNKNOWN]
hasFailed Логический Указывает, не удалось ли публикация и не будет извлечена

Пример ответа

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "publishingStatus": "INPROGRESS",
        "hasFailed": false
    }
}

Примеры кода

В следующих статьях приведены подробные примеры кода, демонстрирующие использование API отправки Microsoft Store на разных языках программирования:

Пример C#: API отправки Microsoft Store для MSI или EXE-приложения

В этой статье приведены примеры кода C#, демонстрирующие использование API отправки Microsoft Store для приложения MSI или EXE. Вы можете просмотреть каждый пример, чтобы узнать больше о демонстрируемой задаче или создать все примеры кода, приведенные в этой статье, в консольное приложение.

Необходимые условия в этих примерах используют следующую библиотеку:

  • Пакет NuGet Newtonsoft.Json из Newtonsoft.

Основная программа В следующем примере реализуется программа командной строки, которая вызывает другие примеры методов в этой статье, чтобы продемонстрировать различные способы использования API отправки Microsoft Store. Чтобы адаптировать эту программу для собственного использования, выполните указанные ниже действия.

  • Назначьте свойство SellerId идентификатору учетной записи Центра партнеров.
  • Назначьте свойство ApplicationId идентификатору приложения, которым требуется управлять.
  • Назначьте свойства ClientId и ClientSecret идентификатору клиента и ключу приложения и замените строку tenantid в URL-адресе TokenEndpoint идентификатором клиента для приложения. Дополнительные сведения см. в разделе Порядок связывания приложения Azure AD с учетной записью Центра партнеров
using System;
using System.Threading.Tasks;

namespace Win32SubmissionApiCSharpSample
{
    public class Program
    {
        static async Task Main(string[] args)
        {
            var config = new ClientConfiguration()
            {
                ApplicationId = "...",
                ClientId = "...",
                ClientSecret = "...",
                Scope = "https://api.store.microsoft.com/.default",
                ServiceUrl = "https://api.store.microsoft.com",
                TokenEndpoint = "...",
                SellerId = 0
            };

            await new AppSubmissionUpdateSample(config).RunAppSubmissionUpdateSample();

        }
    }
}

Вспомогательный класс ClientConfiguration с помощью C#

В примере приложения используется вспомогательный класс ClientConfiguration для передачи данных Azure Active Directory и данных приложения в каждый из примеров методов, использующих API отправки Microsoft Store.

using System;
using System.Collections.Generic;
using System.Text;

namespace Win32SubmissionApiCSharpSample
{
    public class ClientConfiguration
    {
        /// <summary>
        /// Client Id of your Azure Active Directory app.
        /// Example" 00001111-aaaa-2222-bbbb-3333cccc4444
        /// </summary>
        public string ClientId { get; set; }

        /// <summary>
        /// Client secret of your Azure Active Directory app
        /// </summary>
        public string ClientSecret { get; set; }

        /// <summary>
        /// Service root endpoint.
        /// Example: "https://api.store.microsoft.com"
        /// </summary>
        public string ServiceUrl { get; set; }

        /// <summary>
        /// Token endpoint to which the request is to be made. Specific to your Azure Active Directory app
        /// Example: https://login.microsoftonline.com/d454d300-128e-2d81-334a-27d9b2baf002/oauth2/v2.0/token
        /// </summary>
        public string TokenEndpoint { get; set; }

        /// <summary>
        /// Resource scope. If not provided (set to null), default one is used for the production API
        /// endpoint ("https://api.store.microsoft.com/.default")
        /// </summary>
        public string Scope { get; set; }

        /// <summary>
        /// Partner Center Application ID.
        /// Example: 3e31a9f9-84e8-4d2d-9eba-487878d02ebf
        /// </summary>
        public string ApplicationId { get; set; }


        /// <summary>
        /// The Partner Center Seller Id
        /// Example: 123456892
        /// </summary>
        public int SellerId { get; set; }
    }
}

Создание отправки приложения с помощью C#

В следующем примере реализуется класс, использующий несколько методов в API отправки Microsoft Store для обновления отправки приложения.

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Win32SubmissionApiCSharpSample
{
    public class AppSubmissionUpdateSample
    {
        private ClientConfiguration ClientConfig;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="configuration">An instance of ClientConfiguration that contains all parameters populated</param>
        public AppSubmissionUpdateSample(ClientConfiguration configuration)
        {
            this.ClientConfig = configuration;
        }

        /// <summary>
        /// Main method to Run the Sample Application
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public async Task RunAppSubmissionUpdateSample()
        {
            // **********************
            //       SETTINGS
            // **********************
            var appId = this.ClientConfig.ApplicationId;
            var clientId = this.ClientConfig.ClientId;
            var clientSecret = this.ClientConfig.ClientSecret;
            var serviceEndpoint = this.ClientConfig.ServiceUrl;
            var tokenEndpoint = this.ClientConfig.TokenEndpoint;
            var scope = this.ClientConfig.Scope;

            // Get authorization token.
            Console.WriteLine("Getting authorization token");
            var accessToken = await SubmissionClient.GetClientCredentialAccessToken(
                tokenEndpoint,
                clientId,
                clientSecret,
                scope);

            var client = new SubmissionClient(accessToken, serviceEndpoint);

            client.DefaultHeaders = new Dictionary<string, string>()
            {
                {"X-Seller-Account-Id", this.ClientConfig.SellerId.ToString() }
            };

            Console.WriteLine("Getting Current Application Draft Status");
            
            dynamic AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);
            
            Console.WriteLine(AppDraftStatus.ToString());

            Console.WriteLine("Getting Application Packages ");

            dynamic PackagesResponse = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.PackagesUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(PackagesResponse.ToString());

            Console.WriteLine("Getting Single Package");

            dynamic SinglePackageResponse = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.PackageByIdUrlTemplate,
                SubmissionClient.Version, appId, (string)PackagesResponse.responseData.packages[0].packageId), null);

            Console.WriteLine(SinglePackageResponse.ToString());

            Console.WriteLine("Updating Entire Package Set");

            // Update data in Packages list to have final set of updated Packages

            // Example - Updating Installer Parameters
            PackagesResponse.responseData.packages[0].installerParameters = "/s /r new-args";

            dynamic PackagesUpdateRequest = new
            {
                packages = PackagesResponse.responseData.packages
            };

            dynamic PackagesUpdateResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.PackagesUrlTemplate,
                SubmissionClient.Version, appId), PackagesUpdateRequest);

            Console.WriteLine(PackagesUpdateResponse.ToString());

            Console.WriteLine("Updating Single Package's Download Url");

            // Update data in the SinglePackage object

            var SinglePackageUpdateRequest = SinglePackageResponse.responseData.packages[0];

            // Example - Updating Installer Parameters
            SinglePackageUpdateRequest.installerParameters = "/s /r /t new-args";

            dynamic PackageUpdateResponse = await client.Invoke<dynamic>(HttpMethod.Patch, string.Format(SubmissionClient.PackageByIdUrlTemplate,
                SubmissionClient.Version, appId, SinglePackageUpdateRequest.packageId), SinglePackageUpdateRequest);

            Console.WriteLine("Committing Packages");

            dynamic PackageCommitResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.PackagesCommitUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(PackageCommitResponse.ToString());

            Console.WriteLine("Polling Package Upload Status");

            AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);

            while (!((bool)AppDraftStatus.responseData.isReady))
            {
                AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                    SubmissionClient.Version, appId), null);

                Console.WriteLine("Waiting for Upload to finish");

                await Task.Delay(TimeSpan.FromSeconds(2));

                if(AppDraftStatus.errors != null && AppDraftStatus.errors.Count > 0)
                {
                    for(var index = 0; index < AppDraftStatus.errors.Count; index++)
                    {
                        if(AppDraftStatus.errors[index].code == "packageuploaderror")
                        {
                            throw new InvalidOperationException("Package Upload Failed. Please try committing packages again.");
                        }
                    }
                }
            }

            Console.WriteLine("Getting Application Metadata - All Modules");

            dynamic AppMetadata = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.AppMetadataUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppMetadata.ToString());

            Console.WriteLine("Getting Application Metadata - Listings");

            dynamic AppListingsMetadata = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.AppListingsFetchMetadataUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppListingsMetadata.ToString());

            Console.WriteLine("Updating Listings Metadata - Description");

            // Update Required Fields in Listings Metadata Object - Per Language. For eg. AppListingsMetadata.responseData.listings[0]

            // Example - Updating Description
            AppListingsMetadata.responseData.listings[0].description = "New Description Updated By C# Sample Code";

            dynamic ListingsUpdateRequest = new
            {
                listings = AppListingsMetadata.responseData.listings[0]
            };

            dynamic UpdateListingsMetadataResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.AppMetadataUrlTemplate,
                SubmissionClient.Version, appId), ListingsUpdateRequest);

            Console.WriteLine(UpdateListingsMetadataResponse.ToString());

            Console.WriteLine("Getting All Listings Assets");

            dynamic ListingAssets = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ListingAssetsUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(ListingAssets.ToString());

            Console.WriteLine("Creating Listing Assets for 1 Screenshot");

            
            dynamic AssetCreateRequest = new
            {
                language = ListingAssets.responseData.listingAssets[0].language,
                createAssetRequest = new Dictionary<string, int>()
                {
                    {"Screenshot", 1 },
                    {"Logo", 0 }
                }
            };

            dynamic AssetCreateResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.ListingAssetsCreateUrlTemplate,
               SubmissionClient.Version, appId), AssetCreateRequest);

            Console.WriteLine(AssetCreateResponse.ToString());

            Console.WriteLine("Uploading Listing Assets");

            // Path to PNG File to be Uploaded as Screenshot / Logo
            var PathToFile = "./Image.png";
            var AssetToUpload = File.OpenRead(PathToFile);

            await client.UploadAsset(AssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl.Value as string, AssetToUpload);

            Console.WriteLine("Committing Listing Assets");

            dynamic AssetCommitRequest = new
            {
                listingAssets = new
                {
                    language = ListingAssets.responseData.listingAssets[0].language,
                    storeLogos = ListingAssets.responseData.listingAssets[0].storeLogos,
                    screenshots = JToken.FromObject(new List<dynamic>() { new
                {
                    id = AssetCreateResponse.responseData.listingAssets.screenshots[0].id.Value as string,
                    assetUrl = AssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl.Value as string
                }
                }.ToArray())
                }
            };

            dynamic AssetCommitResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.ListingAssetsCommitUrlTemplate,
               SubmissionClient.Version, appId), AssetCommitRequest);

            Console.WriteLine(AssetCommitResponse.ToString());

            Console.WriteLine("Getting Current Application Draft Status before Submission");

            AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppDraftStatus.ToString());

            if (AppDraftStatus == null || !((bool)AppDraftStatus.responseData.isReady))
            {
                throw new InvalidOperationException("Application Current Status is not in Ready Status for All Modules");
            }

            Console.WriteLine("Creating Submission");

            dynamic SubmissionCreationResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.CreateSubmissionUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(SubmissionCreationResponse.ToString());

            Console.WriteLine("Current Submission Status");

            dynamic SubmissionStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.SubmissionStatusPollingUrlTemplate,
                SubmissionClient.Version, appId, SubmissionCreationResponse.responseData.submissionId.Value as string), null);

            Console.Write(SubmissionStatus.ToString());

            // User can Poll on this API to know if Submission Status is INPROGRESS, PUBLISHED or FAILED.
            // This Process involves File Scanning, App Certification and Publishing and can take more than a day.
        }
    }
}

Вспомогательный класс IngestionClient с помощью C#

Класс IngestionClient предоставляет вспомогательные методы, которые используются другими методами в примере приложения для выполнения следующих задач:

  • Получите маркер доступа Azure AD, который можно использовать для вызова методов в API отправки Microsoft Store. После получения маркера у вас есть 60 минут для использования этого маркера в вызовах API отправки Microsoft Store до истечения срока действия маркера. После истечения срока действия маркера можно создать новый маркер.
  • Обработайте HTTP-запросы для API отправки Microsoft Store.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Win32SubmissionApiCSharpSample
{
    /// <summary>
    /// This class is a proxy that abstracts the functionality of the API service
    /// </summary>
    public class SubmissionClient : IDisposable
    {
        public static readonly string Version = "1";
        private HttpClient httpClient;
        private HttpClient imageUploadClient;

        private readonly string accessToken;

        public static readonly string PackagesUrlTemplate = "/submission/v{0}/product/{1}/packages";
        public static readonly string PackageByIdUrlTemplate = "/submission/v{0}/product/{1}/packages/{2}";
        public static readonly string PackagesCommitUrlTemplate = "/submission/v{0}/product/{1}/packages/commit";
        public static readonly string AppMetadataUrlTemplate = "/submission/v{0}/product/{1}/metadata";
        public static readonly string AppListingsFetchMetadataUrlTemplate = "/submission/v{0}/product/{1}/metadata/listings";
        public static readonly string ListingAssetsUrlTemplate = "/submission/v{0}/product/{1}/listings/assets";
        public static readonly string ListingAssetsCreateUrlTemplate = "/submission/v{0}/product/{1}/listings/assets/create";
        public static readonly string ListingAssetsCommitUrlTemplate = "/submission/v{0}/product/{1}/listings/assets/commit";
        public static readonly string ProductDraftStatusPollingUrlTemplate = "/submission/v{0}/product/{1}/status";
        public static readonly string CreateSubmissionUrlTemplate = "/submission/v{0}/product/{1}/submit";
        public static readonly string SubmissionStatusPollingUrlTemplate = "/submission/v{0}/product/{1}/submission/{2}/status";

        public const string JsonContentType = "application/json";
        public const string PngContentType = "image/png";
        public const string BinaryStreamContentType = "application/octet-stream";

        /// <summary>
        /// Initializes a new instance of the <see cref="SubmissionClient" /> class.
        /// </summary>
        /// <param name="accessToken">
        /// The access token. This is JWT a token obtained from Azure Active Directory allowing the caller to invoke the API
        /// on behalf of a user
        /// </param>
        /// <param name="serviceUrl">The service URL.</param>
        public SubmissionClient(string accessToken, string serviceUrl)
        {
            if (string.IsNullOrEmpty(accessToken))
            {
                throw new ArgumentNullException("accessToken");
            }

            if (string.IsNullOrEmpty(serviceUrl))
            {
                throw new ArgumentNullException("serviceUrl");
            }

            this.accessToken = accessToken;
            this.httpClient = new HttpClient
            {
                BaseAddress = new Uri(serviceUrl)
            };
            this.imageUploadClient = new HttpClient();
            this.DefaultHeaders = new Dictionary<string, string>();
        }

        /// <summary>
        /// Gets or Sets the default headers.
        /// </summary>
        public Dictionary<string, string> DefaultHeaders { get; set; }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting
        /// unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.httpClient != null)
            {
                this.httpClient.Dispose();
                this.httpClient = null;
                GC.SuppressFinalize(this);
            }
        }

        /// <summary>
        /// Gets the authorization token for the provided client id, client secret, and the scope.
        /// This token is usually valid for 1 hour, so if your submission takes longer than that to complete,
        /// make sure to get a new one periodically.
        /// </summary>
        /// <param name="tokenEndpoint">Token endpoint to which the request is to be made. Specific to your
        /// Azure Active Directory app. Example: https://login.microsoftonline.com/d454d300-128e-2d81-334a-27d9b2baf002/oauth2/v2.0/token </param>
        /// <param name="clientId">Client Id of your Azure Active Directory app. Example" 00001111-aaaa-2222-bbbb-3333cccc4444</param>
        /// <param name="clientSecret">Client secret of your Azure Active Directory app</param>
        /// <param name="scope">Scope. If not provided, default one is used for the production API endpoint.</param>
        /// <returns>Autorization token. Prepend it with "Bearer: " and pass it in the request header as the
        /// value for "Authorization: " header.</returns>
        public static async Task<string> GetClientCredentialAccessToken(
            string tokenEndpoint,
            string clientId,
            string clientSecret,
            string scope = null)
        {
            if (scope == null)
            {
                scope = "https://api.store.microsoft.com/.default";
            }

            dynamic result;
            using (HttpClient client = new HttpClient())
            {
                string tokenUrl = tokenEndpoint;
                using (
                    HttpRequestMessage request = new HttpRequestMessage(
                        HttpMethod.Post,
                        tokenUrl))
                {
                    string strContent =
                        string.Format(
                            "grant_type=client_credentials&client_id={0}&client_secret={1}&scope={2}",
                            clientId,
                            clientSecret,
                            scope);

                    request.Content = new StringContent(strContent, Encoding.UTF8,
                        "application/x-www-form-urlencoded");

                    using (HttpResponseMessage response = await client.SendAsync(request))
                    {
                        string responseContent = await response.Content.ReadAsStringAsync();
                        result = JsonConvert.DeserializeObject(responseContent);
                    }
                }
            }

            return result.access_token;
        }


        /// <summary>
        /// Invokes the specified HTTP method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="httpMethod">The HTTP method.</param>
        /// <param name="relativeUrl">The relative URL.</param>
        /// <param name="requestContent">Content of the request.</param>
        /// <returns>instance of the type T</returns>
        /// <exception cref="ServiceException"></exception>
        public async Task<T> Invoke<T>(HttpMethod httpMethod,
            string relativeUrl,
            object requestContent)
        {
            using (var request = new HttpRequestMessage(httpMethod, relativeUrl))
            {
                this.SetRequest(request, requestContent);

                using (HttpResponseMessage response = await this.httpClient.SendAsync(request))
                {
                    T result;
                    if (this.TryHandleResponse(response, out result))
                    {
                        return result;
                    }

                    if (response.IsSuccessStatusCode)
                    {
                        var resource = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
                        return resource;
                    }

                    throw new Exception(await response.Content.ReadAsStringAsync());
                }
            }
        }

        /// <summary>
        /// Uploads a given Image Asset file to Asset Storage
        /// </summary>
        /// <param name="assetUploadUrl">Asset Storage Url</param>
        /// <param name="fileStream">The Stream instance of file to be uploaded</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task UploadAsset(string assetUploadUrl, Stream fileStream)
        {
            using (var request = new HttpRequestMessage(HttpMethod.Put, assetUploadUrl))
            {
                request.Headers.Add("x-ms-blob-type", "BlockBlob");
                request.Content = new StreamContent(fileStream);
                request.Content.Headers.ContentType = new MediaTypeHeaderValue(PngContentType);
                using (HttpResponseMessage response = await this.imageUploadClient.SendAsync(request))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        return;
                    }
                    throw new Exception(await response.Content.ReadAsStringAsync());
                }
            }
        }

        /// <summary>
        /// Sets the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="requestContent">Content of the request.</param>
        protected virtual void SetRequest(HttpRequestMessage request, object requestContent)
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.accessToken);

            foreach (var header in this.DefaultHeaders)
            {
                request.Headers.Add(header.Key, header.Value);
            }

            if (requestContent != null)
            {
                request.Content = new StringContent(JsonConvert.SerializeObject(requestContent),
                        Encoding.UTF8,
                        JsonContentType);
                
            }
        }


        /// <summary>
        /// Tries the handle response.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="response">The response.</param>
        /// <param name="result">The result.</param>
        /// <returns>true if the response was handled</returns>
        protected virtual bool TryHandleResponse<T>(HttpResponseMessage response, out T result)
        {
            result = default(T);
            return false;
        }
    }
}

Node.js пример: API отправки Microsoft Store для приложения MSI или EXE

В этой статье содержатся Node.js примеры кода, демонстрирующие использование API отправки Microsoft Store для MSI или EXE-приложения. Вы можете просмотреть каждый пример, чтобы узнать больше о демонстрируемой задаче или создать все примеры кода, приведенные в этой статье, в консольное приложение.

Необходимые условия в этих примерах используют следующую библиотеку:

  • node-fetch версии 2 [npm install node-fetch@2]

Создание отправки приложения с помощью node.js

Следующий пример вызывает другие методы в этой статье, чтобы продемонстрировать различные способы использования API отправки в Microsoft Store. Чтобы адаптировать эту программу для собственного использования, выполните указанные ниже действия.

  • Назначьте свойство SellerId идентификатору учетной записи Центра партнеров.
  • Назначьте свойство ApplicationId идентификатору приложения, которым требуется управлять.
  • Назначьте свойства ClientId и ClientSecret идентификатору клиента и ключу приложения и замените строку tenantid в URL-адресе TokenEndpoint идентификатором клиента для приложения. Дополнительные сведения см. в разделе Порядок связывания приложения Azure AD с учетной записью Центра партнеров

В следующем примере реализуется класс, использующий несколько методов в API отправки Microsoft Store для обновления отправки приложения.

const config = require('./Configuration');
const submissionClient = require('./SubmissionClient');
const fs = require('fs');

var client = new submissionClient(config);

/**
 * Main entry method to Run the Store Submission API Node.js Sample
 */
async function RunNodeJsSample(){
    print('Getting Access Token');
    await client.getAccessToken();
    
    print('Getting Current Application Draft Status');
    var currentDraftStatus = await client.callStoreAPI(client.productDraftStatusPollingUrlTemplate, 'get');
    print(currentDraftStatus);

    print('Getting Application Packages');
    var currentPackages = await client.callStoreAPI(client.packagesUrlTemplate, 'get');
    print(currentPackages);

    print('Getting Single Package');
    var packageId = currentPackages.responseData.packages[0].packageId;
    var packageIdUrl = `${client.packageByIdUrlTemplate}`.replace('{packageId}', packageId);
    var singlePackage = await client.callStoreAPI(packageIdUrl, 'get');
    print(singlePackage);

    print('Updating Entire Package Set');
    // Update data in Packages list to have final set of updated Packages
    currentPackages.responseData.packages[0].installerParameters = "/s /r new-args";
    var packagesUpdateRequest = {
        'packages': currentPackages.responseData.packages
    };
    print(packagesUpdateRequest);
    var packagesUpdateResponse = await client.callStoreAPI(client.packagesUrlTemplate, 'put', packagesUpdateRequest);
    print(packagesUpdateResponse);

    print('Updating Single Package\'s Download Url');
    // Update data in the SinglePackage object
    singlePackage.responseData.packages[0].installerParameters = "/s /r /t new-args";
    var singlePackageUpdateResponse = await client.callStoreAPI(packageIdUrl, 'patch', singlePackage.responseData.packages[0]);
    print(singlePackageUpdateResponse);

    print('Committing Packages');
    var commitPackagesResponse = await client.callStoreAPI(client.packagesCommitUrlTemplate, 'post');
    print(commitPackagesResponse);

    await poll(async ()=>{
        print('Waiting for Upload to finish');
        return await client.callStoreAPI(client.productDraftStatusPollingUrlTemplate, 'get');
    }, 2);

    print('Getting Application Metadata - All Modules');
    var appMetadata = await client.callStoreAPI(client.appMetadataUrlTemplate, 'get');
    print(appMetadata);

    print('Getting Application Metadata - Listings');
    var appListingMetadata = await client.callStoreAPI(client.appListingsFetchMetadataUrlTemplate, 'get');
    print(appListingMetadata);

    print('Updating Listings Metadata - Description');   
    // Update Required Fields in Listings Metadata Object - Per Language. For eg. AppListingsMetadata.responseData.listings[0]
    // Example - Updating Description
    appListingMetadata.responseData.listings[0].description = 'New Description Updated By Node.js Sample Code';
    var listingsUpdateRequest = {
        'listings': appListingMetadata.responseData.listings[0]
    };
    var listingsMetadataUpdateResponse = await client.callStoreAPI(client.appMetadataUrlTemplate, 'put', listingsUpdateRequest);
    print(listingsMetadataUpdateResponse);

    print('Getting All Listings Assets');
    var listingAssets = await client.callStoreAPI(client.listingAssetsUrlTemplate, 'get');
    print(listingAssets);

    print('Creating Listing Assets for 1 Screenshot');
    var listingAssetCreateRequest = {
        'language': listingAssets.responseData.listingAssets[0].language,
        'createAssetRequest': {
            'Screenshot': 1,
            'Logo': 0
        }
    };
    var listingAssetCreateResponse = await client.callStoreAPI(client.listingAssetsCreateUrlTemplate, 'post', listingAssetCreateRequest);
    print(listingAssetCreateResponse);

    print('Uploading Listing Assets');
    const pathToFile = './Image.png';
    const stats = fs.statSync(pathToFile);
    const fileSize = stats.size;
    const fileStream = fs.createReadStream(pathToFile);
    await client.uploadAssets(listingAssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl, fileStream, fileSize);

    print('Committing Listing Assets');
    var assetCommitRequest = {
        'listingAssets': {
            'language': listingAssets.responseData.listingAssets[0].language,
            'storeLogos': listingAssets.responseData.listingAssets[0].storeLogos,
            'screenshots': [{
                'id': listingAssetCreateResponse.responseData.listingAssets.screenshots[0].id,
                'assetUrl': listingAssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl
            }]
        }
    };
    var assetCommitResponse = await client.callStoreAPI(client.listingAssetsCommitUrlTemplate, 'put', assetCommitRequest);
    print(assetCommitResponse);

    print('Getting Current Application Draft Status before Submission');
    currentDraftStatus = await client.callStoreAPI(client.productDraftStatusPollingUrlTemplate, 'get');
    print(currentDraftStatus);
    if(!currentDraftStatus.responseData.isReady){
        throw new Error('Application Current Status is not in Ready Status for All Modules');
    }

    print('Creating Submission');
    var submissionCreationResponse = await client.callStoreAPI(client.createSubmissionUrlTemplate, 'post');
    print(submissionCreationResponse);

    print('Current Submission Status');
    var submissionStatusUrl = `${client.submissionStatusPollingUrlTemplate}`.replace('{submissionId}', submissionCreationResponse.responseData.submissionId);
    var submissionStatusResponse = await client.callStoreAPI(submissionStatusUrl, 'get');
    print(submissionStatusResponse);

    // User can Poll on this API to know if Submission Status is INPROGRESS, PUBLISHED or FAILED.
    // This Process involves File Scanning, App Certification and Publishing and can take more than a day.
}

/**
 * Utility Method to Poll using a given function and time interval in seconds
 * @param {*} func 
 * @param {*} intervalInSeconds 
 * @returns 
 */
async function poll(func, intervalInSeconds){
var result = await func();
if(result.responseData.isReady){
    Promise.resolve(true);
}
else if(result.errors && result.errors.length > 0 && result.errors.find(element => element.code == 'packageuploaderror') != undefined){
throw new Error('Package Upload Failed');
}
else{
    await new Promise(resolve => setTimeout(resolve, intervalInSeconds*1000));
    return await poll(func, intervalInSeconds); 
}
}

/**
 * Utility function to Print a Json or normal string
 * @param {*} json 
 */
function print(json){
    if(typeof(json) == 'string'){
        console.log(json);
    }
    else{
        console.log(JSON.stringify(json));
    }
    console.log("\n");
}

/** Run the Node.js Sample Application */
RunNodeJsSample();

Вспомогательный помощник ClientConfiguration

В примере приложения используется вспомогательный класс ClientConfiguration для передачи данных Azure Active Directory и данных приложения в каждый из примеров методов, использующих API отправки Microsoft Store.

/** Configuration Object for Store Submission API */
var config = {
    version : "1",
    applicationId : "...",
    clientId : "...",
    clientSecret : "...",
    serviceEndpoint : "https://api.store.microsoft.com",
    tokenEndpoint : "...",
    scope : "https://api.store.microsoft.com/.default",
    sellerId : "...",
    jsonContentType : "application/json",
    pngContentType : "image/png",
    binaryStreamContentType : "application/octet-stream"
};

module.exports = config;

Вспомогательный помощник IngestionClient с помощью node.js

Класс IngestionClient предоставляет вспомогательные методы, которые используются другими методами в примере приложения для выполнения следующих задач:

  • Получите маркер доступа Azure AD, который можно использовать для вызова методов в API отправки Microsoft Store. После получения маркера у вас есть 60 минут для использования этого маркера в вызовах API отправки Microsoft Store до истечения срока действия маркера. После истечения срока действия маркера можно создать новый маркер.
  • Обработайте HTTP-запросы для API отправки Microsoft Store.
const fetch = require('node-fetch');
/**
 * Submission Client to invoke all available Store Submission API and Asset Upload to Blob Store
 */
class SubmissionClient{

    constructor(config){
        this.configuration = config;
        this.accessToken = "";
        this.packagesUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/packages`;
        this.packageByIdUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/packages/{packageId}`;
        this.packagesCommitUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/packages/commit`;
        this.appMetadataUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/metadata`;
        this.appListingsFetchMetadataUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/metadata/listings`;
        this.listingAssetsUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/listings/assets`;
        this.listingAssetsCreateUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/listings/assets/create`;
        this.listingAssetsCommitUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/listings/assets/commit`;
        this.productDraftStatusPollingUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/status`;
        this.createSubmissionUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/submit`;
        this.submissionStatusPollingUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/submission/{submissionId}/status`;
    }
    
    async getAccessToken(){
        var params = new URLSearchParams();
        params.append('grant_type','client_credentials');
        params.append('client_id',this.configuration.clientId);
        params.append('client_secret',this.configuration.clientSecret);
        params.append('scope',this.configuration.scope);
        var response = await fetch(this.configuration.tokenEndpoint,{
            method: "POST",
            body: params
        });    
        var data = await response.json();
        this.accessToken = data.access_token;
    }

    async callStoreAPI(url, method, data){
        var request = {
            method: method,
            headers:{
                'Authorization': `Bearer ${this.accessToken}`,
                'Content-Type': this.configuration.jsonContentType,
                'X-Seller-Account-Id': this.configuration.sellerId
            },            
        };
        if(data){
            request.body = JSON.stringify(data);
        }
        var response = await fetch(`${this.configuration.serviceEndpoint}${url}`,request);
        var jsonResponse = await response.json();
        return jsonResponse;
    }

    async uploadAssets(url, stream, size){
        var request = {
            method: 'put',
            headers:{
                'Content-Type': this.configuration.pngContentType,
                'x-ms-blob-type': 'BlockBlob',
                "Content-length": size
            },            
            body: stream
        };
        var response = await fetch(`${url}`,request);
        if(response.ok){
            return response;
        }
        else{
            throw new Error('Uploading of assets failed');
        }
    }
}
module.exports = SubmissionClient;

Дополнительная информация

Если у вас есть вопросы об API отправки Microsoft Store или вам нужна помощь по управлению отправками с помощью этого API, используйте следующие ресурсы:

  • Задайте свои вопросы на наших форумах.
  • Посетите страницу поддержки и попросите один из вариантов поддержки центра партнеров. Если вам будет предложено выбрать тип проблемы и категорию, выберите "Отправка приложения" и "Сертификация" и "Отправка приложения" соответственно.