API de presentación de Microsoft Store para aplicaciones MSI o EXE

Utilice la API de envío de Microsoft Store para aplicaciones MSI o EXE para consultar y crear envíos mediante programación para aplicaciones MSI o EXE para la cuenta del Centro de partners de su organización. Esta API es útil si su cuenta administra muchas aplicaciones y desea automatizar y optimizar el proceso de envío de estos recursos. Esta API usa microsoft Entra ID para autenticar las llamadas desde la aplicación o el servicio.

En los pasos siguientes se describe el proceso completo para utilizar la API de envío de Microsoft Store:

  1. Asegúrese de haber completado todos los requisitos previos.
  2. Antes de llamar a un método de la API de envío de Microsoft Store, obtén un token de acceso de Microsoft Entra ID. Después de obtener un token, tiene 60 minutos para utilizar este token en llamadas a la API de envío de Microsoft Store antes de que el token expire. Después de que el token expire, puede generar uno nuevo.
  3. Llame a la API de envío de Microsoft Store para la aplicación MSI o EXE.

Paso 1: Completar los requisitos previos para utilizar la API de envío de Microsoft Store

Antes de empezar a escribir código para llamar a la API de presentación de Microsoft Store para una aplicación MSI o EXE, asegúrate de haber cumplido los siguientes requisitos previos.

  • Usted (o su organización) debe tener un directorio de id. de Microsoft Entra y debe tener permiso de administrador global para el directorio. Si ya usa Microsoft 365 u otros servicios empresariales de Microsoft, ya tiene el directorio Microsoft Entra ID. De lo contrario, puede crear un nuevo identificador de Microsoft Entra en el Centro de partners sin ningún cargo adicional.
  • Debe asociar una aplicación de Microsoft Entra ID a su cuenta de Partner Center y obtener el identificador del inquilino, el identificador de cliente y la clave. Necesitas estos valores para obtener un token de acceso de Microsoft Entra ID, que utilizarás en las llamadas a la API de envío de Microsoft Store.
  • Prepare la aplicación para utilizarla con la API de envío de Microsoft Store:

Cómo asociar una aplicación de Microsoft Entra ID con su cuenta del Centro de partners

Antes de poder usar la API de envío de Microsoft Store para una aplicación MSI o EXE, debes asociar una aplicación de Microsoft Entra ID a tu cuenta de Partner Center, obtener el id. del inquilino y el id. de cliente de la aplicación, y generar una clave. La aplicación Microsoft Entra ID representa la aplicación o servicio desde la que quieres llamar a la API de envío de Microsoft Store. Necesitas el ID de inquilino, el ID de cliente y la clave para obtener un token de acceso de Microsoft Entra ID que pasarás a la API.

Nota:

Solo tiene que realizar esta tarea una vez. Después de tener el identificador de inquilino, el identificador de cliente y la clave, puede reutilizarlos cada vez que necesite crear un nuevo token de acceso de Id. de Microsoft Entra.

  1. En Partner Center, asocie la cuenta de Partner Center de su organización al directorio de Microsoft Entra ID de su organización.
  2. A continuación, en la página Usuarios de la sección Configuración de la cuenta de Partner Center, agregue la aplicación de Microsoft Entra ID que representa la aplicación o el servicio que usará para acceder a las presentaciones de su cuenta de Partner Center. Asegúrese de asignar a esta aplicación el rol Administrador. Si la aplicación todavía no existe en su directorio de Microsoft Entra ID, puede crear una nueva aplicación de Microsoft Entra ID en Partner Center.
  3. Vuelva a la página Usuarios, haga clic en el nombre de la aplicación Microsoft Entra ID para ir a la configuración de la aplicación y copie los valores id. de inquilino e Id. de cliente.
  4. Para agregar una nueva clave o un secreto de cliente, consulte las instrucciones siguientes o consulte las instrucciones para registrar la aplicación a través de Azure Portal:

Para registrar la aplicación:

  1. Inicie sesión en Azure Portal.

  2. Si tiene acceso a varios inquilinos, use el filtro Directorios y suscripciones del menú superior para ir al inquilino en el que quiere registrar la aplicación.

  3. Busque y seleccione el directorio de Microsoft Entra ID.

  4. En Administrar, seleccione Registros de aplicaciones > Seleccionar la aplicación.

  5. Seleccione Certificados y secretos > Secretos de cliente > Nuevo secreto de cliente.

  6. Agregue una descripción para el secreto de cliente.

  7. Seleccione una expiración para el secreto o especifique una duración personalizada.

  8. La duración del secreto de cliente se limita a dos años (24 meses) o menos. No se puede especificar una duración personalizada superior a 24 meses.

    Nota:

    Microsoft recomienda establecer un valor de expiración de menos de 12 meses.

  9. Seleccione Agregar.

  10. Registre el valor del secreto para su uso en el código de la aplicación cliente. Este valor secreto no se volverá a mostrar una vez que abandone esta página.

Paso 2: Obtener un token de acceso de Microsoft Entra ID

Antes de llamar a cualquiera de los métodos de la API de envío a Microsoft Store para aplicaciones MSI o EXE, debes obtener primero un token de acceso de Microsoft Entra ID que deberás pasar al encabezado Authorization de cada método de la API. Una vez que haya obtenido un token de acceso, tiene 60 minutos para usarlo antes de que expire. Una vez que expire el token, puede actualizarlo para poder seguir utilizándolo en llamadas adicionales a la API.

Para obtener el token de acceso, siga las instrucciones de Service to Service Calls Using Client Credentials para enviar una solicitud HTTP POST al punto de conexión https://login.microsoftonline.com/<tenant_id>/oauth2/token. Esta es una solicitud de ejemplo.

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

Para el valor tenant_id en el URI POST y los parámetros client_id y client_secret, especifique el identificador del arrendatario, el identificador de cliente y la clave de su aplicación que recuperó de Partner Center en la sección anterior. Para el parámetro scope, tiene que especificar https://api.store.microsoft.com/.default.

Una vez que expire el token de acceso, puede seguir las instrucciones que se indican aquí para actualizarlo.

Para obtener ejemplos que muestran cómo obtener un token de acceso mediante C# o Node.js, consulte los ejemplos de código para la API de envío de Microsoft Store para aplicaciones MSI o EXE.

Paso 3: Utilizar la API de envío de Microsoft Store

Una vez que dispongas de un token de acceso de Microsoft Entra ID, podrás llamar a los métodos de la API de envío a Microsoft Store para aplicaciones MSI o EXE. La API incluye muchos métodos que se agrupan en escenarios para las aplicaciones. Para crear o actualizar envíos, normalmente se invocan varios métodos en un orden específico. Para obtener información acerca de cada escenario y la sintaxis de cada método, consulte las secciones siguientes:

Nota:

Después de obtener un token de acceso, tiene 60 minutos para llamar a los métodos en la API de envío de Microsoft Store para aplicaciones MSI o EXE antes de que el token expire.

URL base

La URL base de la API de envío de Microsoft Store para aplicaciones MSI o EXE es https://api.store.microsoft.com

Contratos de API

API Get Current Draft Submission Metadata

Obtiene los metadatos de cada módulo (listados, propiedades o disponibilidad) de la presentación actual en borrador.

Ruta de acceso [todos los módulos]: /submission/v1/product/{productId}/metadata?languages={languages}&includelanguagelist={true/false}
Ruta de acceso [módulo único]: /submission/v1/product/{productId}/metadata/{moduleName}?languages={languages}&includelanguagelist={true/false}
Método: GET

Parámetros de la ruta de acceso

Parámetro Descripción
ID de producto El ID de Partner Center del producto
nombreDelMódulo Módulo del Centro de partners: descripciones, propiedades o disponibilidad

Parámetros de consulta

Parámetro Descripción
Idiomas Opcional Los idiomas de descripción se filtran como cadena separada por comas [límite de hasta 200 idiomas].

Si no está presente, se recuperan los metadatos de los primeros 200 idiomas de descripción disponibles. [Por ejemplo, "en-us, en-gb"].
includelanguagelist Opcional Booleano: si es verdadero, devuelve la lista de idiomas de descripción agregados y su estado de integridad.

Encabezados obligatorios

Encabezado Valor
Authorization: Bearer <Token> El ID de aplicación de Microsoft Entra ID registrado en la cuenta de Centro de socios
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Valor
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After El tiempo en segundos que el cliente debe esperar antes de volver a llamar a las API debido a la limitación de tasa.

Parámetros de respuesta

Nombre Tipo Descripción
soporte de accesibilidad Booleano
términos adicionales de la licencia Cadena
disponibilidad Objeto Datos del módulo de disponibilidad
categoría Cadena Consulte la lista de categorías más adelante
notas de certificación Cadena
código Cadena El código de error del mensaje.
información de contacto Cadena
derechos de autor Cadena
dependsOnDriversOrNT Booleano
descripción Cadena
developedBy Cadena
detectabilidad Cadena [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
freeTrial Cadena [NO_FREE_TRIAL, FREE_TRIAL]
hardwareItemType Cadena
¿Se requiere política de privacidad? Booleano
isRecommended Booleano
isRequired Booleano
isSuccess Booleano
isSystemFeatureRequired Matriz de objetos
lenguaje Cadena Consulte la lista de idiomas más adelante
Listados Matriz de objetos Datos del módulo de descripciones para cada idioma
mercados Matriz de cadenas Consulte la lista de mercados más adelante
Mensaje Cadena Descripción del error
minimumHardware Cadena
requisito mínimo Cadena
compatibilidad con lápiz y tinta Booleano
Precios Cadena [FREE, FREEMIUM, SUBSCRIPTION, PAID]
privacyPolicyUrl Cadena
productDeclarations Objeto
características del producto Matriz de cadenas
Propiedades Objeto Datos del módulo de propiedades
recommendedHardware Cadena
requisito recomendado Cadena
responseData Objeto Contiene la carga de respuesta real para la solicitud
requisitos Matriz de objetos
términos de búsqueda Matriz de cadenas
descripción breve Cadena
subcategoría Cadena Consulte la lista de subcategorías más adelante
supportContactInfo Cadena
systemRequirementDetails Matriz de objetos
Destino Cadena Entidad desde la que se originó el error
sitio web Cadena
Novedades Cadena

API Update Current Draft Submission Metadata

Actualiza los metadatos en cada módulo bajo el borrador de envío. La API verifica

  • Para el envío activo. Si existe, error con el mensaje de error.
  • Si todos los módulos están en estado listo para permitir la operación Guardar borrador.
  • Cada campo de la solicitud se valida de acuerdo con los requisitos de la Tienda.
  • Reglas de validación de detalles de los requisitos del sistema:
    • Valores permitidos en hardwareItemType = Memory: 300 MB, 750 MB, 1 GB, 2 GB, 4 GB, 6 GB, 8 GB, 12 GB, 16 GB, 20 GB
    • Valores permitidos en hardwareItemType = DirectX: DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12
    • Valores permitidos en hardwareItemType = Video_Memory: 1 GB, 2 GB, 4 GB, 6 GB

Ruta de acceso [actualización del módulo completo]: /submission/v1/product/{productId}/metadata
Método: PUT

Ruta de acceso [Actualización del parche del módulo]: /submission/v1/product/{productId}/metadata
Método: PATCH

Comportamiento de la API

En el caso de la API de actualización completa del módulo, es necesario que todos los datos del módulo estén presentes en la solicitud para la actualización completa de cada campo. Cuando un campo no esté presente en la solicitud, su valor predeterminado se usará para sobrescribir el valor actual de ese módulo específico.
En el caso de la API de actualización del módulo Patch, solo es necesario incluir en la solicitud los campos que se vayan a actualizar. Estos valores de campos de la solicitud sobrescribirán sus valores existentes y mantendrán todos los demás campos que no están presentes en la solicitud igual que los actuales para ese módulo específico.

Parámetros de la ruta de acceso

Parámetro Descripción
ID de producto ID de Partner Center del producto

Encabezados obligatorios

Encabezado Valor
Authorization: Bearer <Token> El ID de aplicación de Microsoft Entra ID registrado en la cuenta de Centro de socios
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Parámetros de la solicitud

Nombre Tipo Descripción
disponibilidad Objeto Objeto para contener los metadatos del módulo de disponibilidad
mercados Matriz de cadenas Obligatorio Consulte la lista de mercados más adelante
detectabilidad Cadena Obligatorio [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Booleano Obligatorio
Precios Cadena Obligatorio [FREE, FREEMIUM, SUBSCRIPTION, PAID]
freeTrial Cadena Obligatorio si el precio es DE PAGO o SUSCRIPCIÓN [NO_FREE_TRIAL, FREE_TRIAL]
Propiedades Objeto Objeto para contener los metadatos del módulo de propiedades
¿Se requiere política de privacidad? Booleano Obligatorio
privacyPolicyUrl Cadena Obligatorio si isPrivacyPolicyRequired = true Debe ser una dirección URL válida
sitio web Cadena Debe ser una dirección URL válida
supportContactInfo Cadena Debe ser una dirección URL o dirección de correo electrónico válida
notas de certificación Cadena Recomendado Límite de caracteres = 2000
categoría Cadena Obligatorio Consulte la lista de categorías más adelante
subcategoría Cadena Obligatorio Consulte la lista de subcategorías más adelante
productDeclarations Objeto Obligatorio
isSystemFeatureRequired Matriz de objetos [Táctil, Teclado, Ratón, Cámara, NFC_HCE, NFC_Proximity, Bluetooth_LE, Telefonía, Micrófono]
isRequired Booleano Obligatorio
isRecommended Booleano Obligatorio
hardwareItemType Cadena Obligatorio
systemRequirementDetails Matriz de objetos [Procesador, gráficos, memoria, DirectX, Video_Memory]
requisito mínimo Cadena Obligatorio Para systemRequirementsText, MaxLength = 200

Valores permitidos en hardwareItemType = Memory: [300 MB, 750 MB, 1 GB, 2 GB, 4 GB, 6 GB, 8 GB, 12 GB, 16 GB, 20 GB]

Valores permitidos en hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Valores permitidos en hardwareItemType = Video_Memory: [1 GB, 2 GB, 4 GB, 6 GB]
requisito recomendado Cadena Obligatorio Para systemRequirementsText, MaxLength = 200

Valores permitidos en hardwareItemType = Memory: [300 MB, 750 MB, 1 GB, 2 GB, 4 GB, 6 GB, 8 GB, 12 GB, 16 GB, 20 GB]

Valores permitidos en hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Valores permitidos en hardwareItemType = Video_Memory: [1 GB, 2 GB, 4 GB, 6 GB]
dependsOnDriversOrNT Booleano Obligatorio
soporte de accesibilidad Booleano Obligatorio
compatibilidad con lápiz y tinta Booleano Obligatorio
Listados Objeto Objeto para los datos del módulo de descripciones para un único idioma
lenguaje Cadena Obligatorio Consulte la lista de idiomas más adelante
descripción Cadena Obligatorio Límite de caracteres = 10000
Novedades Cadena Límite de caracteres = 1500
características del producto Matriz de cadena 200 caracteres por característica; hasta 20 características
descripción breve Cadena Límite de caracteres = 1000
términos de búsqueda Matriz de cadena 30 caracteres por término de búsqueda; hasta 7 términos de búsqueda

21 palabras únicas en total entre todos los términos de búsqueda
términos adicionales de la licencia Cadena Obligatorio Límite de caracteres = 10000
derechos de autor Cadena Límite de caracteres = 200
developedBy Cadena Límite de caracteres = 255
requisitos Matriz de objetos 200 caracteres por elemento; hasta 11 elementos en total entre el mínimo y el recomendado]
minimumHardware Cadena Límite de caracteres = 200
recommendedHardware Cadena Límite de caracteres = 200
información de contacto Cadena Límite de caracteres = 200
listingsToAdd Matriz de cadenas Consulte la lista de idiomas más adelante
listadosParaEliminar Matriz de cadenas Consulte la lista de idiomas más adelante

Mercados

Ver tabla | Mercado | Abreviatura | |--------|------------- -| | Afganistán | AF | | Albania | AL | | Argelia | DZ | | Samoa Americana | AS | | Andorra | AD | | Angola | AO | | Anguila | AI | | Antártida | AQ | | Antigua y Barbuda | AG | | Argentina | AR | | Armenia | AM | | Aruba | AW | | Australia | AU | | Austria | AT | | Azerbaiyán | AZ | | Bahamas | BS | | Baréin | BH | | Bangladés | BD | | Barbados | BB | | República de Belarús | BY | | Bélgica | BE | | Belice | BZ | | Benín | BJ | | Bermudas | BM | | Bután | BT | | República Bolivariana de Venezuela | VE | | Bolivia | BO | | Bonaire | BQ | | Bosnia y Herzegovina | BA | | Botsuana | BW | | Isla Bouvet | BV | | Brasil | BR | | Territorio Británico del Océano Índico | IO | | Islas Vírgenes Británicas | VG | | Brunei | BN | | Bulgaria | BG | | Burkina Faso | BF | | Burundi | BI | | Camboya | KH | | Camerún | CM | | Canadá | CA | | Cabo Verde | CV | | Islas Caimán | KY | | República Centroafricana | CF | | Chad | TD | | Chile | CL | | China | CN | | Isla de Navidad | CX | | Islas Cocos (Keeling) | CC | | Colombia | CO | | Comoras | KM | | Congo | CG | | Congo (RDC) | CD | | Islas Cook | CK | | Costa Rica | CR | | Croacia | HR | | Curazao | CW | | Chipre | CY | | República Checa | CZ | | República de Côte d'Ivore | CI | | Dinamarca | DK | | Yibuti | DJ | | Dominica | DM | | República Dominicana | DO | | Ecuador | EC | | Egipto | EG | | El Salvador | SV | | Guinea Ecuatorial | GQ | | Eritrea | ER | | Estonia | EE | | Etiopía | ET | | Islas Malvinas | FK | | Islas Feroe | FO | | Fiyi | FJ | | Finlandia | FI | | Francia | FR | | Guayana Francesa | GF | | Polinesia Francesa | PF | | Territorios Franceses del Sur y Antárticos | TF | | Gabón | GA | | Gambia | GM | | Georgia | GE | | Alemania | DE | | Ghana | GH | | Gibraltar | GI | | Grecia | GR | | Groenlandia | GL | | Granada | GD | | Guadalupe | GP | | Guam | GU | | Guatemala | GT | | Guernsey | GG | | Guinea | GN | | Guinea -Bissau | GW | | Guyana | GY | | Haití | HT | | Isla Heard y las Islas McDonald | HM | | Ciudad del Vaticano | VA | | Honduras | HN | | RAE de Hong Kong | HK | | Hungría | HU | | Islandia | IS | | India | IN | | Indonesia | ID | | Irak | IQ | | Irlanda | IE | | Israel | IL | | Italia | IT | | Jamaica | JM | | Japón | JP | | Jersey | JE | | Jordania | JO | | Kazajistán | KZ | | Kenia | KE | | Kiribati | KI | | Corea | KR | | Kuwait | KW | | Kirguistán | KG | | Laos | LA | | Letonia | LV | | Líbano | LB | | Lesoto | LS | | Liberia | LR | | Libia | LY | | Liechtenstein | LI | | Lituania | LT | | Luxemburgo | LU | | RAE de Macao | MO | | Macedonia del Norte | MK | | Madagascar | MG | | Malaui | MW | | Malasia | MY | | Maldivas | MV | | Malí | ML | | Malta | MT | | Isla de Man | IM | | Islas Marshall | MH | | Martinica | MQ | | Mauritania | MR | | Mauricio | MU | | Mayotte | YT | | México | MX | | Micronesia | FM | | Moldavia | MD | | Mónaco | MC | | Mongolia | MN | | Montenegro | ME | | Montserrat | MS | | Marruecos | MA | | Mozambique | MZ | | Myanmar | MM | | Namibia | NA | | Nauru | NR | | Nepal | NP | | Países Bajos | NL | | Nueva Caledonia | NC | | Nueva Zelanda | NZ | | Nicaragua | NI | | Níger | NE | | Nigeria | NG | | Niue | NU | | Isla Norfolk | NF | | Islas Marianas del Norte | MP | | Noruega | NO | | Omán | OM | | Pakistán | PK | | Palau | PW | | Autoridad Palestina | PS | | Panamá | PA | | Papúa Nueva Guinea | PG | | Paraguay | PY | | Perú | PE | | Filipinas | PH | | Islas Pitcairn | PN | | Polonia | PL | | Portugal | PT | | Catar | QA | | Reunión | RE | | Rumanía | RO | | Rusia | RU | | Ruanda | RW | | San Bartolomé | BL | | Santa Elena, Ascensión y Tristán da Cunha | SH | | San Cristóbal y Nieves | KN | | Santa Lucía | LC | | San Martín (parte francesa) | MF | | San Pedro y Miquelón | PM | | San Vicente y las Granadinas | VC | | Samoa | WS | | San Marino | SM | | Arabia Saudí | SA | | Senegal | SN | | Serbia | RS | | Seychelles | SC | | Sierra Leona | SL | | Singapur | SG | | Sint Maarten (parte neerlandesa) | SX | | Eslovaquia | SK | | Eslovenia | SI | | Islas Salomón | SB | | Somalia | SO | | Sudáfrica | ZA | | Georgia del Sur y las Islas Sandwich del Sur | GS | | España | ES | | Sri Lanka | LK | | Surinam | SR | | Svalbard y Jan Mayen | SJ | | Suazilandia | SZ | | Suecia | SE | | Suiza | CH | | Santo Tomé y Príncipe | ST | | Taiwán | TW | | Tayikistán | TJ | | Tanzania | TZ | | Tailandia | TH | | República Democrática de Timor-Leste | TL | | Togo | TG | | Tokelau | TK | | Tonga | TO | | Trinidad y Tobago | TT | | Túnez | TN | | Turquía | TR | | Turkmenistán | TM | | Islas Turcas y Caicos | TC | | Tuvalu | TV | | Islas Menores Alejadas de EE. UU. | UM | | EE. UU. | VI | | Uganda | UG | | Ucrania | UA | | Emiratos Árabes Unidos | AE | | Reino Unido | GB | | Estados Unidos | US | | Uruguay | UY | | Uzbekistán | UZ | | Vanuatu | VU | | Vietnam | VN | | Wallis y Futuna | WF | | Yemen | YE | | Zambia | ZM | | Zimbabue | ZW | | Islas Åland | AX |

Categorías y subcategorías

Ver tabla | Categoría | Subcategorías | |-----------------------|---------------| | BooksAndReference | EReader, Fiction, Nonfiction, Reference | | Business | AccountingAndfinance, Collaboration, CRM, DataAndAnalytics, FileManagement, InventoryAndlogistics, LegalAndHR, ProjectManagement, RemoteDesktop, SalesAndMarketing, TimeAndExpenses | | DeveloperTools | Database, DesignTools, DevelopmentKits, Networking, ReferenceAndTraining, Servers, Utilities, WebHosting | Education | EducationBooksAndReference, EarlyLearning, InstructionalTools, Language, StudyAids | | Entertainment | (None) | | FoodAndDining | (None) | | GovernmentAndPolitics | (None) | | HealthAndFitness | (None) | | KidsAndFamily | KidsAndFamilyBooksAndReference, KidsAndFamilyEntertainment, HobbiesAndToys, SportsAndActivities, KidsAndFamilyTravel | | Lifestyle | Automotive, DYI, HomeAndGarden, Relationships, SpecialInterest, StyleAndFashion | | Medical | (None) | | MultimediaDesign | IllustrationAndGraphicDesign, MusicProduction, PhotoAndVideoProduction | | Music | (None) | | NavigationAndMaps | (None) | | NewsAndWeather | News, Weather | | PersonalFinance | BankingAndInvestments, BudgetingAndTaxes | | Personalization | RingtonesAndSounds, themes, WallpaperAndLockScreens | | PhotoAndVideo | (None) | | Productivity | (None) | | Security | PCProtection, PersonalSecurity | | Shopping | (None) | | Social | (None) | | Sports | (None) | | Travel | CityGuides, Hotels | | UtilitiesAndTools | BackupAndManage, FileManager |

Idiomas

Ver tabla | Nombre del idioma | Códigos de idioma admitidos | |---------------|------------------------- -| | afrikáans | af, af-za | | albanés | sq, sq-al | | amárico | am, am-et | | armenio | hy, hy-am | | asamés | as, as-in | | azerbaiyano | az-arab, az-arab-az, az-cyrl, az-cyrl-az, az-latn, az-latn-az | | euskera (euskera) | eu, eu-es | | bielorruso | be, be-by | | bengalí | bn, bn-bd, bn-in | | Bosnio | bs, bs-cyrl, bs-cyrl-ba, bs-latn, bs-latn-ba | | Búlgaro | bg, bg-bg | | Catalán | ca, ca-es, ca-es-valencia | | Cherokee | chr-cher, chr-cher-us, chr-latn | | Chino (simplificado) | zh-Hans, zh-cn, zh-hans-cn, zh-sg, zh-hans-sg | | Chino (tradicional) | 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 | | Croata | hr, hr-hr, hr-ba | | Checo | cs, cs-cz | | Danés | da, da-dk | | Dari | prs, prs-af, prs-arab | | Neerlandés | nl, nl-nl, nl-be | | Inglés | en, en-au, en-ca, en-gb, en-ie, en-in, en-nz, en-sg, en-us, en-za, en-bz, en-hk, en-id, en-jm, en-kz, en-mt, en-my, en-ph, en-pk, en-tt, en-vn, en-zw | | Estonio | et, et-ee | | Filipino | fil, fil-latn, fil-ph | | Finlandés | fi, fi-fi | | Francés | 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 | | Gallego | gl, gl-es | | Georgiano | ka, ka-ge | | Alemán | de, de-at, de-ch, de-de, de-lu, de-li | | Griego | el, el-gr | | Gujarati | gu, gu-in | | Hausa | ha, ha-latn, ha-latn-ng | | Hebreo | he, he-il | | Hindi | hi, hi-in | | Húngaro | hu, hu-hu | | Islandés | is, is-is | | Igb - ig-latn, ig-ng | | Indonesio | id, id-id | | Inuktitut (latín) | iu-cans, iu-latn, iu-latn-ca | | irlandés | ga, ga-ie | | isiXhosa | xh, xh-za | | isiZulu | zu, zu-za | | italiano | it, it-it, it-ch | | japonés | ja, ja-jp | | kannada | kn, kn-in | | kazajo | kk, kk-kz | | Jemer | km, km-kh | | K'iche' | quc-latn, qut-gt, qut-latn | | Kinyarwanda | rw, rw-rw | | KiSwahili | sw, sw-ke | | Konkani | kok, kok-in | | Coreano | ko, ko-kr | | Kurdo | ku-arab, ku-arab-iq | | Kirguís | ky-kg, ky-cyrl | | Lao | lo, lo-la | | Letón | lv, lv-lv | | Lituano | lt, lt-lt | | Luxemburgués | lb, lb-lu | | Macedonio | mk, mk-mk | | Malayo | ms, ms-bn, ms-my | | Malayalam | ml, ml-in | | Maltés | mt, mt-mt | | Maorí | mi, mi-latn, mi-nz | | Marathi | mr, mr-in | | Mongol (cirílico) | mn-cyrl, mn-mong, mn-mn, mn-phag | | Nepalí | ne, ne-np | | Noruego | nb, nb-no, nn, nn-no, no, no-no | | Odia | or, or-in | | Persa | fa, fa-ir | | Polaco | pl, pl-pl | | Portugués (Brasil) | pt-br | | Portugués (Portugal) | pt, pt-pt | | Punjabi | pa, pa-arab, pa-arab-pk, pa-deva, pa-in | | Quechua | quz, quz-bo, quz-ec, quz-pe | | Rumano | ro, ro-ro | | Ruso | ru, ru-ru | | Gaélico escocés | gd-gb, gd-latn | | Serbio (latino) | sr-Latn, sr-latn-cs, sr, sr-latn-ba, sr-latn-me, sr-latn-rs | | Serbio (cirílico) | sr-cyrl, sr-cyrl-ba, sr-cyrl-cs, sr-cyrl-me, sr-cyrl-rs | | Sesotho sa Leboa | nso, nso-za | | Setswana | tn, tn-bw, tn-za | | Sindhi | sd-arab, sd-arab-pk, sd-deva | | Cingalés | si, si-lk | | Eslovaco | sk, sk-sk | | Esloveno | sl, sl-si | | Español | es, es-cl, es-co, es-es, es-mx, es-ar, es-bo, es-cr, es-do, es-ec, es-gt, es-hn, es-ni, es-pa, es-pe, es-pr, es-py, es-sv, es-us, es-uy, es-ve | | Sueco | sv, sv-se, sv-fi | | Tayiko (cirílico) | tg-arab, tg-cyrl, tg-cyrl-tj, tg-latn | | Tamil | ta, ta-in | | Tártaro | tt-arab, tt-cyrl, tt-latn, tt-ru | | Telugu | te, te-in | | Tailandés | th, th-th | | Tigrino | ti, ti-et | | Turco | tr, tr-tr | | Turkmeno | tk-cyrl, tk-latn, tk-tm, tk-latn-tr, tk-cyrl-tr | | Ucraniano | uk, uk-ua | | Urdu | ur, ur-pk | | Uigur | ug-arab, ug-cn, ug-cyrl, ug-latn | | Uzbeko (latín) | uz, uz-cyrl, uz-latn, uz-latn-uz | | Vietnamita | vi, vi-vn | | Galés | cy, cy-gb | | Wolof | wo, wo-sn | | Yoruba | yo-latn, yo-ng |

Solicitud de ejemplo

{
    "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"]
}

Encabezados de respuesta

Encabezado Valor
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After El tiempo en segundos que el cliente debe esperar antes de volver a llamar a las API debido a la limitación de velocidad

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto Contiene la carga de respuesta real para la solicitud
URL de sondeo Cadena Dirección URL de sondeo para obtener el estado de cualquier envío en curso
ongoingSubmissionId Cadena Id. de envío de cualquier envío ya en curso

Respuesta de ejemplo

{
    "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 para obtener los paquetes de borrador actuales

Captura los detalles de los paquetes en el borrador del envío actual.

Ruta de acceso [todos los paquetes]: /submission/v1/product/{productId}/packages
Método: GET

Ruta [paquete único]: /submission/v1/product/{productId}/packages/{packageId}
Método: GET

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto
packageId El ID único del paquete que se va a obtener

Encabezados obligatorios

Encabezado Valor
Authorization: Bearer <Token> Utilizando el ID de aplicación de Microsoft Entra ID registrado en la cuenta de Centro de socios
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Valor
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
Paquetes Matriz de objetos Objeto que contiene los datos del módulo de paquetes
packageId Cadena
URL del paquete Cadena
Idiomas Matriz de cadenas
arquitecturas Matriz de cadenas Neutral, X86, X64, Arm, Arm64
isSilentInstall Booleano Debe marcarse como true si el instalador se ejecuta en modo silencioso sin necesidad de conmutadores o, de lo contrario, es false
parámetros del instalador Cadena
genericDocUrl Cadena
errorDetails Matriz de objetos
errorScenario Cadena
errorScenarioDetails Matriz de objetos
errorValue Cadena
errorUrl Cadena
tipo de paquete Cadena

Respuesta de ejemplo

{   
    "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 para actualizar los paquetes de borrador actuales

Actualiza los detalles de los paquetes en el borrador del envío actual.

Ruta [Actualización completa del módulo]: /submission/v1/product/{productId}/packages
Método: PUT

Ruta [Actualización de parche de un solo paquete]: /submission/v1/product/{productId}/packages/{packageId}
Método: PATCH

Comportamiento de la API

En el caso de la API de actualización completa del módulo, es necesario que todos los datos de los paquetes estén presentes en la solicitud para la actualización completa de cada campo. Cuando un campo no esté presente en la solicitud, su valor predeterminado se usará para sobrescribir el valor actual de ese módulo específico. Esto hace que se sobrescriban todos los paquetes existentes con un nuevo conjunto de paquetes de la solicitud. Esto dará lugar a la regeneración de identificadores de paquete, y el usuario debe llamar a la API GET Packages para obtener los identificadores de paquete más recientes.

En el caso de la API de actualización parcial de un único paquete, solo deben incluirse en la solicitud los campos que se vayan a actualizar para un paquete concreto. Estos valores de campos de la solicitud sobrescribirán sus valores existentes y mantendrán todos los demás campos que no están presentes en la solicitud igual que los actuales para ese paquete específico. Los demás paquetes del conjunto permanecen tal como están.

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto
packageId El identificador único del paquete

Encabezados obligatorios

Encabezado Valor
Authorization: Bearer <Token> Utilizando el ID de aplicación de Microsoft Entra ID registrado en la cuenta de Centro de socios
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Parámetros de la solicitud

Nombre Tipo Descripción
Paquetes Matriz de objetos Objeto que contiene los datos del módulo de paquetes [solo es necesario para la actualización completa del módulo]
URL del paquete Cadena Obligatorio
Idiomas Matriz de cadenas Obligatorio
arquitecturas Matriz de cadenas Obligatorio Debe contener una única arquitectura: Neutral, X86, X64, Arm, Arm64
isSilentInstall Booleano Obligatorio Debe marcarse como true si el instalador se ejecuta en modo silencioso sin necesidad de conmutadores o, de lo contrario, es false
parámetros del instalador Cadena Obligatorio si isSilentInstall es falso
genericDocUrl Cadena Obligatorio si packageType es exe Vínculo al documento que contiene detalles de los códigos de error personalizados para el instalador de tipo EXE
errorDetails Matriz de objetos Metadatos para contener códigos de error personalizados y detalles para los instaladores de tipo EXE.
errorScenario Cadena Identifique el escenario de error específico. [instalaciónCanceladaPorElUsuario, aplicaciónYaExiste, instalaciónYaEnProgreso, espacioEnDiscoLleno, seRequiereReinicio, fallaDeRed, paqueteRechazadoDuranteLaInstalación, instalaciónExitosa, varios]
errorScenarioDetails Matriz de objetos
errorValue Cadena Código de error que puede estar presente durante la instalación
errorUrl Cadena Dirección URL con los detalles del error
tipo de paquete Cadena Obligatorio [exe, msi]

Solicitud de ejemplo [actualización completa del módulo]

{
    "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",
    }]
}

Solicitud de ejemplo [actualización de parche de un solo paquete]

{
    "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",
}

Encabezados de respuesta

Encabezado Valor
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos [Lista de mensajes de error o advertencia, si los hay]
código Cadena El código de error del mensaje
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
URL de sondeo Cadena URL de consulta para obtener el estado de la presentación en caso de que ya haya alguna presentación en curso
ongoingSubmissionId Cadena [ID de cualquier envío ya en curso]

Respuesta de ejemplo

{
    "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 de confirmación de paquetes

Confirma el nuevo conjunto de paquetes actualizados mediante las API de actualización de paquetes en el borrador de envío actual. Esta API devuelve una dirección URL de sondeo para realizar un seguimiento de la carga de paquetes.

Ruta: /submission/v1/product/{productId}/packages/commit
Método: POST

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto

Encabezados obligatorios

Encabezado Valor
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado con la cuenta de Partner Center
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Valor
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos [Lista de mensajes de error o advertencia, si los hay]
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
URL de sondeo Cadena URL de consulta para obtener el estado de la carga del paquete o el estado de la presentación en caso de que ya haya una presentación en curso
ongoingSubmissionId Cadena [ID de cualquier envío ya en curso]

Respuesta de ejemplo

{
    "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 Get Current Draft Listing Assets

Captura los detalles de los recursos de descripción en el borrador del envío actual.

Ruta de acceso: /submission/v1/product/{productId}/listings/assets?languages={languages}
Método: GET

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto

Parámetros de consulta

Nombre Descripción
Idiomas [Opcional] Los idiomas de descripción se filtran como cadena separada por comas [límite de hasta 200 idiomas]. Si no está presente, se recuperan los datos de recursos de los primeros 200 idiomas de descripción disponibles. (Por ejemplo, "en-us, en-gb").

Encabezados obligatorios

Encabezado Valor
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado con la cuenta de Partner Center
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Valor
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
listado de activos Matriz de objetos Mostrando los detalles del recurso para cada idioma
lenguaje Cadena
storeLogos Matriz de objetos
capturas de pantalla Matriz de objetos
id Cadena
assetUrl Cadena Debe ser una dirección URL válida
tamaño de imagen Objeto
width Entero
altura Entero

Respuesta de ejemplo

{   
"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 para crear recursos de anuncio

Crea una nueva carga de recursos de descripción en el borrador del envío actual.

Actualización de los recursos de descripción

La API de envío de Microsoft Store para aplicaciones EXE o MSI usa direcciones URL de SAS generadas en tiempo de ejecución en almacenes de blobs para cada carga de recursos de imagen individual, junto con una llamada a la API Commit después de que la carga se completa correctamente. Para tener la capacidad de actualizar los recursos de descripción y, a su vez, poder agregar o quitar configuraciones regionales en el módulo de descripciones, se puede usar el siguiente enfoque:

  1. Utilice la API Create Listing Asset para enviar una solicitud relacionada con la carga de recursos junto con el idioma, el tipo y el recuento de recursos.
  2. En función del número de recursos solicitados, los identificadores de recurso se crean a petición y crearían una dirección URL de SAS a corto plazo y la enviarían de vuelta en el cuerpo de la respuesta en el tipo de recursos. Puede utilizar esta dirección URL para cargar recursos de imagen de tipo específico mediante clientes HTTP [Put Blob (API REST) - Azure Storage | Microsoft Docs].
  3. Después de cargar, puede usar la API Commit Listing Assets para enviar también la nueva información del id. de recurso recibida anteriormente de la llamada API anterior. La API única confirmará internamente los datos de los recursos de descripción después de la validación.
  4. Este enfoque sobrescribirá de manera eficaz todo el conjunto de imágenes anteriores del tipo de recurso en el idioma específico que se envía en la solicitud. Por lo tanto, se eliminarán los recursos cargados previamente.

Ruta: /submission/v1/product/{productId}/listings/assets/create
Método: POST

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto

Encabezados obligatorios

Encabezado Descripción
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado con la cuenta de Partner Center
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Parámetros de la solicitud

Nombre Tipo Descripción
lenguaje Cadena Obligatorio
createAssetRequest Objeto Obligatorio
Captura de pantalla Entero Obligatorio si ISV tiene que actualizar capturas de pantalla o agregar un nuevo idioma de descripción [1 - 10]
Logotipo Entero Obligatorio si ISV tiene que actualizar logotipos o agregar un nuevo idioma de descripción [1 o 2]

Encabezados de respuesta

Encabezado Descripción
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
listado de activos Objeto Objeto que contiene información de StoreLogos y Screenshots que se cargarán
lenguaje Cadena
storeLogos Matriz de objetos
capturas de pantalla Matriz de objetos
id Cadena
primaryAssetUploadUrl Cadena Dirección URL principal para cargar el recurso de descripción mediante la API REST de Azure Blob
secondaryAssetUploadUrl Cadena URL secundaria para subir el recurso del anuncio mediante la API REST de Azure Blob
httpMethod Método de HTTP El método HTTP necesario para cargar recursos a través de las direcciones URL de carga de recursos: principal o secundaria
httpHeaders Objeto Objeto con claves como encabezados obligatorios que van a estar presentes en la llamada a la API Upload a las direcciones URL de carga de recursos. Si el valor no está vacío, los encabezados deben tener valores específicos. De lo contrario, los valores se calculan durante la llamada API.

Respuesta de ejemplo

{
    "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 Commit Listing Assets

Confirma el nuevo recurso de descripción cargado mediante los detalles de la API Create Assets en el borrador del envío actual.

Ruta de acceso: /submission/v1/product/{productId}/listings/assets/commit
Método: PUT

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto

Encabezados obligatorios

Encabezado Descripción
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado con la cuenta de Partner Center
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Parámetros de la solicitud

Nombre Tipo Descripción
listingAssets Objeto
lenguaje Cadena
storeLogos Matriz de objetos
capturas de pantalla Matriz de objetos
id Cadena Debe ser un identificador existente que el usuario quiera conservar desde la API Get Current Listing Assets o un nuevo identificador con el que se cargó un nuevo recurso en la API Create Listing Assets.
assetUrl Cadena Debe ser la dirección URL del recurso existente que el usuario desea conservar desde la API Get Current Listing Assets o la dirección URL de carga principal o secundaria, con la que se cargó un nuevo recurso en la API Create Listing Assets. Debe ser una dirección URL válida

Solicitud de ejemplo

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

Encabezados de respuesta

Encabezado Descripción
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
URL de sondeo Cadena Dirección URL de sondeo para obtener el estado de cualquier envío en curso
ongoingSubmissionId Cadena Id. de envío de cualquier envío ya en curso

Respuesta de ejemplo

{
    "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 de sondeo del estado del módulo

Se puede crear una API para comprobar la preparación del módulo antes del envío. Además, valida el estado de carga de los paquetes.

Ruta de acceso: /submission/v1/product/{productId}/status
Método: GET

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto ID de Partner Center del producto

Encabezados obligatorios

Encabezado Descripción
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado con la cuenta de Partner Center
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Descripción
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
estáListo Booleano Indica si todos los módulos están en estado listo, incluida la carga de paquetes
ongoingSubmissionId Cadena Id. de envío de cualquier envío ya en curso

Respuesta de ejemplo

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

Crear API de envío

Crea una presentación a partir del borrador actual para una aplicación MSI o EXE. La API comprueba:

  • El envío activo y produce un mensaje de error si existe un envío activo.
  • si todos los módulos están listos para crear la presentación.
  • Cada campo del envío se valida según los requisitos de Store

Ruta: /submission/v1/product/{productId}/submit
Método: POST

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto El ID de Partner Center del producto

Encabezados obligatorios

Encabezado Descripción
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado en la cuenta de Partner Center
X-Seller-Account-Id Identificador de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Descripción
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After El tiempo en segundos que el cliente debe esperar antes de volver a llamar a las API debido a la limitación de tasa.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Objetivo Cadena Entidad desde la que se originó el error
responseData Objeto
URL de sondeo Cadena Dirección URL de sondeo para obtener el estado de preparación del módulo, incluida la carga de paquetes para el envío
Id de envío Cadena Id. del envío recién creado
ongoingSubmissionId Cadena Id. de envío de cualquier envío ya en curso

Respuesta de ejemplo

{
    "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 de consulta periódica del estado de envío

API para comprobar el estado del envío.

Ruta: /submission/v1/product/{productId}/submission/{submissionId}/status
Método: GET

Parámetros de la ruta de acceso

Nombre Descripción
ID de producto El ID de Partner Center del producto

Encabezados obligatorios

Encabezado Descripción
Authorization: Bearer <Token> Uso del identificador de aplicación de Microsoft Entra ID registrado con la cuenta de Partner Center
X-Seller-Account-Id ID de vendedor de la cuenta de Partner Center

Encabezados de respuesta

Encabezado Descripción
X-Correlation-ID El identificador único de tipo GUID para cada solicitud. Se puede compartir con el equipo de soporte técnico para analizar cualquier problema.
Retry-After Tiempo en segundos que el cliente debe esperar antes de llamar a las API de nuevo debido a la limitación de volumen.

Parámetros de respuesta

Nombre Tipo Descripción
isSuccess Booleano
Errores Matriz de objetos Lista de mensajes de error o advertencia, si los hay
código Cadena El código de error del mensaje.
Mensaje Cadena Descripción del error
Destino Cadena Entidad desde la que se originó el error
responseData Objeto
publishingStatus Cadena Estado de publicación del envío: [INPROGRESS, PUBLISHED, FAILED, UNKNOWN]
hasFailed Booleano Indica si la publicación ha producido un error y no se reintentará

Respuesta de ejemplo

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

Ejemplos de código

En los artículos siguientes se proporcionan ejemplos de código detallados que muestran cómo usar la API de envío de Microsoft Store en distintos lenguajes de programación:

Ejemplo de C#: API de envío de Microsoft Store para aplicaciones MSI o EXE

En este artículo se proporcionan ejemplos de código de C# que muestran cómo usar la API de envío de Microsoft Store para aplicaciones MSI o EXE. Puede revisar cada ejemplo para obtener más información sobre la tarea que muestra o puede compilar todos los ejemplos de código de este artículo en una aplicación de consola.

Requisitos previos Estos ejemplos utilizan la biblioteca siguiente:

  • Paquete NuGet Newtonsoft.Json de Newtonsoft.

Programa principal El siguiente ejemplo implementa un programa de línea de comandos que llama a los otros métodos de ejemplo de este artículo para mostrar diferentes maneras de usar la API de envío de Microsoft Store. Para adaptar este programa para su propio uso:

  • Asigne la propiedad SellerId al identificador de vendedor de la cuenta del Centro de partners.
  • Asigne la propiedad ApplicationId al identificador de la aplicación que desea administrar.
  • Asigne las propiedades ClientId y ClientSecret al identificador de cliente y la clave de la aplicación, y reemplace la cadena tenantid en la dirección URL de TokenEndpoint por el identificador de inquilino de la aplicación. Para obtener más información, consulte Cómo asociar una aplicación de Microsoft Entra ID a su cuenta de Partner Center.
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();

        }
    }
}

Clase auxiliar ClientConfiguration mediante C#

La aplicación de ejemplo usa la clase auxiliar ClientConfiguration para pasar los datos del directorio de Microsoft Entra ID y los datos de la aplicación a cada uno de los métodos de ejemplo que utilizan la API de envíos de Microsoft Store.

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

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

        /// <summary>
        /// Client secret of your Microsoft Entra ID 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 Microsoft Entra ID 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; }
    }
}

Crear un envío de aplicación con C#

En el siguiente ejemplo se implementa una clase que utiliza varios métodos en la API de envío de Microsoft Store para actualizar un envío de aplicación.

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.
        }
    }
}

Clase auxiliar IngestionClient mediante C#

La clase IngestionClient proporciona métodos auxiliares que otros métodos de la aplicación de ejemplo utilizan para realizar las tareas siguientes:

  • Obtén un token de acceso de Microsoft Entra ID que se pueda utilizar para llamar a métodos de la API de envío de Microsoft Store. Después de obtener un token, tiene 60 minutos para utilizar este token en llamadas a la API de envío de Microsoft Store antes de que el token expire. Después de que el token expire, puede generar uno nuevo.
  • Procesar las solicitudes HTTP para la API de envío de 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 Microsoft Entra ID 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
        /// Microsoft Entra ID Directory app. Example: https://login.microsoftonline.com/d454d300-128e-2d81-334a-27d9b2baf002/oauth2/v2.0/token </param>
        /// <param name="clientId">Client Id of your Microsoft Entra ID Directory app. Example" 00001111-aaaa-2222-bbbb-3333cccc4444</param>
        /// <param name="clientSecret">Client secret of your Microsoft Entra ID 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;
        }
    }
}

Ejemplo de Node.js: API de envío de Microsoft Store para aplicaciones MSI o EXE

En este artículo se proporcionan ejemplos de código de Node.js que muestran cómo usar la API de envío de Microsoft Store para aplicaciones MSI o EXE. Puede revisar cada ejemplo para obtener más información sobre la tarea que muestra o puede compilar todos los ejemplos de código de este artículo en una aplicación de consola.

Requisitos previos Estos ejemplos utilizan la biblioteca siguiente:

  • node-fetch v2 [npm install node-fetch@2]

Crear un envío de aplicación con node.js

El siguiente ejemplo llama a los otros métodos de ejemplo de este artículo para mostrar diferentes maneras de usar la API de envío de Microsoft Store. Para adaptar este programa para su propio uso:

  • Asigne la propiedad SellerId al identificador de vendedor de la cuenta del Centro de partners.
  • Asigne la propiedad ApplicationId al identificador de la aplicación que desea administrar.
  • Asigne las propiedades ClientId y ClientSecret al identificador de cliente y la clave de la aplicación, y reemplace la cadena tenantid en la dirección URL de TokenEndpoint por el identificador de inquilino de la aplicación. Para obtener más información, consulte Cómo asociar una aplicación de Microsoft Entra ID a su cuenta de Partner Center.

En el siguiente ejemplo se implementa una clase que utiliza varios métodos en la API de envío de Microsoft Store para actualizar un envío de aplicación.

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();

Asistente de ClientConfiguration

La aplicación de ejemplo usa la clase auxiliar ClientConfiguration para pasar los datos del directorio de Microsoft Entra ID y los datos de la aplicación a cada uno de los métodos de ejemplo que utilizan la API de envíos de 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;

Asistente IngestionClient mediante node.js

La clase IngestionClient proporciona métodos auxiliares que otros métodos de la aplicación de ejemplo utilizan para realizar las tareas siguientes:

  • Obtén un token de acceso de Microsoft Entra ID que se pueda utilizar para llamar a métodos de la API de envío de Microsoft Store. Después de obtener un token, tiene 60 minutos para utilizar este token en llamadas a la API de envío de Microsoft Store antes de que el token expire. Después de que el token expire, puede generar uno nuevo.
  • Procesar las solicitudes HTTP para la API de envío de 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;

Ayuda adicional

Si tiene preguntas sobre la API de envío de Microsoft Store o necesita ayuda para administrar los envíos con esta API, utilice los siguientes recursos:

  • Formule preguntas en nuestros foros.
  • Visite nuestra página de soporte técnico y solicite una de las opciones de soporte técnico asistido para el Centro de partners. Si se le pide que elija un tipo de problema y una categoría, elija Envío y certificación de la aplicación y Envío de una aplicación, respectivamente.

Preguntas más frecuentes

  1. ¿Qué puedo hacer a través de la API de envío de Microsoft Store?

    La API de envío de Microsoft Store permite a los desarrolladores automatizar las tareas de administración de aplicaciones, entre las que se incluyen:

    • Creación y envío de nuevas actualizaciones de aplicaciones.
    • Carga y administración de paquetes de aplicaciones.
    • Actualización de metadatos y listados de la tienda.

    Puede integrar esta API en los flujos de trabajo de integración continua o de compilación, automatizando tareas rutinarias para simplificar el proceso de publicación.