Cordova In App Purchases – Validating Subscriptions

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

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:

  1. Get the receipt through the Cordova plugin, you get back a base64 encoded string
  2. Send this string to Apple’s verify receipt API, which will send you back a human readable receipt
  3. Send this receipt to your server to validate
  4. 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:

  1. Get the receipt from Apple (requires user iTunes login)
  2. Send the receipt (base64 encoded string) to Apple’s verify receipt url
  3. 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:

// 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
})

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

…

function validateReceipt( $receipt ) {
	
	if( empty( $receipt ) ) return;

	// get the transactions from the receipt object
	$transactions = json_decode( $receipt, true )&#91;"in_app"&#93;;

	// I use a one week grace period to account for billing issues
	$oneweekago = strtotime('-1 week');

	$active = "false";

	/* loop through transactions and try to find one that's not expired. Data looks like this:
	$transactions = array(
		array( "expires_date" => "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;

}

?>

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.


Get more content like this

Get valuable insight on business and money in your inbox once per week.

Posted by Scott

  1. This is awesome man, thank you!

    Reply

  2. This is for ios.Any solution in android for same status validation of subscription?

    Reply

  3. 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.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Want to learn more about mobile apps and business? Enter your email below.
Holler Box

Ionic + WordPress

Enter your email below and I'll send you more details on my Ionic + WordPress toolkit.
Holler Box