This document will illustrate sample code involved with each operation.
This is derived from the InGameStore sample which is continuously updated to reflect best practices.
Preparing to call XStore APIs
All XStore APIs operate over an XStoreContextHandle created using XStoreCreateContext.
This context allows you to perform store operations in the context of the specified user on console and the default user available on PCs.
On consoles, the context is invalided after a Suspend or Quick Resume event.
To safely handle these conditions we recommend closing a XStoreContextHandle and recreating it whenever the game resumes from a suspended state.
1. Determining what can be purchased
What a game usually offers for purchase is their add-ons.
The following code demonstrates the basic XStoreQueryAssociatedProductsAsync API call needed for the game to know what products are available.
Only purchasable add-ons associated with the game are automatically returned in this query.
Unrelated products associated with the same publisher (i.e. configured with the same Partner Center account) can also be made to return in this call so long as the game is set with a "can sell" relationship to this product in the Product relationship setup section in Partner Center.
Only purchasable products are returned with XStoreQueryAssociatedProductsAsync; products that are only granted in bundles or otherwise not set to be independently purchasable will not return.
For these use XStoreQueryProductsAsync described below.
There is no upfront knowledge of the number of products that will be returned, therefore the count must be accumulated.
Paging
Handling paging is now optional.
Select a maximum value to pass that is guaranteed to be larger than the lifetime expected size of your catalog.
For large catalogs or desiring to segment the result handling to show progress, detecting and handling paging can be implemented.
See XStoreQueryAssociatedProductsAsync for more details.
Other options
XStoreQueryProductsAsync can be used to query specific products, if the store IDs are known or if other actionFilters are desired.
"Actions" are usage scenarios that apply to a product, which include verbs like Purchase, License, Gift, and Redeem.
XStoreQueryAssociatedProductsForStoreIdAsync can be used to query associated products for other games.
This can be useful, for example, to cross sell a different title's add-ons.
XStoreShowAssociatedProductsUIAsync will transition the user to the Microsoft Store app to a view of associated products, which can be filtered by product kind.
This can be an alternative to having to enumerate available products to present in an in-game interface.
2. Evaluating what products are owned
This will involve essentially the same code as the above with these replacements:
With these API, the results will comprise the products that are specifically entitled by the calling user's account.
Entitled can mean directly owned, but also can mean satisfied by means of owning a parent bundle or subscription.
Note that this can also be determined by the results of XStoreQueryAssociatedProductsAsync (and related functions) as XStoreProduct contains an isInUserCollection field that is set to true when it is entitled.
Consumable ownership
Consumable quantity is noted in XStoreProduct.skus[i].collectionData.quantity.
Typically there is only one SKU for a consumable product.
Quantity can also be inquired independently by using XStoreQueryConsumableBalanceRemainingAsync, but this is discouraged for a large number of consumables individually as each will incur a service call.
Also, to maintain integrity of consumable-based ecosystems, it's highly recommended to utiltize service validation and redemption of consumables.
For more information, see Consumable-based ecosystems.
Durable ownership
It is not enough to check if the account owns the product to determine that they should be entitled to use the product in-game.
Durables must adhere to the content sharing policy that is described in Product sharing model for games.
XStoreAcquireLicenseForPackageAsync is used for durables with a package to determine if it is licensable according to the provisions of content sharing.
Showing the purchase flow for a purchasable product is as simple as passing in the Store ID into the XStoreShowPurchaseUIAsync API:
C++
voidMakePurchase(constchar* storeId){
auto async = new XAsyncBlock{};
async->context = &storeId;
async->queue = m_asyncQueue;
async->callback = [](XAsyncBlock *async)
{
constchar* = reinterpret_cast<constchar*>(async->context);
HRESULT hr = XStoreShowPurchaseUIResult(async);
if (SUCCEEDED(hr))
{
printf("Purchase succeeded (%s)\n", storeId);
// Refresh ownership and update game
}
else
{
printf("Purchase failed (%s) 0x%x\n", storeId, hr);
if (hr == E_GAMESTORE_ALREADY_PURCHASED)
{
printf("Already own this\n");
}
}
delete async;
};
HRESULT hr = XStoreShowPurchaseUIAsync(
m_xStoreContext,
storeId,
nullptr, // Can be used to override the title bar textnullptr, // Can be used to provide extra details to purchase
async);
if (FAILED(hr))
{
delete async;
printf("Error calling XStoreShowPurchaseUIAsync : 0x%x\n", hr);
return;
}
}
In addition to handling the results of a potential purchase in the async callback, purchases can also be made outside of the game by explicitly switching to the Microsoft Store.
They can also be made on Xbox.com, PC, mobile apps, or other outlets.
It is recommended for there to be a place in the game that reliably refreshes product ownership on demand, such as a transition to the in-game store or somewhere in the settings, and not just solely as part of the initial sign-in flow.