Manage a large catalog of in-app products

If your app offers a large in-app product catalog, you can optionally follow the process described in this topic to help manage your catalog. In releases before Windows 10, the Store has a limit of 200 product listings per developer account, and the process described in this topic can be used to work around that limitation. Starting with Windows 10, the Store has no limit to the number of product listings per developer account, and the process described in this article is no longer necessary.

Important

This article demonstrates how to use members of the Windows.ApplicationModel.Store namespace. This namespace is no longer being updated with new features, and we recommend that you use the Windows.Services.Store namespace instead. The Windows.Services.Store namespace supports the latest add-on types, such as Store-managed consumable add-ons and subscriptions, and is designed to be compatible with future types of products and features supported by Partner Center and the Store. The Windows.Services.Store namespace was introduced in Windows 10, version 1607, and it can only be used in projects that target Windows 10 Anniversary Edition (10.0; Build 14393) or a later release in Visual Studio. For more information, see In-app purchases and trials.

To enable this capability, you will create a handful of product entries for specific price tiers, with each one able to represent hundreds of products within a catalog. Use the RequestProductPurchaseAsync method overload that specifies an app-defined offer associated with an in-app product listed in the Store. In addition to specifying an offer and product association during the call, your app should also pass a ProductPurchaseDisplayProperties object that contains the large catalog offer details. If these details are not provided, the details for the listed product will be used instead.

The Store will only use the offerId from the purchase request in the resulting PurchaseResults. This process does not directly modify the information originally provided when listing the in-app product in the Store.

Prerequisites

  • This topic covers Store support for the representation of multiple in-app offers using a single in-app product listed in the Store. If you are unfamiliar with in-app purchases please review Enable in-app product purchases to learn about license information, and how to properly list your in-app purchase in the Store.
  • When you code and test new in-app offers for the first time, you must use the CurrentAppSimulator object instead of the CurrentApp object. This way you can verify your license logic using simulated calls to the license server instead of calling the live server. To do this, you need to customize the file named WindowsStoreProxy.xml in %userprofile%\AppData\local\packages\<package name>\LocalState\Microsoft\Windows Store\ApiData. The Microsoft Visual Studio simulator creates this file when you run your app for the first timeā€”or you can also load a custom one at runtime. For more info, see Using the WindowsStoreProxy.xml file with CurrentAppSimulator.
  • This topic also references code examples provided in the Store sample. This sample is a great way to get hands-on experience with the different monetization options provided for Universal Windows Platform (UWP) apps.

Make the purchase request for the in-app product

The purchase request for a specific product within a large catalog is handled in much the same way as any other purchase request within an app. When your app calls the new RequestProductPurchaseAsync method overload, your app provides both an OfferId and a ProductPurchaseDisplayProperties object populated with the name of the in-app product.

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

Report fulfillment of the in-app offer

Your app will need to report product fulfillment to the Store as soon as the offer has been fulfilled locally. In a large catalog scenario, if your app does not report offer fulfillment, the user will be unable to purchase any in-app offers using that same Store product listing.

As mentioned earlier, the Store only uses provided offer info to populate the PurchaseResults, and does not create a persistent association between a large catalog offer and Store product listing. As a result you need to track user entitlement for products, and provide product-specific context (such as the name of the item being purchased or details about it) to the user outside of the RequestProductPurchaseAsync operation.

The following code demonstrates the fulfillment call, and a UI messaging pattern in which the specific offer info is inserted. In the absence of that specific product info, the example uses info from the product 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;
}