Convert existing iOS paid app to freemium model with in-app purchase
Asked Answered
K

7

59

I currently have a paid app in the store. Apple have not allowed a 'lite' version to be submitted as well, so I have no choice but to update the current paid version to a freemium (with in app purchase) model. I have the problem of not loosing functionality for v1 users that have purchased the app the first time round.

Is there any way to determine if an application have been updated from a previously installed version so I can unlock the paid parts of the app?

Two similar questions (from a few months ago):

Transition an existing paid for app to free version with In App Purchase

iPhone + upgrade existing paid application on app store to free application with In App purchase + what about the customers who have already purchased the paid application

Kings answered 17/9, 2010 at 13:18 Comment(5)
possible duplicate of Transition an existing paid for app to free version with In App PurchaseKastroprauxel
As it stands, this is an exact duplicate of the first question. What are you asking that is different? The answers provided there should apply in your case.Kastroprauxel
I appreciate the question is the same, but the answers given were around a year ago. The SDK has moved on a few times since then. That and the suggestions given don't really answer the problem.Kings
How did you solve this problem!?Jessjessa
Can you post, how did you solved this problem?Valentijn
C
34

There is now an Apple-approved way to do this on both iOS and macOS. The originally downloaded version of the app can be obtained from the receipt using the info key Original Purchased Version. You can then decide whether to unlock features if that version predates the switch to IAP.

For instance, once you have retrieved the receipt info:

NSArray *versionsSoldWithoutIAP = @[@"1.0", @"1.1", @"1.2", @"1.3"];
NSString *originalPurchasedVersion = [receiptInfoDict objectForKey:@"Original Purchased Version"];
for (NSString *version in versionsSoldWithoutIAP) {
    if ([version isEqualToString:originalPurchasedVersion]) {
        // user paid for the currently installed version
    }
}

For more info see the WWDC 13 video Using Receipts to Protect Your Digital Sales. At 5:40 the presenter comments: "I think the most exciting thing that's in the receipt this year, especially for you guys if you have a paid app in the store is that we've included information in the receipt that's going to let you do a transition from being a paid app to being a free app with in-app purchases without leaving behind all the customers that have already paid for your app."

Cytoplasm answered 5/9, 2016 at 16:32 Comment(6)
I believe the key name is original_application_version instead of Original Purchased Version.Riposte
for those who have no idea how to obtain receiptInfoDict: it's being returned as the remote validation response: developer.apple.com/library/content/releasenotes/General/…Pinta
amazing, that caused my team such a headache before we start to search for it, thx a lot!Troop
Could someone please show a more complete version of this?Meijer
For me this returns an empty response. What should I do? (hint: I have no IAP in place, I just want to get the orignal purchased version from app store).Heber
How about Android version of this? Is this possible to do the same transition for Google Play users?Japhetic
S
21

With iOS7, iOS app can verify app store receipt, which contains app download date. By using this donwload date, you could determine if a customer is previously purchased or not

Spender answered 18/9, 2013 at 2:51 Comment(4)
To expand upon this, NSBundle has a property - appStoreReceiptURL, which may (but isn't guaranteed to) return a receipt history for the app. Although I don't think it contains a download date for the app itself (according to the Receipt Fields documentation, it's a field on IAP receipts, not the app receipt), you can get the version number that was originally purchased and use this to check. Feel free to correct me, I haven't tested this out yet.Oxalis
This could be the ideal solution if it works. Does anyone know if Android has a similar property?Panoptic
@Oxalis - appStoreReceiptURL returns the receipt associated with the purchase of your app, not the history of IAP... developer.apple.com/library/ios/documentation/Cocoa/Reference/…Alkalimeter
Is this still the way to go? Is the "app download date" just the date that they installed the app? If it's only the download/install date, this would not be effective because what if they deleted it and reinstalled at a later time. Or is it actually the date they purchased the app, which would make more sense?Pulling
N
11

First, I just want to say I personally think the freemium model is great. It has worked out very well for many developers. People love to download free apps, and will do it on a whim, but pay much more attention to an app before spending $0.99 (Which is due to the effect of free - for more info on that, check out Dan Ariely's book Predictably Irrational)

For more info on freemium, google it - There have been tons of articles written about the success of it.


Ok, back to the actual question:

Theres a couple ways you can handle a situtation like this, although the unfortunate matter here is none of them are fool proof.

  • The best solution would probably be for your users to have accounts. Without knowing the specifics of your app, I can't say whether or not user accounts are appropiate for your app. User accounts stored on your server have many additional benefits, including user management, and tracking what purchases a user has made. This will allow users who delete the app, and then re-install it, or get a new device, to maintain their purchased content. Furher, whenever you use in-app purchase, you should validate the purchase on your own server (or with Apple), which a server based user manegment system can all do. If your totally in over your head with creating your own user management server, check out Parse. Its dead simple to create an amazing backend server (for basically free)
  • iCloud Key/Value type of system. I'm not very familiar with how this would work - so I'll move on.
  • Another, not nearly as fool proof solution (but much quicker/easier to implement) is to use NSUserDefaults. You can store an object when the user makes a purchase, or with the date a user installs your app. Then if you issue an update converting your app to freemium. Then in the new update, check which purchases the user has made or the date they installed it, and react accordingly. For info on how to do that with NSUserDefaults, check out my answer to another question on implementing that: NSUserDefaults and app versions. But this solution does present the following pitfalls:

  • If the user deletes your app, the NSUserDefaults are lost forever

  • If the user didn't install the update setting up the NSUserDefault system, but then installed the update with the new freemium model, the app would treat them as if they hadn't purchased the content.

In summery, this is a difficult question, with not a lot of easy/perfect options.

Anyway,

Hope that helped!

Naamann answered 26/2, 2011 at 15:55 Comment(8)
This solution doesn't protect you from the fact that an original paying user may delete and then re-install the app. In such case the user defaults will be deleted and then after re-install of the freemium version he/she will lose the extra features. So this solution, which partially works, can be improved by saving the UDID of current customers in a server, thus giving an extra protection. Of course you are still not protected from users deleting the app and doing a complete reset of their iPhone. But it's a minor case.Glorianna
True, but those seem like extreme cases. However, if you would rather be better protected, more power to you. let me know in this thread how it work outNaamann
Better store it in the iCloud Key/Value Store if possible, instead of user defaults since they get deleted when a user removes the app!Insnare
When this question was active, iCloud wasn't publicly availableNaamann
This could run afoul of the iOS developer terms and conditions which state "3.3.3 Without Apples prior written approval or as permitted under Section 3.3.23 (In App Purchase API), an Application may not provide, unlock or enable additional features or functionality through distribution mechanisms other than the App Store or VPP/B2B Program Site." Anyone know if Apple cares about this scenario? I had an app denied that featured external unlocking (before I knew this), but this case seems like it might be acceptable.Samson
I've never had a problem with this. Although, rereading this answer, I'm realizing there are better solutions nowadays. But this post is almost two years old.Naamann
Good to know, thanks. I'd love to hear about some better solutions... I've been glued to Google for hours looking for ways to get around this.Samson
Thanks Andrew! Looks like it's down to these options: NSUserDefaults, iCloud key/value, and homegrown user accounts.Samson
S
2

I'm dealing with the same thing and came up with the following idea: Create the freemium version under a new name and app ID. Keep the existing paid app in the app store, but raise the price to something absurd and clearly state in the description that the app is there to maintain support for existing users and that new users should try the freemium version instead.

Existing paid users won't lose support for their existing app and can delete and install any time it without re-purchasing.

You won't have to keep updating the old paid app, either. Just keep it in the app store.

The downside is that existing paid users will not be able to migrate smoothly to the freemium version to get any extra features you add in the future without re-paying for what they already have.

Still trying to decide if this will work for me but it could be a good option for others. Comments appreciated.

Samson answered 20/12, 2012 at 21:35 Comment(4)
Did you try this? What did users think? Have the same problem. Trying to figure out how to proceed.Kaliningrad
I did not try it, sorry. I ended up updating the paid app to set a flag in people's iCloud accounts stating that they have the paid app. Then I used a force-upgrade flag I had in the app that disables the app. Customers could no longer use the app and were forced to upgrade to the new version that sets the flag. It caused a little confusion but I made sure the wording was smooth so people knew what was happening. That's as far as I've gotten with it.Samson
This wouldn't work with current review guidlines. Apple doesn't allows two versions of the same app anymore.Ordinance
Absurdly high prices will not work: Apple Review Guidelines, section "3. Business": "...we won’t distribute apps and in-app purchase items that are clear rip-offs.".Anabolism
S
2

I've been thinking about this problem for some time now. I have a substantial amount of customers that paid for my (in App Store terms) high-price niche-App and I'd hate having to tell them to re-purchase as I plan to migrate to an In-App Purchase model.

The idea I came up with (and I'll ask Apple support whether it's legal) is to phase out the current paid App but ship a last update for it that allows "unlocking" the In-App purchases of the new App based on the In-App model. I was thinking about a challenge response scheme:

  • User has installed paid App on his device
  • User installs new In-App App and opens it. The new App detects the paid version and offers to unlock the In-App purchases (on this device only of course and as long as the App isn't deleted)
  • The new App generates a nonce, signs it and calls the old App with it via an URL Scheme
  • The old App decrypts the nonce, adds +1 one to it and signs it again. Calls back to the new App via URL scheme
  • The new App validates the nonce and unlocks the features

The scheme can be easily implemented using a pre-shared key. It's of course a weakness on jail-broken devices, but then every App storing In-App receipts has those problems.

Schutz answered 6/6, 2013 at 6:13 Comment(3)
Note that this may be against section 11.1 of the review guidelines: developer.apple.com/appstore/resources/approval/… I did contact the App Review team to what they recommend to upgrade the App.Schutz
Please share what they answered!Jessjessa
I was sent a link to a couple of resoucres with a comment saying 'We recognize this doesn't completely address your request but we hope you find these resources helpful.' I ended up not trying to sneak this approach through the App Review process. Turns out you can't do promotion codes for In App purchases, so I ended up maintaining a "Pro" and "lite+IAP" version....Schutz
W
2

You can check the 'original_application_version' of the receipt. All iOS downloaded from the appStore have a receipt even if it is a free app.

TPInAppReceipt is a simple swift library that can help you with this.

import TPInAppReceipt

do {
  /// Initialize receipt
  let receipt = try InAppReceipt.localReceipt() 

  let originalAppVersion = receipt.originalAppVersion
  let buildSoldWithoutIAP = 22

  let originalAppVersionInt = Int(originalAppVersion) ?? 23
  if originalAppVersionInt <= buildSoldWithoutIAP {
                
      // unlock all features
      UserDefaults.standard.set(true, forKey: "isPaid")
  }

} catch {
  print(error)
}

Note: The receipt.originalAppVersion returned is the build number as at the time the user first purchased the app from the appSore. Also, the receipt won't be available in the sandbox environment until you purchase or restore an inAppPurchase first.

Wernsman answered 18/10, 2022 at 11:32 Comment(0)
P
1

With iOS 16 and macOS 13.0 you can do this directly with StoreKit:

import StoreKit

do {
     // Get the appTransaction.
      let shared = try await AppTransaction.shared
        if case .verified(let appTransaction) = shared {
            // Hard-code the major version number in which the app's business model changed.
            #if os(macOS)
            let newBusinessModelVersion = "2" // CFBundleShortVersionString
            #else
            let newBusinessModelVersion = "42" // CFBundleVersion
            #endif
            
            // Get the major version number of the version the customer originally purchased.
            #if os(macOS)
            let versionComponents = appTransaction.originalAppVersion.split(separator: ".")
            let originalVersion = versionComponents[0]
            #else
            let originalVersion = appTransaction.originalAppVersion
            #endif

            NSLog("original purchased version: \(originalVersion)")

            if originalVersion < newBusinessModelVersion {
                // This customer purchased the app before the business model changed.
                // Deliver content that they're entitled to based on their app purchase.
            }
            else {
                // This customer purchased the app after the business model changed.
            }
        }
    }
catch {
    // Handle errors.
}

https://developer.apple.com/documentation/storekit/apptransaction/3954447-originalappversion

Phraseograph answered 16/1 at 19:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.