应用内购买和试用

Windows SDK 提供了可用于实现以下功能的 API,以便从通用 Windows 平台 (UWP) 应用中获取更多收益:

  • 应用内购买 无论你的应用是否免费,你都可以直接从应用中销售内容或新的应用功能(例如解锁游戏的下一关)。

  • 试用功能 如果你在合作伙伴中心将应用配置为免费试用,则可通过在试用期内排除或限制某些功能吸引客户购买应用的完整版。 也可以在客户购买你的应用之前,启用仅在试用期才会出现的某些功能,如横幅或水印。

本文概述了 UWP 应用中的应用内购买和试用的工作原理。

选择要使用的命名空间

有两个不同的命名空间可用于向 UWP 应用添加应用内购买和试用功能,具体取决于应用面向哪个 Windows 10 或 Windows 11 版本。 尽管这些命名空间中的 API 服务于相同的目标,但它们的设计截然不同,并且两个 API 之间的代码不兼容。

重要

Windows.ApplicationModel.Store 命名空间不再更新新功能,建议为应用改用 Windows.Services.Store 命名空间(如果可能)。 在使用桌面桥的 Windows 桌面应用程序或使用合作伙伴中心开发沙盒的应用或游戏中,Windows.ApplicationModel.Store 命名空间不受支持(例如,与 Xbox Live 集成的任何游戏都是这种情况)

基本概念

Store 中提供的每个商品通常称为“产品”。 大多数开发人员仅使用以下类型的产品:应用和加载项

加载项是在应用上下文中向客户提供的产品或功能:例如,应用或游戏中使用的货币、游戏的新地图或武器、在没有广告的情况下使用应用的功能,或者能够提供此类内容的应用的数字内容(如音乐或视频)。 每个应用和加载项都有一个关联的许可证,指示用户是否有权使用该应用或加载项。 如果用户有权试用应用或加载项,则许可证还提供有关试用的其他信息。

若要在应用中向客户提供加载项,必须在合作伙伴中心为应用定义加载项,以便使 Microsoft Store 知道它。 然后,应用可以使用 Windows.Services.Store 或 Windows.ApplicationModel.Store 命名空间中的 API 以应用内购买的形式向用户出售加载项

UWP 应用可以提供以下类型的加载项。

加载项类型 说明
Durable 一个加载项,其持续时间为你在合作伙伴中心指定的生存期。

默认情况下,耐用品加载项永远不会过期,在这种情况下,其只能购买一次。 如果为该加载项指定特定持续时间,用户可以在其过期后重新购买。
开发人员管理的易耗品 可以购买、使用并在使用后再次购买的加载项。 你负责跟踪加载项所表示商品的用户余额。

当用户使用与该加载项关联的任何商品时,你负责维护用户的余额,并在用户使用完所有商品后向 Store 报告加载项的购买已完成。 在你的应用将之前的加载项购买报告为已完成前,用户无法再次购买该加载项。

例如,如果你的加载项表示游戏中的 100 个硬币,并且用户消耗了 10 个硬币,则你的用户或服务必须为该用户保留 90 个硬币的新剩余余额。 在用户消耗完全部 100 个硬币后,你的应用必须将加载项报告为已完成,然后用户才可以再次购买 100 个硬币的加载项。
Store 管理的易耗品 可以随时购买、使用和再次购买的加载项。 Store 会跟踪加载项表示的商品的余额。

当用户使用与该加载项关联的任何商品时,你负责向 Store 报告这些商品已完成,Store 会更新用户的余额。 用户可以根据需要多次购买加载项(他们不需要首先使用这些项目)。 你的应用可以随时查询用户的当前余额。

例如,如果你的加载项在游戏中表示 100 个硬币的初始数量,并且用户消耗了 50 个硬币,则你的应用将向 Microsoft Store 报告 50 个单位的加载项已完成,然后 Microsoft Store 会更新剩余余额。 如果用户再次购买你的加载项(增加 100 个硬币),他们现在总共有 150 个硬币。

注意 若要使用 Microsoft Store 管理的易耗品,应用必须在 Visual Studio 中面向 Windows 10 周年纪念版(10.0;版本 14393)或更高版本,并且必须使用 Windows.Services.Store 命名空间,而不是 Windows.ApplicationModel.Store 命名空间。
订阅 耐用品加载项,客户会持续定期付费以持续使用该加载项。 客户可以随时取消订阅,以避免产生进一步费用。

注意 若要使用订阅加载项,应用必须在 Visual Studio 中面向 Windows 10 周年纪念版(10.0;版本 14393)或更高版本,并且必须使用 Windows.Services.Store 命名空间,而不是 Windows.ApplicationModel.Store 命名空间。

注意

其他类型的加载项(例如带有包的耐用品加载项(也称为可下载内容或 DLC))仅适用于有限的开发人员,本文档不进行介绍。

使用 Windows.Services.Store 命名空间进行应用内购买和试用

本部分概述了 Windows.Services.Store 命名空间的重要任务和概念。 在 Visual Studio 中,此命名空间仅适用于面向 Windows 10 周年纪念版(10.0;内部版本 14393)或更高版本(对应于 Windows 10 版本 1607)的应用。 建议应用尽可能使用 Windows.Services.Store 命名空间而不是 Windows.ApplicationModel.Store 命名空间。 有关 Windows.ApplicationModel.Store 命名空间的信息,请参阅本文

本部分内容

StoreContext 类入门

Windows.Services.Store 命名空间的主要入口点是 StoreContext。 此类提供的方法可用于获取当前应用及其可用加载项的信息、获取当前应用或其加载项的许可证信息、为当前用户购买应用或加载项以及执行其他任务。 若要获取 StoreContext 对象,请执行以下操作之一:

  • 在单用户应用(即仅在启动应用的用户的上下文中运行的应用)中,使用静态 GetDefault 方法获取可用于访问用户的 Microsoft Store 相关数据的 StoreContext 对象。 大多数通用 Windows 平台 (UWP) 应用都是单用户应用。

    Windows.Services.Store.StoreContext context = StoreContext.GetDefault();
    
  • 多用户应用中,使用静态 GetForUser 方法获取 StoreContext 对象,该对象可用于访问在使用应用时通过其 Microsoft 帐户登录的特定用户的相关数据。 以下示例获取第一个可用用户的 StoreContext 对象

    var users = await Windows.System.User.FindAllAsync();
    Windows.Services.Store.StoreContext context = StoreContext.GetForUser(users[0]);
    

注意

使用桌面桥的 Windows 桌面应用程序必须执行额外的步骤来配置 StoreContext 对象,然后才能使用此对象。 有关详细信息,请参阅此部分

拥有 StoreContext 对象后,可以开始调用此对象的方法来获取当前应用及其加载项的 Store 产品信息、检索当前应用及其加载项的许可证信息、为当前用户购买应用或加载项,以及执行其他任务。 有关可以使用此对象执行的常见任务的详细信息,请参阅以下文章:

有关演示如何使用 StoreContext 和 Windows.Services.Store 命名空间中的其他类型的示例应用,请参阅 Store 示例

实现应用内购买

使用 Windows.Services.Store 命名空间在应用中向客户提供应用内购买

  1. 如果应用提供可供客户购买的加载项,请在合作伙伴中心为应用创建加载项提交

  2. 在应用中编写代码以检索应用或应用提供的加载项的产品信息,然后确定许可证是否处于活动状态(即用户是否有使用应用或加载项的许可证)。 如果许可证未处于活动状态,则会显示一个 UI,以应用内购买的形式向用户出售应用或加载项。

  3. 如果用户选择购买应用或加载项,请使用适当的方法来购买产品:

  4. 按照本文中的测试指南测试你的实现。

实现试用功能

使用 Windows.Services.Store 命名空间排除或限制应用的试用版功能

  1. 在合作伙伴中心将应用配置为免费试用

  2. 在应用中编写代码以检索应用或应用提供的加载项的产品信息,然后确定与应用关联的许可证是否为试用版许可证

  3. 如果是试用版,请排除或限制应用中的某些功能,然后在用户购买完整许可证后启用这些功能。 有关详细信息和代码示例,请参阅实现应用的试用版

  4. 按照本文中的测试指南测试你的实现。

测试应用内购买或试用实现

如果应用使用 Windows.Services.Store 命名空间中的 API 来实现应用内购买或试用功能,则必须将应用发布到 Store 并将其下载到开发设备,才可使用其许可证进行测试。 按照以下过程测试代码:

  1. 如果应用尚未在 Microsoft Store 中发布和提供,请确保应用满足 Windows 应用认证工具包最低要求、将应用提交到合作伙伴中心,并确保应用通过认证过程。 可以配置应用,使其在测试期间无法在 Store 中被发现。 请注意正确配置软件包外部测试版。 可能无法下载错误配置的软件包外部测试版。

  2. 接下来,确保你已完成以下操作:

  3. 在项目在 Visual Studio 中打开的情况下,单击“项目菜单”,指向“Microsoft Store”,然后单击“将应用与 Microsoft Store 关联”。 完成向导中的说明以将应用项目与合作伙伴中心帐户中要用于测试的应用关联。

    注意

    如果未将项目与 Store 中的应用关联,StoreContext 方法会将其返回值的 ExtendedError 属性设置为错误代码值 0x803F6107。 此值指示 Store 完全不知道该应用。

  4. 如果尚未这样做,请从上一步中指定的 Store 安装应用,运行该应用一次,然后将其关闭。 这可确保将应用的有效许可证安装到开发设备。

  5. 在 Visual Studio 中,开始运行或调试项目。 代码应从与本地项目关联的 Store 应用中检索应用和加载项数据。 如果系统提示重新安装应用,请按照说明操作,然后运行或调试项目。

    注意

    完成这些步骤后,可以继续更新应用的代码,然后在开发计算机上调试更新的项目,而无需将新的包提交到 Store。 只需将应用的 Store 版本下载到开发计算机一次即可获取将用于测试的本地许可证。 只需在完成测试后向 Store 提交新的应用包,并向客户提供应用内购买或试用相关功能。

如果应用使用 Windows.ApplicationModel.Store 命名空间,则可以在应用中使用 CurrentAppSimulator 类在测试期间模拟许可证信息,然后再将应用提交到 Store。 有关详细信息,请参阅 CurrentApp 和 CurrentAppSimulator 类入门

注意

Windows.Services.Store 命名空间不提供可用于在测试期间模拟许可证信息的类。 如果使用 Windows.Services.Store 命名空间中来实现应用内购买或试用,则必须将应用发布到 Store 并将其下载到开发设备,才可使用其许可证进行测试,如上所述

应用内购买的收据

Windows.Services.Store 命名空间不提供可用于在应用代码中获取成功购买的交易收据的 API。 此体验不同于与使用 Windows.ApplicationModel.Store 命名空间的应用,后者可以使用客户端 API 检索交易收据

如果使用 Windows.Services.Store 命名空间实现应用内购买,并且想要验证给定客户是否已购买应用或加载项,则可以使用 Microsoft Store 集合 REST API 中的查询产品方法。 此方法的返回数据可确认指定的客户是否具有给定产品的权利,并提供用户获取该产品的交易数据。 Microsoft Store 集合 API 使用 Azure AD 身份验证来检索此信息。

将 StoreContext 类与桌面桥配合使用

使用桌面桥的桌面应用程序可以使用 StoreContext 类来实现应用内购买和试用。 但是,如果你有一个 Win32 桌面应用程序或者一个其窗口句柄 (HWND) 与呈现框架相关联的的桌面应用程序(如 WPF 或 Windows 应用 SDK 应用程序),则该应用程序必须配置 StoreContext 对象,以便为该对象所显示的模式对话框指定哪个应用程序窗口是所有者窗口。

许多 StoreContext 成员(以及通过 StoreContext 对象访问的其他相关类型的成员)向用户显示 Store 相关操作(例如购买产品)的模式对话框。 如果桌面应用程序没有配置 StoreContext 对象来指定模式对话框的所有者窗口,则此对象返回不准确的数据或错误信息

若要在使用桌面桥的桌面应用程序中配置 StoreContext 对象,请执行以下步骤

对于 .NET 6 或更高版本

如果应用程序通过 C# 结合使用 .NET 6 或更高版本编写,请执行以下步骤。

  1. 确保将项目文件中的 TargetFramework 属性设置为特定的 Windows SDK 版本以访问 Windows 运行时 API,该 API 提供对 WinRT.Interop 命名空间的访问权限。 例如:

    <PropertyGroup>
      <!-- You can also target other versions of the Windows SDK and .NET, e.g. "net6.0-windows10.0.19041.0" -->
      <TargetFramework>net6.0-windows10.0.22000.0</TargetFramework>
    </PropertyGroup>
    
  2. 按照本文前面部分所述,使用 GetDefault 方法(或 GetForUser 方法,如果应用是多用户应用)获取 StoreContext 对象。 若要使用指定的窗口句柄初始化对话框,请使用 WinRT.Interop.WindowNative.GetWindowHandle 和 WinRT.Interop.InitializeWithWindow.Initialize 方法(请参阅检索窗口句柄 (HWND)显示依赖于 CoreWindow 的 WinRT UI 对象

    StoreContext context = StoreContext.GetDefault();
    // Obtain window handle by passing in pointer to the window object
    var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(windowObject);
    // Initialize the dialog using wrapper function for IInitializeWithWindow
    WinRT.Interop.InitializeWithWindow.Initialize(context, hwnd); 
    

对于早期版本的 .NET 或 C++

如果应用程序使用早期版本的 .NET 或 C++ 编写,请执行以下步骤。

  1. 执行下列操作之一,使应用能够访问 IInitializeWithWindow 接口:

    • 如果应用程序使用 C# 或 Visual Basic 之类的托管语言(.NET 6 之前)编写,则在应用代码中使用 ComImport 属性声明 IInitializeWithWindow 接口,如以下 C# 示例所示。 此示例假设代码文件具有 System.Runtime.InteropServices 命名空间的 using 语句

      [ComImport]
      [Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
      [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      public interface IInitializeWithWindow
      {
          void Initialize(IntPtr hwnd);
      }
      
    • 如果应用程序是采用 C++ 编写的,请在代码中添加对 shobjidl.h 头文件的引用。 此头文件包含 IInitializeWithWindow 接口的声明

  2. 按照本文前面部分所述,使用 GetDefault 方法(或 GetForUser 方法,如果你的应用是多用户应用)获取 StoreContext 对象,并将此对象转换为 IInitializeWithWindow 对象。 然后,调用 IInitializeWithWindow.Initialize 方法,并传递希望作为 StoreContext 方法显示的任何模式对话框的所有者的窗口句柄。 以下 C# 示例演示如何将应用主窗口的句柄传递给该方法。 另请参阅检索窗口句柄 (HWND)显示依赖于 CoreWindow 的 WinRT UI 对象

    StoreContext context = StoreContext.GetDefault();
    IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)context;
    initWindow.Initialize(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
    

产品、SKU 和可用性

Store 中的每个产品至少有一个 SKU,每个 SKU 至少有一个可用性。 已经为合作伙伴中心的大多数开发人员抽象掉这些概念,大多数开发人员永远不需要为其应用或加载项定义 SKU 或可用性。 但是,由于 Windows.Services.Store 命名空间中 Store 产品的对象模型包括 SKU 和可用性,因此对这些概念有基本了解对于某些方案很有用

Object 说明
产品 产品是指 Store 中提供的任何类型的产品,包括应用或加载项。

Store 中的每个产品都有相应的 StoreProduct 对象。 此类提供可用于访问产品 Store ID、Store 一览的图像和视频以及定价信息等数据的属性。 它还提供可用于购买产品的方法。
SKU SKU 是产品的特定版本,具有自己的说明、价格和其他独特的产品详细信息。 每个应用或加载项都有一个默认 SKU。 大多数开发人员只有在发布应用的完整版本和试用版时才会为一个应用设置多个 SKU(在 Store 目录中,每个版本都是同一应用的不同 SKU)。

某些发布者能够定义自己的 SKU。 例如,大型游戏发布者可能会发布一款游戏,其中一个 SKU 在不允许显示红色血液的市场中显示绿色血液,而另一个 SKU 在所有其他市场中显示红色血液。 或者,销售数字视频内容的发布者可能会为视频发布两个 SKU,一个用于高清版本,另一个用于标清版本。

Store 中的每个 SKU 都有相应的 StoreSku 对象。 每个 StoreProduct 都有一个 SKU 属性,可用于访问产品的 SKU。
可用性 可用性是带有自己独特定价信息的 SKU 的特定版本。 每个 SKU 都有默认可用性。 某些发布者能够定义自己的可用性,以便为给定 SKU 引入不同的价格选项。

Store 中的每个可用性都有相应的 StoreAvailability 对象。 每个 StoreSku 都有一个 Availabilities 属性,可用于访问 SKU 的可用性。 对于大多数开发人员来说,每个 SKU 都有一个默认可用性。

Store ID

Store 中的每个应用、加载项或其他产品都有关联的 Store ID(有时也称为产品 Store ID)。 许多 API 需要 Store ID 才能对应用或加载项执行操作。

Store 中任何产品的 Store ID 为 12 个字符的字母数字字符串,例如 9NBLGGH4R315。 可通过多种不同的方式获取 Store 中产品的 Store ID:

  • 对于应用,你可以在合作伙伴中心的“应用标识”页上获取 Store ID。
  • 对于加载项,你可以在合作伙伴中心的加载项概述页上获取 Store ID。
  • 对于任何产品,也可以使用表示产品的 StoreProduct 对象的 StoreId 属性以编程方式获取 Store ID

对于具有 SKU 和可用性的产品,SKU 和可用性也有自己的不同格式的 Store ID。

对象 Store ID 格式
SKU SKU 的 Store ID 格式为 <product Store ID>/xxxx,其中 xxxx 是 4 个字符的字母数字字符串,用于标识产品的 SKU。 例如 9NBLGGH4R315/000N。 此 ID 由 StoreSku 对象的 StoreId 属性返回,并且有时称为“SKU Store ID”
可用性 可用性的 Store ID 的格式为 <product Store ID>/xxxx/yyyyyyyyyyyy,其中 xxxx 是一个 4 个字符的字母数字字符串,用于标识产品的 SKU;yyyyyyyyyyyy 是一个 12 个字符的字母数字字符串,用于标识 SKU 的可用性。 例如 9NBLGGH4R315/000N/4KW6QZD2VN6X。 此 ID 由 StoreAvailability 对象的 StoreId 属性返回,并且有时称为“可用性 Store ID”

如何在代码中使用加载项的产品 ID

如果要在应用上下文中向客户提供加载项,你必须在合作伙伴中心创建加载项提交时为加载项输入唯一产品 ID。 你可以使用此产品 ID 来引用代码中的加载项,但可以使用产品 ID 的具体场景取决于你在应用中用于应用内购买的命名空间。

注意

你在合作伙伴中心为加载项输入的产品 ID 不同于此加载项的 Store ID。 Store ID 由合作伙伴中心生成。

使用 Windows.Services.Store 命名空间的应用

如果你的应用使用 Windows.Services.Store 命名空间,则可以使用产品 ID 轻松标识表示加载项的 StoreProduct 或表示加载项许可证的 StoreLicense。 产品 ID 由 StoreProduct.InAppOfferTokenStoreLicense.InAppOfferToken 属性公开。

注意

尽管产品 ID 是标识代码中的加载项的有用方法,但 Windows.Services.Store 命名空间中的大多数操作都使用加载项的 Store ID,而不是产品 ID。 例如,若要以编程方式检索应用的一个或多个已知加载项,请将加载项的 Store ID(而不是产品 ID)传递给 GetStoreProductsAsync 方法。 同样,若要将易耗品加载项报告为已完成,请将加载项的 Store ID(而不是产品 ID)传递给 ReportConsumableFulfillmentAsync 方法。

使用 Windows.ApplicationModel.Store 命名空间的应用

如果应用使用 Windows.ApplicationModel.Store 命名空间,你需要使用自己在合作伙伴中心向加载项分配的产品 ID 来进行大多数操作。 例如: