Condividi tramite


API di invio a Microsoft Store per l'app MSI o EXE

Usare l'API di invio a Microsoft Store per l'app MSI o EXE per eseguire query a livello di codice e creare invii per le app MSI o EXE per l'account del Partner Center dell'organizzazione. Questa API è utile se l'account gestisce molte app e si desidera automatizzare e ottimizzare il processo di invio per queste risorse. Questa API usa Azure Active Directory (Azure AD) per autenticare le chiamate dall'app o dal servizio.

I passaggi seguenti descrivono il processo end-to-end di uso dell'API di invio di Microsoft Store:

  1. Assicurarsi di aver completato tutti i prerequisiti.
  2. Prima di chiamare un metodo nell'API di invio di Microsoft Store, ottenere un token di accesso di Azure AD. Dopo aver ottenuto un token, hai 60 minuti per usare questo token nelle chiamate all'API di invio a Microsoft Store prima della scadenza del token. Dopo la scadenza del token, è possibile generare un nuovo token.
  3. Richiamare l'API di invio a Microsoft Store per l'app MSI o EXE.

Passaggio 1: completare i prerequisiti per l'uso dell'API di invio a Microsoft Store

Prima di iniziare a scrivere il codice per richiamare l'API di invio a Microsoft Store per app MSI o EXE, verificare di aver completato i prerequisiti seguenti.

  • L'utente o l'organizzazione deve disporre di una directory di Azure AD e dell'autorizzazione come amministratore aziendale per la directory. Se si usa già Microsoft 365 o altri servizi aziendali Microsoft, si dispone già di una directory di Azure AD. In caso contrario, è possibile creare una nuova directory di Azure AD nel Partner Center senza costi aggiuntivi.
  • È necessario associare un'applicazione Azure AD all'account del Centro per i partner e ottenere l'ID tenant, l'ID client e la chiave. Questi valori sono necessari per ottenere un token di accesso di Azure AD, che sarà usato nelle chiamate all'API di invio di Microsoft Store.
  • Preparare l'app per l'uso con l'API di invio a Microsoft Store:
    • Se l'app non esiste ancora nel Partner Center, è necessario crearla riservandone il nome nel Partner Center. Non è possibile usare un API di invio a Microsoft Store per creare un'app nel Partner Center. È necessario lavorare nel Partner Center per crearla e quindi è possibile usare l'API per accedere all'app e creare invii per essa a livello di codice.
    • Per poter creare un invio per una determinata app usando questa API, è innanzitutto necessario creare un invio per 'app nel Centro per i partner, includendo le risposte al questionario sulle classificazioni in base all'età. Dopo aver eseguito questa operazione, sarà possibile creare nuovi invii a livello di codice per questa app usando l'API.
    • Se si sta creando o aggiornando un invio di un'app ed è necessario includere un nuovo pacchetto, preparare i dettagli del pacchetto.
    • Se si sta creando o aggiornando un invio di un'app ed è necessario includere screenshot o immagini per la presentazione nello Store, preparare gli screenshot e le immagini dell'app.

Procedura di associazione di un'applicazione Azure AD all'account del Centro per i partner

Per poter usare l'API di invio a Microsoft Store per app MSI o EXE, è necessario associare un'applicazione Azure AD al proprio account del Partner Center, recuperare l'ID tenant e l'ID client per l'applicazione e generare una chiave. L'applicazione Azure AD rappresenta l'app o il servizio da cui si vuole chiamare l'API di invio di Microsoft Store. Per ottenere un token di accesso di Azure AD da passare all'API, sono necessari l'ID tenant, l'ID client e la chiave.

Nota

È sufficiente eseguire questa attività una sola volta. Quando si dispone di ID tenant, ID client e chiave, è possibile riutilizzarli ogni volta che è necessario creare un nuovo token di accesso Azure AD.

  1. Nel Centro per i partner, associare l'account del Centro per i partner dell'organizzazione alla directory di Azure AD dell'organizzazione.
  2. Quindi, dalla pagina Utenti nella sezione Impostazioni account del Centro per i partner, aggiungere l'applicazione Azure AD che rappresenta l'app o il servizio che verrà usato per accedere agli invii per l'account del Centro per i partner. Assicurarsi di assegnare a questa applicazione il ruolo di Manager. Se l'applicazione non esiste ancora nella directory di Azure AD, è possibile creare una nuova applicazione Azure AD nel Centro per i partner.
  3. Tornare alla pagina Utenti, fare clic sul nome dell'applicazione Azure AD per passare alle impostazioni e copiare i valori di ID tenant e ID client.
  4. Per aggiungere una nuova chiave o client segreto, vedere le istruzioni seguenti o fare riferimento alle istruzioni per registrare l'app tramite il portale Azure:

Per registrare l'app:

  1. Accedi al portale di Azure.

  2. Se si ha accesso a più tenant, usare il filtro Directory e sottoscrizioni nel menu in alto e passare al tenant in cui si vuole registrare l'applicazione.

  3. Cerca e seleziona Azure Active Directory.

  4. In Gestisci selezionare Registrazioni app, > quindi selezionare l'applicazione.

  5. Selezionare Certificati & segreti > Segreti client > Nuovo segreto client.

  6. Aggiungere una descrizione per il segreto client.

  7. Selezionare una scadenza per il segreto o specificare una durata personalizzata.

  8. La durata del segreto client è limitata a due anni (24 mesi) o meno. Non è possibile specificare una durata personalizzata di più di 24 mesi.

    Nota

    Microsoft consiglia di impostare un valore di scadenza inferiore a 12 mesi.

  9. Selezionare Aggiungi.

  10. Registrare il valore del segreto da usare nel codice dell'applicazione client. Questo valore del segreto non viene mai più visualizzato dopo aver lasciato questa pagina.

Passaggio 2: ottenere un token di accesso di Azure AD

Prima di chiamare un metodo nell'API di invio a Microsoft Store per app MSI o EXE, è necessario ottenere un token di accesso di Azure AD da passare all'intestazione Autorizzazione di ogni metodo nell'API. Dopo aver ottenuto un token di accesso, questo sarà disponibile per 60 minuti prima della scadenza. Dopo la scadenza del token, è possibile aggiornarlo in modo da continuare a usarlo nelle successive chiamate all'API.

Per ottenere il token di accesso, seguire le istruzioni in [Chiamate da servizio a servizio tramite credenziali client]/azure/active-directory/azuread-dev/v1-oauth2-client-creds-grant-flow) per inviare un POST HTTP all'endpoint https://login.microsoftonline.com/<tenant_id>/oauth2/token. Di seguito è riportata una richiesta di esempio.

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

Per il tenant_id valore in POST URI e i parametri client_id e client_secret, specificare l'ID tenant, l'ID client e la chiave per l'applicazione recuperata dal Centro per i partner nella sezione precedente. Per il parametro ambito, è necessario specificare https://api.store.microsoft.com/.default.

Dopo la scadenza del token di accesso, è possibile aggiornarlo seguendo le istruzioni riportate qui.

Per esempi che illustrano come ottenere un token di accesso usando C# o Node.js, vedere gli esempi di codice per l'API di invio a Microsoft Store per app MSI o EXE.

Passaggio 3: usare l''API di invio di Microsoft Store

Dopo aver ottenuto un token di accesso Azure AD, è possibile richiamare i metodi nell'API di invio a Microsoft Store per app MSI o EXE. L'API include molti metodi raggruppati in scenari per le app. Per creare o aggiornare invii, in genere si richiamano più metodi in un ordine specifico. Per informazioni su ogni scenario e sulla sintassi di ogni metodo, vedere le sezioni seguenti:

Nota

Dopo aver ottenuto un token di accesso, si hanno 60 minuti per richiamare i metodi nell'API di invio a Microsoft Store per app MSI o EXE prima della scadenza del token.

URL di base

L'URL di base per l'API di invio a Microsoft Store per app EXE o MSI è: https://api.store.microsoft.com

API Contracts

Ottenere API metadati di invio della bozza corrente

Recupera i metadati in ogni modulo (elenchi, proprietà o disponibilità) nell'invio della bozza attuale.

Percorso [Tutti i moduli]: /submission/v1/product/{productId}/metadata?languages={languages}&includelanguagelist={true/false}
Percorso [Modulo singolo]: /submission/v1/product/{productId}/metadata/{moduleName}?languages={languages}&includelanguagelist={true/false}
Metodo: GET

Parametri del percorso

Parametro Descrizione
productId ID del Partner Center del prodotto
moduleName Modulo del Partner Center: presentazioni, proprietà o disponibilità

Parametri della query

Parametro Descrizione
lingue Facoltativo : le lingue in elenco vengono filtrate come stringa delimitata da virgole [limite massimo 200 lingue].

Se assente, vengono recuperati i primi 200 metadati delle lingue disponibili in elenco. [ad esempio: "en-us, en-gb"].
includelanguagelist Valore booleano facoltativo: se true, restituisce l'elenco delle lingue aggiunte all'elenco e il relativo stato di completezza.

Intestazioni obbligatorie

Intestazione Valore
Authorization: Bearer <Token> ID dell'app Azure AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Valore
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
accessibilitySupport Booleano
additionalLicenseTerms String
availability Object Dati del modulo di disponibilità
category String Vedere l'elenco delle categorie riportato di seguito
certificationNotes String
codice String Il codice di errore del messaggio
contactInfo String
copyright String
dependsOnDriversOrNT Booleano
description Stringa
developedBy String
individuabilità String [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
freeTrial String [NO_FREE_TRIAL, FREE_TRIAL]
hardwareItemType String
isPrivacyPolicyRequired Booleano
isRecommended Booleano
isRequired Booleano
isSuccess Booleano
isSystemFeatureRequired Matrice di oggetti
lingua String Visualizzare l'elenco delle lingue riportato di seguito
presentazioni Matrice di oggetti Elencare i dati del modulo per ogni lingua
mercati Matrice di stringhe Vedere l'elenco dei mercati di seguito
messaggio String Descrizione dell'errore
minimumHardware String
minimumRequirement String
penAndInkSupport Booleano
prezzi String [FREE, FREEMIUM, SUBSCRIPTION, PAID]
privacyPolicyUrl String
productDeclarations Object
productFeatures Matrice di stringhe
proprietà Object Dati del modulo Proprietà
recommendedHardware String
recommendedRequirement String
responseData Object Contiene il payload di risposta effettivo per la richiesta
requisiti Matrice di oggetti
searchTerms Matrice di stringhe
shortDescription String
sottocategoria String Vedere l'elenco delle sottocategorie riportato di seguito
supportContactInfo String
systemRequirementDetails Matrice di oggetti
bersaglio String Entità da cui ha avuto origine l'errore
sito web String
whatsNew String

Risposta di esempio

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

Aggiornare l'API dei metadati di invio bozza attuale

Aggiorna i metadati in ogni modulo sotto invi della bozza. Controlli dell'API

  • Per Invio Active. Se esiste, errore con messaggio di errore.
  • Se tutti i moduli sono in stato pronto per consentire l'operazione Salva bozza.
  • Ogni campo nell'invio viene convalidato in base ai requisiti dello Store
  • Regole di convalida dei dettagli dei requisiti di sistema:
    • Valori consentiti in hardwareItemType = Memoria: 300 MB, 750 MB, 1 GB, 2 GB, 4 GB, 6 GB, 8 GB, 12 GB, 16 GB, 20 GB
    • Valori consentiti in hardwareItemType = DirectX: DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12
    • Valori consentiti in hardwareItemType = Video_Memory: 1 GB, 2 GB, 4 GB, 6 GB

Percorso [Aggiornamento modulo completo]: /submission/v1/product/{productId}/metadata
Metodo: PUT

Percorso [Aggiornamento patch modulo]: /submission/v1/product/{productId}/metadata
Metodo: PATCH

Comportamento API:

Nel caso dell'API di aggiornamento del modulo completo, è necessario che tutti i dati del modulo siano presenti nella richiesta di aggiornamento completo di ogni campo. Se il campo non è presente nella richiesta, viene usato il relativo valore predefinito per sovrascrivere il valore corrente per tale modulo specifico.
Nel caso dell'API di aggiornamento del modulo patch: solo i campi da aggiornare devono essere presenti nella richiesta. Questi valori di campo della Richiesta sovrascriveranno i valori esistenti, mantenendo tutti gli altri campi che non sono presenti nella Richiesta, come quello corrente per quel modulo specifico.

Parametri del percorso

Parametro Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Valore
Authorization: Bearer <Token> ID dell'app Azure AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Parametri della richiesta

Nome Tipo Descrizione
availability Object Oggetto per contenere i metadati del modulo disponibilità
mercati Matrice di stringhe Obbligatorio Vedere l'elenco dei mercati di seguito
individuabilità String Obbligatorio [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Booleano Obbligatorio
prezzi String Obbligatorio [FREE, FREEMIUM, SUBSCRIPTION, PAID]
freeTrial String Obbligatorio se i prezzi sono in PAGAMENTO o ABBONAMENTO [NO_FREE_TRIAL, FREE_TRIAL]
proprietà Object Oggetto in cui contenere i metadati del modulo proprietà
isPrivacyPolicyRequired Booleano Obbligatorio
privacyPolicyUrl String Obbligatorio se isPrivacyPolicyRequired = true Deve essere un URL valido
sito web String Deve essere un URL valido
supportContactInfo String Deve essere un indirizzo URL o un indirizzo e-mail valido
certificationNotes String Consigliato Limite di caratteri = 2000
category String Obbligatorio Vedere l'elenco delle categorie di seguito
sottocategoria String Obbligatorio Vedere l'elenco delle sottocategorie di seguito
productDeclarations Object Obbligatorio
isSystemFeatureRequired Matrice di oggetti [Touch, tastiera, mouse, fotocamera, NFC_HCE, NFC_Proximity, Bluetooth_LE, telefonia, microfono]
isRequired Booleano Obbligatorio
isRecommended Booleano Obbligatorio
hardwareItemType String Obbligatorio
systemRequirementDetails Matrice di oggetti [Processore, Grafica, Memoria, DirectX, Video_Memory]
minimumRequirement String Obbligatorio per systemRequirementsText, lunghezza massima = 200

Valori consentiti in hardwareItemType = Memoria: [300 MB, 750 MB, 1 GB, 2 GB, 4 GB, 6 GB, 8 GB, 12 GB, 16 GB, 20 GB]

Valori consentiti in hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Valori consentiti in hardwareItemType = Video_Memory: [1 GB, 2 GB, 4 GB, 6 GB]
recommendedRequirement String Obbligatorio per systemRequirementsText, lunghezza massima = 200

Valori consentiti in hardwareItemType = Memoria: [300 MB, 750 MB, 1 GB, 2 GB, 4 GB, 6 GB, 8 GB, 12 GB, 16 GB, 20 GB]

Valori consentiti in hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Valori consentiti in hardwareItemType = Video_Memory: [1 GB, 2 GB, 4 GB, 6 GB]
dependsOnDriversOrNT Booleano Obbligatorio
accessibilitySupport Booleano Obbligatorio
penAndInkSupport Booleano Obbligatorio
presentazioni Object Oggetto per elencare i dati del modulo per una singola lingua
lingua String Obbligatorio Vedere l'elenco delle lingue seguenti
description Stringa Obbligatorio Limite caratteri = 10000
whatsNew String Limite di caratteri = 1500
productFeatures Matrice di stringa 200 caratteri per funzionalità; Fino a 20 funzionalità
shortDescription String Limite di caratteri = 1000
searchTerms Matrice di stringa 30 caratteri per termine di ricerca; Fino a 7 termini di ricerca

21 parole univoche in TOTALE in tutti i termini di ricerca
additionalLicenseTerms String Obbligatorio Limite caratteri = 10000
copyright String Limite di caratteri = 200
developedBy String Limite di caratteri = 255
requisiti Matrice di oggetti 200 caratteri per voce; Fino a 11 voci in TOTALE tra minimo e consigliato]
minimumHardware String Limite di caratteri = 200
recommendedHardware String Limite di caratteri = 200
contactInfo String Limite di caratteri = 200
listingsToAdd Matrice di stringhe Visualizzare l'elenco delle lingue riportato di seguito
listingsToRemove Matrice di stringhe Visualizzare l'elenco delle lingue riportato di seguito

Mercati

Mercato Abbreviazione
Afghanistan AF
Albania AL
Algeria DZ
Samoa americane AS
Andorra AD
Angola AO
Anguilla AI
Antartide AQ
Antigua e Barbuda AG
Argentina AR
Armenia Mattina
Aruba AW
Australia AU
Austria AT
Azerbaigian AZ
Bahamas BS
Bahrein BH
Bangladesh BD
Barbados BB
Bielorussia BY
Belgio BE
Belize BZ
Benin BJ
Bermuda BM
Bhutan BT
Repubblica bolivariana del Venezuela VE
Bolivia BO
Bonaire BQ
Bosnia ed Erzegovina BA
Botswana BW
Isola Bouvet BV
Brasile BR
Territorio britannico dell’Oceano Indiano IO
Isole Vergini Britanniche VG
Brunei BN
Bulgaria BG
Burkina Faso BF
Burundi BI
Cambogia KH
Camerun CA
Canada CA
Capo Verde CV
Isole Cayman KY
Repubblica Centrafricana CF
Ciad TD
Cile CL
Cina CN
Isola Christmas CX
Isole Cocos (Keeling) CC
Colombia CO
Comore KM
Congo CG
Congo (RDC) CD
Isole Cook CK
Costa Rica CR
Croazia HR
Curaçao CW
Cipro CY
Repubblica Ceca CZ
Costa d'Avorio CI
Danimarca DK
Gibuti DJ
Dominica DM
Repubblica Dominicana DO
Ecuador EC
Egitto EG
El Salvador SV
Guinea Equatoriale GQ
Eritrea ER
Estonia EE
Etiopia ET
Isole Falkland FK
Isole Fær Øer FO
Figi FJ
Finlandia FI
Francia FR
Guyana francese GF
Polinesia Francese PF
Terre australi e antartiche francesi TF
Gabon Disponibilità generale
Gambia GM
Georgia GE
Germania DE
Ghana GH
Gibilterra GI
Grecia GR
Groenlandia GL
Grenada GD
Guadalupa GP
Guam GU
Guatemala GT
Guernsey GG
Guinea GN
Guinea-Bissau GW
Guyana GY
Haiti HT
Heard e McDonald HM
Città del Vaticano VA
Honduras HN
Hong Kong SAR HK
Ungheria HU
Islanda IS
India IN
Indonesia ID
Iraq IQ
Irlanda Internet Explorer
Israele IL
Italia IT
Giamaica JM
Giappone JP
Jersey JE
Giordania JO
Kazakistan KZ
Kenya KE
Kiribati KI
Corea del Sud KR
Kuwait KW
Kirghizistan KG
Laos LA
Lettonia LV
Libano LB
Lesotho LS
Liberia LR
Libia LY
Liechtenstein LI
Lituania LT
Lussemburgo LU
Macao SAR MO
Macedonia del Nord MK
Madagascar MG
Malawi MW
Malaysia MY
Maldive MV
Mali ML
Malta MT
Isola di Man IM
Isole Marshall MH
Martinica MQ
Mauritania MR
Mauritius MU
Mayotte YT
Messico MX
Micronesia FM
Moldavia MD
Monaco MC
Mongolia MN
Montenegro - ME
Montserrat MS
Marocco MA
Mozambico MZ
Myanmar MM
Namibia ND
Nauru NR
Nepal NP
Paesi Bassi NL
Nuova Caledonia NC
Nuova Zelanda NZ
Nicaragua NI
Niger NE
Nigeria NG
Niue NU
Isola Norfolk NF
Isole Marianne settentrionali Punto di gestione (MP)
Norvegia NO
Oman OM
Pakistan PK
Palau PW
Autorità Palestinese PS
Panama PA
Papua Nuova Guinea PG
Paraguay PY
Perù PE
Filippine PH
Isole Pitcairn PN
Polonia PL
Portogallo PT
Qatar QA
Riunione RE
Romania RO
Russia RU
Ruanda RW
Saint Barthélemy BL
Sant'Elena, Ascensione e Tristan da Cunha SH
Saint Kitts e Nevis KN
Saint Lucia LC
Saint Martin (Parte francese) MF
Saint Pierre e Miquelon Pomeriggio
Saint Vincent e Grenadine VC
Samoa WS
San Marino SM
Arabia Saudita SA
Senegal SN
Serbia RS
Seychelles SC
Sierra Leone SL
Singapore SG
Sint Maarten (Parte olandese) SX
Slovacchia SK
Slovenia SI
Isole Salomone SB
Somalia SO
Sudafrica ZA
Georgia del Sud e Sandwich Australi GS
Spagna ES
Sri Lanka LK
Suriname SR
Svalbard e Jan Mayen SJ
Swaziland SZ
Svezia SE
Svizzera CH
São Tomé e Príncipe ST
Taiwan TW
Tagikistan TJ
Tanzania TZ
Thailandia TH
Timor Leste TL
Tog - TG
Tokelau TK
Tonga TO
(Trinidad e Tobago) - TT
Tunisia TN
Türkiye TR
Turkmenistan TM
Isole Turks e Caicos TC
Tuvalu TV
Altre isole americane del Pacifico UM
U.S. Vergini Americane VI
Uganda UG
Ucraina UA
Emirati Arabi Uniti (EAU) AE
Regno Unito GB
Stati Uniti Stati Uniti
Uruguay UY
Uzbekistan UZ
Vanuatu VU
Vietnam VN
Wallis e Futuna WF
Yemen YE
Zambia ZM
Zimbabwe ZW
Isole Åland AX

Categorie e sottocategorie

Categoria Sottocategorie
BooksAndReference EReader, Fiction, Nonfiction, Riferimento
Azienda AccountingAndfinance, Collaboration, CRM, DataAndAnalytics, FileManagement, InventoryAndlogistics, LegalAndHR, ProjectManagement, RemoteDesktop, SalesAndMarketing, TimeAndExpenses
DeveloperTools Database, DesignTools, DevelopmentKits, Networking, ReferenceAndTraining, Servers, Utilities, WebHosting
Istruzione EducationBooksAndReference, EarlyLearning, InstructionalTools, Language, StudyAids
Entertainment (Nessuno)
FoodAndDining (Nessuno)
GovernmentAndPolitics (Nessuno)
HealthAndFitness (Nessuno)
KidsAndFamily KidsAndFamilyBooksAndReference, KidsAndFamilyEntertainment, HobbiesAndToys, SportsAndActivities, KidsAndFamilyTravel
Lifestyle Automotive, DYI, HomeAndGarden, Relationships, SpecialInterest, StyleAndFashion
Medical (Nessuno)
MultimediaDesign IllustrationAndGraphicDesign, MusicProduction, PhotoAndVideoProduction
Musica (Nessuno)
NavigationAndMaps (Nessuno)
NewsAndWeather Notizie, meteo
PersonalFinance BankingAndInvestments, BudgetingAndTaxes
Personalization RingtonesAndSounds, themes, WallpaperAndLockScreens
PhotoAndVideo (Nessuno)
Produttività (Nessuno)
Sicurezza PCProtection, PersonalSecurity
Acquisti (Nessuno)
Social (Nessuno)
Sport (Nessuno)
Viaggi CityGuides, Hotels
UtilitiesAndTools BackupAndManage, FileManager

Lingue

Nome della lingua Codici lingua supportati
Afrikaans af, af-za
Albanese sq, sq-al
Amharico am, am-et
Armeno hy, hy-am
Assamese as, as-in
Azero az-arab, az-arab-az, az-cyrl, az-cyrl-az, az-latn, az-latn-az
Basco (Province basche) eu, eu-es
Bielorusso be, be-by
Bengalese bn, bn-bd, bn-in
Bosniaco bs, bs-cyrl, bs-cyrl-ba, bs-latn, bs-latn-ba
Bulgaro bg, bg-bg
Catalano ca, ca-es, ca-es-valencia
Cherokee chr-cher, chr-cher-us, chr-latn
Cinese semplificato zh-Hans, zh-cn, zh-hans-cn, zh-sg, zh-hans-sg
Cinese tradizionale 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
Croato hr, hr-hr, hr-ba
Ceco cs, cs-cz
Danese da, da-dk
Dari prs, prs-af, prs-arab
Olandese nl, nl-nl, nl-be
italiano 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
Estone et, et-ee
Filippino - fil, fil-latn, fil-ph
Finlandese fi, fi-fi
Francese 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
Galiziano gl, gl-es
Georgiano ka, ka-ge
Tedesco de, de-at, de-ch, de-de, de-lu, de-li
Greco el, el-gr
Gujarati gu, gu-in
Hausa ha, ha-latn, ha-latn-ng
Ebraico he, he-il
Hindi hi, hi-in
Ungherese hu, hu-hu
Islandese is, is-is
Igb - ig-latn, ig-ng
Indonesiano id, id-id
Inuktitut (Latino) iu-cans, iu-latn, iu-latn-ca
Irlandese ga, ga-ie
isiXhosa xh, xh-za
isiZulu zu, zu-za
Italiano it, it-it, it-ch
Giapponese ja, ja-jp
Kannada kn, kn-in
Kazako kk, kk-kz
Khmer 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
Curdo ku-arab, ku-arab-iq
Kirghiso ky-kg, ky-cyrl
Lao lo, lo-la
Lettone lv, lv-lv
Lituano lt, lt-lt
Lussemburghese lb, lb-lu
Macedone mk, mk-mk
Malese ms, ms-bn, ms-my
Malayalam ml, ml-in
Maltese mt, mt-mt
Maori mi, mi-latn, mi-nz
Marathi mr, mr-in
Mongolo (cirillico) mn-cyrl, mn-mong, mn-mn, mn-phag
Nepalese ne, ne-np
Norvegese nb, nb-no, nn, nn-no, no, no-no
Odia or, or-in
Persiano fa, fa-ir
Polacco pl, pl-pl
Portoghese (Brasile) pt-br
Portoghese (Portogallo) pt, pt-pt
Punjabi pa, pa-arab, pa-arab-pk, pa-deva, pa-in
Quechua quz, quz-bo, quz-ec, quz-pe
Rumeno ro, ro-ro
Russo ru, ru-ru
Scozzese Gaelico gd-gb, gd-latn
Serbo (alfabeto latino) sr-Latn, sr-latn-cs, sr, sr-latn-ba, sr-latn-me, sr-latn-rs
Serbo (alfabeto cirillico) sr-cyrl, sr-cyrl-ba, sr-cyrl-cs, sr-cyrl-me, sr-cyrl-rs
Sotho del nord nso, nso-za
Setswana tn, tn-bw, tn-za
Sindhi sd-arab, sd-arab-pk, sd-deva
Sinhala si, si-lk
Slovacco sk, sk-sk
Sloveno sl, sl-si
Spagnolo 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
Svedese sv, sv-se, sv-fi
Tagico (cirillico) tg-arab, tg-cyrl, tg-cyrl-tj, tg-latn
Tamil ta, ta-in
Tataro tt-arab, tt-cyrl, tt-latn, tt-ru
Telugu te, te-in
Thai th, th-th
Tigrino ti, ti-et
Turco tr, tr-tr
Turkmeno tk-cyrl, tk-latn, tk-tm, tk-latn-tr, tk-cyrl-tr
Ucraino uk, uk-ua
Urdu ur, ur-pk
Uiguro ug-arab, ug-cn, ug-cyrl, ug-latn
Uzbeco (alfabeto latino) uz, uz-cyrl, uz-latn, uz-latn-uz
Vietnamita vi, vi-vn
Gallese cy, cy-gb
Wolof wo, wo-sn
Yoruba yo-latn, yo-ng

Richiesta di esempio

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

Intestazioni di risposta

Intestazione Valore
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object Contiene il payload di risposta effettivo per la richiesta
pollingUrl String URL di polling per ottenere lo stato di qualsiasi invio in corso
ongoingSubmissionId String ID invio di qualsiasi invio già in corso

Risposta di esempio

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

Get Current Draft Packages API

Recupera i dettagli del pacchetto nell'invio corrente della bozza.

Percorso [Tutti i pacchetti]: /submission/v1/product/{productId}/packages
Metodo: GET

Percorso [Pacchetto singolo]: /submission/v1/product/{productId}/packages/{packageId}
Metodo: GET

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto
packageId ID univoco del pacchetto da recuperare

Intestazioni obbligatorie

Intestazione Valore
Authorization: Bearer <Token> Uso dell'ID dell'app Azure AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Valore
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
packages Matrice di oggetti Oggetto per contenere i dati del modulo del pacchetto
packageId String
packageUrl String
lingue Matrice di stringhe
architetture Matrice di stringhe [Neutral, X86, X64, Arm, Arm64]
isSilentInstall Booleano Questo valore deve essere contrassegnato come true se il programma di installazione viene eseguito in modalità invisibile all'utente senza richiedere commutatori o false
installerParameters String
genericDocUrl String
errorDetails Matrice di oggetti
errorScenario String
errorScenarioDetails Matrice di oggetti
errorValue String
errorUrl String
packageType String

Risposta di esempio

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

Aggiornare l'API Current Draft Packages

Aggiorna i dettagli del pacchetto nell'invio corrente della bozza.

Percorso [Aggiornamento modulo completo]: /submission/v1/product/{productId}/packages
Metodo: PUT

Percorso [Aggiornamento patch pacchetto singolo]: /submission/v1/product/{productId}/packages/{packageId}
Metodo: PATCH

Comportamento API:

Nel caso dell'API di aggiornamento del modulo completo, è necessario che tutti i dati dei pacchetti siano presenti nella richiesta di aggiornamento completo di ogni campo. Se il campo non è presente nella richiesta, viene usato il relativo valore predefinito per sovrascrivere il valore corrente per tale modulo specifico. Ciò comporta la sovrascrittura di tutti i pacchetti esistenti con un nuovo set di pacchetti dalla richiesta. Ciò comporterà la rigenerazione degli ID pacchetto e l'utente deve chiamare l'API GET Packages per gli ID pacchetto più recenti.

Nel caso dell'API aggiornamento patch pacchetto singolo: solo i campi da aggiornare per un determinato pacchetto devono essere presenti nella richiesta. Questi valori di campo della richiesta sovrascriveranno i valori esistenti, mantenendo tutti gli altri campi che non sono presenti nella richiesta, come quello corrente per quel pacchetto specifico. Gli altri pacchetti nel set rimangono invariati.

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto
packageId L'ID univoco del pacchetto

Intestazioni obbligatorie

Intestazione Valore
Authorization: Bearer <Token> Uso dell'ID dell'app Azure AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Parametri della richiesta

Nome Tipo Descrizione
packages Matrici di oggetti Oggetto per contenere i dati del modulo del pacchetto [Obbligatorio solo per l'aggiornamento completo del modulo]
packageUrl String Obbligatorio
lingue Matrice di stringhe Obbligatorio
architetture Matrice di stringhe Obbligatorio Dovrebbe contenere un'architettura singola - Neutro, X86, X64, Arm, Arm64
isSilentInstall Booleano Necessario Questo valore deve essere contrassegnato come true se il programma di installazione viene eseguito in modalità invisibile all'utente senza richiedere commutatori o false
installerParameters String Obbligatorio se isSilentInstall è false
genericDocUrl String Obbligatorio se packageType è exe Link al documento contenente i dettagli dei codici di errore personalizzati per il programma di installazione del tipo EXE
errorDetails Matrici di oggetti Metadati per contenere codici di errore personalizzati e dettagli per i programmi di installazione di tipo EXE.
errorScenario String Identificare lo scenario di errore specifico. [installationCancelledByUser, applicationAlreadyExists, installationAlreadyInProgress, diskSpaceIsFull, rebootRequired, networkFailure, packageRejectedDuringInstallation, installationSuccessful, miscellaneous]
errorScenarioDetails Matrici di oggetti
errorValue String Codice di errore che può essere presente durante l'installazione
errorUrl String URL per informazioni dettagliate sull'errore
packageType String Obbligatorio [exe, msi]

Richiesta di esempio [aggiornamento completo del modulo]

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

Richiesta di esempio [Aggiornamento patch pacchetto singolo]

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

Intestazioni di risposta

Intestazione Valore
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti [Elenco di messaggi di errore o di avvertenza, se presenti]
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
pollingUrl String [URL di polling per ottenere lo stato dell'invio in caso di invio già in corso]
ongoingSubmissionId String [ID invio di qualsiasi invio già in corso]

Risposta di esempio

{
    "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 pacchetti commit

Esegue il commit del nuovo set di pacchetti aggiornato usando le API di aggiornamento dei pacchetti nell'invio bozza corrente. Questa API restituisce un URL di polling per tenere traccia del caricamento del pacchetto.

Percorso: /submission/v1/product/{productId}/packages/commit
Metodo: POST

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Valore
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Valore
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti [Elenco di messaggi di errore o di avvertenza, se presenti]
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
pollingUrl String [URL di polling per ottenere lo stato di caricamento o invio del pacchetto in caso di invio già in corso]
ongoingSubmissionId String [ID invio di qualsiasi invio già in corso]

Risposta di esempio

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

Ottenere l'API corrente di listato bozza di asset

Recupera i dettagli dell'asset di inserzione sotto l'invio della bozza corrente.

Percorso: /submission/v1/product/{productId}/listings/assets?languages={languages}
Metodo: GET

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Parametri della query

Nome Descrizione
lingue [Facoltativo] le lingue in elenco vengono filtrate come stringa delimitata da virgole [limite massimo 200 lingue]. Se assente, vengono recuperati i primi 200 dati relativi alle risorse della lingua di presentazione disponibili. (per es, “en-us, en-gb")

Intestazioni obbligatorie

Intestazione Valore
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Valore
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
listingAssets Matrice di oggetti Elencare i dettagli dell'asset per ogni lingua
lingua String
storeLogos Matrice di oggetti
screenshot Matrice di oggetti
id String
assetUrl String Deve essere un URL valido
imageSize Object
width Intero
height Intero

Risposta di esempio

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

Crea API elenco di asset

Crea un nuovo caricamento asset elenco nell'invio della bozza corrente.

Aggiornamento degli asset di presentazione

L'API per l'invio in Microsoft Store per l'app EXE o MSI usa URL SAS generati da runtime nei Blob Store per ogni singolo caricamento di asset immagine, insieme a una chiamata API Commit dopo che il caricamento ha esito positivo. Per poter aggiornare gli asset di presentazione e, a sua volta, per poter aggiungere/rimuovere le impostazioni locali nel modulo di presentazione, è possibile usare l'approccio seguente:

  1. Usare l'API Crea asset di presentazione per inviare richieste relative al caricamento degli asset insieme a lingua, tipo e numero di asset.
  2. In base al numero di asset richiesti, gli ID asset vengono creati su richiesta e creerebbero un URL di firma di accesso condiviso a breve termine e lo inviano nuovamente nel corpo della risposta nel tipo di asset. È possibile usare questo URL per caricare asset immagine di tipo specifico usando client HTTP [Put Blob (API REST) - Archiviazione di Azure | Microsoft Docs].
  3. Dopo il caricamento, è possibile usare l'API Commit Listing Assets per inviare anche le nuove informazioni sull'ID risorsa ricevute in precedenza dalla chiamata API precedente. L'API singola esegue il commit interno dei dati dell'elenco degli asset dopo la convalida.
  4. Questo approccio sovrascrive efficacemente l'intero set di immagini precedenti del tipo di asset in una lingua specifica che viene inviata nella richiesta. Di conseguenza, gli asset caricati in precedenza verranno rimossi.

Percorso: /submission/v1/product/{productId}/listings/assets/create
Metodo: POST

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Descrizione
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Parametri della richiesta

Nome Tipo Descrizione
lingua String Obbligatorio
createAssetRequest Object Obbligatorio
Schermata Intero Obbligatorio se ISV deve aggiornare gli screenshot o aggiungere una nuova lingua di presentazione [1 - 10]
Logo Intero Obbligatorio se ISV deve aggiornare i loghi o aggiungere una nuova lingua di presentazione [1 o 2]

Intestazioni di risposta

Intestazione Descrizione
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
listingAssets Object Oggetto contenente i dettagli di StoreLogos e Screenshot da caricare
lingua String
storeLogos Matrice di oggetti
screenshot Matrice di oggetti
id String
primaryAssetUploadUrl String URL principale per caricare l'asset di presentazione con l'API REST Blob Azure
secondaryAssetUploadUrl String URL secondario per caricare l'asset di presentazione con l'API REST Blob Azure
httpMethod Metodo HTTP Il metodo HTTP deve essere usato per caricare gli asset tramite gli URL di caricamento asset , principale o secondario
httpHeaders Object Oggetto con chiavi come intestazioni obbligatorie da presentare nella chiamata API di caricamento degli asset agli URL di caricamento degli asset. Se il valore non è vuoto, le intestazioni devono avere valori specifici. In caso contrario, i valori vengono calcolati durante la chiamata API.

Risposta di esempio

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

            }]
        }
    } 
}

Commit Listing Assets API

Esegue il commit del nuovo asset di presentazione caricato usando i dettagli dell'API Crea asset nell'invio bozza corrente.

Percorso: /submission/v1/product/{productId}/listings/assets/commit
Metodo: PUT

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Descrizione
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Parametri della richiesta

Nome Tipo Descrizione
listingAssets Object
lingua String
storeLogos Matrici oggetto
screenshot Matrici oggetto
id String Deve essere un ID esistente che l'utente vuole rendere persistente dall'API Recupera asset di presentazione corrente o nuovo ID con cui è stato caricato un nuovo asset nell'API Crea asset di presentazione.
assetUrl String Deve essere l'URL dell'asset esistente che l'utente vuole rendere persistente dall'API Recupera asset di presentazione corrente o dall'URL di caricamento - Principale o Secondario, usando il quale è stato caricato un nuovo asset nell'API Crea asset di presentazione. Deve essere un URL valido

Richiesta di esempio

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

Intestazioni di risposta

Intestazione Descrizione
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
pollingUrl String URL di polling per ottenere lo stato di qualsiasi invio in corso
ongoingSubmissionId String ID invio di qualsiasi invio già in corso

Risposta di esempio

{
    "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 di polling dello stato del modulo

API per verificare l'idoneità del modulo prima di poter creare l'invio. Convalida anche lo stato di caricamento del pacchetto.

Percorso: /submission/v1/product/{productId}/status
Metodo: GET

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Descrizione
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Descrizione
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
isReady Booleano Indica se tutti i moduli sono in stato pronto, incluso il caricamento del pacchetto
ongoingSubmissionId String ID invio di qualsiasi invio già in corso

Risposta di esempio

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

API di creazione dell'invio

Crea un invio dalla bozza corrente per l'app MSI o EXE. Controlli dell'API:

  • per l'invio attivo e ha esito negativo con messaggio di errore se esiste un invio attivo.
  • se tutti i moduli sono in stato pronto per creare l'invio.
  • ogni campo nell'invio viene convalidato in base ai requisiti dello Store

Percorso:/submission/v1/product/{productId}/submit
Metodo: POST

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Descrizione
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Descrizione
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
pollingUrl String URL di polling per ottenere lo stato di idoneità del modulo, incluso il caricamento del pacchetto per l'invio
submissionId String ID per l'invio appena creato
ongoingSubmissionId String ID invio di qualsiasi invio già in corso

Risposta di esempio

{
    "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 di polling dello stato dell'invio

API per controllare lo stato di invio.

Percorso: /submission/v1/product/{productId}/submission/{submissionId}/status
Metodo: GET

Parametri del percorso

Nome Descrizione
productId ID del Partner Center del prodotto

Intestazioni obbligatorie

Intestazione Descrizione
Authorization: Bearer <Token> Uso dell'ID dell'app App AD registrato con l'account del Partner Center
X-Seller-Account-Id ID venditore dell'account del Partner Center

Intestazioni di risposta

Intestazione Descrizione
X-Correlation-ID ID univoco del tipo GUID per ogni richiesta. Questa operazione può essere condivisa con il team di supporto per l'analisi di qualsiasi problema.
Retry-After Tempo in secondi che il client deve attendere prima di richiamare nuovamente le API a causa della limitazione della frequenza.

Parametri di risposta

Nome Tipo Descrizione
isSuccess Booleano
errori Matrice di oggetti Elenco di messaggi di errore o di avvertenza, se presenti
codice String Il codice di errore del messaggio
messaggio String Descrizione dell'errore
bersaglio String Entità da cui ha avuto origine l'errore
responseData Object
publishingStatus String Stato di pubblicazione dell'invio - [INPROGRESS, PUBLISHED, FAILED, UNKNOWN]
hasFailed Booleano Indica se la pubblicazione non è riuscita e non verrà ritentata

Risposta di esempio

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

Esempi di codice

Gli articoli seguenti forniscono esempi di codice dettagliati che illustrano come usare l'API di invio di Microsoft Store in linguaggi di programmazione diversi:

Esempio C#: API di invio a Microsoft Store per l'app MSI o EXE

Questo articolo fornisce esempi di codice C# che illustrano come usare l'API di invio di Microsoft Store per l'app MSI o EXE. È possibile esaminare ogni esempio per altre informazioni sull'attività illustrata oppure compilare tutti gli esempi di codice in questo articolo in un'applicazione da console.

Prerequisiti Questi esempi usano la libreria seguente:

  • Pacchetto NuGet Newtonsoft.Json da Newtonsoft.

Programma principale L'esempio seguente implementa un programma da riga di comando che chiama gli altri metodi di esempio di questo articolo per illustrare diversi modi di uso dell'API di invio di Microsoft Store. Per adattare questo programma per l'uso personale:

  • Assegnare la proprietà SellerId all'ID venditore dell'account del Partner Center.
  • Assegnare la proprietà ApplicationId all'ID dell'app che si vuole gestire.
  • Assegnare le proprietà ClientId e ClientSecret all'ID client e alla chiave per l'app e sostituire la stringa tenantid nell'URL TokenEndpoint con l'ID tenant dell'app. Per altre informazioni, vedere Come associare un'applicazione Azure AD all'account del Centro per i partner
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();

        }
    }
}

Classe helper ClientConfiguration con C#

L'app di esempio usa la classe helper ClientConfiguration per passare i dati di Azure Active Directory e i dati dell'app a ogni metodo di esempio che usa l'API di invio di Microsoft Store.

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

namespace Win32SubmissionApiCSharpSample
{
    public class ClientConfiguration
    {
        /// <summary>
        /// Client Id of your Azure Active Directory app.
        /// Example" ba3c223b-03ab-4a44-aa32-38aa10c27e32
        /// </summary>
        public string ClientId { get; set; }

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

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

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

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

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


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

Creare un invio di app usando C#

L'esempio seguente implementa una classe che usa diversi metodi nell'API di invio di Microsoft Store per aggiornare un invio di un'app.

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

Classe helper di IngestionClient con C#

La classe IngestionClient fornisce metodi helper che vengono usati da altri metodi nella stessa app di esempio per eseguire le attività seguenti:

  • Ottenere il token di accesso di Azure AD che può essere usato per chiamare metodi nell'API di invio di Microsoft Store. Dopo aver ottenuto un token, hai 60 minuti per usare questo token nelle chiamate all'API di invio a Microsoft Store prima della scadenza del token. Dopo la scadenza del token, è possibile generare un nuovo token.
  • Elaborare le richieste HTTP per l'API di invio di Microsoft Store.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

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

        private readonly string accessToken;

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

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

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

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

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

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

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

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

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

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

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

            return result.access_token;
        }


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

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

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

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

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

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

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

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


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

Esempio Node.js: API di invio a Microsoft Store per l'app MSI o EXE

Questo articolo fornisce esempi di codice Node.js che illustrano come usare l'API di invio di Microsoft Store per l'app MSI o EXE. È possibile esaminare ogni esempio per altre informazioni sull'attività illustrata oppure compilare tutti gli esempi di codice in questo articolo in un'applicazione da console.

Prerequisiti Questi esempi usano la libreria seguente:

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

Creare un invio di app usando node.js

L'esempio seguente chiama gli altri metodi di esempio in questo articolo per illustrare diversi modi per usare l'API di invio a Microsoft Store. Per adattare questo programma per l'uso personale:

  • Assegnare la proprietà SellerId all'ID venditore dell'account del Partner Center.
  • Assegnare la proprietà ApplicationId all'ID dell'app che si vuole gestire.
  • Assegnare le proprietà ClientId e ClientSecret all'ID client e alla chiave per l'app e sostituire la stringa tenantid nell'URL TokenEndpoint con l'ID tenant dell'app. Per altre informazioni, vedere Come associare un'applicazione Azure AD all'account del Centro per i partner

L'esempio seguente implementa una classe che usa diversi metodi nell'API di invio di Microsoft Store per aggiornare un invio di un'app.

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

Helper ClientConfiguration

L'app di esempio usa la classe helper ClientConfiguration per passare i dati di Azure Active Directory e i dati dell'app a ogni metodo di esempio che usa l'API di invio di 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;

Helper di IngestionClient con node.js

La classe IngestionClient fornisce metodi helper che vengono usati da altri metodi nella stessa app di esempio per eseguire le attività seguenti:

  • Ottenere il token di accesso di Azure AD che può essere usato per chiamare metodi nell'API di invio di Microsoft Store. Dopo aver ottenuto un token, hai 60 minuti per usare questo token nelle chiamate all'API di invio a Microsoft Store prima della scadenza del token. Dopo la scadenza del token, è possibile generare un nuovo token.
  • Elaborare le richieste HTTP per l'API di invio di 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;

Informazioni aggiuntive

Per eventuali domande sull'API di invio di Microsoft Store o per assistenza nella gestione degli invii con questa API, usare le risorse seguenti:

  • Porre le domande sui nostri forum.
  • Visitare la nostra pagina di supporto e richiedere una delle opzioni di assistenza del Centro per i partner. Se viene richiesto di scegliere un tipo di problema e una categoria, scegliere rispettivamente Invio e certificazione di un'app e Invio di un'app.