API отправки Microsoft Store для приложения MSI или EXE
Используйте API отправки Microsoft Store для MSI или EXE-приложения для программного запроса и создания отправки приложений MSI или EXE для учетной записи Центра партнеров вашей организации. Этот API полезен, если ваша учетная запись управляет множеством приложений, и вы хотите автоматизировать и оптимизировать процесс отправки для этих ресурсов. Этот API использует Azure Active Directory (Azure AD) для проверки подлинности вызовов из приложения или службы.
Следующие шаги описывают комплексный процесс использования API отправки Microsoft Store:
- Убедитесь, что вы выполнили все предварительные требования.
- Перед вызовом метода в API отправки Microsoft Store получите маркер доступа Azure AD. После получения маркера у вас есть 60 минут для использования этого маркера в вызовах API отправки Microsoft Store до истечения срока действия маркера. После истечения срока действия маркера можно создать новый маркер.
- Вызовите 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.
- В Центре партнеров свяжите учетную запись Центра партнеров своей организации с каталогом Azure AD организации.
- Затем на странице "Пользователи" в разделе параметров учетной записи Центра партнеров добавьте приложение Azure AD, представляющее приложение или службу, которую вы будете использовать для доступа к отправке для учетной записи Центра партнеров. Убедитесь, что этому приложению назначена роль Менеджер. Если приложение еще не существует в каталоге Azure AD, можно создать новое приложение Azure AD в Центре партнеров.
- Вернитесь на страницу Пользователи, щелкните имя приложения Azure AD, чтобы перейти к параметрам приложения, и скопируйте идентификатор арендатора и идентификатор клиента.
- Чтобы добавить новый ключ или секрет клиента, см. следующие инструкции или инструкции по регистрации приложения на портале Azure.
Чтобы зарегистрировать приложение, выполните приведенные действия.
Войдите на портал Azure.
Если у вас есть доступ к нескольким арендаторам, в верхнем меню используйте фильтр Directories + subscriptions (Каталоги и подписки) , чтобы выбрать арендатор, в котором следует зарегистрировать приложение.
Найдите и выберите Azure Active Directory.
В разделе "Управление" выберите Регистрация приложений > Выберите приложение.
Выберите сертификаты и секреты > > секретов клиента New client secret.
Добавьте описание секрета клиента.
Выберите срок действия секрета или укажите настраиваемое время существования.
Время существования секрета клиента ограничено двумя годами (24 месяца) или меньше. Для настраиваемого времени существования нельзя задать значение, превышающее 24 месяца.
Примечание.
Корпорация Майкрософт рекомендует задать значение срока действия менее 12 месяцев.
Выберите Добавить.
Запишите значение секрета, чтобы затем использовать его в коде клиентского приложения. Это значение секрета больше нигде не отображается после закрытия страницы.
Шаг 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 фиксации после успешной отправки. Чтобы иметь возможность обновлять ресурсы списка, и, в свою очередь, иметь возможность добавлять и удалять языковые стандарта в модуле перечисления, можно использовать следующий подход:
- Используйте API создания списка активов для отправки запросов относительно отправки ресурсов вместе с языком, типом и количеством ресурсов.
- На основе количества запрошенных ресурсов идентификаторы активов создаются по запросу и будут создавать краткосрочный URL-адрес SAS и отправлять его обратно в текст ответа в соответствии с типом активов. Этот URL-адрес можно использовать для отправки ресурсов изображений определенного типа с помощью HTTP-клиентов [Put BLOB-объект (REST API) — служба хранилища Azure | Документация Майкрософт].
- После отправки вы можете использовать API "Зафиксировать активы" для отправки новых сведений об идентификаторе ресурса, полученных ранее из предыдущего вызова API. Один API будет внутренне фиксировать данные о ресурсах перечисления после проверки.
- Этот подход эффективно перезаписывает весь набор предыдущих образов типа ресурса в определенном языке, который отправляется в запросе. Следовательно, ранее загруженные ресурсы будут удалены.
Путь: /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, используйте следующие ресурсы:
- Задайте свои вопросы на наших форумах.
- Посетите страницу поддержки и попросите один из вариантов поддержки центра партнеров. Если вам будет предложено выбрать тип проблемы и категорию, выберите "Отправка приложения" и "Сертификация" и "Отправка приложения" соответственно.
Windows developer