App developers are increasingly turning to subscriptions to build a sustainable business model. This is understandable; the economics of a one-time-purchase business model favor short-lived apps and games, but building a high-quality app requires ongoing revenue. However, many consumers are feeling “subscription fatigue”. Each subscription may seem reasonable on its own, but subscriptions add up quickly, and nobody wants to be spending $100/month on app subscriptions.
For years, developers have been asking Apple to support paid upgrades in their app stores. That hasn’t happened yet, but I want to make the argument that Apple doesn’t really need to do anything; developers can already implement paid upgrades today in the App Store, with just a little bit of extra work. In fact, I’ll argue that we can do it better now than what we’d get if Apple added an explicit “paid update” option that required users to pay before updating their app.
- Full access to all features when purchasing
- No separate in-app purchases required upon initial purchase
- Users can choose to upgrade to new versions on their own schedule
- Users can choose to skip an upgrade
- Developers can provide a discounted upgrade price to existing owners of the app
Paid upgrades aren’t right for every app; in some cases a one-time purchase is the right solution, and in others a subscription may be make more sense. But having paid upgrades as an option helps everyone.
So how are we going to design this? Instead of having separate apps in the App Store, we’re going to implement this all within a single app. However, various features of the app may be unavailable to some users.
When we release version 1.0 of our app, everyone who purchases the app will have full access to all of its features. We’ll call that feature set A. They paid for those features, after all. Hooray!
Version 2.0 brings with it new features, feature set B. We’re implementing paid upgrades here, so a non-consumable in-app purchase is available for our existing users to get access to the new set of features. However, new users who purchase the app at this point are buying the app with the 2.0 features included, so we immediately unlock all features, both A and B, for new users.
Version 3.0 adds additional features. Again, anyone who purchases the app for the first time will have all features available, A, B, and C. They bought the app as it stands right now, and those are the features they get. Users with earlier feature packs can choose to upgrade to the 3.0 feature pack via in-app purchase, which includes all features from all versions.
This is a critical point: Users do not have to purchase every update along the way. If a user bought version 1 and doesn’t feel a need for the additional features in version 2, they don’t have to upgrade. Later, when they update to version 3.0, they decide they want feature set C, so they buy the in-app upgrade. When they do, they get B as well, so they have A, B, and C. Users are not buying feature sets individually; they’re simply choosing when to upgrade to the latest version. It is not possible for a user to have A and C, but not B. There’s a linear progression to the features.
Providing a linear feature history makes things simpler for developers as well. Instead of testing 2n combinations of feature sets, there are just N configurations to test; one for each feature release.
Note that I’ve phrased everything in terms of additional features. Bug fixes would be released as part of the app and would generally be released to all users, whether or not they’ve upgraded to the current feature set. It would be silly for a developer to fix a bug but lock the fix behind an in-app purchase. So everyone gets bug fixes, and even your users that still just have feature set A still feel like they’re getting support from the developer. This actually works better than requiring users to pay for an app update; developers can roll out bug fixes to users on old “feature versions” of the app without having to build separate hotfix builds for each release.
The key feature of this scheme is that users who purchase the app new immediately have access to all features. This enables users to charge a fair price up front.
How do we implement this?
The app store receipt included with every app purchase includes the
original_purchase_date fields. These fields can be used to determine what the base feature set should be for a given user. If the
original_application_version is 2.0, for example, then we know that all 2.0 features should be available for this user, but the 3.0+ features will require an in-app purchase.
The app store receipt also includes information about in-app purchases. Simply scan the receipt for the highest level of in-app purchase and you’ll know what features to unlock. Everything we need is already available in the app store receipt!
We can adjust the pricing of the in-app purchase products independently of the app store price, which allows us to provide upgrade pricing to our existing users.
Will this work for my app?
This systems works well for any app that primarily adds value by adding new features. Productivity apps and professional apps are a particularly good fit. It’s probably not a good fit for most games.
Any caveats I should know about?
Unfortunately, yes: Family Sharing throws things off a bit. If I purchase an app at version 1.0 and my wife downloads it for the first time (via Family Sharing) at version 2.0, what feature level should she expect? You could make a reasonable argument for both 1.0 and 2.0, and Apple hasn’t documented which one we should expect. It doesn’t matter, though, because the actually answer is “neither”. In my testing, Family Sharing receipts had a purchase date corresponding to the most recent release (3.0) of the app, even if the family member downloaded the app years ago. I’ve filed Radar #33076385 regarding this issue, but I’m not aware of any workaround right now.
I know that’s a heavy caveat, but it’s one that Apple can (and should fix). If this kind of business model interests you, I’d encourage you to file your own bugs with Apple requesting a fix.
And if you do try this out, please let me know! You can find me @bjhomer on Twitter.