Skip to content

Instantly share code, notes, and snippets.

@Aetopia
Last active November 3, 2024 11:47
Show Gist options
  • Save Aetopia/a3ac6471b314e9461fc24d9cc8f9d46d to your computer and use it in GitHub Desktop.
Save Aetopia/a3ac6471b314e9461fc24d9cc8f9d46d to your computer and use it in GitHub Desktop.
How to verify if a valid license exists for a given Microsoft Store app on a given device.

This code sample shows one can verify if a license exists for a given Microsoft Store app.

public static async Task<ReadOnlyDictionary<string, bool>> VerifyAsync(params string[] productIds)
{
    using StringContent content = new($"{{\"IdType\":\"ProductId\",\"ProductIds\":[{string.Join(",", productIds.Select(_ => $"\"{_}\""))}]}}", Encoding.UTF8, "application/json");
    productIds = (await Client.ParseAsync(RequestUris.Item1, content).ConfigureAwait(false)).Descendants("ProductId").Select(_ => _.Value).ToArray();

    using var stream = await Client.GetStreamAsync(string.Format(RequestUris.Item2, string.Join(",", productIds))).ConfigureAwait(false); var source = stream.Parse();
    var result = await LicenseManager.GetSatisfactionInfosAsync(source.Ids("ContentIds"), source.Ids("KeyIds")).AsTask().ConfigureAwait(false);

    if (result.ExtendedError is not null) throw result.ExtendedError;
    return new(productIds.Zip(result.LicenseSatisfactionInfos.Select(_ => _.Value.IsSatisfied), (Key, Value) => new { Key, Value }).ToDictionary(_ => _.Key, _ => _.Value));
}
  • First resolve & validate specified product IDs.

  • Get content & key IDs for given product IDs.

  • Check if given licenses exist on the system & return the result.

using System;
using System.IO;
using System.Xml;
using System.Linq;
using System.Text;
using System.Net.Http;
using System.Xml.Linq;
using Windows.Globalization;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.Serialization.Json;
using Windows.ApplicationModel.Store.LicenseManagement;
public static class Store
{
static readonly HttpClient Client = new();
static readonly (string, string) RequestUris;
static Store()
{
var market = new GeographicRegion().CodeTwoLetter;
RequestUris = new(
$"https://storeedgefd.dsx.mp.microsoft.com/v9.0/products?market={market}&locale=iv&deviceFamily=Windows.Desktop",
$"https://storesdk.dsx.mp.microsoft.com/v8.0/Sdk/products/contentId?market={market}&locale=iv&deviceFamily=Windows.Desktop&productIds={{0}}"
);
}
public static async Task<ReadOnlyDictionary<string, bool>> VerifyAsync(params string[] productIds)
{
using StringContent content = new($"{{\"IdType\":\"ProductId\",\"ProductIds\":[{string.Join(",", productIds.Select(_ => $"\"{_}\""))}]}}", Encoding.UTF8, "application/json");
productIds = (await Client.ParseAsync(RequestUris.Item1, content).ConfigureAwait(false)).Descendants("ProductId").Select(_ => _.Value).ToArray();
using var stream = await Client.GetStreamAsync(string.Format(RequestUris.Item2, string.Join(",", productIds))).ConfigureAwait(false); var source = stream.Parse();
var result = await LicenseManager.GetSatisfactionInfosAsync(source.Ids("ContentIds"), source.Ids("KeyIds")).AsTask().ConfigureAwait(false);
if (result.ExtendedError is not null) throw result.ExtendedError;
return new(productIds.Zip(result.LicenseSatisfactionInfos.Select(_ => _.Value.IsSatisfied), (Key, Value) => new { Key, Value }).ToDictionary(_ => _.Key, _ => _.Value));
}
static IEnumerable<string> Ids(this XElement source, string name) => source.Descendants(name).SelectMany(_ => _.Elements().Select(_ => _.Value));
static async Task<XElement> ParseAsync(this HttpClient source, string requestUri, HttpContent content)
{
using var message = await source.PostAsync(requestUri, content).ConfigureAwait(false); message.EnsureSuccessStatusCode();
using var stream = await message.Content.ReadAsStreamAsync().ConfigureAwait(false);
return stream.Parse();
}
static XElement Parse(this Stream source)
{
using var reader = JsonReaderWriterFactory.CreateJsonReader(source, XmlDictionaryReaderQuotas.Max);
return XElement.Load(reader);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment