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: }
Comments
- Anonymous
October 09, 2015
Exactly! I'm facing the same problem and I was thinking about the same approach. Looks like I was right about that since you have ended up with this approach too. Thanks anyway.