다음을 통해 공유


MSI 또는 EXE 앱용 Microsoft Store 제출 API

MSI 또는 EXE 앱용 Microsoft Store 제출 API를 사용하여 사용자 또는 조직의 파트너 센터 계정에 대한 MSI 또는 EXE 앱에 대한 제출을 프로그래밍 방식으로 쿼리하고 만듭니다. 이 API는 계정에서 많은 앱을 관리하고 이러한 자산에 대한 제출 프로세스를 자동화하고 최적화하려는 경우 유용합니다. 이 API는 Azure AD(Azure Active Directory)를 사용하여 앱 또는 서비스의 호출을 인증합니다.

다음 단계에서는 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 및 키를 가져와야 합니다. 이러한 값은 Microsoft Store 제출 API 호출에 사용할 Azure AD 액세스 토큰을 가져오는 데 필요합니다.
  • Microsoft 스토어 제출 API에서 사용하도록 앱을 준비합니다.

파트너 센터 계정에 Azure AD 애플리케이션을 연결하는 방법

Microsoft Store 제출 API for MSI 또는 EXE 앱을 사용하려면 먼저 Azure AD 애플리케이션을 Partner Center 계정과 연결하고 애플리케이션의 테넌트 ID와 클라이언트 ID를 검색한 후 키를 생성해야 합니다. Azure AD 애플리케이션은 호출하고자 하는 Microsoft Store 제출 API를 나타내는 앱 또는 서비스입니다. API에 전달하는 Azure AD 액세스 토큰을 가져오려면 테넌트 ID, 클라이언트 ID 및 키가 필요합니다.

참고 항목

이 작업은 한 번만 수행하면 됩니다. 테넌트 ID, 클라이언트 ID 및 키가 있으면 새 Azure AD 액세스 토큰을 만들어야 할 때마다 다시 사용할 수 있습니다.

  1. 파트너 센터에서 조직의 Azure AD 디렉터리에 조직의 파트너 센터 계정을 연결합니다.
  2. 다음으로 Partner Center의 Account Settings 섹션에 있는 Users 페이지에서 Partner Center 계정에 대한 제출물에 액세스하는 데 사용할 앱 또는 서비스를 나타내는 Azure AD 애플리케이션을 추가합니다. 이 응용 프로그램에 관리자 역할을 할당해야 합니다. 애플리케이션이 Azure AD 디렉터리에 아직 없는 경우에는 파트너 센터에서 새 Azure AD 애플리케이션 만들기로 진행할 수 있습니다.
  3. 사용자 페이지로 돌아가서 Azure AD 응용 프로그램의 이름을 클릭하여 응용 프로그램 설정으로 이동하고 테넌트 ID 및 클라이언트 ID 값을 복사합니다.
  4. 새 키 또는 클라이언트 암호를 추가하려면 다음 지침을 참조하거나 Azure Portal을 통해 앱을 등록하는 지침을 참조하세요.

앱을 등록하려면:

  1. Azure Portal에 로그인합니다.

  2. 여러 테넌트에 액세스할 수 있는 경우 위쪽 메뉴의 디렉터리 + 구독 필터 를 사용하여 애플리케이션을 등록하려는 테넌트로 전환합니다.

  3. Azure Active Directory를 검색하고 선택합니다.

  4. Manage(관리)에서 App registrations(앱 등록)를 > 선택합니다. 응용 프로그램을 선택합니다.

  5. 인증서 & 비밀 > 클라이언트 비밀 > 새 클라이언트 비밀을 선택합니다.

  6. 클라이언트 비밀에 대한 설명을 추가합니다.

  7. 암호에 대해 만료를 선택하거나 사용자 지정 수명을 지정합니다.

  8. 클라이언트 암호 수명은 2년(24개월) 이하로 제한됩니다. 사용자 지정 수명은 24개월 이상으로 지정할 수 없습니다.

    참고 항목

    12개월 미만의 만료 값을 설정하는 것이 좋습니다.

  9. 추가를 선택합니다.

  10. 클라이언트 애플리케이션 코드에서 사용할 비밀 값을 기록합니다. 이 비밀 값은 이 페이지에서 나가면 다시 표시되지 않습니다.

2단계: Azure AD 액세스 토큰 가져오기

MSI 또는 EXE 앱용 Microsoft Store 제출 API에서 메서드를 호출하기 전에 API의 각 메서드의 Authorization 헤더에 전달하는 Azure AD 액세스 토큰을 먼저 얻어야 합니다. 액세스 토큰을 가져온 후 만료되기까지 60분이 걸립니다. 토큰이 만료된 후 API에 대한 추가 호출에서 계속 사용할 수 있도록 토큰을 새로 고칠 수 있습니다.

액세스 토큰을 얻으려면 [Service to Service Calls Using Client Credentials]/azure/active-directory/azuread-dev/v1-oauth2-client-creds-grant-flow)의 지시에 따라 HTTP POST를 https://login.microsoftonline.com/<tenant_id>/oauth2/token 끝점으로 보냅니다. 샘플 요청은 다음과 같습니다.

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

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

POST URI의 tenant_id 값과 client_idclient_secret 매개 변수의 경우 이전 섹션의 Partner Center에서 검색한 애플리케이션의 테넌트 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에는 앱에 대한 시나리오로 그룹화된 많은 메서드가 포함되어 있습니다. 제출을 만들거나 업데이트하려면 일반적으로 특정 순서로 여러 메서드를 호출합니다. 각 시나리오 및 각 방법의 구문에 대한 자세한 내용은 다음 섹션을 참조하십시오:

참고 항목

액세스 토큰을 얻은 후 토큰이 만료되기 전에 MSI 또는 EXE 앱용 Microsoft Store 제출 API에서 메서드를 호출하는 데 60분이 걸립니다.

Base URL

EXE 또는 MSI 앱용 Microsoft Store 제출 API의 기본 URL은 다음과 같습니다. https://api.store.microsoft.com

API 계약

현재 초안 제출 메타데이터 API 가져오기

현재 초안 제출에서 각 모듈(목록, 속성 또는 가용성)의 메타데이터를 가져옵니다.

경로 [모든 모듈]: /submission/v1/product/{productId}/metadata?languages={languages}&에 언어 목록={true/false}가 포함되어 있습니다
경로 [단일 모듈]: /submission/v1/product/{productId}/metadata/{moduleName}?languages={languages}&includelanguagelist={true/false}
메서드: GET

경로 매개 변수

매개 변수 설명
productId 제품의 파트너 센터 ID
moduleName 파트너 센터 모듈 – 목록, 속성 또는 가용성

쿼리 매개 변수

매개 변수 설명
언어 선택 사항 목록 언어는 쉼표로 구분된 문자열[최대 200개 언어 제한]로 필터링됩니다.

없는 경우 사용 가능한 처음 200개 목록 언어 메타데이터가 검색됩니다. [ 예: "en-us, en-gb"].
includelanguagelist 선택적 부울 – true이면 추가된 목록 언어 목록과 해당 완전성 상태 반환합니다.

필수 헤더

헤더
Authorization: Bearer <Token> 파트너 센터 계정에 등록된 Azure AD 앱 ID
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
accessibilitySupport Boolean
additionalLicenseTerms 문자열
사용 가능성 Object 가용성 모듈 데이터
category 문자열 아래 범주 목록을 참조하세요.
certificationNotes 문자열
코드 문자열 메시지의 오류 코드
contactInfo 문자열
저작권 문자열
dependsOnDriversOrNT Boolean
description 문자열
developedBy 문자열
발견 가능성 문자열 [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
freeTrial 문자열 [NO_FREE_TRIAL, FREE_TRIAL]
hardwareItemType 문자열
isPrivacyPolicyRequired Boolean
isRecommended Boolean
isRequired Boolean
isSuccess Boolean
isSystemFeatureRequired 개체의 배열
language 문자열 아래 언어 목록을 참조하세요.
목록 개체의 배열 각 언어에 대한 모듈 데이터 나열
시장 문자열 배열 아래 시장 목록을 참조하세요.
message 문자열 오류에 대한 설명입니다
minimumHardware 문자열
minimumRequirement 문자열
penAndInkSupport Boolean
가격 책정 문자열 [무료, FREEMIUM, 구독, 지불된]
privacyPolicyUrl 문자열
productDeclarations Object
productFeatures 문자열 배열
속성 Object 속성 모듈 데이터
recommendedHardware 문자열
recommendedRequirement 문자열
responseData Object 요청에 대한 실제 응답 페이로드를 포함합니다.
요구 사항 개체의 배열
searchTerms 문자열 배열
shortDescription 문자열
하위 범주 문자열 아래 하위 범주 목록 참조
supportContactInfo 문자열
systemRequirementDetails 개체의 배열
target 문자열 오류가 발생한 엔터티입니다.
웹 사이트 문자열
whatsNew 문자열

샘플 응답

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

현재 초안 제출 메타데이터 API 업데이트

초안 제출에서 각 모듈의 메타데이터를 업데이트. API 검사

  • 활성 제출의 경우 있는 경우 오류 메시지와 함께 실패합니다.
  • 모든 모듈이 준비되면 초안 저장 작업을 허용하도록 상태.
  • 제출의 각 필드는 Microsoft Store의 요구 사항에 따라 유효성이 검사됩니다.
  • 시스템 요구 사항 세부 정보 유효성 검사 규칙:
    • hardwareItemType의 허용되는 값 = 메모리: 300MB, 750MB, 1GB, 2GB, 4GB, 6GB, 8GB, 12GB, 16GB, 20GB
    • Allowed Values in hardwareItemType = DirectX: DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12
    • hardwareItemType에서 허용되는 값 = Video_Memory: 1GB, 2GB, 4GB, 6GB

경로 [전체 모듈 업데이트]: /submission/v1/product/{productId}/metadata
메서드: PUT

경로 [모듈 패치 업데이트]: /제출/v1/제품/{productId}/메타데이터
방법: PATCH

API 동작

전체 모듈 업데이트 API의 경우 모든 필드의 전체 업데이트 요청에 전체 모듈 데이터가 있어야 합니다. 요청에 없는 모든 필드의 기본값은 특정 모듈의 현재 값을 덮어쓰는 데 사용됩니다.
패치 모듈 업데이트 API의 경우 업데이트할 필드만 요청에 있어야 합니다. 요청의 이러한 필드 값은 기존 값을 덮어쓰고 요청에 없는 다른 모든 필드를 특정 모듈의 현재 필드와 동일하게 유지합니다.

경로 매개 변수

매개 변수 설명
productId 제품의 파트너 센터 ID

필수 헤더

헤더
Authorization: Bearer <Token> 파트너 센터 계정에 등록된 Azure AD 앱 ID
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

요청 매개 변수

속성 형식 설명
사용 가능성 Object 가용성 모듈 메타데이터를 보유할 개체
시장 문자열 배열 필수 : 아래 시장 목록을 참조하세요.
발견 가능성 문자열 필수 [DISCOVERABLE, DEEPLINK_ONLY]
enableInFutureMarkets Boolean Required
가격 책정 문자열 필수 [무료, FREEMIUM, 구독, 유료]
freeTrial 문자열 가격 책정이 유료이거나 구독 인 경우 필수 [NO_FREE_TRIAL, FREE_TRIAL]
속성 Object 속성 모듈 메타데이터를 저장할 개체
isPrivacyPolicyRequired Boolean Required
privacyPolicyUrl 문자열 isPrivacyPolicyRequired = true 인 경우 필수 URL이어야 합니다.
웹 사이트 문자열 반드시 유효한 URI여야 합니다.
supportContactInfo 문자열 반드시 유효한 URL 또는 이메일 주소여야 합니다
certificationNotes 문자열 권장 문자 제한 = 2000
category 문자열 필수 항목은 아래 범주 목록을 참조하세요.
하위 범주 문자열 필수 아래 하위 카테고리 목록 참조
productDeclarations Object Required
isSystemFeatureRequired 개체의 배열 [터치, 키보드, 마우스, 카메라, NFC_HCE, NFC_Proximity, Bluetooth_LE, 전화 통신, 마이크]
isRequired Boolean Required
isRecommended Boolean Required
hardwareItemType 문자열 Required
systemRequirementDetails 개체의 배열 [프로세서, 그래픽, 메모리, DirectX, Video_Memory]
minimumRequirement 문자열 systemRequirementsText에 필요 , MaxLength = 200

하드웨어에서 허용되는 값 ItemType = 메모리: [300MB, 750MB, 1GB, 2GB, 4GB, 6GB, 8GB, 12GB, 16GB, 20GB]

Allowed Values in hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Allowed Values in hardwareItemType = Video_Memory: [1GB, 2GB, 4GB, 6GB]
recommendedRequirement 문자열 systemRequirementsText에 필요 , MaxLength = 200

하드웨어에서 허용되는 값 ItemType = 메모리: [300MB, 750MB, 1GB, 2GB, 4GB, 6GB, 8GB, 12GB, 16GB, 20GB]

Allowed Values in hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Allowed Values in hardwareItemType = Video_Memory: [1GB, 2GB, 4GB, 6GB]
dependsOnDriversOrNT Boolean Required
accessibilitySupport Boolean Required
penAndInkSupport Boolean Required
목록 Object 단일 언어에 대한 모듈 데이터를 나열하는 개체
language 문자열 필수 아래 언어 목록 참조
description 문자열 필수 문자 제한 = 10000
whatsNew 문자열 문자 제한 = 1500
productFeatures 문자열의 배열 기능당 200자; 최대 20개의 기능
shortDescription 문자열 문자 제한 = 1000
searchTerms 문자열의 배열 검색어당 30자; 최대 7개의 검색어

모든 검색어에서 총 21개의 고유한 단어 합계
additionalLicenseTerms 문자열 필수 문자 제한 = 10000
저작권 문자열 문자 제한 = 200
developedBy 문자열 문자 제한 = 255
요구 사항 개체의 배열 항목당 200자; 최소 항목과 권장 항목 사이의 최대 11개 항목 합계]
minimumHardware 문자열 문자 제한 = 200
recommendedHardware 문자열 문자 제한 = 200
contactInfo 문자열 문자 제한 = 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
도미니카 공화국 DO
에콰도르 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
나미비아 해당 없음
나우루 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
동티모르(Timor-Leste) Tl
Tog - TG
토켈라우 TK
통가 TO
트리니다드 토백 - TT
튀니지 TN
튀르키예 TR
투르크메니스탄 TM
터크스 케이커스 제도 TC
투발루 TV
미국령 외딴섬 UM
미국 버진 아일랜드 VI
우간다 UG
우크라이나 UA
아랍에미리트 AE
영국 GB
미국 US
우루과이 UY
우즈베키스탄 UZ
바누아투 VU
베트남 VN
왈리스-푸투나 제도 WF
예멘 YE
잠비아 ZM
짐바브웨 ZW
올란드 제도 Dynamics AX

범주 및 하위 범주

범주 하위 범주
BooksAndReference EReader, 소설, 논픽션, 참조
비즈니스 AccountingAndfinance, Collaboration, CRM, DataAndAnalytics, FileManagement, InventoryAndlogistics, LegalAndHR, ProjectManagement, RemoteDesktop, SalesAndMarketing, TimeAndExpenses
개발 도구 Database, DesignTools, DevelopmentKits, 네트워킹, ReferenceAndTraining, 서버, 유틸리티, WebHosting
교육 EducationBooksAndReference, EarlyLearning, InstructionalTools, Language, StudyAids
Entertainment (없음)
FoodAndDining (없음)
GovernmentAndPolitics (없음)
HealthAndFitness (없음)
KidsAndFamily KidsAndFamilyBooksAndReference, KidsAndFamilyEntertainment, HobbiesAndToys, SportsAndActivities, KidsAndFamilyTravel
라이프스타일 자동차, DYI, HomeAndGarden, 관계, SpecialInterest, StyleAndFashion
의료 (없음)
MultimediaDesign IllustrationAndGraphicDesign, 음악Production, PhotoAndVideoProduction
음악 (없음)
NavigationAndMaps (없음)
NewsAndWeather 뉴스, 날씨
PersonalFinance BankingAndInvestments, BudgetingAndTaxes
개인 설정 벨소리AndSounds, 테마, WallpaperAndLockScreens
PhotoAndVideo (없음)
생산성 (없음)
보안 PCProtection, PersonalSecurity
쇼핑 (없음)
소셜 (없음)
스포츠 (없음)
여행 시티기데스, 호텔
UtilitiesAndTools BackupAndManage, FileManager

언어

language_name 지원되는 언어 코드
아프리칸스어 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
영어 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
히브리어 그, 허일
힌디어 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
KiSwahili 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
오디아어 또는, 또는-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
Sesotho sa Leboa 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
우르두어 your, your-pk
위구르어 ug-arab, ug-cn, ug-cyrl, ug-latn
우즈베크어(라틴 문자) uz, uz-cyrl, uz-latn, uz-latn-uz
베트남어 vi, vi-vn
웨일스어 cy, cy-gb
월라프어 wo, wo-sn
요루바어 yo-latn, yo-ng

샘플 요청

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

응답 헤더

헤더
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 클라이언트가 API를 다시 호출하기 전에 대기해야 하는 시간(초)

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object 요청에 대한 실제 응답 페이로드를 포함합니다.
pollingUrl 문자열 진행 중인 제출의 상태 가져오기 위한 폴링 URL
ongoingSubmissionId 문자열 이미 진행 중인 제출의 제출 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

현재 초안 제출에서 패키지 세부 정보를 가져옵니다.

경로 [모든 패키지]: /submission/v1/product/{productId}/packages
메서드: GET

경로 [단일 패키지]: /submission/v1/product/{productId}/packages/{packageId}
메서드: GET

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID
packageId 가져올 패키지의 고유 ID

필수 헤더

헤더
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD 앱 ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
패키지 개체의 배열 패키지 모듈 데이터를 저장할 개체
packageId 문자열
packageUrl 문자열
언어 문자열 배열
아키텍처 문자열 배열 [Neutral, X86, X64, Arm, Arm64]
isSilentInstall Boolean 스위치 또는 false를 요구하지 않고 설치 관리자가 자동 모드로 실행되는 경우 true로 표시되어야 합니다.
installerParameters 문자열
genericDocUrl 문자열
errorDetails 개체의 배열
errorScenario 문자열
errorScenarioDetails 개체의 배열
errorValue 문자열
errorUrl 문자열
packageType 문자열

샘플 응답

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

현재 초안 패키지 API 업데이트

현재 초안 제출에 따라 패키지 세부 정보를 업데이트합니다.

경로 [전체 모듈 업데이트]: /submission/v1/product/{productId}/packages
메서드: PUT

경로 [단일 패키지 패치 업데이트]: /제출/v1/제품/{productId}/패키지/{packageId}
방법: PATCH

API 동작

전체 모듈 업데이트 API의 경우 모든 필드의 전체 업데이트를 요청하려면 전체 패키지 데이터가 있어야 합니다. 요청에 없는 필드는 기본값을 사용하여 해당 특정 모듈의 현재 값을 덮어씁니다. 그러면 요청의 새 패키지 집합으로 모든 기존 패키지를 덮어씁니다. 그러면 패키지 ID가 다시 생성되고 사용자는 최신 패키지 ID에 대해 GET Packages API를 호출해야 합니다.

단일 패키지 패치 업데이트 API의 경우 지정된 패키지에 대해 업데이트해야 하는 필드만 요청에 있어야 합니다. 요청의 이러한 필드 값은 기존 값을 덮어쓰며 요청에 없는 다른 모든 필드는 해당 특정 패키지의 현재 값과 동일하게 유지됩니다. 집합의 다른 패키지는 그대로 다시 기본.

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID
packageId 패키지의 고유 ID

필수 헤더

헤더
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD 앱 ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

요청 매개 변수

속성 형식 설명
패키지 객체 배열 패키지 모듈 데이터를 저장할 개체 [전체 모듈 업데이트에만 필요]
packageUrl 문자열 Required
언어 문자열 배열 Required
아키텍처 문자열 배열 필수 는 단일 아키텍처를 포함해야 합니다. 중립, X86, X64, Arm, Arm64
isSilentInstall Boolean 필수 설치 프로그램이 스위치를 필요로 하지 않고 무음 모드로 실행되는 경우 true로 표시하거나 그렇지 않으면 false로 표시해야 합니다
installerParameters 문자열 isSilentInstall이 false이면 필수입니다.
genericDocUrl 문자열 packageType이 EXE 형식 설치 관리자에 대한 사용자 지정 오류 코드의 세부 정보를 포함하는 문서에 대한 exe 링크인 경우 필수입니다.
errorDetails 객체 배열 EXE 유형 설치 관리자에 대한 사용자 지정 오류 코드 및 세부 정보를 보관하는 메타데이터입니다.
errorScenario 문자열 특정 오류 시나리오를 식별합니다. [installationCancelledByUser, applicationAlreadyExists, installationAlreadyInProgress, diskSpaceIsFull, rebootRequired, networkFailure, packageRejectedDuringInstallation, installationSuccessful, 기타]
errorScenarioDetails 객체 배열
errorValue 문자열 설치 중에 표시될 수 있는 오류 코드
errorUrl 문자열 오류에 대한 세부 정보가 있는 URL
packageType 문자열 필수 [exe, msi]

샘플 요청 [전체 모듈 업데이트]

{
    "packages":[{
        "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
        "languages": ["en-us"],
        "architectures": ["X86"],
        "isSilentInstall": true,
        "installerParameters": "/s",
        "genericDocUrl": "https://docs.contoso.com/doclink",
        "errorDetails": [{
            "errorScenario": "rebootRequired",
            "errorScenarioDetails": [{
                "errorValue": "ERR001001",
                "errorUrl": "https://errors.contoso.com/errors/ERR001001"
            }]
        }],
        "packageType": "exe",
    }]
}

샘플 요청 [단일 패키지 패치 업데이트]

{
    "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
    "languages": ["en-us"],
    "architectures": ["X86"],
    "isSilentInstall": true,
    "installerParameters": "/s",
    "genericDocUrl": "https://docs.contoso.com/doclink",
    "errorDetails": [{
        "errorScenario": "rebootRequired",
        "errorScenarioDetails": [{
            "errorValue": "ERR001001",
            "errorUrl": "https://errors.contoso.com/errors/ERR001001"
        }]
    }],
    "packageType": "exe",
}

응답 헤더

헤더
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 [오류 또는 경고 메시지가 있는 경우 목록]
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
pollingUrl 문자열 [이미 진행 중인 제출의 경우 제출 상태를 가져오기 위한 폴링 URL]
ongoingSubmissionId 문자열 [이미 진행중인 제출물의 제출 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

필수 헤더

헤더
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 [오류 또는 경고 메시지가 있는 경우 목록]
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
pollingUrl 문자열 [이미 진행 중인 제출의 경우 패키지 업로드 또는 제출 상태 상태 가져오기 위한 폴링 URL]
ongoingSubmissionId 문자열 [이미 진행중인 제출물의 제출 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

쿼리 매개 변수

속성 설명
언어 [선택사항] 목록 언어는 쉼표로 구분된 문자열로 필터링됩니다 [최대 200개 언어 제한]. 없는 경우 사용 가능한 처음 200개 목록 언어의 자산 데이터가 검색됩니다. (e.g., “en-us, en-gb")

필수 헤더

헤더
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
listingAssets 개체의 배열 각 언어에 대한 자산 세부 정보 나열
language 문자열
storeLogos 개체의 배열
스크린샷 개체의 배열
id 문자열
assetUrl 문자열 반드시 유효한 URI여야 합니다.
imageSize Object
width 정수
높이 정수

샘플 응답

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

목록 자산 API 만들기

현재 초안 제출에서 새 목록 자산 업로드를 만듭니다.

자산 나열 업데이트

EXE 또는 MSI 앱용 Microsoft Store 제출 API는 업로드가 성공한 후 커밋 API 호출과 함께 각 개별 이미지 자산 업로드에 대해 Blob Store에 런타임 생성 SAS URL을 사용합니다. 목록 자산을 업데이트하고 목록 모듈에서 로캘을 추가/제거할 수 있도록 하기 위해 다음 방법을 사용할 수 있습니다.

  1. 목록 자산 만들기 API를 사용하여 자산의 언어, 유형 및 수와 함께 자산 업로드에 대한 요청을 보냅니다.
  2. 요청된 자산 수에 따라 자산 ID는 요청 시 생성되며 단기 SAS URL을 만들고 자산 유형에 따라 응답 본문으로 다시 보냅니다. 이 URL을 사용하여 HTTP 클라이언트 [Put Blob(REST API) - Azure Storage |를 사용하여 특정 유형의 이미지 자산을 업로드할 수 있습니다. Microsoft Docs].
  3. 업로드한 후 커밋 목록 자산 API를 사용하여 이전 API 호출에서 이전에 받은 새 자산 ID 정보도 보낼 수 있습니다. 단일 API는 유효성 검사 후 목록 자산 데이터를 내부적으로 커밋합니다.
  4. 이 방법은 요청에서 전송되는 특정 언어로 자산 형식의 이전 이미지의 전체 집합을 효과적으로 덮어씁 수 있습니다. 따라서 이전에 업로드한 자산이 제거됩니다.

경로: /submission/v1/product/{productId}/listings/assets/create
메서드: POST

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID

필수 헤더

헤더 설명
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

요청 매개 변수

속성 형식 설명
언어 문자열 Required
createAssetRequest Object Required
스크린샷 정수 ISV가 스크린샷을 업데이트하거나 새 목록 언어 를 추가해야 하는 경우 필요 [1 - 10]
로고 정수 ISV가 로고를 업데이트하거나 새로운 목록 언어를 추가해야 하는 경우 필요합니다 [1 또는 2]

응답 헤더

헤더 설명
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
listingAssets Object 업로드할 StoreLogos 및 스크린샷의 세부 정보가 포함된 개체
language 문자열
storeLogos 개체의 배열
스크린샷 개체의 배열
id 문자열
primaryAssetUploadUrl 문자열 Azure Blob REST API를 사용하여 목록 자산을 업로드하는 기본 URL
secondaryAssetUploadUrl 문자열 Azure Blob REST API를 사용하여 상장 자산을 업로드하는 보조 URL
httpMethod HTTP 메서드 자산 업로드 URL (기본 또는 보조)을 통해 자산을 업로드하는 데 필요한 HTTP 메서드
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"}

            }]
        }
    } 
}

목록 자산 API 커밋

현재 초안 제출에서 자산 만들기 API의 세부 정보를 사용하여 업로드된 새 목록 자산을 커밋합니다.

경로: /submission/v1/product/{productId}/listings/assets/commit
메서드: PUT

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID

필수 헤더

헤더 설명
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

요청 매개 변수

속성 형식 설명
listingAssets Object
language 문자열
storeLogos 개체 배열
스크린샷 개체 배열
id 문자열 현재 목록 자산 가져오기 API에서 유지하려는 기존 ID 또는 목록 자산 만들기 API에서 새 자산이 업로드된 새 ID여야 합니다.
assetUrl 문자열 사용자가 현재 목록 자산 가져오기 API에서 유지하려는 기존 자산의 URL이거나 새 자산이 목록 자산 만들기 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
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
pollingUrl 문자열 진행 중인 제출의 상태를 가져오는 폴링 URL
ongoingSubmissionId 문자열 이미 진행중인 제출물의 제출 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}/상태
메서드: GET

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID

필수 헤더

헤더 설명
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더 설명
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
isReady Boolean 패키지 업로드를 포함하여 모든 모듈이 준비 상태인지를 나타냅니다.
ongoingSubmissionId 문자열 이미 진행중인 제출물의 제출 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}/제출
메서드: POST

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID

필수 헤더

헤더 설명
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더 설명
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
pollingUrl 문자열 제출용 패키지 업로드를 포함하여 모듈 준비 상태 가져오기 위한 폴링 URL
submissionId 문자열 새로 만든 제출의 ID
ongoingSubmissionId 문자열 이미 진행중인 제출물의 제출 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": ""
    }
}

제출 상태 폴링 API

제출 상태를 검사 API입니다.

경로: /submission/v1/product/{productId}/submission/{submissionId}/상태
메서드: GET

경로 매개 변수

속성 설명
productId 제품의 파트너 센터 ID

필수 헤더

헤더 설명
Authorization: Bearer <Token> Partner Center 계정에 등록된 Azure AD App ID 사용
X-Seller-Account-Id 파트너 센터 계정의 판매자 ID

응답 헤더

헤더 설명
X-Correlation-ID 각 요청에 대한 GUID 형식 고유 ID입니다. 이는 문제를 분석하기 위해 지원 팀과 공유할 수 있습니다.
Retry-After 속도 제한으로 인해 API를 다시 호출하기 전에 클라이언트가 기다려야 하는 시간(초)입니다.

응답 매개 변수

속성 형식 설명
isSuccess Boolean
오류 개체의 배열 오류 또는 경고 메시지(있는 경우) 목록
코드 문자열 메시지의 오류 코드
message 문자열 오류에 대한 설명입니다
target 문자열 오류가 발생한 엔터티입니다.
responseData Object
publishingStatus 문자열 제출 게시 상태 - [INPROGRESS, PUBLISHED, FAILED, UNKNOWN]
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 Submission API

이 문서에서는 MSI 또는 EXE 앱에 Microsoft Store 제출 API를 사용하는 방법을 설명하는 C# 코드 예제를 제공합니다. 각 예시를 검토하여 예시에서 보여 주는 작업에 대해 자세히 알아보거나 이 문서의 모든 코드 예시를 콘솔 애플리케이션으로 빌드할 수 있습니다.

필수 구성 요소 다음 예제에서는 다음 라이브러리를 사용합니다.

  • Newtonsoft의 Newtonsoft.Json NuGet 패키지.

메인 프로그램 다음 예제는 Microsoft Store 제출 API를 사용하는 다양한 방법을 설명하기 위해 이 문서의 다른 예제 방법을 호출하는 명령줄 프로그램을 구현합니다. 다음을 따라 이 프로그램을 자신의 용도에 맞게 조정합니다.

  • 파트너 센터 계정의 판매자 ID에 SellerId 속성을 할당합니다.
  • 관리하려는 앱의 ID에 ApplicationId 속성을 할당합니다.
  • ClientId 및 ClientSecret 속성을 앱의 클라이언트 ID 및 키에 할당하고 TokenEndpoint URL의 tenantid 문자열을 앱의 tenantID로 바꿉니다. 자세한 정보는 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 도우미 클래스

샘플 앱은 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" 00001111-aaaa-2222-bbbb-3333cccc4444
        /// </summary>
        public string ClientId { get; set; }

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

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

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

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

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


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

C를 사용하여 앱 제출 만들기Create an app submission using 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 committing packages again.");
                        }
                    }
                }
            }

            Console.WriteLine("Getting Application Metadata - All Modules");

            dynamic AppMetadata = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.AppMetadataUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppMetadata.ToString());

            Console.WriteLine("Getting Application Metadata - Listings");

            dynamic AppListingsMetadata = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.AppListingsFetchMetadataUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppListingsMetadata.ToString());

            Console.WriteLine("Updating Listings Metadata - Description");

            // Update Required Fields in Listings Metadata Object - Per Language. For eg. AppListingsMetadata.responseData.listings[0]

            // Example - Updating Description
            AppListingsMetadata.responseData.listings[0].description = "New Description Updated By C# Sample Code";

            dynamic ListingsUpdateRequest = new
            {
                listings = AppListingsMetadata.responseData.listings[0]
            };

            dynamic UpdateListingsMetadataResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.AppMetadataUrlTemplate,
                SubmissionClient.Version, appId), ListingsUpdateRequest);

            Console.WriteLine(UpdateListingsMetadataResponse.ToString());

            Console.WriteLine("Getting All Listings Assets");

            dynamic ListingAssets = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ListingAssetsUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(ListingAssets.ToString());

            Console.WriteLine("Creating Listing Assets for 1 Screenshot");

            
            dynamic AssetCreateRequest = new
            {
                language = ListingAssets.responseData.listingAssets[0].language,
                createAssetRequest = new Dictionary<string, int>()
                {
                    {"Screenshot", 1 },
                    {"Logo", 0 }
                }
            };

            dynamic AssetCreateResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.ListingAssetsCreateUrlTemplate,
               SubmissionClient.Version, appId), AssetCreateRequest);

            Console.WriteLine(AssetCreateResponse.ToString());

            Console.WriteLine("Uploading Listing Assets");

            // Path to PNG File to be Uploaded as Screenshot / Logo
            var PathToFile = "./Image.png";
            var AssetToUpload = File.OpenRead(PathToFile);

            await client.UploadAsset(AssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl.Value as string, AssetToUpload);

            Console.WriteLine("Committing Listing Assets");

            dynamic AssetCommitRequest = new
            {
                listingAssets = new
                {
                    language = ListingAssets.responseData.listingAssets[0].language,
                    storeLogos = ListingAssets.responseData.listingAssets[0].storeLogos,
                    screenshots = JToken.FromObject(new List<dynamic>() { new
                {
                    id = AssetCreateResponse.responseData.listingAssets.screenshots[0].id.Value as string,
                    assetUrl = AssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl.Value as string
                }
                }.ToArray())
                }
            };

            dynamic AssetCommitResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.ListingAssetsCommitUrlTemplate,
               SubmissionClient.Version, appId), AssetCommitRequest);

            Console.WriteLine(AssetCommitResponse.ToString());

            Console.WriteLine("Getting Current Application Draft Status before Submission");

            AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppDraftStatus.ToString());

            if (AppDraftStatus == null || !((bool)AppDraftStatus.responseData.isReady))
            {
                throw new InvalidOperationException("Application Current Status is not in Ready Status for All Modules");
            }

            Console.WriteLine("Creating Submission");

            dynamic SubmissionCreationResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.CreateSubmissionUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(SubmissionCreationResponse.ToString());

            Console.WriteLine("Current Submission Status");

            dynamic SubmissionStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.SubmissionStatusPollingUrlTemplate,
                SubmissionClient.Version, appId, SubmissionCreationResponse.responseData.submissionId.Value as string), null);

            Console.Write(SubmissionStatus.ToString());

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

C를 사용하는 IngestionClient 도우미 클래스#

Ingestion Client 클래스는 샘플 앱의 다른 방법에서 다음 작업을 수행하는 데 사용되는 도우미 방법을 제공합니다:

  • 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" 00001111-aaaa-2222-bbbb-3333cccc4444</param>
        /// <param name="clientSecret">Client secret of your Azure Active Directory app</param>
        /// <param name="scope">Scope. If not provided, default one is used for the production API endpoint.</param>
        /// <returns>Autorization token. Prepend it with "Bearer: " and pass it in the request header as the
        /// value for "Authorization: " header.</returns>
        public static async Task<string> GetClientCredentialAccessToken(
            string tokenEndpoint,
            string clientId,
            string clientSecret,
            string scope = null)
        {
            if (scope == null)
            {
                scope = "https://api.store.microsoft.com/.default";
            }

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

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

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

            return result.access_token;
        }


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

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

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

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

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

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

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

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


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

Node.js 샘플: MSI 또는 EXE 앱용 Microsoft Store Submission API

이 문서에서는 MSI 또는 EXE 앱에 Microsoft Store 제출 API를 사용하는 방법을 보여주는 Node.js 코드 예제를 제공합니다. 각 예시를 검토하여 예시에서 보여 주는 작업에 대해 자세히 알아보거나 이 문서의 모든 코드 예시를 콘솔 애플리케이션으로 빌드할 수 있습니다.

필수 구성 요소 다음 예제에서는 다음 라이브러리를 사용합니다.

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

node.js 사용하여 앱 제출 만들기

다음 예제에서는 이 문서의 다른 예제 메서드를 호출하여 Microsoft Store 제출 API를 사용하는 다양한 방법을 보여 줍니다. 다음을 따라 이 프로그램을 자신의 용도에 맞게 조정합니다.

  • 파트너 센터 계정의 판매자 ID에 SellerId 속성을 할당합니다.
  • 관리하려는 앱의 ID에 ApplicationId 속성을 할당합니다.
  • ClientId 및 ClientSecret 속성을 앱의 클라이언트 ID 및 키에 할당하고 TokenEndpoint URL의 tenantid 문자열을 앱의 tenantID로 바꿉니다. 자세한 정보는 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 도우미

샘플 앱은 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 사용하여 IngestionClient 도우미

Ingestion Client 클래스는 샘플 앱의 다른 방법에서 다음 작업을 수행하는 데 사용되는 도우미 방법을 제공합니다:

  • 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의 제출을 관리하는 데 도움이 필요한 경우 다음의 리소스를 사용하세요.

  • 포럼에서 질문하세요.
  • 지원 페이지를 방문하여 파트너 센터에 대한 보조 지원 옵션 중 하나를 요청하세요. 문제 유형 및 범주를 선택하라는 메시지가 표시되면 앱 제출 및 인증 및 앱 제출하기를 각각 선택합니다.