共用方式為


啟用應用程式的訂閱附加元件

您的通用 Windows 平台 (UWP) 應用程式可以為客戶提供訂用帳戶附加元件的應用程式內購買。 您可以透過自動化固定計費週期在您的應用程式中銷售數位產品 (例如應用程式功能或數位內容)。

注意

若要在應用程式中啟用購買訂用帳戶附加元件,您的專案在 Visual Studio 中必須以 Windows 10 年度版 (10.0;組建 14393) 或更新版本為目標 (這相對應於 Windows 10 版本 1607),而且必須使用 Windows.Services.Store 命名空間中的 API 來實作應用程式內購買體驗,而不是 Windows.ApplicationModel.Store 命名空間。 如需這些命名空間之間差異的詳細資訊,請參閱應用程式內購買和試用版

功能要點

適用於 UWP 應用程式的訂用帳戶附加元件支援下列功能:

  • 您可以選擇 1 個月、3 個月、6 個月、1 年或 2 年的訂閱期間。
  • 您可以新增 1 週或 1 個月的免費試用期間到您的訂用帳戶。
  • Windows SDK 提供的 API 可讓您在應用程式中使用,以取得應用程式可用訂用帳戶附加元件的相關資訊,並啟用購買訂用帳戶附加元件。 我們也提供您可以從服務呼叫的 REST API 來管理使用者的訂用帳戶
  • 您可以檢視分析報告,當中提供指定時段內的訂用帳戶獲取數目、作用中訂閱者數目及取消的訂閱數目。
  • 客戶可以在其 Microsoft 帳戶的 https://account.microsoft.com/services 頁面上管理其訂用帳戶。 客戶可以使用此頁面來檢視他們取得的所有訂用帳戶、取消訂用帳戶,以及變更與其訂用帳戶相關聯的付款方式。

為應用程式啟用訂閱附加元件的步驟

若要在應用程式中啟用購買訂用帳戶附加元件,請遵循下列步驟。

  1. 在合作夥伴中心為您的訂用帳戶建立附加元件提交,並發佈提交。 當您遵循附加元件提交程序時,請特別注意下列屬性:

    • 產品類型:請確定您選取 [訂用帳戶]

    • 訂用帳戶期間:為您的訂用帳戶選擇週期性計費期間。 發佈附加元件之後,您便無法變更訂閱期間。

      每個訂用帳戶附加元件都支援單一訂用帳戶期間和試用期間。 您必須為您想要在應用程式中提供的每個訂用帳戶類型建立不同的訂用帳戶附加元件。 例如,如果您想要提供不含試用版的每月訂用帳戶、具有一個月試用版的每月訂用帳戶、沒有試用版的年度訂用帳戶,以及具有一個月試用版的年度訂用帳戶,您必須建立四個訂用帳戶附加元件。

    • 試用期間:請考慮為您的訂用帳戶選擇 1 週或 1 個月的試用期,讓使用者在購買之前先試用您的訂用帳戶內容。 發佈訂用帳戶附加元件之後,您便無法變更或移除試用期間。

      若要取得訂用帳戶的免費試用版,使用者必須透過應用程式內標準購買程序購買您的訂用帳戶,包括有效的付款形式。 在試用期間,不會向他們收取任何費用。 試用期間結束時,訂用帳戶會自動轉換為完整訂閱,並在付費訂閱的第一個期間透過使用者的付款方式收費。 如果使用者選擇在試用期間取消其訂用帳戶,訂閱會維持使用中狀態,直到試用期結束為止。 某些試用期間不適用於所有訂用帳戶期間。

      注意

      每位客戶只能為訂用帳戶附加元件取得一次免費試用版。 客戶取得訂用帳戶的免費試用版之後,市集會防止相同的客戶再次取得相同的免費試用版訂閱。

    • 可見度:如果您要建立測試附加元件,您只會用來測試訂用帳戶的應用程式內購買體驗,建議您選取其中一個 [在市集中隱藏] 選項。 否則,您可以為案例最佳的可見度選項。

    • 定價:在本區段中選擇訂用帳戶的價格。 發佈附加元件之後,您便無法提高訂用帳戶的價格。 不過,您可以稍後降低價格。

      重要

      根據預設,當您建立任何附加元件時,價格一開始會設定為 [免費]。 因為在完成附加元件提交之後,您無法提高訂用帳戶附加元件的價格,所以請務必在這裡選擇訂用帳戶的價格。

  2. 在您的應用程式中,使用 Windows.Services.Store 命名空間中的 API 來判斷目前使用者是否已取得您的訂用帳戶附加元件,然後將它以應用程式內購買的形式提供給使用者。 如需詳細資訊,請參閱本文中的程式碼範例

  3. 在應用程式中測試訂用帳戶的應用程式內購買實作。 您必須從市集下載應用程式一次,下載到您的開發裝置,才能使用其授權進行測試。 如需詳細資訊,請參閱應用程式內購買的測試指引

  4. 建立並發佈包含已更新應用程式套件的應用程式提交,包括已測試的程式碼。 如需詳細資訊,請參閱應用程式提交

程式碼範例

本節中的程式碼範例示範如何使用 Windows.Services.Store 命名空間中的 API 來取得目前應用程式訂閱附加元件的相關資訊,並代表目前使用者要求購買訂用帳戶附加元件。

這些範例具有下列必要條件:

  • Windows 10 年度版本 (10.0;組建 14393) 或更新版本為目標之通用 Windows 平台 (UWP) 應用程式的 Visual Studio 專案。
  • 您已在合作夥伴中心建立應用程式提交,且此應用程式會在市集中發佈。 您可以選擇將應用程式設定為在您進行測試時無法在市集中探索。 如需詳細資訊,請參閱測試指導方針
  • 您已在合作夥伴中心為應用程式建立訂用帳戶附加元件

這些範例中的程式碼假設:

  • 程式碼檔案含有 Windows.Services.StoreSystem.Threading.Tasks 命名空間的 using 陳述式。
  • 應用程式是單一使用者應用程式僅會在啟動應用程式的使用者內容中執行。 如需詳細資訊,請參閱應用程式內購買和試用版

注意

如果您有使用傳統型橋接器的傳統型應用程式,可能需要新增未顯示在這些範例中的其他程式碼來設定 StoreContext 物件。 如需詳細資訊,請參閱在使用傳統型橋接器的傳統型應用程式中使用 StoreContext 類別

購買訂用帳戶附加元件

此範例示範如何代表目前客戶要求為您的應用程式購買已知的訂用帳戶附加元件。 此範例也會示範如何處理訂用帳戶有試用期間的情況。

  1. 程式碼會先判斷客戶是否已經有訂用帳戶的有效授權。 如果客戶已經有有效的授權,您的程式碼應該視需要解除鎖定訂用帳戶功能 (因為這是您應用程式的專屬功能,因此會以範例中的註解來識別)。
  2. 接下來,程式碼會取得 StoreProduct 物件,表示您想要代表客戶購買的訂用帳戶。 程式碼假設您已經知道您要購買之訂用帳戶附加元件的 Store ID,而且您已將此值指派給 subscriptionStoreId 變數。
  3. 然後,程式碼會判斷訂用帳戶是否有可用的試用版。 或者,您的應用程式可以使用這項資訊向客戶顯示可用試用版或完整訂用帳戶的詳細資料。
  4. 最後,程式碼會呼叫 RequestPurchaseAsync 方法來要求購買訂閱。 如果訂用帳戶有試用版,則會將試用版提供給客戶購買。 否則,將提供完整的訂用帳戶以供購買。
private StoreContext context = null;
StoreProduct subscriptionStoreProduct;

// Assign this variable to the Store ID of your subscription add-on.
private string subscriptionStoreId = "";  

// This is the entry point method for the example.
public async Task SetupSubscriptionInfoAsync()
{
    if (context == null)
    {
        context = StoreContext.GetDefault();
        // If your app is a desktop app that uses the Desktop Bridge, you
        // may need additional code to configure the StoreContext object.
        // For more info, see https://aka.ms/storecontext-for-desktop.
    }

    bool userOwnsSubscription = await CheckIfUserHasSubscriptionAsync();
    if (userOwnsSubscription)
    {
        // Unlock all the subscription add-on features here.
        return;
    }

    // Get the StoreProduct that represents the subscription add-on.
    subscriptionStoreProduct = await GetSubscriptionProductAsync();
    if (subscriptionStoreProduct == null)
    {
        return;
    }

    // Check if the first SKU is a trial and notify the customer that a trial is available.
    // If a trial is available, the Skus array will always have 2 purchasable SKUs and the
    // first one is the trial. Otherwise, this array will only have one SKU.
    StoreSku sku = subscriptionStoreProduct.Skus[0];
    if (sku.SubscriptionInfo.HasTrialPeriod)
    {
        // You can display the subscription trial info to the customer here. You can use 
        // sku.SubscriptionInfo.TrialPeriod and sku.SubscriptionInfo.TrialPeriodUnit 
        // to get the trial details.
    }
    else
    {
        // You can display the subscription purchase info to the customer here. You can use 
        // sku.SubscriptionInfo.BillingPeriod and sku.SubscriptionInfo.BillingPeriodUnit
        // to provide the renewal details.
    }

    // Prompt the customer to purchase the subscription.
    await PromptUserToPurchaseAsync();
}

private async Task<bool> CheckIfUserHasSubscriptionAsync()
{
    StoreAppLicense appLicense = await context.GetAppLicenseAsync();

    // Check if the customer has the rights to the subscription.
    foreach (var addOnLicense in appLicense.AddOnLicenses)
    {
        StoreLicense license = addOnLicense.Value;
        if (license.SkuStoreId.StartsWith(subscriptionStoreId))
        {
            if (license.IsActive)
            {
                // The expiration date is available in the license.ExpirationDate property.
                return true;
            }
        }
    }

    // The customer does not have a license to the subscription.
    return false;
}

private async Task<StoreProduct> GetSubscriptionProductAsync()
{
    // Load the sellable add-ons for this app and check if the trial is still 
    // available for this customer. If they previously acquired a trial they won't 
    // be able to get a trial again, and the StoreProduct.Skus property will 
    // only contain one SKU.
    StoreProductQueryResult result =
        await context.GetAssociatedStoreProductsAsync(new string[] { "Durable" });

    if (result.ExtendedError != null)
    {
        System.Diagnostics.Debug.WriteLine("Something went wrong while getting the add-ons. " +
            "ExtendedError:" + result.ExtendedError);
        return null;
    }

    // Look for the product that represents the subscription.
    foreach (var item in result.Products)
    {
        StoreProduct product = item.Value;
        if (product.StoreId == subscriptionStoreId)
        {
            return product;
        }
    }

    System.Diagnostics.Debug.WriteLine("The subscription was not found.");
    return null;
}

private async Task PromptUserToPurchaseAsync()
{
    // Request a purchase of the subscription product. If a trial is available it will be offered 
    // to the customer. Otherwise, the non-trial SKU will be offered.
    StorePurchaseResult result = await subscriptionStoreProduct.RequestPurchaseAsync();

    // Capture the error message for the operation, if any.
    string extendedError = string.Empty;
    if (result.ExtendedError != null)
    {
        extendedError = result.ExtendedError.Message;
    }

    switch (result.Status)
    {
        case StorePurchaseStatus.Succeeded:
            // Show a UI to acknowledge that the customer has purchased your subscription 
            // and unlock the features of the subscription. 
            break;

        case StorePurchaseStatus.NotPurchased:
            System.Diagnostics.Debug.WriteLine("The purchase did not complete. " +
                "The customer may have cancelled the purchase. ExtendedError: " + extendedError);
            break;

        case StorePurchaseStatus.ServerError:
        case StorePurchaseStatus.NetworkError:
            System.Diagnostics.Debug.WriteLine("The purchase was unsuccessful due to a server or network error. " +
                "ExtendedError: " + extendedError);
            break;

        case StorePurchaseStatus.AlreadyPurchased:
            System.Diagnostics.Debug.WriteLine("The customer already owns this subscription." +
                    "ExtendedError: " + extendedError);
            break;
    }
}

取得目前應用程式之訂用帳戶附加元件的相關資訊

此程式碼範例示範如何取得應用程式中所有可用的訂用帳戶附加元件的資訊。 若要取得這項資訊,請先使用 GetAssociatedStoreProductsAsync 方法來取得 StoreProduct 物件的集合,這些物件代表應用程式每個可用的附加元件。 然後,取得每個產品的 StoreSku,並使用 IsSubscriptionSubscriptionInfo 屬性來存取訂用帳戶資訊。

private StoreContext context = null;

public async Task GetSubscriptionsInfo()
{
    if (context == null)
    {
        context = StoreContext.GetDefault();
        // If your app is a desktop app that uses the Desktop Bridge, you
        // may need additional code to configure the StoreContext object.
        // For more info, see https://aka.ms/storecontext-for-desktop.
    }

    // Subscription add-ons are Durable products.
    string[] productKinds = { "Durable" };
    List<String> filterList = new List<string>(productKinds);

    StoreProductQueryResult queryResult =
        await context.GetAssociatedStoreProductsAsync(productKinds);

    if (queryResult.ExtendedError != null)
    {
        // The user may be offline or there might be some other server failure.
        System.Diagnostics.Debug.WriteLine($"ExtendedError: {queryResult.ExtendedError.Message}");
        return;
    }

    foreach (KeyValuePair<string, StoreProduct> item in queryResult.Products)
    {
        // Access the Store product info for the add-on.
        StoreProduct product = item.Value;

        // For each add-on, the subscription info is available in the SKU objects in the add-on. 
        foreach (StoreSku sku in product.Skus)
        {
            if (sku.IsSubscription)
            {
                // Use the sku.SubscriptionInfo property to get info about the subscription. 
                // For example, the following code gets the units and duration of the 
                // subscription billing period.
                StoreDurationUnit billingPeriodUnit = sku.SubscriptionInfo.BillingPeriodUnit;
                uint billingPeriod = sku.SubscriptionInfo.BillingPeriod;
            }
        }
    }
}

從您的服務管理訂用帳戶

在更新的應用程式進入市集後,客戶可以購買您的訂用帳戶附加元件之後,您可能會碰到需要為客戶管理訂用帳戶的情況。 我們提供您可以從服務呼叫的 REST API,以執行下列訂用帳戶管理工作:

  • 取得使用者有權使用的訂用帳戶。 如果您的訂用帳戶是跨平台服務的一部分,您可以呼叫此 API 來判斷指定的使用者是否具有訂用帳戶的權利,以及其訂用帳戶在 UWP 應用程式內容中的狀態。 然後,您可以使用此資訊來更新服務支援之其他平台上的訂用帳戶狀態。

  • 變更特定使用者的訂用帳戶計費狀態。 使用此 API 取消、延長或停用訂用帳戶的自動更新。

取消

客戶可以使用其 Microsoft 帳戶的 https://account.microsoft.com/services 頁面來檢視他們取得的所有訂用帳戶、取消訂用帳戶,以及變更與其訂用帳戶相關聯的付款方式。 當客戶使用此頁面取消訂閱時,他們在目前的計費期間可繼續存取訂閱。 他們不會就目前計費期間的任何部分取得退款。 在目前計費期間結束時,其訂用帳戶便會停用。

您也可以使用 REST API 變更特定使用者的訂用帳戶計費狀態,代表使用者取消訂用帳戶。

訂用帳戶續約和寬限期

在每個計費期間的某個時間點,我們會嘗試向客戶的信用卡收取下一個計費期間的費用。 如果收費失敗,客戶的訂用帳戶會進入「催繳」狀態。 這表示其訂用帳戶目前計費期間的其餘部分仍處於作用中狀態,但我們會定期嘗試向信用卡收費,以自動更新訂用帳戶。 此狀態最多可能持續兩週,直到目前計費期間結束及下一個計費週期的續約日期。

我們對訂用帳戶計費不提供寬限期。 如果我們無法在目前計費期間結束時成功向客戶的信用卡收費,訂用帳戶將會取消,客戶將無法在目前的計費期間之後再存取訂用帳戶。

不支援的情節

訂用帳戶附加元件目前不支援下列案例。

  • 目前不支援透過市集直接向客戶銷售訂閱。 訂用帳戶僅適用於應用程式內購買數位產品。
  • 客戶無法使用其 Microsoft 帳戶的 https://account.microsoft.com/services 頁面來切換訂用帳戶期間。 若要切換至不同的訂用帳戶期間,客戶必須取消其目前的訂用帳戶,然後從您的應用程式購買具有不同訂用帳戶週期的訂用帳戶。
  • 訂用帳戶附加元件目前不支援階層切換 (例如,將客戶從基本訂用帳戶切換到具有更多功能的進階訂用帳戶)。
  • 訂閱附加元件目前不支援銷售促銷碼
  • 將訂用帳戶附加元件可見度設定為 [停止獲取] 之後,更新現有的訂用帳戶。 如需詳細資訊,請參閱設定附加元件定價和可用性