アプリ内製品の大規模なカタログの管理

アプリ内製品のカタログが大きくなる場合、カタログを管理するためにこのトピックで説明するプロセスを採用できます。 Windows 10 より前のリリースでは、開発者アカウントごとに 200 製品の表示制限があり、このトピックで説明するプロセスを使ってこの制限を回避できます。 Windows 10 以降では、開発者アカウントごとに表示されるストアの製品数に制限がないため、この記事で説明されているプロセスは必要ありません。

重要

この記事では、Windows.ApplicationModel.Store 名前空間のメンバーの使用方法について説明します。 この名前空間は更新されなくなり、新機能も追加されないため、代わりに Windows.Services.Store 名前空間を使用することをお勧めします。 Windows.Services.Store 名前空間は、Microsoft Store で管理されるコンシューマブルなアドオンやサブスクリプションなど、最新の種類のアドオンをサポートしており、パートナー センターと Microsoft Store で今後サポートされる製品および機能の種類と互換性を持つように設計されています。 Windows.Services.Store 名前空間は、Windows 10 バージョン 1607 で導入され、Visual Studio で、Windows 10 Anniversary Edition (10.0、ビルド 14393) 以降のリリースをターゲットとするプロジェクトでのみ使用できます。 詳しくは、「アプリ内購入と試用版」をご覧ください。

この機能を有効にするには、特定の価格帯に対して少数の製品エントリを作成し、個々のエントリで、カタログ内の多数の製品を表します。 RequestProductPurchaseAsync メソッドのオーバーロードを使って、ストアに掲載されたアプリ内購入製品に関連する、アプリで定義された販売を指定します。 呼び出し中に販売と製品の関連付けを指定することに加えて、アプリでは、大規模なカタログ販売の詳細を格納する ProductPurchaseDisplayProperties オブジェクトも渡す必要があります。 これらの詳細が指定されていない場合、一覧に掲載された製品の詳細が代わりに使われます。

ストアでは、結果の PurchaseResults で、購入要求からの offerId のみを使います。 このプロセスは、ストアにアプリ内製品を掲載したときに最初に提供された情報を直接変更するわけではありません。

前提条件

  • このトピックでは、ストアの一覧に表示される 1 つのアプリ内購入製品を使って複数のアプリ内販売を表現することに対する、ストアのサポートについて説明します。 アプリ内購入に詳しくない場合は、「アプリ内製品購入の有効化」を読んで、ライセンス情報と、ストアでアプリ内購入を適切に一覧表示する方法を確かめてください。
  • 新しいアプリ内販売のコード記述やテストを初めて行うときは、CurrentApp オブジェクトではなく、CurrentAppSimulator オブジェクトを使う必要があります。 そうすることで、実稼働サーバーを呼び出すのではなく、ライセンス サーバーへのシミュレートされた呼び出しを使って、ライセンス ロジックを検証できます。 そのためには、%userprofile%\AppData\local\packages\<package name>\LocalState\Microsoft\Windows Store\ApiData で WindowsStoreProxy.xml という名前のファイルをカスタマイズする必要があります。 このファイルは、アプリを初めて実行するときに Microsoft Visual Studio シミュレーターによって作られます。カスタマイズされたファイルを実行時に読み込むこともできます。 詳しくは、「CurrentAppSimulator での WindowsStoreProxy.xml ファイルの使用」をご覧ください。
  • このトピックでは、ストア サンプルで提供されているコード例も参照します。 このサンプルを利用すると、ユニバーサル Windows プラットフォーム (UWP) アプリに提供されるさまざまな収益化オプションを体験できます。

アプリ内製品に対する購入要求

大規模なカタログ内の特定の製品に対する購入要求は、他のアプリ内購入要求とほとんど同様に処理されます。 アプリが新しい RequestProductPurchaseAsync メソッドのオーバーロードを呼び出すときに、アプリは OfferId と、アプリ内製品の名前が設定された ProductPurchaseDisplayProperties オブジェクトの両方を提供します。

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 を設定し、大規模なカタログ販売とストアの製品一覧の間に永続的な関連付けは作成しません。 その結果、製品に対するユーザーの権限を追跡し、製品固有のコンテキスト (購入される項目の名前やその詳細など) を 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;
}