I recently built a fairly complex in app purchase system for a client that uses recurring subscriptions.
It allows users to purchase a recurring subscription which gives them access to membership content in the app. Recurring purchases are quite a bit more complex than one-time purchases, because you have to keep track of when the membership is cancelled.
I found out that there is more to it than plugin documentation tells you, especially when you want to verify if a user is still subscribed. In this post I’ll explain what you need to do to setup a recurring in app purchase subscription, and how to validate the subscription to see when a user has cancelled.
What Are In App Purchases?
In app purchases are required to buy anything that is used in the app. The payments go through the native payment system for Apple or Google, and they take a 30% fee. You don’t need this to purchase physical items like t-shirts, only for stuff like unlocking membership content, buying an ebook, or purchasing tokens used in a game.
These purchases can be one time or recurring. One-time purchases are not that difficult to setup, but there are tricky parts to subscriptions that I’ll cover in this tutorial.
Technology
- Ionic Framework (3.9.2)
- WordPress with a membership plugin (doesn’t really matter which one, but we used Paid Memberships Pro and WooCommerce Memberships)
- cordova-plugin-inapppurchase
- AppPresser (Not required)
I’ll be focusing on a Typescript implementation using Ionic Native.
Setup
You’ll need an Ionic project with the Ionic Native in app purchase plugin and module installed.
Follow Alex Disler’s tutorials to setup your in app purchases in iTunes and Google Play, and add a tester:
I’ll explain my methods, but you can skip to the source code if you like.
Create the Subscription
Purchasing a subscription is fairly straightforward. Hook up a button that runs the subscribe method. I’d recommend putting this in a provider so it can be reused, there is an example of this in the source code.
The ID is the name of your in app purchase in iTunes or Google Play, such as ‘member-subscription’.
You’ll also need to add a way to restore purchases, which is given in the example code.
To learn more about creating subscriptions, see Alex Disler’s tutorials I linked to in the beginning of the post. In this post I’ll focus on dealing with subscriptions that have already been created.
Validation and Cancellation
The difficult part about in app purchase subscriptions is figuring out if and when someone cancels. The only way to do that is grab the receipt, which contains all of a user’s purchases, and check if they have a recent payment.
Here’s how this goes on iOS:
- Get the receipt through the Cordova plugin, you get back a base64 encoded string
- Send this string to Apple’s verify receipt API, which will send you back a human readable receipt
- Send this receipt to your server to validate
- Server sends response back to app if user has cancelled or not
The worst part is that you can’t check this every time the app is opened, because Apple requires an iTunes sign in to get the receipt. You will need to only check for a receipt each billing period so you don’t annoy your app users.
Here’s how I do this.
Only Check Every X Days
Since I can’t check every time the app is opened, I set an interval for how often I want to check the subscription status. This could be after the app is opened X times, or you could compare dates to see when 31 days have passed.
When that interval is up, I call my method to check the subscription status. The first thing it does is get the receipt.
Checking Subscription Status
In a provider, I have a checkStatus method that I call when the interval is up. It is a chain of promises in this order:
- Get the receipt from Apple (requires user iTunes login)
- Send the receipt (base64 encoded string) to Apple’s verify receipt url
- Get back human readable receipt, and send that to our server for validation
The promise below resolves with the server response, which would tell us if the membership is active or not. The code below should go in the same provider as the code above for purchasing the subscription.
The code above is a provider that you need to add to your app module. It returns a promise with the membership status, so we can call that in our app.component.ts file (or any page) like this:
[javascript]
// call checkStatus() from our provider after interval is up
this.IAP.checkStatus().then( response => {
console.log(‘check status response’, response)
// do something based on response
})
[/javascript]
The receipt you get from Apple has all customer purchases contained in the in_app part of the receipt object. When you send the receipt to your server, you will need to loop over this array and find payments to your specific in app purchase ID. Then you can check the expires_date and see if there is one that is not expired yet.
Here’s a sample of what my server side code looks like.
[php]
“2017-12-14 21:19:13 Etc/GMT” ),
array( “expires_date” => “2018-3-9 21:19:13 Etc/GMT” ),
array( “expires_date” => “2017-12-14 21:19:13 Etc/GMT” ),
);
*/
foreach ( $transactions as $transaction ) {
// compare dates.
if( strtotime( $transaction[‘expires_date’] ) > $oneweekago ) {
// if expiration date is after one week ago, membership is still active
$active = “true”;
}
}
if( $active === “false” && !empty( $username ) ) {
// membership is not active, so update their membership data
$this->remove_user_membership( $username );
}
// send status back to app
return $active;
}
?>
[/php]
I’m using this in a custom WordPress WP-API endpoint, but you can use a normal POST request to a PHP page with code like above to get the same result.
This will send the membership status back to the app, either “true” or “false.” You should edit the code above so you don’t return “false” if there’s an error, deleting the user membership by accident.
This system is not perfect without a lot of testing. You need something in place to make sure that if the subscription check fails (user refuses to login, or server is not available) that it will check again.
Notes on App Review
If you create an app with in app purchases, Apple is more strict with their review. Make sure to add terms exactly as described in their documentation. You need their terms displayed in the iTunes app store description, and on the in app purchase page.
Comments
4 responses to “Cordova In App Purchases – Validating Subscriptions”
This is awesome man, thank you!
Apple advises not to call their verify end point directly from the client app
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
This is for ios.Any solution in android for same status validation of subscription?
I know about the scheme (1 month is 5 minutes and so on) but I have a wrong expire_date: I’m living in Germany, Europe and I have the problem that the expire date is wrong. it is one hour earlier. It is for example 17:00 and should be 18:00. Is this a Sandbox thing and later in buy mode I get the correct GMT? Or is there a way to adjust the date to the correct gmt timezone?
Thank you.