Windows 8 apps: how to move from Freemium to Free + In-App Purchases

Lately, my colleague Wouter van Ranst had an interesting question on how to switch from a freemium app to a free app with optional in-app purchases. His app (Angry Verbs - https://bit.ly/1hUv6jB) is currently in the store as a free time –unlimited trial. Some features are limited until when the users buys the app. In an upcoming version he wants to transition to a free version (no trial) but with the features separately sold as in-app purchases.

Of course, an interesting questions then comes into your mind: what about users that have previously bought the app? (They do not need to buy individual components in the future version…)

Current Situation: app has unlimited time trial with limited functionality, can be purchased for 2,99 EUR

Not purchased

LicenseInformation.IsTrial is true

Purchased

LicenseInformation.IsTrial is false

Future situation: free app, no option to buy, only in app purchases

Was purchased in the above situation

LicenseInformation.IsTrial is false

Was not purchased in the above situation ( = was downloaded as trial in above)

LicenseInformation.IsTrial is false

New free download

LicenseInformation.IsTrial is false

For anyone who is still on a trial license—meaning downloaded your app as a trial before switching to the freemium (free app + paid IAP) model, we can’t automatically change the license state.  The problem is that in the future free app, the IsTrial will allways return false so you can’t rely on this data alone in order to determine which features should be enabled. In order to resolve this, you need to find out the PurchaseDate from the receipt and compare it against the date your new app version became available. You can grab the receipt, and if the license was full and the purchase date was before it went to freemium, it was a customer who bought the full app; if the license was full and the purchaseDate was after it was freemium, the customer did not pay for the app.

Here’s is some supporting code that can help you determine whether the app has been bought before:

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using System.Threading.Tasks;
  6: using Windows.ApplicationModel.Store;
  7: using Windows.Data.Xml.Dom;
  8:  
  9: namespace License
  10: {
  11:     public class MyLicense 
  12:     {
  13:         private LicenseInformation _li = null;
  14:  
  15:         public MyLicense()
  16:         {
  17: #if DEBUG
  18:             _li = CurrentAppSimulator.LicenseInformation;
  19: #else
  20:             _li = CurrentApp.LicenseInformation;
  21: #endif
  22:         }
  23:  
  24:         public async Task<bool> HasBeenBoughtPreviously()
  25:         {
  26:             string receipt;
  27:  
  28:             //Get the receipt
  29: #if DEBUG
  30:             receipt = await CurrentAppSimulator.GetAppReceiptAsync();
  31: #else
  32:             receipt = await CurrentApp.GetAppReceiptAsync();
  33: #endif
  34:  
  35:             //Parse the receipt
  36:             var xmlRcpt = new XmlDocument();
  37:             xmlRcpt.LoadXml(receipt);
  38:             XmlNodeList xmlPurchaseDateNodes = xmlRcpt.SelectNodes("/Receipt/AppReceipt[@LicenseType='Full']/@PurchaseDate");
  39:  
  40:             if (xmlPurchaseDateNodes.Count == 0)
  41:                 return false;
  42:  
  43:             var purchaseDate = DateTime.Parse(xmlPurchaseDateNodes.First().NodeValue.ToString());
  44:  
  45:             if (purchaseDate < new DateTime(2014, 4, 1))
  46:                 return true;    //The app has been purchased before apr/1st > this is the full license
  47:  
  48:             return false;
  49:         }
  50:  
  51:         public async Task<bool> HasBoughtSomething()
  52:         {
  53:             //Did this user buy the previous full version?
  54:             if (await HasBeenBoughtPreviously())
  55:                 return true;
  56:  
  57:             // Did this user buy at least one feature as an in-app purchase?
  58:             if (_li.ProductLicenses.Any(pl => pl.Value.ProductId.StartsWith("Feature_") && pl.Value.IsActive))
  59:                 return true;
  60:  
  61:             return false;
  62:         }
  63:  
  64:         public async Task<bool> IsTrial()
  65:         {
  66:             return _li.IsTrial;
  67:         }
  68:  
  69:         public async Task<bool> HasBoughtFeature(string featureName)
  70:         {
  71:             if (await HasBeenBoughtPreviously())
  72:                 return true;
  73:  
  74:             var pid = string.Format("Feature_{0}", featureName);
  75:             if (_li.ProductLicenses[pid].IsActive)
  76:                 return true;
  77:  
  78:             return false;
  79:         }
  80:  
  81:         public async Task<bool> BuyFeature(string featureName)
  82:         {
  83:             string path = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
  84:  
  85:             var pid = string.Format("Feature_{0}", featureName);
  86:  
  87:             try
  88:             {
  89: #if DEBUG
  90:                 var r = await CurrentAppSimulator.RequestProductPurchaseAsync(pid);
  91: #else
  92:                 var r = await CurrentApp.RequestProductPurchaseAsync(pid); 
  93: #endif
  94:             }
  95:             catch (Exception) { }
  96:  
  97:             if (_li.ProductLicenses[pid].IsActive)
  98:                 return true;
  99:  
  100:             return false;
  101:         }
  102:     }
  103: }