MSI または EXE アプリの Microsoft Store 申請 API

MSI または EXE アプリ用の Microsoft Store 申請 API を使用して、自分または組織のパートナー センター アカウントの MSI または EXE アプリの申請をプログラムで照会して作成します。 この API は、アカウントで多数のアプリを管理しており、これらのアセットの申請プロセスを自動化および最適化する場合に便利です。 この API は、Azure Active Directory (Azure AD) を使って、アプリまたはサービスからの呼び出しを認証します。

次の手順では、Microsoft Store 申請 API を使用するエンド ツー エンドのプロセスについて説明します。

  1. すべての前提条件を完了したことを確認します。
  2. Microsoft Store 申請 API でメソッドを呼び出す前に、Azure AD アクセス トークンを取得します。 トークンを取得した後、Microsoft Store 申請 API の呼び出しでこのトークンを使用できるのは、その有効期限が切れるまでの 60 分間です。 トークンの有効期限が切れた後は、新しいトークンを生成できます。
  3. MSI または EXE アプリ用の Microsoft Store 申請 API を呼び出します。

手順 1: Microsoft Store 申請 API を使用するための前提条件を完了する。

MSI または EXE アプリ用の Microsoft Store 申請 API を呼び出すコードの記述を開始する前に、次の前提条件を満たしていることを確認してください。

  • 自分 (または自分の組織) に Azure AD ディレクトリがあり、自分がそのディレクトリに対するグローバル管理者のアクセス許可を持っている必要があります。 Microsoft 365 または Microsoft の他のビジネス サービスをすでに使用している場合、Azure AD ディレクトリをすでに所有しています。 それ以外の場合は、追加料金なしにパートナー センターで新しい Azure AD を作成できます。
  • Azure AD アプリケーションをパートナー センター アカウントと関連付け、テナント ID、クライアント ID、キーを取得する必要があります。 Azure AD アクセス トークンを取得するにはこれらの値が必要です。Microsoft Store 申請 API への呼び出しにこれらを使用します。
  • Microsoft Store 申請 API で使用するアプリを準備します。

Azure AD アプリケーションをパートナー センター アカウントと関連付ける方法

MSI または EXE アプリ用の Microsoft Store 申請 API を使用する前に、Azure AD アプリケーションをパートナー センター アカウントに関連付け、アプリケーションのテナント ID とクライアント ID を取得して、キーを生成する必要があります。 Azure AD アプリケーションは、Microsoft Store 申請 API を呼び出すアプリまたはサービスを表します。 API に渡す Azure AD アクセス トークンを取得するには、テナント ID、クライアント ID、キーが必要です。

Note

この作業を行うのは一度だけです。 テナント ID、クライアント ID、キーがあれば、新しい Azure AD アクセス トークンを作成する必要がある度にそれらを再利用できます。

  1. パートナー センターで、組織のパートナー センター アカウントを組織の Azure AD ディレクトリに関連付けます
  2. 次に、パートナー センターの [アカウント設定] セクションの [ユーザー] ページから、パートナー センター アカウントの申請にアクセスするために使用するアプリまたはサービスを表す Azure AD アプリケーションを追加します。 このアプリケーションにマネージャー ロールを確実に割り当てます。 アプリケーションがまだ Azure AD ディレクトリに存在しない場合、パートナー センターで新しい Azure AD アプリケーションを作成できます。
  3. [ユーザー] ページに戻り、Azure AD アプリケーションの名前をクリックしてアプリケーション設定に移動し、テナント ID とクライアント ID の値を書き留めます。
  4. 新しいキーまたはクライアント シークレットを追加するには、次の手順を参照するか、Azure Portal を使用してアプリを登録する手順を参照してください。

アプリを登録するには:

  1. Azure portal にサインインします。

  2. 複数のテナントにアクセスできる場合は、トップ メニューの [ディレクトリとサブスクリプション] フィルター を使用して、アプリケーションを登録するテナントに切り替えます。

  3. Azure Active Directory を検索して選択します。

  4. [管理] で [アプリの登録] を選択し、>アプリケーションを選択します。

  5. >[証明書 & シークレット][クライアント シークレット]>[新しいクライアント シークレット] を選択します。

  6. クライアント シークレットの説明を追加します。

  7. シークレットの有効期限を選択するか、カスタムの有効期間を指定します。

  8. クライアント シークレットの有効期間は、2 年間 (24 か月) 以下に制限されています。 24 か月を超えるカスタムの有効期間を指定することはできません。

    Note

    Microsoft では、有効期限の値は 12 か月未満に設定することをお勧めしています。

  9. [追加] を選択します。

  10. クライアント アプリケーションのコードで使用できるように、"シークレットの値を記録します"。 このページからの移動後は、このシークレットの値は "二度と表示されません"。

手順 2:Azure AD アクセス トークンを取得する

MSI または EXE アプリ用の Microsoft Store 申請 API でメソッドを呼び出す前に、まず、API の各メソッドの Authorization ヘッダーに渡す Azure AD アクセス トークンを取得する必要があります。 アクセス トークンを取得したら、期限が切れる 60 分が経過する前に使用します。 トークンの有効期限が切れた後は、トークンを更新してそれ以降の API 呼び出しで引き続き使用できます。

アクセス トークンを取得するには、「[クライアント資格情報を使用したサービス間呼び出し]/azure/active-directory/azuread-dev/v1-oauth2-client-creds-grant-flow」の手順に従って、https://login.microsoftonline.com/<tenant_id>/oauth2/token エンドポイントに HTTP POST を送信します。 要求の例を次に示します。

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

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

tenant_idPOST URI と client_idandclient_secret パラメーターの値には、前のセクションでパートナー センターから取得したアプリケーションのテナント ID、クライアント ID、およびキーを指定します。 scope パラメーターには、 を指定する必要がありますhttps://api.store.microsoft.com/.default

アクセス トークンの有効期限が切れた後は、この手順に従って更新できます。

C# または Node.js を使用してアクセス トークンを取得する方法を示す例については、MSI または EXE アプリの Microsoft Store 申請 API のコード例を参照してください。

手順 3: Microsoft Store 申請 API を使用する

Azure AD アクセス トークンを取得したら、MSI または EXE アプリ用の Microsoft Store 申請 API でメソッドを呼び出すことができます。 API には、アプリのシナリオにグループ化された多くのメソッドが含まれています。 申請を作成または更新するには、通常、複数のメソッドを特定の順序で呼び出します。 各シナリオと各メソッドの構文については、次のセクションを参照してください。

Note

アクセス トークンを取得した後、トークンの有効期限が切れる前に、MSI または EXE アプリ用の Microsoft Store 申請 API でメソッドを呼び出すために 60 分かかります。

ベース URL

EXE または MSI アプリ用の Microsoft Store 申請 API のベース URL は次のとおりです。https://api.store.microsoft.com

API コントラクト

Current Draft Submission Metadata API を取得する

現在の下書き送信の下書きにある各モジュールのメタデータ(リスト、プロパティ、または可用性)を取得します。

パス [All Modules]:/submission/v1/product/{productId}/metadata?languages={languages}&includelanguagelist={true/false}
パス [Single Module]:/submission/v1/product/{productId}/metadata/{moduleName}?languages={languages}>includelanguagelist={true/false}
メソッド: GET

パス パラメーター

パラメーター 説明
productId 製品のパートナー センター ID
moduleName パートナー センター モジュール – 登録情報、プロパティ、または可用性

クエリ パラメーター

パラメーター 説明
languages オプション: リスト言語はコンマ区切り文字列としてフィルタリングされます (最大 200 言語の制限)。

存在しない場合は、最初の 200 個の使用可能なリスト言語のメタデータが取得されます。 [ e.g.,“en-us, en-gb"].
includelanguagelist オプションのブール値 – true の場合、追加されたリスト言語のリストとその完全性ステータスを返します。

必須のヘッダー

表題 Value
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

表題 Value
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
accessibilitySupport Boolean
additionalLicenseTerms String
availability Object 可用性 モジュール データ
category String 以下のカテゴリのリストを参照してください
certificationNotes String
code String メッセージのエラーコード
ContactInfo String
著作権 String
dependsOnDriversOrNT Boolean
description String
developedBy String
Discoverability String [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
FreeTrial String [NO_FREE_TRIAL, FREE_TRIAL]
hardwareItemType String
isPrivacyPolicyRequired Boolean
isRecommended Boolean
isRequired Boolean
IsSuccess Boolean
isSystemFeatureRequired オブジェクトの配列
言語 String 以下の言語のリストを参照してください
listings オブジェクトの配列 各言語のモジュール・データのリスト
市場 文字列の配列 以下の市場のリストを参照してください
message String エラーの説明
minimumHardware String
minimumRequirement String
penAndInkSupport Boolean
価格 String [無料、フリーミアム、サブスクリプション、有料]
privacyPolicyUrl String
productDeclarations Object
ProductFeatures 文字列の配列
properties Object プロパティ モジュール データ
recommendedHardware String
recommendedRequirement String
responseData Object 要求の実際の応答ペイロードが含まれます
要件  オブジェクトの配列
SearchTerms 文字列の配列
shortDescription String
subcategory String 以下のサブカテゴリのリストを参照してください
supportContactInfo String
systemRequirementDetails オブジェクトの配列
ターゲット String エラーの発生元エンティティ
Web サイト String
whatsNew String

応答のサンプル

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

現在の下書き送信メタデータ API の更新

下書き提出中の各モジュールのメタデータを更新します。 API チェック

  • アクティブな申請の場合。 存在する場合は、エラー メッセージで失敗します。
  • すべてのモジュールが「ドラフトの保存」操作を許可する準備完了状態にある場合。
  • 申請の各フィールドは、ストアの要件に従って検証されます
  • システム要件の詳細検証ルール:
    • hardwareItemType = メモリで使用できる値: 300MB、750MB、1GB、2GB、4GB、6GB、8GB、12GB、16GB、20GB
    • hardwareItemType = DirectX で使用できる値: DX9、DX10、DX11、DX12-FEATURELEVEL11、DX12-FEATURELEVEL12
    • hardwareItemType = Video_Memory で使用できる値: 1 GB、2 GB、4 GB、6 GB

Path [Full Module Update]:/submission/v1/product/{productId}/metadata
メソッド: PUT

Path [Module Patch Update]:/submission/v1/product/{productId}/metadata
メソッド: パッチ

API ビヘイビアー

Full Module Update API の場合 – すべてのフィールドを完全に更新するには、モジュールデータ全体が Request に存在する必要があります。 リクエストに存在しないフィールドの場合、そのデフォルト値は、その特定のモジュールの現在の値を上書きするために使用されます。
パッチ モジュール更新 API の場合、更新するフィールドのみが要求に存在する必要があります。 リクエストのこれらのフィールド値は既存の値を上書きし、リクエストに存在しない他のすべてのフィールドは、その特定のモジュールの現在のフィールドと同じままになります。

パス パラメーター

パラメーター 説明
productId 製品のパートナー センター ID

必須のヘッダー

表題 Value
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

要求パラメーター

名前 種類 説明
availability Object 可用性モジュールのメタデータを保持するオブジェクト
市場 文字列の配列 必須 以下の市場のリストを参照してください
Discoverability String 必須[DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Boolean 必須
価格 String 必須[無料、フリーミアム、サブスクリプション、有料]
FreeTrial String 価格が有料またはサブスクリプションの場合は必須[NO_FREE_TRIAL、FREE_TRIAL]
properties Object プロパティ モジュール のメタデータを保持するオブジェクト
isPrivacyPolicyRequired Boolean 必須
privacyPolicyUrl String isPrivacyPolicyRequired = true の場合は必須 有効な URL である必要があります
Web サイト String 有効な URI である必要があります。
supportContactInfo String 有効な URL またはメール アドレスである必要があります。
certificationNotes String 推奨文字数の制限 = 2000
category String 必須以下のカテゴリのリストを参照してください
subcategory String 必須以下のサブカテゴリのリストを参照してください
productDeclarations Object 必須
isSystemFeatureRequired オブジェクトの配列 [タッチ、キーボード、マウス、カメラ、NFC_HCE、NFC_Proximity、Bluetooth_LE、テレフォニー、マイク]
isRequired Boolean 必須
isRecommended Boolean 必須
hardwareItemType String 必須
systemRequirementDetails オブジェクトの配列 [プロセッサ、グラフィックス、メモリ、DirectX、Video_Memory]
minimumRequirement String systemRequirementsText に必須、MaxLength = 200

hardwareItemType で使用できる値 = メモリ: [300MB、750MB、1GB、2GB、4GB、6GB、8GB、12GB、16GB、20GB]

hardwareItemType = DirectX で使用できる値: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

hardwareItemType = Video_Memory で使用できる値: [1GB, 2GB, 4GB, 6GB]
recommendedRequirement String systemRequirementsText に必須、MaxLength = 200

hardwareItemType で使用できる値 = メモリ: [300MB、750MB、1GB、2GB、4GB、6GB、8GB、12GB、16GB、20GB]

hardwareItemType = DirectX で使用できる値: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

hardwareItemType = Video_Memory で使用できる値: [1GB, 2GB, 4GB, 6GB]
dependsOnDriversOrNT Boolean 必須
accessibilitySupport Boolean 必須
penAndInkSupport Boolean 必須
listings Object 単一言語のモジュール・データをリストするオブジェクト
言語 String 必須 以下の言語のリストを参照してください
description String 必須 文字の制限 = 10000
whatsNew String 文字数制限 = 1500
ProductFeatures 文字列の配列 機能ごとに 200 文字。最大20の機能
shortDescription String 文字数制限 = 1000
SearchTerms 文字列の配列 検索語句ごとに 30 文字。最大 7 つの検索語句

すべての検索語で合計 21 個の一意の単語
additionalLicenseTerms String 必須 文字の制限 = 10000
著作権 String 文字制限 = 200
developedBy String 文字制限 = 255
要件  オブジェクトの配列 項目あたり 200 文字。最小と推奨の間で合計11項目まで]
minimumHardware String 文字制限 = 200
recommendedHardware String 文字制限 = 200
ContactInfo String 文字制限 = 200
listingsToAdd 文字列の配列 以下の言語のリストを参照してください
listingsToRemove 文字列の配列 以下の言語のリストを参照してください

市場

マーケット 省略形
アフガニスタン AF
アルバニア AL
アルジェリア DZ
米領サモア AS
アンドラ AD
アンゴラ AO
アンギラ AI
南極 AQ
アンティグア・バーブーダ AG
アルゼンチン AR
アルメニア AM
アルバ AW
オーストラリア AU
オーストリア AT
アゼルバイジャン AZ
バハマ BS
バーレーン BH
バングラデシュ BD
バルバドス BB
ベラルーシ BY
ベルギー BE
ベリーズ BZ
ベナン BJ
バミューダ BM
ブータン BT
ベネズエラ・ボリバル共和国 VE
ボリビア BO
ボネール島 BQ
ボスニア・ヘルツェゴビナ BA
ボツワナ BW
ブーベ島 BV
ブラジル BR
英領インド洋地域 IO
英領ヴァージン諸島 VG
ブルネイ BN
ブルガリア BG
ブルキナファソ BF
ブルンジ BI
カンボジア KH
カメルーン CM
カナダ CA
カーボベルデ CV
ケイマン諸島 KY
中央アフリカ共和国 CF
チャド TD
チリ CL
中国 CN
クリスマス島 CX
ココス (キーリング) 諸島 Cc
コロンビア CO
コモロ KM
コンゴ共和国 CG
コンゴ民主共和国 CD
クック諸島 CK
コスタリカ CR
クロアチア HR
キュラソー CW
キプロス CY
チェコ共和国 CZ
コートジボワール CI
デンマーク DK
ジブチ DJ
ドミニカ国 DM
ドミニカ共和国 推奨
エクアドル EC
エジプト EG
エルサルバドル SV
赤道ギニア GQ
エリトリア ER
エストニア EE
エチオピア ET
フォークランド諸島 FK
フェロー諸島 FO
フィジー FJ
フィンランド FI
フランス FR
仏領ギアナ GF
仏領ポリネシア PF
フランス領南極地方 TF
ガボン GA
ガンビア GM
ジョージア GE
ドイツ DE
ガーナ GH
ジブラルタル GI
ギリシャ GR
グリーンランド GL
グレナダ GD
グアドループ GP
グアム GU
グアテマラ GT
ガーンジー GG
ギニア GN
ギニアビサウ GW
ガイアナ GY
ハイチ HT
ハード島とマクドナルド諸島 HM
バチカン VA
ホンジュラス HN
香港特別行政区 HK
ハンガリー HU
アイスランド IS
インド IN
インドネシア ID
イラク IQ
アイルランド IE
イスラエル IL
イタリア IT
ジャマイカ JM
日本 JP
ジャージー JE
ヨルダン JO
カザフスタン KZ
ケニア KE
キリバス KI
韓国 KR
クウェート KW
キルギスタン KG
ラオス LA
ラトビア LV
レバノン LB
レソト LS
リベリア LR
リビア LY
リヒテンシュタイン LI
リトアニア LT
ルクセンブルク LU
マカオ MO
北マケドニア MK
マダガスカル MG
マラウイ MW
マレーシア MY
モルディブ MV
マリ ML
マルタ MT
マン島 IM
マーシャル諸島 MH
マルティニーク MQ
モーリタニア MR
モーリシャス MU
マヨット YT
メキシコ MX
ミクロネシア FM
モルドバ MD
モナコ MC
モンゴル MN
モンテネグル - ME
モントセラト MS
モロッコ MA
モザンビーク MZ
ミャンマー MM
ナミビア NA
ナウル NR
ネパール NP
オランダ NL
ニューカレドニア NC
ニュージーランド NZ
ニカラグア NI
ニジェール NE
ナイジェリア NG
ニウエ NU
ノーフォーク島 NF
北マリアナ諸島 MP
ノルウェー 使用不可
オマーン OM
パキスタン PK
パラオ PW
パレスチナ自治政府 PS
パナマ PA
パプアニューギニア PG
パラグアイ PY
ペルー PE
フィリピン PH
ピトケアン諸島 PN
ポーランド PL
ポルトガル PT
カタール QA
レユニオン RE
ルーマニア RO
ロシア RU
ルワンダ RW
サン バルテルミー島 BL
セントヘレナ、アセンションおよびトリスタンダクーニャ諸島 SH
セントクリストファー・ネーヴィス KN
セントルシア LC
サン・マルタン島 (フランス領地域) MF
サンピエール島・ミクロン島 PM
セントビンセント及びグレナディーン諸島 VC
サモア WS
サンマリノ SM
サウジアラビア SA
セネガル SN
セルビア RS
セーシェル SC
シエラレオネ SL
シンガポール SG
シント・マールテン島 (オランダ領地域) SX
スロバキア SK
スロベニア SI
ソロモン諸島 SB
ソマリア SO
南アフリカ ZA
サウスジョージア・サウスサンドウィッチ諸島 GS
スペイン ES
スリランカ LK
スリナム SR
スバールバル諸島・ヤンマイエン島 SJ
スワジランド SZ
スウェーデン SE
スイス CH
サントメ・プリンシペ ST
台湾 TW
タジキスタン TJ
タンザニア TZ
タイ TH
ティモール・レステ TL
Tog - TG
トケラウ TK
トンガ TO
トリニダード・トバッグ - TT
チュニジア TN
トルコ TR
トルクメニスタン TM
タークス・カイコス諸島 TC
ツバル TV
米領その他の米領諸島 UM
米領バージン諸島 VI
ウガンダ UG
ウクライナ UA
アラブ首長国連邦 AE
英国 GB
United States US
ウルグアイ UY
ウズベキスタン UZ
バヌアツ VU
ベトナム VN
ウォリス・フツナ WF
イエメン YE
ザンビア ZM
ジンバブエ ZW
オーランド諸島 AX

カテゴリとサブカテゴリ

カテゴリ サブカテゴリ
書籍と参考書 Eリーダー、フィクション、ノンフィクション、リファレンス
事業 会計および財務、コラボレーション、CRM、データと分析、ファイル管理、在庫およびロジスティクス、法務および人事、プロジェクト管理、リモートデスクトップ、販売およびマーケティング、時間と費用
開発者ツール データベース, 設計ツール, 開発キット, ネットワーキング, リファレンスとトレーニング, サーバー, ユーティリティ, ウェブホスティング
教育 教育書籍と参考書、早期学習、教育ツール、言語、学習支援
エンターテイメント (なし)
フードとダイニング (なし)
政府と政治 (なし)
健康とフィットネス (なし)
子供と家族 子供と家族&書籍と参考書、キッズとファミリーエンターテイメント、趣味・玩具、スポーツとアクティビティ、キッズとファミリートラベル
ライフスタイル 自動車 、 DYI 、 ホームアンドガーデン 、 関係 、 スペシャルインタレスト 、 スタイルとファッション
医療 (なし)
マルチメディアデザイン イラスト・グラフィックデザイン、音楽制作、写真・映像制作
ミュージック (なし)
ナビゲーションと地図 (なし)
ニュースと天気予報 ニュース, 天気
パーソナルファイナンス 銀行と投資、予算編成と税金
パーソナル化 着メロとサウンド, テーマ, 壁紙とロック画面
写真とビデオ (なし)
生産性 (なし)
セキュリティ PC保護、個人セキュリティ
ショッピング (なし)
ソーシャル (なし)
スポーツ (なし)
移動 シティガイド,ホテル
ユーティリティとツール バックアップと管理、ファイルマネージャー

言語

言語名 サポートされている言語コード
アフリカーンス語 af、af-za
アルバニア語 sq、sq-al
アムハラ語 am、am-et
アルメニア語 hy、hy-am
アッサム語 as、as-in
アゼルバイジャン語 az-arab、az-arab-az、az-cyrl、az-cyrl-az、az-latn、az-latn-az
バスク語 (バスク) eu、eu-es
ベラルーシ語 be、be-by
ベンガル語 bn、bn-bd、bn-in
ボスニア語 bs、bs-cyrl、bs-cyrl-ba、bs-latn、bs-latn-ba
ブルガリア語 bg、bg-bg
カタルニア語 ca、ca-es、ca-es-valencia
チェロキー語 chr-cher、chr-cher-us、chr-latn
簡体中国語 zh-Hans、zh-cn、zh-hans-cn、zh-sg、zh-hans-sg
繁体中国語 zh-Hant、zh-hk、zh-mo、zh-tw、zh-hant-hk、zh-hant-mo、zh-hant-tw、zh-mo、zh-tw、zh-hant-hk、zh-hant-mo、zh-hant-tw
クロアチア語 hr、hr-hr、hr-ba
チェコ語 cs、cs-cz
デンマーク語 da、da-dk
ダリー語 prs、prs-af、prs-arab
オランダ語 nl、nl-nl、nl-be
English 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
エストニア語 et、et-ee
Filipin - fil、fil-latn、fil-ph
フィンランド語 fi、fi-fi
フランス語 fr、fr-be 、fr-ca 、fr-ch 、fr-fr 、fr-lu、fr-cd、fr-ci、fr-cm、fr-ht、fr-ma、fr-mc、fr-ml、fr-re、frc-latn、frp-latn
ガリシア語 gl、gl-es
ジョージア語 ka、ka-ge
ドイツ語 de、de-at、de-ch、de-de、de-lu、de-li
ギリシャ語 el、el-gr
グジャラート語 gu、gu-in
ハウサ語 ha、ha-latn、ha-latn-ng
ヘブライ語 he、he-il
ヒンディー語 hi、hi-in
ハンガリー語 hu、hu-hu
アイスランド語 is、is-is
Igb - ig-latn、ig-ng
インドネシア語 id、id-id
イヌクティトット語 (ラテン) iu-cans、iu-latn、iu-latn-ca
アイルランド語 ga、ga-ie
コサ語 xh、xh-za
ズールー語 zu、zu-za
イタリア語 it、it-it、it-ch
日本語 ja、ja-jp
カンナダ語 kn、kn-in
カザフ語 kk、kk-kz
カンボジア語 km、km-kh
キチェ語 quc-latn、qut-gt、qut-latn
キニヤルワンダ語 rw、rw-rw
スワヒリ語 sw、sw-ke
コーンクニー語 kok、kok-in
韓国語 ko、ko-kr
クルド語 ku-arab、ku-arab-iq
キルギス語 ky-kg、ky-cyrl
ラオス語 lo、lo-la
ラトビア語 lv、lv-lv
リトアニア語 lt、lt-lt
ルクセンブルク語 lb、lb-lu
マケドニア語 mk、mk-mk
マレー語 ms、ms-bn、ms-my
マラヤーラム語 ml、ml-in
マルタ語 mt、mt-mt
マオリ語 mi、mi-latn、mi-nz
マラーティー語 mr、mr-in
モンゴル語 (キリル) mn-cyrl、mn-mong、mn-mn、mn-phag
ネパール語 ne、ne-np
ノルウェー語 nb、nb-no、nn、nn-no、no、no-no
オディア語 or、or-in
ペルシャ語 fa、fa-ir
ポーランド語 pl、pl-pl
ポルトガル語 (ブラジル) pt-br
ポルトガル語 (ポルトガル) pt、pt-pt
パンジャーブ語 pa、pa-arab、pa-arab-pk、pa-deva、pa-in
ケチュア語 quz、quz-bo、quz-ec、quz-pe
ルーマニア語 ro、ro-ro
ロシア語 ru、ru-ru
スコットランド ゲール語 gd-gb、gd-latn
セルビア語 (ラテン) sr-Latn、sr-latn-cs、sr、sr-latn-ba、sr-latn-me、sr-latn-rs
セルビア語 (キリル) sr-cyrl、sr-cyrl-ba、sr-cyrl-cs、sr-cyrl-me、sr-cyrl-rs
セソト サ レボア語 nso、nso-za
セツワナ語 tn、tn-bw、tn-za
シンド語 sd-arab、sd-arab-pk、sd-deva
シンハラ語 si、si-lk
スロバキア語 sk、sk-sk
スロベニア語 sl、sl-si
スペイン語 es、es-cl、es-co、es-es、es-mx、es-ar、es-bo、es-cr、es-do、es-ec、es-gt、es-hn、es-ni、es-pa、es-pe、es-pr、es-py、es-sv、es-us、es-uy、es-ve
スウェーデン語 sv、sv-se、sv-fi
タジク語 (キリル) tg-arab、tg-cyrl、tg-cyrl-tj、tg-latn
タミル語 ta、ta-in
タタール語 tt-arab、tt-cyrl、tt-latn、tt-ru
テルグ語 te、te-in
タイ語 th、th-th
ティグリニア語 ti、ti-et
トルコ語 tr、tr-tr
トルクメン語 tk-cyrl、tk-latn、tk-tm、tk-latn-tr、tk-cyrl-tr
ウクライナ語 uk、uk-ua
ウルドゥ語 ur、ur-pk
ウイグル語 ug-arab、ug-cn、ug-cyrl、ug-latn
ウズベク語 (ラテン) uz、uz-cyrl、uz-latn、uz-latn-uz
ベトナム語 vi、vi-vn
ウェールズ語 cy、cy-gb
ウォロフ語 wo、wo-sn
ヨルバ語 yo-latn、yo-ng

要求のサンプル

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

応答ヘッダー

表題 Value
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のためにクライアントが API を再度呼び出すまでに待機する必要がある時間 (秒単位)

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元エンティティ
responseData Object 要求の実際の応答ペイロードが含まれます
pollingUrl String 進行中の送信のステータスを取得するためのポーリングURL
ongoingSubmissionId String 既に進行中の提出書類の提出書類 ID

サンプル応答

{
    "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 を取得する。

現在の下書き提出でパッケージの詳細を取得します。

パス [All Packages]: /submission/v1/product/{productId}/packages
メソッド: GET

パス [Single Package]: /submission/v1/product/{productId}/packages/{packageId}
メソッド: GET

パス パラメーター

名前 説明
productId 製品のパートナー センター ID
packageId フェッチするパッケージの一意の ID

必須のヘッダー

表題 Value
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

表題 Value
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
packages オブジェクトの配列 パッケージ モジュール データを保持するオブジェクト
packageId String
packageUrl: String
languages 文字列の配列
architectures 文字列の配列 [Neutral, X86, X64, Arm, Arm64]
isSilentInstall Boolean これは、インストーラーがスイッチを必要とせずにサイレントモードで実行されている場合はtrueとしてマークするか、そうでない場合はfalseにする必要があります。
installerParameters String
genericDocUrl String
errorDetails オブジェクトの配列
errorScenario String
errorScenarioDetails オブジェクトの配列
errorValue String
errorUrl String
packageType String

応答のサンプル

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

現在のドラフトパッケージの更新 API

現在の下書き提出のパッケージの詳細を更新します。

Path [Full Module Update]: /submission/v1/product/{productId}/packages
メソッド: PUT

パス [Single Package Patch Update]: /submission/v1/product/{productId}/packages/{packageId}
メソッド: パッチ

API ビヘイビアー

完全モジュール更新 API の場合、すべてのフィールドの完全な更新の要求には、パッケージデータ全体が存在する必要があります。 リクエストに存在しないフィールドは、そのデフォルト値を使用して、その特定のモジュールの現在の値を上書きします。 これにより、既存のすべてのパッケージが、要求からの新しいパッケージのセットで上書きされます。 これにより、パッケージ ID が再生成され、ユーザーは最新のパッケージ ID に対して GET Packages API を呼び出す必要があります。

単一パッケージ・パッチ更新APIの場合、特定のパッケージに対して更新されるフィールドのみがリクエストに存在する必要があります。 リクエストからのこれらのフィールド値は既存の値を上書きし、リクエストに存在しない他のすべてのフィールドは、その特定のパッケージの現在のフィールドと同じままにします。 セット内の他のパッケージはそのまま残ります。

パス パラメーター

名前 説明
productId 製品のパートナー センター ID
packageId パッケージの一意の ID

必須のヘッダー

表題 Value
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

要求パラメーター

名前 種類 説明
packages オブジェクトの配列 パッケージモジュールデータを保持するオブジェクト [モジュール全体の更新にのみ必要]
packageUrl: String 必須
languages 文字列の配列 必須
architectures 文字列の配列 必須 1 つのアーキテクチャ (Neutral、X86、X64、Arm、Arm64) を含める必要があります
isSilentInstall Boolean 必須 これはインストーラーがスイッチを必要とせずにサイレントモードで実行されている場合は true としてマークするか、そうでない場合は false にする必要があります
installerParameters String isSilentInstall が false の場合は必須
genericDocUrl String packageType が exe の場合は必須 EXE タイプのインストーラーのカスタム エラー コードの詳細を含むドキュメントへのリンク
errorDetails オブジェクトの配列 EXE タイプのインストーラーのカスタム エラー コードと詳細を保持するメタデータ。
errorScenario String 具体的なエラーシナリオを特定します。 [installationCancelledByUser, applicationAlreadyExists, installationAlreadyInProgress, diskSpaceIsFull, rebootRequired, networkFailure, packageRejectedDuringInstallation, installationSuccessful, miscellaneous]
errorScenarioDetails オブジェクトの配列
errorValue String インストール中に表示される可能性のあるエラーコード
errorUrl String エラーの詳細が記載された URL
packageType String 必須 [exe, msi]

サンプル要求 [Full Module Update]

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

サンプル要求 [Single Package Patch Update]

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

応答ヘッダー

表題 Value
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 [エラーまたは警告メッセージのリスト (存在する場合)]
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元エンティティ
responseData Object
pollingUrl String [すでに進行中の提出の場合に提出ステータスを取得するためのポーリングURL]
ongoingSubmissionId String [既に進行中の提出書類の提出書類 ID]

応答のサンプル

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

コミット パッケージ API

現在の下書き申請で、パッケージ更新 API を使用して更新された新しいパッケージ セットをコミットします。 この API は、パッケージのアップロードを追跡するためのポーリング URL を返します。

パス: /submission/v1/product/{productId}/packages/commit
メソッド: POST

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

必須のヘッダー

表題 Value
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

表題 Value
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 [エラーまたは警告メッセージのリスト (存在する場合)]
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
pollingUrl String [パッケージのアップロードのステータスを取得するためのポーリングURL、またはすでに進行中のサブミッションの場合のサブミッションステータス]
ongoingSubmissionId String [既に進行中の提出書類の提出書類 ID]

応答のサンプル

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

現在のドラフト リスト アセット API を取得する

現在の下書き提出でリスト資産の詳細をフェッチします。

パス: /submission/v1/product/{productId}/listings/assets?languages={languages}
メソッド: GET

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

クエリ パラメーター

名前 説明
languages [オプション]リスト言語は、コンマ区切りの文字列としてフィルタリングされます (最大 200 言語の制限)。 存在しない場合は、最初の 200 個の使用可能なリスト言語のアセットデータが取得されます。 (e.g., “en-us, en-gb")

必須のヘッダー

表題 Value
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

表題 Value
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
listingAssets オブジェクトの配列 各言語のアセット詳細の一覧表示
言語 String
storeLogos オブジェクトの配列
スクリーンショット オブジェクトの配列
ID String
assetUrl String 有効な URI である必要があります。
imageSize Object
width 整数型
height 整数型

応答のサンプル

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

リスティングアセット API の作成

現在の下書きの提出の下に新しいリストアセットのアップロードを作成します。

リスティング資産の更新

EXE または MSI アプリ用の Microsoft Store 申請 API では、個々の画像資産のアップロードごとに、ランタイムで生成された SAS URL を BLOB ストアに使用し、アップロードが成功した後の Commit API 呼び出しを使用します。 リスティングアセットを更新し、リスティングモジュールでロケールを追加/削除できるようにするには、次のアプローチを使用できます。

  1. Create Listing Asset APIを使用して、アセットのアップロードに関するリクエストを、アセットの言語、種類、数とともに送信します。
  2. 要求された資産の数に基づいて、資産 ID はオンデマンドで作成され、短期間の SAS URL が作成され、資産の種類の応答本文で返送されます。 この URL を使用して、HTTP クライアントを使用して特定の種類の画像アセットをアップロードできます [Put Blob (REST API) - Azure Storage |Microsoft Docs] です。
  3. アップロード後、Commit Listing Assets API を使用して、以前の API 呼び出しで以前に受信した新しいアセット ID 情報も送信できます。 単一のAPIは、検証後にリスト資産データを内部的にコミットします。
  4. このアプローチは、リクエストで送信されている特定の言語のアセットタイプの以前の画像のセット全体を事実上上書きします。 そのため、以前にアップロードされたアセットは削除されます。

パス: /submission/v1/product/{productId}/listings/assets/create
メソッド: POST

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

必須のヘッダー

Header 説明
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

要求パラメーター

名前 種類 説明
language String 必須
createAssetRequest Object 必須
Screenshot 整数型 ISV がスクリーンショットを更新したり、新しい登録言語を追加したりする必要がある場合は必須 [1 - 10]
ロゴ 整数型 ISV がロゴを更新したり、新しい登録情報言語を追加したりする必要がある場合に必要 [1 または 2]

応答ヘッダー

ヘッダー 説明
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
listingAssets Object アップロードするStoreLogosとスクリーンショットの詳細を含むオブジェクト
言語 String
storeLogos オブジェクトの配列
スクリーンショット オブジェクトの配列
ID String
primaryAssetUploadUrl String Azure Blob REST API を使用して登録資産をアップロードするためのプライマリ URL
secondaryAssetUploadUrl String Azure Blob REST API を使用して登録資産をアップロードするためのセカンダリ URL
httpMethod HTTP メソッド HTTP メソッドは、アセットのアップロード URL (プライマリまたはセカンダリ) を介してアセットをアップロードするために使用する必要があります
httpHeaders Object アセットアップロード URL へのアップロード API 呼び出しに存在する 要求された ヘッダーとしてのキーを持つオブジェクト。 値が空でない場合、ヘッダーには特定の値が必要です。 それ以外の場合、値は API 呼び出し中に計算されます。

応答のサンプル

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

            }]
        }
    } 
}

Commit Listing Assets API

現在の下書き送信で、Create Assets API の詳細を使用してアップロードされた新しいリストアセットをコミットします。

パス: /submission/v1/product/{productId}/listings/assets/commit
メソッド: PUT

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

必須のヘッダー

Header 説明
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

要求パラメーター

名前 種類 説明
listingAssets Object
言語 String
storeLogos オブジェクトの配列
スクリーンショット オブジェクトの配列
ID String ユーザーがGet Current Listing Assets APIから保持する既存のID、またはCreate Listing Assets APIで新しいアセットがアップロードされた新しいIDのいずれかである必要があります。
assetUrl String ユーザーがGet Current Listing Assets APIから保持する既存のアセットのURL、またはCreate Listing Assets APIで新しいアセットがアップロードされたアップロードURL - プライマリまたはセカンダリのいずれかである必要があります。 有効な URI である必要があります。

要求のサンプル

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

応答ヘッダー

ヘッダー 説明
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
pollingUrl String 進行中の送信の状態を取得するためのポーリング URL
ongoingSubmissionId String 既に進行中の提出書類の提出書類 ID

応答のサンプル

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

モジュール状態ポーリング API

送信前にモジュールの準備状況を確認するための API。 また、パッケージのアップロード状態も検証します。

パス: /submission/v1/product/{productId}/status
メソッド: GET

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

必須のヘッダー

Header 説明
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

ヘッダー 説明
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
isReady Boolean パッケージのアップロードを含め、すべてのモジュールが準備完了状態にあるかどうかを示します。
ongoingSubmissionId String 既に進行中の提出書類の提出書類 ID

応答のサンプル

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

申請 API の作成

MSI または EXE アプリの現在の下書きから申請を作成します。 API チェック:

  • アクティブな送信の場合、アクティブな送信が存在する場合、エラー メッセージが表示されて失敗します。
  • すべてのモジュールが提出書類を作成する準備完了状態にある場合。
  • 申請の各フィールドは、ストアの要件に従って検証されます

パス:/submission/v1/product/{productId}/submit
メソッド: POST

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

必須のヘッダー

Header 説明
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

ヘッダー 説明
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
pollingUrl String 送信用のパッケージのアップロードなど、モジュールの準備状況を取得するためのポーリング URL
submissionId String 新しく作成されたサブミッションの ID
ongoingSubmissionId String 既に進行中の提出書類の提出書類 ID

応答のサンプル

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

Submission Status Polling API

申請状況を確認するための API。

パス: /submission/v1/product/{productId}/submission/{submissionId}/status
メソッド: GET

パス パラメーター

名前 説明
productId 製品のパートナー センター ID

必須のヘッダー

Header 説明
Authorization: Bearer <Token> パートナー センター アカウントに登録されている Azure AD アプリ ID を使用する
X-Seller-Account-Id パートナー センター アカウントの販売者 ID

応答ヘッダー

ヘッダー 説明
X-Correlation-ID 各要求の GUID の種類の一意の ID。 これは、問題を分析するためにサポートチームと共有できます。
Retry-After レート制限のために API を再度呼び出す前にクライアントが待機する必要がある時間 (秒単位)。

応答パラメーター

名前 種類 説明
IsSuccess Boolean
エラー オブジェクトの配列 エラーまたは警告メッセージのリスト (存在する場合)
code String メッセージのエラーコード
message String エラーの説明
ターゲット String エラーの発生元のエンティティ
responseData Object
publishingStatus String 提出書類の発行ステータス - [進行中、公開済み、失敗、不明]
hasFailed Boolean 公開が失敗し、再試行されないかどうかを示します

応答のサンプル

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

コード例

次の記事では、さまざまなプログラミング言語で Microsoft Store 申請 API を使用する方法を示す詳細なコード例を示します。

C# サンプル: MSI または EXE アプリ用の Microsoft Store 申請 API

この記事では、MSI または EXE アプリ用の Microsoft Store 申請 API を使用する方法を示す C# コード例を示します。 各例を確認して、それぞれが対応するタスクについて詳しく知ることができます。また、この記事のすべてのコード例を使って、コンソール アプリケーションをビルドすることもできます。

前提条件 これらの例では、次のライブラリを使用します。

  • Newtonsoft の Newtonsoft.Json NuGet パッケージ。

メイン プログラム 次の例では、この記事の他のメソッド例を呼び出すコマンド ライン プログラムを実装して、Microsoft Store 申請 API を使用するさまざまな方法を示します。 このプログラムを自分の用途に適合させるには:

  • 販売者 ID プロパティをパートナー センター アカウントの販売者 ID に割り当てます。
  • アプリケーション ID プロパティを、管理するアプリの ID に割り当てます。
  • クライアント ID プロパティと クライアントシークレット プロパティをアプリのクライアント ID とキーに割り当て、トークンエンドポイント URL の テナント ID 文字列をアプリのテナント ID に置き換えます。 詳しくは、「Azure AD アプリケーションをパートナー センター アカウントに関連付ける方法」をご覧ください。
using System;
using System.Threading.Tasks;

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

            await new AppSubmissionUpdateSample(config).RunAppSubmissionUpdateSample();

        }
    }
}

C# を使用したクライアント構成ヘルパー クラス

サンプル アプリでは、ClientConfiguration ヘルパー クラスを使用して、Azure Active Directory データとアプリ データを、Microsoft Store 申請 API を使用する各サンプル メソッドに渡します。

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

C# を使用してアプリの申請を作成する。

次の例では、Microsoft Store 申請 API のいくつかのメソッドを使用してアプリの申請を更新するクラスを実装します。

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

C# を使用した IngestionClient ヘルパー クラス

インジェスト クライアント クラスには、サンプル アプリ内の他のメソッドによって次のタスクを実行するために使用されるヘルパー メソッドが用意されています。

  • Microsoft Store 申請 API のメソッドを呼び出すために使用できる Azure AD アクセス トークンを取得します。 トークンを取得した後、Microsoft Store 申請 API の呼び出しでこのトークンを使用できるのは、その有効期限が切れるまでの 60 分間です。 トークンの有効期限が切れた後は、新しいトークンを生成できます。
  • Microsoft Store 申請 API の HTTP 要求を処理します。
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;
        }
    }
}

Node.js サンプル: MSI または EXE アプリ用の Microsoft Store 申請 API

この記事では、MSI または EXE アプリ用の Microsoft Store 申請 API の使用方法を示す Node.js コード例を示します。 各例を確認して、それぞれが対応するタスクについて詳しく知ることができます。また、この記事のすべてのコード例を使って、コンソール アプリケーションをビルドすることもできます。

前提条件 これらの例では、次のライブラリを使用します。

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

node.js を使用してアプリの申請を作成する

次の例では、この記事の他のサンプル メソッドを呼び出して、Microsoft Store 申請 API を使用するさまざまな方法を示します。 このプログラムを自分の用途に適合させるには:

  • 販売者 ID プロパティをパートナー センター アカウントの販売者 ID に割り当てます。
  • アプリケーション ID プロパティを、管理するアプリの ID に割り当てます。
  • クライアント ID プロパティと クライアントシークレット プロパティをアプリのクライアント ID とキーに割り当て、トークンエンドポイント URL の テナント ID 文字列をアプリのテナント ID に置き換えます。 詳しくは、「Azure AD アプリケーションをパートナー センター アカウントに関連付ける方法」をご覧ください。

次の例では、Microsoft Store 申請 API のいくつかのメソッドを使用してアプリの申請を更新するクラスを実装します。

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

var client = new submissionClient(config);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

クライアント構成 ヘルパー

サンプル アプリでは、ClientConfiguration ヘルパー クラスを使用して、Azure Active Directory データとアプリ データを、Microsoft Store 申請 API を使用する各サンプル メソッドに渡します。

/** 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;

Node.js を使用した インジェストクライアント ヘルパー

インジェスト クライアント クラスには、サンプル アプリ内の他のメソッドによって次のタスクを実行するために使用されるヘルパー メソッドが用意されています。

  • Microsoft Store 申請 API のメソッドを呼び出すために使用できる Azure AD アクセス トークンを取得します。 トークンを取得した後、Microsoft Store 申請 API の呼び出しでこのトークンを使用できるのは、その有効期限が切れるまでの 60 分間です。 トークンの有効期限が切れた後は、新しいトークンを生成できます。
  • Microsoft Store 申請 API の HTTP 要求を処理します。
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;

追加のヘルプ

Microsoft Store 申請 API について質問がある場合、またはこの API を使用した申請の管理についてサポートが必要な場合は、次のリソースを使用してください。

  • フォーラムで質問してください。
  • サポート ページにアクセスし、パートナー センターのサポート オプションのいずれかをリクエストしてください。 問題の種類とカテゴリを選択するように求められたら、それぞれ [アプリの申請と認定] と [アプリの申請] を選択します。