The App Store Small Business Program lowers the commission rate that Apple Developer Program (ADP) members need to pay Apple for sales on apps and in-app purchases. However the program would be available only to members earning less than one million dollars (US) per year and do not participate in app transfers. Doing any of those two would automatically expel the member from the program.
“But I recently incorporated a company for my app’s business because a partner is joining,” you might thought, “I’ll be transferring the app from my individual developer account into our company-owned one. It would still be a small business earning well under a million.” Indeed, when you are collaborating with someone else on an app, it is a good idea to form a separate legal entity to house the collaboration, profits, and expenses. Having an entity would also help when you need to scale up, hire employees, and have a more orderly teardown when you and your partners need to go on separate ways.
Wouldn’t it be great if you can transfer apps from your individual ADP into your company’s ADP yet retain the small business program benefits? How about having the option to sell your app as a going concern without getting kicked out of the program?
Yes, there is a way to “transfer” applications without needing to engage in the app transfer process. Read on to find out more.
App Transfer without App Transfer
In short, the “transfer” is done as part of a major version upgrade. The receiving entity would publish the new major version of the app while the old app version would be taken off sale by the originating entity. It follows that its customers would need to opt in to this upgrade. The upgraded app would offer an upgrade discount to entice customers of the old version to upgrade. Being a major version upgrade, this would be a good opportunity to ask more money from existing customers. However nothing prevents the new app from offering the upgrade for free (i.e. zero-cost for the customer to migrate to the new version).
This migration is done through in-app purchases (iAP). The new app would be offered as a free download but with its core features enabled only through iAP(s) — whether it’s a perpetual light version with purchases to enable the full feature or free time-limited trial of full features. New customers would need to purchase these enabling iAPs at their respective standard prices. However customers of the old app would have the option to purchase an alternative set of iAPs which enables the same set of features but at a lower cost (or even no cost). Of course, the new app would only offer these alternative iAPs when it has confidently conclude that the user is a customer of the old app. Inversely, users who could not demonstrate in good faith that s/he has purchased the old app could not access these alternative iAPs.
The new app would validate the App Store receipt of the old app to verify whether the user has purchased the latter. Upon successful validation, which may include ensuring that the user had purchased pre-requisite iAPs of the old app, the new app would show these “upgrade” iAP products and make them available for purchase. Consequentially if the new app could not validate the old app’s purchase or otherwise the presence of key iAPs in the old app, it would hide these alternative iAP products.
How the new app gets hold of the old app’s App Store receipt depends on the apps’ platform and whether the apps already have a server-side component needed as part of its core functionality.
- On macOS: Scan the bundle directory of the old app.
- On iOS, iPadOS, and tvOS: The old app would pass the receipt to the new app.
- On watchOS: Delegate to the iOS app or use an Internet-based backend.
- Server available: The old app would use the backend to pass the receipt to the new app.
The new app would validate the old app’s receipt in a similar way that it validates its own receipt file. The major differences would be the receipt’s app bundle identifier as well as the iAP product identifiers — as those would belong to the old app.
macOS Receipt Validation
On the Mac, the new app can simply prompt the user for the location of the old app, using a standard file open dialog. It would then create an
NSBundle instance pointing to the file bundle of the old app, verify its bundle identifier that it is indeed the old app, and then get hold of the receipt file through the
Using a file open dialog to locate the old app’s bundle is necessary since Mac App Store apps are mandatorily sandboxed. In other words, those apps they won’t be able to access the directory bundle of other apps (much less their receipts) without the user’s consent. Another option would be to provide a drop target for the user to drag-and-drop the old app’s bundle into.
That said, if the new app is not sandboxed (i.e. distributed outside the Mac App Store), it can use
URLForApplicationWithBundleIdentifier to find the old app bundle’s location and read its receipt. This independently-distributed app can then enable the appropriate upgrade flow as per its licensing scheme.
iOS, iPadOS and tvOS Receipt Passing
The strict sandboxing of iOS, iPadOS, and tvOS applications does not allow the new app to simply read the receipt of the old one; the latter would need to pass it to the former. The old app would call
appStoreReceiptURL from its own main bundle then provide its contents to the new app. Which in turn can be done in two ways:
- Option A: App-to-App communication.
- Option B: Common storage location.
In Option A, the new app would invoke the old app and ask for the latter’s receipt data through a Callback URL. In turn, the old app would read its receipt file and return it to the new app, again through custom URL scheme invocation with the receipt content encoded in a query parameter. This would be visible to the user as the new app bounces into the old app and then back again to the new app.
In Option B, the old app would save a copy of its receipt file into a common location as selected by the user. Afterwards the new app would ask the user where the receipt file, then read and validate it. In both instances, the two apps would prompt the user of the common folder containing the receipt through the system’s document picker.
From a user experience point of view, Option A would be easier for the user. Although the user would certainly notice that the workflow involves bouncing between apps, s/he would not need to be actively involved in it. But implementing this option requires greater efforts — taking care that the new app would need to be ready to receive the callback URL invocation and not consume too much memory such that it gets evicted when the old app is activated to read its receipt file. Testing would be troublesome too, as it involves ensuring smooth interaction between two applications.
Option B would be easier to program, being a simple file export in the old app and a corresponding file import in the new app. Unfortunately this would be more cognitively challenging for the user to accomplish. The user would have to make a decision on a common folder location when the old app exports its receipt file and then remembers this location when the new app wants the receipt file that was exported by the old app.
Be sure to consider the trade-offs between development efforts and burdening your user’s cognition. Remember that the more friction there is, the more likely your users would be unwilling to go through it. However if your user base is technically trained and motivated enough to go through the upgrade, you may cut corners a bit and sacrifice some user experience points and trade it for less development and testing efforts.
watchOS Receipt Passing
Standalone watchOS apps doesn’t have good any inter-app communication method as of this writing. WatchKit apps can’t handle custom URL schemes, thus the Callback URL method won’t work. Nor there is a way for apps on the watch to invoke another app.
Migrating purchases of standalone watchOS apps would need to be done through a backend on the Internet. The old app would upload the receipt file to the server and then the new app would download it. It follows that both applications would need to have the user signed in using the same identity. This is discussed more in the next section.
However if the watchOS app is a companion to an iOS app — delivered as embedded inside an iOS app or activated through an in-app purchase in its iOS host — you can delegate license transfer to the apps’ respective iOS host apps. Thus you can use the receipt passing technique described earlier to transfer purchases of your watchOS companion apps.
Note that the
appStoreReceiptURL API is available on the watch. This is the method you would use to locate the receipt file from a standalone watchOS app in order to verify it and enable purchasable features.
When you have a Backend
If your app already require users to be signed in to your Internet-based backend, you can also use this backend to escrow the receipt file between the old app and the new one. That is the old app would upload the receipt file to the server under the user’s credentials. Correspondingly the server would use Apple’s server-side receipt parser to validate and decode its contents. In turn, the new app would simply ask the same backend to see whether the user has indeed purchased the old app.
Note that the user would need to be signed in using identical identities from both applications. Take special care when using Sign In with Apple — be sure that you have the user’s true e-mail address, instead of a forwarding address that Apple generates. Otherwise your backend might not be getting the same identity from the two applications — even when the user signs in using the same Apple ID. If the user opted not to reveal his/her true e-mail address, Apple would generate separate surrogate identities for the two applications since they came from differing ADP accounts. Which in turn would prevent you from linking the old app’s purchase into the new one.
Sadly, subscriptions would need to start from scratch. The user would need to cancel his/her subscription from the old app and then start a new one from the new app. Alternatively, you could also make the old subscription group unavailable, notify your users to migrate, and then discontinue the subscription group after the grace period has elapsed. Re-starting subscriptions would also mean losing out on the lowered commission charges from users who has been subscribing for more than a year.
On the bright side, you could also provide offers in the new app to entice customers of the old app to switch subscriptions. This could include a grandfathered subscription plan, a lower-priced subscription rate, or some credits in the new subscription plan. The app store receipt would contain subscription purchases, renewals, and their dates — hence you can use the same techniques described above to determine what was the user’s original subscription plan and enable the appropriate (originally hidden) subscription tiers in the new app. Use the techniques describe above to get the receipt of the old app and read it in the new app.
Have a look into your apps’ feature roadmaps and take notes when do you plan to deliver major feature updates. Align these milestones with the time you would likely need to switch from a personal ADP into one owned by an entity. Likewise when the time you would need to move the app between entities — such as selling it so that someone else would continue its development.