Developing an App that offers subscriptions is no easy task. Architecting a subscription service is complex and full of design considerations. This post will elaborate on the areas you need to figure out up front before going down that path.
The App I created is a hybrid architecture which means it uses the one code base for an iTunes and Google Play App. If you are not familiar with App coding it’s beyond annoying… You have to program once in Android and again in iOS. Most Apps/companies have a native setup with two separate code bases for iOS and Android.
Regardless of which architecture you use, implementing a subscription service is complex and full of design considerations. This post will elaborate on the areas you need to figure out up front before going down that path.
I mentioned above that I used one code base. The code/language I used is JavaScript with Phonegap plugins. Even though there is a standard programming language I still had to architect a solution for iOS and Android. Why you ask… well because Apple makes you use their In-App purchase framework and Google doesn’t care what you use.
Seriously, Google has zero care how you implement a payment processor. Having said that I have heard they now have a subscription payment option. However, this was not an option when I began our architecture and development.
Therefore for iOS we used the In-App subscription services that Apple recommends and for Android we used Stripe. We could have used the same Stripe code for both iOS and Android but Apple will not approve any App that doesn’t use In-App purchases. This is as annoying as a new Macbook that doesn’t have a USB port!
On both platforms our pricing is $9.99 a month with a 7 day free trial or $99.99 per year with a 7 day free trial. All the pricing and free trials are easy enough to setup within either iTunes Connect or Stripe. Up to this point we are in the calm water… but now it’s time for the heavy lifting.
Here are the five main consideration areas:
The Initial Payment
Checking Active Subscriptions
Cancelling
Renewing – Without offering another 7-day free trial
Reporting – Who is in trial vs. subscribed vs. cancelled, etc.
The Initial Payments
Apple – This isn’t overly difficult. When you initially load the App you make a service call to iTunes storekit.load method and that will return all the active items in your store. In our case for example it returns four subscription types: Monthly w/ Trial, Monthly, Annual w/ Trial and Annual.
I created a monthly and annual without trial in case a user reactivates a subscription so they do not get the free trial more than once. I was told after development that Apple actually checks this automatically on their end to avoid this situation. But I had already programmed it on my own and cannot confirm that is actually true. If anyone knows are sure please add it to the comments on this article.
Once the store is loaded in the App I just created buttons with the pricing options for the user to choose. If the user taps an offer, I send an buy request to iTunes and Apple initiates and completes the transaction from the users phone/iTunes account.
Android / Stipe – On this path I use the same page for Apple with monthly and annual buttons. When the App initially loads I check the phone type (iPhone or Android). So when Android users tap a selection they are then taken to a payment screen to enter their credit card information.
Now pay attention… Stripe is super simple to implement on the web but not here. That’s because on the web when you click “Pay with Stripe” a pop-up opens and then the payment is handled by Stripe. Essentially you do not have to do anything except send the payment to Stripe and they process it and send you a response. However, in an App you cannot “pop-up” a new window so the basic implementation will not work.
That’s no big deal for “The App Man” though… I just created a JavaScript Ajax call with the Stripe API and then used their PHP API to create a web service to process the payment and return a success or failure response. Sorry to geek out on you but in short the Stripe API allows customization and that’s what I did. Once Stripe works its magic it gives me transaction ID’s that I store with each users account.
Checking Active Subscriptions
As I mentioned before when the App initially loads we have a device check to determine if it is an iPhone or Android. We will now use this to call either iTunes or Stripe and get the current status of the subscription. Every time the user freshly loads the App we need to run a check to see if they are active. When I say “Freshly Loads” I mean that if the App has not been used in over four hours we are going to run this check.
Apple – This is where Apple starts to get more difficult. To check a subscription status you have to do the following. First, when the storekit.load function executes it calls Apple and gets the subscription products (monthly, annual). However, it also looks for and if found returns a value for: sk_receiptForProduct. This is a transaction ID for the subscription that is related to the users phone/iTunes account. Now, if the user has been on a monthly subscription for 12 months guess what… it will return 12 transaction ID’s.
The transaction ID is a encrypted string about 2000 characters long that you then have to encode and send to Apple for validation. Apple will send you back a status ID of the transaction.
Here is what you will run into while developing and testing. In the Apple sandbox environment Apple will renew a monthly subscription every 5 minutes to simulate a month has passed. After six renewals the account will be expire. In theory this is great but in actuality it sucks.
The reason it sucks is because now you have 6 transaction ID’s. Every time you build the App moving forward those 6 transactions stay with your phone. Apple didn’t bother to create anyway to refresh or clear past transactions. So on the second build you will have 12, then 18… As you can see the more you build and test the more calls to the receipt validation service you have to call.
In my case my App wouldn’t load because I was making 40 calls to the receipt validation service to see if my subscription was active. But here is what I discovered through trial and error. Every single transaction ID, although a different sting, returns the same subscription status. So programmatically I just store the final transaction ID and then send that one time for validation.
If the transaction ID is valid the user is send to the dashboard and if not we send them to a renew page.
Android / Stripe – This implementation was fairly easy to develop. To do this I just take the users transaction ID from their account an call a Strip PHP service that I wrote. This returns all the data associated with their subscription. If they are active I send them to the dashboard and if they have expired they go to the renew page.
Cancelling
Be sure to make the terms of your subscription clear and upfront – don’t be an A-hole! Not to mention that Apple will not approve your App if you are being deceptive.
Apple – In order for a user to cancel their subscription they have to go into their iPhone settings, then iTunes and cancel their subscription. In the test environment there is no way to actually do this as of this writing other than waiting for the subscription to expire as stated earlier. I guess we just assume if Apple approves our App it will work.
Android / Stripe – Again the Stripe API allows us to create our own cancel process. Within the App we created a section that shows the subscription status, free trial days remaining and next bill date. We then created a custom PHP service that will cancel the user’s account. The only rub here is that if the user cancels we still need to give them access to the App through the remainder of the month/year they paid for. Stripe has accounted for this within their API so it’s really just a matter of showing the user a nice message like: “Your account has been cancelled and you will still have access to the App until 10/20/20”. Once the expiration date is reached we will pick this up with the subscription status check we mentioned earlier in consideration 2.
Renewing Subscriptions
Apple – Pretty straightforward here… Apple auto renews every 30 days. Upon login we check the transaction ID and if it is expired we send them to an App page with our renew offers.
Android / Stripe – Upon login we check the Stripe transaction ID with and if it is expired and past the “good until” date we route them to the renew page. So if their user account has a transaction ID they will never see the free trial again.
Reporting
Apple – This is where Apple has really dropped the ball. There is absolutely no way to determine how many of your accounts are in free trial vs. active vs. expired from a reporting standpoint. Right now there are two options I have found. Either build your own system or Revenue Cat. Revenue Cat is a startup that can handle this process and has reporting on these metrics. The only issue is that they currently do not work with hybrid Apps. I have been speaking with their CEO and they are expanding their API to accommodate this and many other new features. Needless to say I can’t wait until this is ready.
I have also spoken with several other companies that have created their own tracking and all have assured me that it is a huge undertaking. So beware because your marketing and business teams are going to want to know this data. Apple is taking 30% of each subscription you sell but providing no reporting / metrics at this time.
Android / Stipe – No problems and no issues here. The Stripe interface is mature and well designed to see the status of accounts by many different groupings.
Wrapping Up
Subscriptions within Apps are very popular in concept and Apple is making a huge push for Apps to include them in their offerings. Google Play is also expanding out their platform to support subscriptions and pay for usage Apps. The main issue for a product developer or architect is defining how to accommodate and develop a solution for iOS and Android. From a marketing and usage standpoint we need to also consider reporting.
I started with a solid plan from the beginning and halfway through had to change course several times to get to a final solution. Such is the life of an early adopter. I wanted to share my experience with you so that you can learn what to account for in your App architecture. You likely have a completely different code base and design but the overall considerations will still need to be addressed.
If you need help, please do not hesitate to call me.