使用应用商店服务管理定向优惠

如果你在合作伙伴中心的“参与”>“定向优惠”页面中为应用创建了“定向优惠”,请在应用代码中使用 Microsoft Store 定向优惠 API 检索信息,以便为此定向优惠实现应用内体验。 有关定向优惠和如何在仪表板中创建定向优惠的更多信息,请参阅使用定向优惠最大化参与度和转换率

定向优惠 API 是一个简单的 REST API,可用来获取为当前用户提供的定向优惠,具体取决于用户是否属于该定向优惠所针对的客户细分的一部分。 若要在你的应用中使用此 API,请执行以下步骤:

  1. 为当前已登录应用的用户获取 Microsoft 帐户令牌
  2. 为当前用户获取定向优惠
  3. 为与定向优惠之一关联的加载项实现应用内购买体验。 有关如何实现应用内购买的详细信息,请参阅此文章

有关演示所有上述步骤的完整代码示例,请参阅本文末尾的代码示例。 以下部分提供有关每个步骤的更多详细信息。

为当前用户获取 Microsoft 帐户令牌

在你的应用代码中,为当前已登录的用户获取 Microsoft 帐户 (MSA) 令牌。 对于 Microsoft Store 定向优惠 API 中的每种方法,你都必须将此令牌传递到 Authorization 请求标头中。 此令牌供 Microsoft Store 用于检索为当前用户提供的定向优惠。

若要获取 MSA 令牌,请使用 WebAuthenticationCoreManager 类来获取使用范围为 devcenter_implicit.basic,wl.basic 的令牌。 下面的示例演示如何执行此操作。 此示例是完整示例]的一个片段,它需要完整示例中提供的 using 语句。

private async Task<string> GetMicrosoftAccountTokenAsync()
{
    var msaProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync(
        "https://login.microsoft.com", "consumers");

    WebTokenRequest request = new WebTokenRequest(msaProvider, "devcenter_implicit.basic,wl.basic");
    WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);

    if (result.ResponseStatus == WebTokenRequestStatus.Success)
    {
        return result.ResponseData[0].Token;
    }
    else
    {
        return string.Empty;
    }
}

有关获取 MSA 令牌的详细信息,请参阅 Web 帐户管理器

为当前用户获取定向优惠

为当前用户获得了 MSA 令牌之后,请调用 https://manage.devcenter.microsoft.com/v2.0/my/storeoffers/user URI 的 GET 方法为当前用户获取可用的定向优惠。 有关此 REST 方法的更多信息,请参阅获取定向优惠

此方法将会返回与针对当前用户提供的定向优惠关联的加载项产品 ID。 你可以通过这些信息将一个或多个定向优惠作为应用内购买提供给用户。

下面的示例演示如何为当前用户获取定向优惠。 此示例是完整示例的一个片段。 它需要 Newtonsoft 的 Json.NET 库和完整示例中提供的其他类和 using 语句。

private async Task<List<TargetedOfferData>> GetTargetedOffersForUserAsync(string msaToken)
{
    if (string.IsNullOrEmpty(msaToken))
    {
        System.Diagnostics.Debug.WriteLine("Microsoft Account token is null or empty.");
        return null;
    }

    HttpClient httpClientGetOffers = new HttpClient();
    httpClientGetOffers.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", msaToken);
    List<TargetedOfferData> availableOfferData = null;

    try
    {
        string getOffersResponse = await httpClientGetOffers.GetStringAsync(new Uri(storeOffersUri));
        availableOfferData = 
            Newtonsoft.Json.JsonConvert.DeserializeObject<List<TargetedOfferData>>(getOffersResponse);
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }

    return availableOfferData;
}

完整代码示例

以下代码示例演示了以下任务:

  • 为当前用户获取 MSA 令牌。
  • 通过使用获取定向优惠方法为当前用户获取所有定向优惠。
  • 购买与定向优惠关联的加载项。

此示例需要 Newtonsoft 的 Json.NET 库。 此示例使用此库对 JSON 格式的数据进行序列化和反序列化。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Windows.Services.Store;
using Windows.Security.Authentication.Web.Core;

namespace DocumenationExamples
{
    public class TargetedOffersExample
    {
        private const string storeOffersUri = "https://manage.devcenter.microsoft.com/v2.0/my/storeoffers/user";
        private const string jsonMediaType = "application/json";
        private static string[] productKinds = { "Durable", "Consumable", "UnmanagedConsumable" };
        private static StoreContext storeContext = StoreContext.GetDefault();

        public async void DemonstrateTargetedOffers()
        {
            // Get the Microsoft Account token for the current user.
            string msaToken = await GetMicrosoftAccountTokenAsync();

            if (string.IsNullOrEmpty(msaToken))
            {
                System.Diagnostics.Debug.WriteLine("Microsoft Account token could not be retrieved.");
                return;
            }

            // Get the targeted Store offers for the current user.
            List<TargetedOfferData> availableOfferData =
                await GetTargetedOffersForUserAsync(msaToken);

            if (availableOfferData == null || availableOfferData.Count == 0)
            {
                System.Diagnostics.Debug.WriteLine("There was an error retrieving targeted offers," +
                    "or there are no targeted offers available for the current user.");
                return;
            }

            // Get the product ID of the add-on that is associated with the first available offer
            // in the response data.
            TargetedOfferData offerData = availableOfferData[0];
            string productId = offerData.Offers[0];

            // Get the Store ID of the add-on that has the matching product ID, and then purchase the add-on.
            List<String> filterList = new List<string>(productKinds);
            StoreProductQueryResult queryResult = await storeContext.GetAssociatedStoreProductsAsync(filterList);
            foreach (KeyValuePair<string, StoreProduct> result in queryResult.Products)
            {
                if (result.Value.InAppOfferToken == productId)
                {
                    await PurchaseOfferAsync(result.Value.StoreId);
                    return;
                }
            }

            System.Diagnostics.Debug.WriteLine("No add-on with the specified product ID could be found " +
                "for the current app.");
            return;
        }

        private async Task<string> GetMicrosoftAccountTokenAsync()
        {
            var msaProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync(
                "https://login.microsoft.com", "consumers");

            WebTokenRequest request = new WebTokenRequest(msaProvider, "devcenter_implicit.basic,wl.basic");
            WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);

            if (result.ResponseStatus == WebTokenRequestStatus.Success)
            {
                return result.ResponseData[0].Token;
            }
            else
            {
                return string.Empty;
            }
        }

        private async Task<List<TargetedOfferData>> GetTargetedOffersForUserAsync(string msaToken)
        {
            if (string.IsNullOrEmpty(msaToken))
            {
                System.Diagnostics.Debug.WriteLine("Microsoft Account token is null or empty.");
                return null;
            }

            HttpClient httpClientGetOffers = new HttpClient();
            httpClientGetOffers.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", msaToken);
            List<TargetedOfferData> availableOfferData = null;

            try
            {
                string getOffersResponse = await httpClientGetOffers.GetStringAsync(new Uri(storeOffersUri));
                availableOfferData = 
                    Newtonsoft.Json.JsonConvert.DeserializeObject<List<TargetedOfferData>>(getOffersResponse);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

            return availableOfferData;
        }

        private async Task PurchaseOfferAsync(string storeId)
        {
            if (string.IsNullOrEmpty(storeId))
            {
                System.Diagnostics.Debug.WriteLine("storeId is null or empty.");
                return;
            }

            // Purchase the add-on for the current user. Typically, a game or app would first show
            // a UI that prompts the user to buy the add-on; for simplicity, this example
            // simply purchases the add-on.
            StorePurchaseResult result = await storeContext.RequestPurchaseAsync(storeId);

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

            switch (result.Status)
            {
                case StorePurchaseStatus.AlreadyPurchased:
                    System.Diagnostics.Debug.WriteLine("The user has already purchased the product.");
                    break;

                case StorePurchaseStatus.Succeeded:
                    System.Diagnostics.Debug.WriteLine("The purchase was successful.");
                    break;

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

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

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

                default:
                    System.Diagnostics.Debug.WriteLine("The purchase was unsuccessful due to an unknown error. " +
                        "ExtendedError: " + extendedError);
                    break;
            }
        }
    }

    public class TargetedOfferData
    {
        [JsonProperty(PropertyName = "offers")]
        public IList<string> Offers { get; } = new List<string>();

        [JsonProperty(PropertyName = "trackingId")]
        public string TrackingId { get; set; }
    }
}