Enable consumable in-app product purchases (HTML)
[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]
Offer consumable in-app products—items that can be purchased, used, and purchased again—through the Store commerce platform to provide your customers with a purchase experience that is both robust and reliable. This is especially useful for things like in-game currency (gold, coins, etc.) that can be purchased and then used to purchase specific power-ups.
What you need to know
Technologies
- Windows Runtime
- Windows.ApplicationModel.Store
Prerequisites
This topic covers the purchase and fulfillment reporting of consumable in-app products. If you are unfamiliar with in-app products, please review Enable in-app product purchases to learn about license information, and how to properly list in-app products in the Store.
When you code and test new in-app products for the first time, you must use the CurrentAppSimulator object instead of the CurrentApp object. This way you can verify your license logic using simulated calls to the license server instead of calling the live server. To do this, you need to customize the file named "WindowsStoreProxy.xml" in %userprofile%\AppData\local\packages\<package name>\LocalState\Microsoft\Windows Store\ApiData. The Microsoft Visual Studio simulator creates this file when you run your app for the first time—or you can also load a custom one at runtime. For more info, see CurrentAppSimulator docs.
This topic also references code examples provided in the Trial app and in-app purchase sample available in the MSDN Code Gallery. This sample is a great way to get hands-on experience with the different monetization options provided for Windows Store apps.
Instructions
Step 1: Making the purchase request
The initial purchase request is made with RequestProductPurchaseAsync like any other purchase made through the Store. The difference for consumable in-app products is that after a successful purchase, a customer cannot purchase the same product again until the app has notified the Store that the previous purchase was successfully fulfilled. It's your app's responsibility to fulfill purchased consumables and notify the Store of the fulfillment.
The following example shows a consumable in-app product purchase request. You'll notice code comments indicating when your app should conduct its local fulfillment of the consumable in-app product for two different scenarios—when the request is successful, and when the request is not successful because of an unfulfilled purchase of that same product.
function purchaseProduct1() {
CurrentAppSimulator.requestProductPurchaseAsync("product1").done(
function (purchaseResults) {
if (purchaseResults.status === ProductPurchaseStatus.succeeded) {
tempTransactionId["product1"] = purchaseResults.transactionId;
// Grant the user their purchase here, and then pass the product ID and transaction ID to currentApp.reportConsumableFulfillment
// To indicate local fulfillment to the Windows Store.
} else if (purchaseResults.status === ProductPurchaseStatus.notFulfilled) {
tempTransactionId["product1"] = purchaseResults.transactionId;
// First check for unfulfilled purchases and grant any unfulfilled purchases from an earlier transaction.
// Once products are fulfilled pass the product ID and transaction ID to currentApp.reportConsumableFulfillment
// To indicate local fulfillment to the Windows Store.
}
}
);
}
Step 2: Tracking local fulfillment of the consumable
When granting your customer access to the consumable in-app product, it's important to keep track of which product is fulfilled (productId), and which transaction that fulfillment is associated with (transactionId).
Important Your app is responsible for the accurately reporting fulfillment to the Store. This step is essential to maintaining a fair and reliable purchase experience for your customers.
The following example demonstrates use of the PurchaseResults properties from the RequestProductPurchaseAsync call in the previous step to identify the purchased product for fulfillment. An array is used to store the product information in a location that can later be referenced to confirm that local fulfillment was successful.
function grantFeatureLocally(productId, transactionId) {
var nextIndex = grantedIds[productId].length;
grantedIds[productId][nextIndex] = transactionId;
// Grant the user the content, such as by increasing some kind of asset count
}
This next example shows how to use the array from the previous example to access product ID/transaction ID pairs that are later used when reporting fulfillment to the Store.
Important Whatever methodology your app uses to track and confirm fulfillment, your app must demonstrate due diligence to ensure that your customers are not charged for items they haven't received.
function isLocallyFulfilled(productId, transactionId) {
for (var i in grantedIds[productId]) {
if (grantedIds[productId][i] === transactionId) {
return true;
}
}
return false;
}
Step 3: Reporting product fulfillment to the Store
Once local fulfillment is completed, your app must make a ReportConsumableFulfillmentAsync call that includes the productId and the transaction the product purchase is included in.
Important Failure to report fulfilled consumable in-app products to the Store will result in the user being unable to purchase that product again until fulfillment for the previous purchase is reported.
var result = FulfillmentResult;
result = CurrentAppSimulator.reportConsumableFulfillmentAsync("product1", tempTransactionId["product1"]);
Step 4: Identifying unfulfilled purchases
Your app can use the GetUnfulfilledConsumablesAsync method to check for unfulfilled consumable in-app products at any time. This method should be called on a regular basis to check for unfulfilled consumables that exist due to unanticipated app events like an interruption in network connectivity or app termination.
The following example demonstrates how GetUnfulfilledConsumablesAsync can be used to enumerate unfulfilled consumables, and how your app can iterate through this list to complete local fulfillment.
CurrentAppSimulator.getUnfulfilledConsumablesAsync().done(
function (unfulfilledList) {
unfulfilledList.forEach(function (product) {
logMessage += "\nProduct Id: " + product.productId + " Transaction Id: " + product.transactionId;
// This is where you would pass the product ID and transaction ID to currentAppSimulator.reportConsumableFulfillment
// To indicate local fulfillment to the Windows Store.
});
});
Related topics
Enable in-app product purchases