locked
Strategies for large numbers of possible in-app purchases

    General discussion

  • Hi,

    In my Windows 8 application, I want to enable in-app purchases. It is not the usual remove-the-adverts IAP so I need some advice.

    In my application, you will be able to download data for a specific country / region / state. Any country in the world would be downloadable separately, so that makes more than 500 different products.

    In the MS article "How to manage a large catalog of in-app products", I can read that there is a limit of 200 IAP products per dev account so I can't use that option. The "catalog" option is not very clear, from what I understand, you can have a few different product just to have different price ranges ("offer") and when the user want to buy an actual product, you pick the corresponding offer and just display a custom label that will say "buy xxx fo (price-of-the-offer)$".

    So in my case, that could make sense, I could have a "tiny country" offer for 1$, "small country" offer for 5$, "big country" offer for 10$ and so on.

    I am not sure I understand the "fulfillment" option very well. Is it just about informing the store that the user have successfully purchased the "product"? Will I be able to check if the user have bought product using:

    var licenseInformation = currentApp.licenseInformation;
    if (licenseInformation.productLicenses.lookup("product3").isActive) {
    ...

    I tried using this code, mostly taken from the MS samples:

    currentApp.requestProductPurchaseAsync("product3", offerId, displayProperties).done(
                function (purchaseResults) {
                    if (purchaseResults.status === ProductPurchaseStatus.succeeded) {
                        grantFeatureLocally("product3", purchaseResults.transactionId);
                        fulfillProduct1("product3", purchaseResults);
                    } else if (purchaseResults.status === ProductPurchaseStatus.notFulfilled) {
                        if (!isLocallyFulfilled("product3", purchaseResults.transactionId)) {
                            grantFeatureLocally("product3", purchaseResults.transactionId);
                        }
                        fulfillProduct1("product3", purchaseResults);
                    } else if (purchaseResults.status === ProductPurchaseStatus.notPurchased) {
                        log("Product 3 was not purchased.", "sample", "status");
                    }
                    var licenseInformation = currentApp.licenseInformation;
                    if (licenseInformation.productLicenses.lookup("product3").isActive) {
                        WinJS.log && WinJS.log("You can use Product 3.", "sample", "status");
                    } else {
                        WinJS.log && WinJS.log("You don't own Product 3 You must buy Product 3 before you can use it.", "sample", "error");
                    }
                },
                function () {
                    log("Unable to buy product 1.", "sample", "error");
                });

    The "purchaseResults" status is "ProductPurchaseStatus.succeeded" and then the sample calls "grantFeatureLocally" and "fulfillProduct1". First method just adds the transaction ID to a JavaScript array used as some kind of memory and the other method calls :

    currentApp.reportConsumableFulfillmentAsync(productId, purchaseResults.transactionId).done(
                function (result) {
                    switch (result) {
                        case FulfillmentResult.succeeded:
                            if (purchaseResults.status === ProductPurchaseStatus.notFulfilled) {
                                log("You already purchased " + product1ListingName + offerIdMsg + " and it was just fulfilled.", "sample", "status");
                            } else {
                                log("You bought and fulfilled " + displayPropertiesName + offerIdMsg, "sample", "status");
                            }
                            break;

    Which would be an async call to MS to confirm the purchase from what I can read.

    But when I call 

    licenseInformation.productLicenses.lookup("product3").isActive

    even after this async call, it returns a false value, which mean that I can't easily determine if the product has been bought or not. Maybe it is the MS sample and the CurrentAppSimulator that does not behave like it should behave when in production?

    So I am looking for advices on strategies for large numbers of in-app purchases. I want to be able to have 500+ different downloadable products and be able to easily check via Microsoft APIs if the product (referenced by a unique ID) was bought by the user or not. I would not mind having a limited number of price ranges.

    Thanks a lot,

    Fabien

    Sunday, September 28, 2014 11:50 PM

All replies

  • The large catalog system is part of the consumable in-app purchase feature and is new to Windows 8.1. It is not available to Windows 8 apps.

    The app fulfills the purchase to let the system know that the purchase is complete. After that the app is in charge of keeping track of the purchased item. The store no longer does so explicitly, although you may be able to reconstitute the purchase from receipts.

    True consumables are easier to understand:

    The user purchases a product "100 gold pieces". Until purchase is still in progress until the app fulfills it. The user cannot purchase another 100 gold pieces. The app fulfills the product, adds 100 to the user's goldPieces total, and the purchase is done. The user can now purchase another 100 gold pieces.

    A large catalog will be similar:

    The user purchases a product "big country" (with the large catalog comment "Canada"). This purchase is still in progress until the app fulfills it. The user cannot purchase another "big country". The app fulfills the product and stores "Canada" in its database. The user can now purchase another big country.

    In both cases, the app keeps track of the purchase once it is fulfilled.

    --Rob

    Monday, September 29, 2014 12:11 AM
    Owner
  • Hello Rob and thanks for the detailed answer,

    So I need to keep record of every purchase made by the user. I also need to make this information "roam" over all his device so that he can re-download the country file if he bought it on another device.

    Are roamingSettings a safe place to do that or you would advice another strategy? Maybe a roaming File in the roaming folder because the information could be quite large but in that case, I could need to store some large values. But then, I would need to add encryption somehow so that the user can't edit the file on his computer?

    Also, what happens to roamingSettings if the user uninstall the app, are they wiped out? I liked the classic IAP process because any purchase would be linked to the user's account and I wouldn't need to take care of that kind of sync but I guess it will be harder that this in my case :)

    Is the receipt query making a request to MS servers or does it work while offline? Because I would need to check if the user owns the product while offline.

    Thanks again,

    Fabien

    Monday, September 29, 2014 8:00 AM
  • Where is best depends on the scenario.

    Roaming settings or folder may be a good place for this, but there is a size limit so if you need to get very big you might want to take it to your own server. This would also let you persist the purchase as long as you'd like. You could obfuscate a roamed save file via encryption, but a determined enough user could read it if it's kept locally.

    Roaming settings will persist in the cloud for a while after the last time they are touched, even if the app is uninstalled, but not forever. I think it's currently about a month, but I don't believe that is contractual. If the user uninstalls and the roamed settings expire then the app could reconstitute them from receipts.

    See the Guidelines for Roaming app data for more details.

    --Rob

    Wednesday, October 01, 2014 12:17 AM
    Owner
  • Thanks a lot,

    It seems like currentApp.getProductReceiptAsync("product3") is not implemented when using the simulator, so I can't try retreiving receipts for my testing. I get a JavaScript error "Not Implemented".

    Would it work when in production?

    I might need to simplify my pricing to get back into the classic IAP process, could save me some headaches

    Fabien

    Wednesday, October 01, 2014 5:10 PM
  • Yes. Receipts are implemented in the production store, but not in the simulator.
    Friday, October 03, 2014 5:19 PM
    Owner
  • Thanks a lot for taking the time to answer accurately to all my questions Rob :)
    Saturday, October 04, 2014 8:42 AM