Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Artikel enthält Python-Codebeispiele zeigt das Verwenden der Microsoft Store-Übermittlungs-API für diese Aufgaben:
- Abrufen eines Azure AD-Zugriffstokens zur Nutzung mit der Microsoft Store-Übermittlungs-API.
- Erstellen einer App-Übermittlung
- Konfigurieren von Store-Eintragsdaten für die App-Übermittlung, einschließlich der erweiterten Eintragsoptionen Spiele und Trailer.
- Hochladen der ZIP-Datei mit den Paketen, Eintragsbildern und Trailerdateien für die App-Übermittlung.
- Ausführen des Commit für eine App-Übermittlung.
Erstellen einer App-Übermittlung
Dieser Code ruft andere Beispielklassen und Funktionen auf, um die Microsoft Store-Übermittlungs-API zum Erstellen und Ausführen eines Commits einer App-Übermittlung mit Optionen und einem Trailer verwendet. So passen Sie den Code für eigene Zwecke an:
- Weisen Sie die
tenant-Variable zur Mandanten-ID für Ihre App zu und weisen Sie die Variablenclientundsecretzur Client-ID und dem Schlüssel für die App zu. Weitere Informationen finden Sie unter Zuordnen einer Azure AD-Anwendung zu Ihrem Partner Center-Konto. - Weisen Sie die
application_id-Variable zur Store-ID der App zu, für die eine Übermittlung erstellen möchten.
import time
from devcenterclient import DevCenterClient, DevCenterAccessTokenClient
import submissiondatasamples as samples
# Add your tenant ID, client ID, and client secret here.
tenant = ""
client = ""
secret = ""
acc_token_client = DevCenterAccessTokenClient(tenant, client, secret)
acc_token = acc_token_client.get_access_token("https://manage.devcenter.microsoft.com")
dev_center = DevCenterClient("manage.devcenter.microsoft.com", acc_token)
# The application ID is taken from your app dashboard page's URI in Dev Center,
# e.g. https://developer.microsoft.com/en-us/dashboard/apps/{application_id}/
application_id = ""
# Get the application object, and cancel any in progress submissions.
is_ok, app = dev_center.get_application(application_id)
assert is_ok
if "pendingApplicationSubmission" in app:
in_progress_submission_id = app["pendingApplicationSubmission"]["id"]
is_ok = dev_center.cancel_in_progress_submission(application_id, in_progress_submission_id)
assert is_ok
# Create a new submission, based on the last published submission.
is_ok, submission = dev_center.create_submission(application_id)
assert is_ok
submission_id = submission["id"]
# The following fields are required:
submission["applicationCategory"] = "Games_Fighting"
submission["listings"] = samples.get_listings_object()
submission["Pricing"] = samples.get_pricing_object()
submission["packages"] = [samples.get_package_object()]
submission["allowTargetFutureDeviceFamilies"] = samples.get_device_families_object()
# The app must have the hasAdvancedListingPermission set to True in order for gaming options
# and trailers to be applied. If that's not the case, you can still update the app and
# its submissions through the API, but gaming options and trailers won't be saved.
if not "hasAdvancedListingPermission" in app or not app["hasAdvancedListingPermission"]:
print("This application does not support gaming options or trailers.")
else:
submission["gamingOptions"] = [samples.get_gaming_options_object()]
submission["trailers"] = [samples.get_trailer_object()]
# Continue updating the submission_json object with additional options as needed.
# After you've finished, call the Update API with the code below to save it:
is_ok, submission = dev_center.update_submission(application_id, submission_id, submission)
assert is_ok
# All images and packages should be located in a single ZIP file. In the submission JSON,
# the file names for all objects requiring them (icons, packages, etc.) must exactly
# match the file names from the ZIP file.
zip_file_path = ""
is_ok = dev_center.upload_zip_file_for_submission(application_id, submission_id, zip_file_path)
assert is_ok
# Committing the submission will start the submission process for it. Once committed,
# the submission can no longer be changed.
is_ok = dev_center.commit_submission(application_id, submission_id)
assert is_ok
# After committing, you can poll the commit API for the status of the submission's process using
# the following code.
waiting_for_commit_start = True
while waiting_for_commit_start:
is_ok, submission_status = dev_center.get_submission_status(application_id, submission_id)
assert is_ok
waiting_for_commit_start = submission_status == "CommitStarted"
if waiting_for_commit_start:
time.sleep(60)
Ein Azure AD-Zugriffstoken abrufen und Aufrufen der Übermittlungs-API
Im folgenden Beispiel werden die folgenden Klassen definiert:
- Die
DevCenterAccessTokenClient-Klasse definiert eine Hilfsmethode, die IhretenantId,clientIdundclientSecretWerte zum Erstellen eines Azure AD-Zugriffstokens zur Verwendung mit Microsoft Store-Übermittlungs-API verwendet. - Die
DevCenterClientKlasse definiert Hilfsmethoden, die eine Vielzahl von Methoden in der Microsoft Store-Übermittlungs-API aufrufen und die ZIP-Datei mit den Paketen, Bildern und Trailerdateien für die App-Übermittlung hochladen.
import http.client
import json
import requests
class DevCenterAccessTokenClient(object):
"""A client for acquiring access tokens from AAD to use with the Dev Center Client."""
def __init__(self, tenant_id, client_id, client_secret):
self.tenant_id = tenant_id
self.client_id = client_id
self.client_secret = client_secret
def get_access_token(self, resource):
"""Acquires an access token to the specific resource via the AAD tenant."""
body_format = "grant_type=client_credentials&client_id={0}&client_secret={1}&resource={2}"
body = body_format.format(self.client_id, self.client_secret, resource)
access_headers = {"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"}
token_conn = http.client.HTTPSConnection("login.microsoftonline.com")
token_relative_path = "/{0}/oauth2/token".format(self.tenant_id)
token_conn.request("POST", token_relative_path, body, headers=access_headers)
token_response = token_conn.getresponse()
token_json = json.loads(token_response.read().decode())
token_conn.close()
return token_json["access_token"]
class DevCenterClient(object):
"""A client for the Dev Center API."""
def __init__(self, base_uri, access_token):
self.base_uri = base_uri
self.request_headers = {
"Authorization": "Bearer " + access_token,
"Content-type": "application/json",
"User-Agent": "Python"
}
def get_application(self, application_id):
"""Returns the application as defined in Dev Center."""
path = "/v1.0/my/applications/{0}".format(application_id)
return self._get(path)
def cancel_in_progress_submission(self, application_id, submission_id):
"""Cancels the in-progress submission."""
path = "/v1.0/my/applications/{0}/submissions/{1}".format(application_id, submission_id)
return self._delete(path)
def create_submission(self, application_id):
"""Creates a new submission in Dev Center. This is identical to clicking
the Create Submission button in Dev Center."""
path = "/v1.0/my/applications/{0}/submissions".format(application_id)
return self._post(path)
def update_submission(self, application_id, submission_id, submission):
"""Updates the submission in Dev Center using the JSON provided."""
path = "/v1.0/my/applications/{0}/submissions/{1}"
path = path.format(application_id, submission_id)
return self._put(path, submission)
def get_submission(self, application_id, submission_id):
"""Gets the submission in Dev Center."""
path = "/v1.0/my/applications/{0}/submissions/{1}"
path = path.format(application_id, submission_id)
return self._get(path)
def commit_submission(self, application_id, submission_id):
"""Commits the submission to Dev Center. Once committed, Dev Center will
begin processing the submission and verify package integrity and send
it for certification."""
path = "/v1.0/my/applications/{0}/submissions/{1}/commit"
path = path.format(application_id, submission_id)
return self._post(path)
def get_submission_status(self, application_id, submission_id):
"""Returns the current state of the submission in Dev Center,
such as is the submission in certification, committed, publishing,
etc."""
path = "/v1.0/my/applications/{0}/submissions/{1}/status"
path = path.format(application_id, submission_id)
response_ok, response_obj = self._get(path)
if "status" in response_obj:
return (response_ok, response_obj["status"])
else:
return (response_ok, "Unknown")
def upload_zip_file_for_submission(self, application_id, submission_id, zip_file_path):
"""Uploads a ZIP file for the Submission API for the submission object."""
is_ok, submission = self.get_submission(application_id, submission_id)
if not is_ok:
raise "Failed to get submission."
zip_file = open(zip_file_path, 'rb')
upload_uri = submission["fileUploadUrl"].replace("+", "%2B")
upload_headers = {"x-ms-blob-type": "BlockBlob"}
upload_response = requests.put(upload_uri, zip_file, headers=upload_headers)
upload_response.raise_for_status()
def _get(self, path):
return self._invoke("GET", path)
def _post(self, path, obj=None):
return self._invoke("POST", path, obj)
def _put(self, path, obj=None):
return self._invoke("PUT", path, obj)
def _delete(self, path):
return self._invoke("DELETE", path)
def _invoke(self, method, path, obj=None):
body = ""
if not obj is None:
body = json.dumps(obj)
conn = http.client.HTTPSConnection(self.base_uri)
conn.request(method, path, body, self.request_headers)
response = conn.getresponse()
response_body = response.read().decode()
response_body_length = int(response.headers["Content-Length"])
response_obj = None
if not response_body is None and response_body_length != 0:
response_obj = json.loads(response_body)
response_ok = self._response_ok(response)
conn.close()
return (response_ok, response_obj)
def _response_ok(self, response):
status_code = int(response.status)
return status_code >= 200 and status_code <= 299
Abrufen von Eintragsdaten für eine App-Übermittlung
Das folgende Beispiel definiert Hilfsfunktionen, die JSON-formatierte Eintragsdaten für eine neue Beispiel-App-Übermittlung zurückgeben.
def get_listings_object():
"""Gets a sample listings map for a submission."""
listings = {
# Each listing is targeted at a specific language-locale code, e.g. EN-US.
"en-us" : {
# This structure holds basic information to display in the store.
"baseListing" : {
"copyrightAndTrademarkInfo" : "(C) 2017 Microsoft",
# Up to 7 keywords may be provided in a listing.
"keywords" : ["SampleApp", "SampleFightingGame", "GameOptions"],
"licenseTerms" : "http://example.com/licenseTerms.aspx",
"privacyPolicy" : "http://example.com/privacyPolicy.aspx",
"supportContact" : "support@example.com",
"websiteUrl" : "http://example.com",
"description" : "A sample game showing off gameplay options code.",
"features" : ["Doesn't crash", "Likes to eat chips"],
"releaseNotes" : "Initial release",
"recommendedHardware" : [],
# If your app works better with specific hardware (or needs it), you can
# add or update values here.
"hardwarePreferences": ["Keyboard", "Mouse"],
# The title of the app must match a reserved name for the app in Dev Center.
# If it doesn't, attempting to update the submission will fail.
"title" : "Super Dev Center API Simulator 2017",
"images" : [
# There are several types of images available; at least one screenshot
# is required.
{
# The file name is relative to the root of the uploaded ZIP file.
"fileName" : "img/screenshot.png",
"description" : "A basic screenshot of the app.",
"imageType" : "Screenshot"
}
]
},
# If there are any specific overrides to above information for Windows 8,
# Windows 8.1, Windows Phone 7.1, 8.0, or 8.1, you can add information here.
"platformOverrides" : {}
}
}
return listings
def get_package_object():
"""Gets a sample package for the submission in Dev Center."""
package = {
# The file name is relative to the root of the uploaded ZIP file.
"fileName" : "bin/super_dev_ctr_api_sim.appxupload",
# If you haven't begun to upload the file yet, set this value to "PendingUpload".
"fileStatus" : "PendingUpload"
}
return package
def get_pricing_object():
"""Gets a sample pricing object for a submission."""
pricing = {
# How long the trial period is, if one is allowed. Valid values are NoFreeTrial,
# OneDay, SevenDays, FifteenDays, ThirtyDays, or TrialNeverExpires.
"trialPeriod" : "NoFreeTrial",
# Maps to the default price for the app.
"priceId" : "Free",
# If you'd like to offer your app in different markets at different prices, you
# can provide priceId values per language/locale code.
"marketSpecificPricing" : {}
}
return pricing
def get_device_families_object():
"""Gets a sample device families object for a submission."""
device_families = {
# Supported values are Desktop, Mobile, Xbox, and Holographic. To make
# the app available on that specific platform, set the value to True.
"Desktop" : True,
"Mobile" : False,
"Xbox" : True,
"Holographic" : False
}
return device_families
def get_gaming_options_object():
"""Gets a sample gaming options object for a submission."""
gaming_options = {
# The genres of your app.
"Genres" : ["Games_Fighting"],
# Set this to True if your game supports local multiplayer. This field is required.
"IsLocalMultiplayer" : True,
# If local multiplayer is supported, you must provide the minimum and maximum players
# supported. Valid values are between 2 and 1000 inclusive.
"LocalMultiplayerMinPlayers" : 2,
"LocalMultiplayerMaxPlayers" : 4,
# Set this to True if your game supports local co-op play. This field is required.
"IsLocalCooperative" : True,
# If local co-op is supported, you must provide the minimum and maximum players
# supported. Valid values are between 2 and 1000 inclusive.
"LocalCooperativeMinPlayers" : 2,
"LocalCooperativeMaxPlayers" : 4,
# Set this to True if your game supports online multiplayer. This field is required.
"IsOnlineMultiplayer" : True,
# If online multiplayer is supported, you must provide the minimum and maximum players
# supported. Valid values are between 2 and 1000 inclusive.
"OnlineMultiplayerMinPlayers" : 2,
"OnlineMultiplayerMaxPlayers" : 4,
# Set this to true if your game supports online co-op play. This field is required.
"IsOnlineCooperative" : True,
# If online co-op is supported, you must provide the minimum and maximum players
# supported. Valid values are between 2 and 1000 inclusive.
"OnlineCooperativeMinPlayers" : 2,
"OnlineCooperativeMaxPlayers" : 4,
# If your game supports broadcasting a stream to other players, set this field to True.
# The field is required.
"IsBroadcastingPrivilegeGranted" : True,
# If your game supports cross-device play (e.g. a player can play on an Xbox One with
# their friend who's playing on a PC), set this field to True. This field is required.
"IsCrossPlayEnabled" : True,
# If your game supports Kinect usage, set this field to "Enabled", otherwise, set it to
# "Disabled". This field is required.
"KinectDataForExternal" : "Disabled",
# Free text about any other peripherals that your game supports. This field is optional.
"OtherPeripherals" : "Supports the usage of all fighting joysticks."
}
return gaming_options
def get_trailer_object():
"""Gets a sample trailer object for the submission in Dev Center."""
trailer = {
# This is the filename of the trailer. The file name is a relative path to the
# root of the ZIP file to be uploaded to the API.
"VideoFileName" : "trailers/main/my_awesome_trailer.mpeg",
# Aside from the video itself, a trailer can have image assets such as screenshots
# or alternate images. These are separated by language-locale code, e.g. EN-US.
"TrailerAssets" : {
"en-us" : {
# The title of the trailer to display in the store.
"Title" : "Main Trailer",
# The list of images provided with the trailer that are shown
# when the trailer isn't playing.
"ImageList" : [
{
# The file name of the image. The file name is a relative
# path to the root of the ZIP
# file to be uploaded to the API.
"FileName" : "trailers/main/thumbnail.png",
# A plaintext description of what the image represents.
"Description" : "The thumbnail for the trailer shown " +
"before the user clicks play"
},
{
"FileName" : "trailers/main/alt-img.png",
"Description" : "The image to show after the trailer plays"
}
]
}
}
}
return trailer