다음을 통해 공유


앱 내 구매 제품의 큰 카탈로그 관리하기

앱에서 큰 앱 내 제품 카탈로그를 제공하는 경우 필요에 따라 이 항목에 설명된 프로세스를 따라 카탈로그를 관리할 수 있습니다. Windows 10 이전 릴리스에서는 스토어의 개발자 계정당 제품 목록이 200개로 제한되며, 이 항목에 설명된 프로세스를 사용하여 해당 제한을 해결할 수 있습니다. Windows 10부터 스토어는 개발자 계정당 제품 목록 수에 제한이 없으며 이 문서에 설명된 프로세스는 더 이상 필요하지 않습니다.

Important

이 문서에서는 Windows.ApplicationModel.Store 네임스페이스의 멤버를 사용하는 방법을 보여 줍니다. 이 네임스페이스는 더 이상 새 기능으로 업데이트되지 않으므로 Windows.Services.Store 네임스페이스를 대신 사용하는 것이 좋습니다. Windows.Services.Store 네임스페이스는 Store 관리 소모성 추가 기능 및 구독 등의 최신 추가 기능 유형을 지원하며 파트너 센터 및 Store에서 지원하는 이후 제품 및 기능 유형과 호환되도록 설계되었습니다. Windows.Services.Store 네임스페이스는 Windows 10 버전 1607에 도입되었으며 Windows 10 Anniversary Edition(10.0, 빌드 14393) 또는 Visual Studio의 최신 릴리스를 대상으로 하는 프로젝트에만 사용할 수 있습니다. 자세한 정보는 앱 내 구매 및 평가판을 참조하세요.

이 기능을 사용하도록 설정하려면 특정 가격 계층에 대한 소수의 제품 항목을 만들고 각 항목은 카탈로그 내에서 수백 개의 제품을 나타낼 수 있습니다. 스토어에 나열된 앱 내 제품과 연결된 앱 정의 제품을 지정하는 RequestProductPurchaseAsync 메서드 오버로드를 사용합니다. 통화 중에 제품 및 제품 연결을 지정하는 것 외에도 앱은 큰 카탈로그 제품 세부 정보가 포함된 ProductPurchaseDisplayProperties 개체를 전달해야 합니다. 이러한 세부 정보가 제공되지 않으면 나열된 제품에 대한 세부 정보가 대신 사용됩니다.

스토어는 결과 PurchaseResults에 있는 구매 요청에서 offerId를 사용합니다. 이 프로세스는 스토어에 앱 내 제품을 나열할 때 원래 제공된 정보를 직접 수정하지 않습니다.

필수 조건

  • 이 항목에서는 스토어에 나열된 단일 앱 내 제품을 사용하여 여러 앱 내 제품을 나타내기 위한 스토어 지원에 대해 설명합니다. 앱 내 구매에 익숙하지 않은 경우에는 앱 내 제품 구매 사용을 검토하여 라이선스 정보 및 앱 내 구매를 Store에 올바르게 나열하는 방법에 대해 알아볼 수 있습니다.
  • 새로운 앱 내 오퍼를 처음으로 코딩하고 테스트할 때는 CurrentApp 개체 대신에 CurrentAppSimulator 개체를 사용해야 합니다. 이렇게 하면 라이브 서버를 호출하는 대신 라이선스 서버에 대한 시뮬레이션된 호출을 사용하여 라이선스 논리를 확인할 수 있습니다. 이렇게 하려면 %userprofile%\AppData\local\packages\<package name>\LocalState\Microsoft\Windows Store\ApiData에 있는 WindowsStoreProxy.xml 파일을 사용자 지정해야 합니다. Microsoft Visual Studio 시뮬레이터는 앱을 처음 실행할 때 이 파일을 만들거나 런타임에 사용자 지정 파일을 로드할 수도 있습니다. 자세한 정보는 CurrentAppSimulator에서 WindowsStoreProxy.xml 파일 사용을 참조하세요.
  • 이 항목에서는 Store 샘플에 제공된 코드 예제도 참조합니다. 이 샘플은 UWP(유니버설 Windows 플랫폼) 앱에 제공되는 다양한 수익 창출 옵션을 사용하여 실습하기 좋은 방법입니다.

앱 내 제품에 대한 구매 요청 만들기

큰 카탈로그 내의 특정 제품에 대한 구매 요청은 앱 내의 다른 구매 요청과 거의 동일한 방식으로 처리됩니다. 앱이 새 RequestProductPurchaseAsync 메서드 오버로드를 호출할 때 앱은 앱에서 앱 내 제품의 이름으로 채워진 OfferIdProductPurchaseDisplayProperties 개체를 모두 제공합니다.

string offerId = "1234";
string displayPropertiesName = "MusicOffer1";
var displayProperties = new ProductPurchaseDisplayProperties(displayPropertiesName);

try
{
    PurchaseResults purchaseResults = await CurrentAppSimulator.RequestProductPurchaseAsync(
        "product1", offerId, displayProperties);
    switch (purchaseResults.Status)
    {
        case ProductPurchaseStatus.Succeeded:
            // Grant the user their purchase here, and then pass the product ID and transaction ID
            // to currentAppSimulator.reportConsumableFulfillment to indicate local fulfillment to
            // the Windows Store.
            break;
        case ProductPurchaseStatus.NotFulfilled:
            // First check for unfulfilled purchases and grant any unfulfilled purchases from an
            // earlier transaction. Once products are fulfilled pass the product ID and transaction
            // ID to currentAppSimulator.reportConsumableFulfillment to indicate local fulfillment
            // to the Windows Store.
            break;
        case ProductPurchaseStatus.NotPurchased:
            // Notify user that the purchase was not completed due to cancellation or error.
            break;
    }
}
catch (Exception)
{
    // Notify the user of the purchase error.
}

앱 내 제품의 처리 보고

앱은 제품이 로컬로 처리되는 즉시 스토어에 제품 처리를 보고해야 합니다. 대규모 카탈로그 시나리오에서 앱이 제품 처리를 보고하지 않으면 사용자는 동일한 스토어 제품 목록을 사용하여 앱 내 제품을 구매할 수 없습니다.

앞에서 언급한 것처럼 스토어는 제공된 제품 정보만 사용하여 PurchaseResults를 채우고 큰 카탈로그 제품과 Store 제품 목록 간에 영구 연결을 만들지 않습니다. 따라서 제품에 대한 사용자 자격을 추적하고 RequestProductPurchaseAsync 작업 외부의 사용자에게 제품별 컨텍스트(예: 구매 중인 항목의 이름 또는 세부 정보)를 제공해야 합니다.

다음 코드는 처리 호출 및 특정 오퍼 정보가 삽입되는 UI 메시징 패턴을 보여 줍니다. 특정 제품 정보가 없는 경우 이 예제에서는 제품 ListingInformation의 정보를 사용합니다.

string offerId = "1234";
product1ListingName = product1.Name;
string displayPropertiesName = "MusicOffer1";

if (String.IsNullOrEmpty(displayPropertiesName))
{
    displayPropertiesName = product1ListingName;
}
var offerIdMsg = " with offer id " + offerId;
if (String.IsNullOrEmpty(offerId))
{
    offerIdMsg = " with no offer id";
}

FulfillmentResult result = await CurrentAppSimulator.ReportConsumableFulfillmentAsync(productId, transactionId);
switch (result)
{
    case FulfillmentResult.Succeeded:
        Log("You bought and fulfilled " + displayPropertiesName + offerIdMsg);
        break;
    case FulfillmentResult.NothingToFulfill:
        Log("There is no purchased product 1 to fulfill.");
        break;
    case FulfillmentResult.PurchasePending:
        Log("You bought product 1. The purchase is pending so we cannot fulfill the product.");
        break;
    case FulfillmentResult.PurchaseReverted:
        Log("You bought product 1. But your purchase has been reverted.");
        // Since the user' s purchase was revoked, they got their money back.
        // You may want to revoke the user' s access to the consumable content that was granted.
        break;
    case FulfillmentResult.ServerError:
        Log("You bought product 1. There was an error when fulfilling.");
        break;
}