Engineering Subscriptions 

Session 705 WWDC 2018

Learn the best practices for architecting your subscription infrastructure using StoreKit and server-side logic. Find out about simple engineering techniques to keep your subscribers longer, and how to utilize new tools and APIs to give your subscribers the best experience.

[ Music ]

Good afternoon.

[ Applause ]

My name is Pete Hare and I’m an engineering manager on the App Store team here at Apple.

We’re here to discuss the best ways to build your app and server infrastructure to support the subscriptions.

I’m going to go over a few topics here today.

Firstly, we’ll discuss the best ways to actually build your app and server architecture.

We’ve got some tips and tricks around how to enhance the in-app experience for your user.

My colleague Michael will get up and talk a bit about reducing subscriber churn.

And then finally, we’ve got some new announcements around analytics and reporting tools available to you.

But first up let’s talk a bit about how to build your app and server infrastructure.

Let’s start off by asking a simple question, what is a subscription?

Well a subscription gives a user access to your content or service by them repeatedly paying you some amount of money.

When you look at this at the engineering layer a subscription is really just a set of repeating transactions each of which unlock some subscription period.

To use subscriptions in your app there’s a couple of things that you need to do as a developer to be able to handle these transactions as they come in.

Let’s go through each of these steps.

Firstly, it starts off with your app receiving a transaction.

Once your app has received a transaction you need to go ahead and verify that it’s an authentic transaction, that money really has changed hands.

Then it’s up to you to update and maintain that user’s subscription state for ongoing access to your service.

So let’s go into each of these in a little more detail.

Firstly, let’s talk a bit about receiving that transaction in your app.

Now whether it’s the initial purchase of a subscription or a renewal transaction for a prescription your app is set up to handle subscriptions and transactions using the StoreKit framework.

Now when you are set up to handle transactions using StoreKit the App Store will make these charges on a user’s credit card in the background.

And anytime a transaction occurs it informs your app of these transactions via a thing called the SKPaymentTransactionsObserver.

This transaction observer object is really the central piece of in-app purchases when it comes to your application.

It’s actually just a protocol in StoreKit, it looks like this and you can set it on any object.

In this example we’re just setting it on the AppDelegate itself, but the really important thing is that you add a Transaction Observer to the default payment queue as early on in the application lifecycle as possible.

Once you’ve got a Transaction Observer registered on the default payment queue you’re ready to start receiving transactions as they occur in the background.

You receive transactions on a callback in the Transaction Observed called updatedTransactions and it’s here that StoreKit will inform your app of a set of transactions for you to process.

They can come in various different states that we’re not going to go completely into in this talk, but keep an eye out for a transaction in the purchased state.

That’s a transaction that StoreKit is telling your app is ready for verification and unlocking.

Once you’ve got a transaction the purchase state you’re ready to go with that next step, which is to verify that it is an authentic transaction.

So when it comes to checking for authenticity how can you know that money really has changed hands?

You use a thing called the App Store receipt.

The App Store receipt is just like a receipt you’d get in a department store, it’s a proof of purchase that a user has bought something that they say they’ve bought.

In this case it’s a trusted record of the initial app download and any in-app purchases that have occurred for this app.

This is a digital document, it’s stored on the user’s device, we provide an API for you to access it and it’s put there by the App Store.

We also sign this document using certificates so that you can check to make sure that it is an authentic document that’s actually issued by Apple.

And finally, the document is for your app on that device only.

So if you’ve worked with subscriptions before you’ll notice that maybe one user with multiple devices has receipts that look slightly different on each device.

When it comes to actually verifying this transaction that your app’s been told about the first step that you need to do is to actually verify that the document in question, the App Store receipt, is authentic.

So how do you do that?

You can do this in two ways.

Firstly, you can use on-device validation and this is where directly on the user’s device you can use a series of checks to check the certificates used to sign this app and verify that it’s authentic.

Or you can use the technique called server-to-server validation.

This second technique is where you take that binary encoded receipt data, send it up to your own server, and then from your server over to the App Store for processing.

And the App Store will actually do those checks for you.

You can use either of these techniques, but whichever one you choose it’s important not to use online validation directly from the user’s device, this is not a secure way of actually validating this document is authentic.

But let’s compare each of these two approaches in a little more detail, especially around subscription management and order renewable subscriptions.

Both of these techniques give you a way to validate that this is an authentic document.

They also give you access to the contents of the receipt, any transactions that have occurred for this particular user.

But when it comes to auto renewable subscriptions there’s a few key advantages that server-to-server receipt validation actually gives you over on-device receipt validation.

Firstly, we include some additional subscription information in the response to that validation check.

You can use this, Michael will talk about a little later in this talk.

Your server is always on to handle those renewal transactions in the background.

This is really important if you’ve got a service with maybe multiple platforms.

Your server is not susceptible to a device clock change.

If you’re using on-device receipt validation for subscription management on the user’s device there’s actually nothing stopping the user from winding their clock back and putting themselves into a valid subscription period, maybe a free trial that they’ve actually lapsed their subscription from.

And finally, it’s just a little simpler.

The server-to-server validation you’re dealing with JSON API, you don’t have to build OpenSSL or use ASN.1 decoding.

So with all these things in mind we’re really encouraging more and more people to actually adopt server-to-server validation when it comes to maintaining an auto renewable subscription state.

If you’ve got a simple utility app that doesn’t need any kind of networking you can still use on-device validation for subscription management.

And if you’re interested in finding more about that I’d invite you to check out the video from last year’s events StoreKit Talk where we went into more detail about on-device receipt validation.

But for the purposes of this talk we’re really going to focus more on the server to server techniques outlined here.

So let’s go back to our example and see how we can use server-to-server validation for this transaction that we’re processing.

Back here in our Transaction Observer you can access that binary receipt data using the App Store receipt URL API on the main bundle.

Once you have this URL you can pull out the binary data located at that spot on the file system and you can pull out that receiptData and base64Encode and this will give you a nice string that you can send up to your own server for processing.

You might provide some in-app networking API for the current user.

When you’re sending that data up to your server for processing firstly you’ll do this securely obviously.

You can send it up to maybe a process transaction endpoint on your server.

In this endpoint you might have a user ID associated with the current user for an account on your own system.

You can send this receipt data up to your server and then once you get that on your server you can establish a secure connection to the App Store’s verify receipt endpoint.

And you can send over that receipt data to the App Store.

Here you can include a password field, this is just a shared secret between your app and the App Store.

You can set this up in App Store Connect and store it on your server.

And when you send this receipt data to the verify receipt endpoint the verify receipt endpoint will respond with JSON payload that looks a little like this.

The first thing to check when you’re verifying that this transaction is authentic is this status field.

This is an indication that Apple has actually issued this document in the first place.

Once you’ve verified that the status is zero like this you can check the contents of the receipt portion of this payload.

This is the decoded version of that binary data that you just sent to verify receipt endpoint.

So in here you can do things like verify that the bundle ID in this receipt matches your app’s bundle ID.

And then you can inspect the in-app array, this contains a list of transactions for this particular user for your app.

And you can verify that the product ID associated with this receipt is the one associated with your app.

So assuming that these all match you’re determining that this receipt entitles this particular user to your subscription product you’re ready to go ahead now with that third step, updating the user’s subscription state.

Now in the same way that each subscription period starts with a transaction it also ends with an expiry date.

And the response from verify receipt tells us each of these expiry dates for each transaction.

So looking back at this response from verify receipt you’ll notice that there’s this expires date field in the transaction and the response.

Let’s bring up the user table now that you might be saving this data on your server.

You can take this expires date from this transaction and actually populate this into a field on your own server, maybe the latest expires date field for this particular user.

And this field is going to become the new source of truth on your server for whether or not this user is a subscriber.

While you’re here you should also keep track of this other field, original transaction ID, and you can save that in the field called original transaction ID against this current user as well.

Well come back to this one in just a moment as to why that’s important.

But once you’ve saved these two bits of information against this current user on your server you’re ready to go ahead with the last step, which is to tell the device that this transaction has actually passed your verification checks.

And then when your device gets this callback it can call Finish Transaction back down in your Transaction Observer.

This is a really important step because finishing the transactional will actually clear it out of your payment queue.

And if you don’t call Finish Transaction it might reappear there the next time the app launches for processing.

So make sure you finish every transaction that begins in StoreKit.

Once you’ve finished the transaction you’ve got an updated subscription state in your server, the user is now free to enjoy that subscription period on your service.

Now let’s take another look at that user table I mentioned that you’re storing on your server.

Each user who purchases a subscription using this setup will be assigned a unique original transaction ID, that’s that field that you saved from the transaction response.

And this identifier essentially becomes that user’s subscription ID.

And it’s important because it shows up in all subsequent renewal transactions.

Let’s take a look at how this works.

So let’s say that you’re verifying a renewal transaction, this happens in just the same way you might use the same process transaction endpoint on your own server.

When you do those techniques of verifying the transactions a valid one, and you get up to that stage of updating the user’s subscription state you’ll observe here that there’s now multiple transactions since this is a renewal transaction.

Now according to your existing server-side logic this latest expires date is now a date in the past, so the user is not currently a subscriber and you need to figure out are they still a subscriber based on the data in this receipt.

So how can you use this receipt data and make that deduction?

Well for discovering whether or not the user has an active subscription you can pull out the transactions associated with that original transaction ID.

And then you can find the transaction that has the latest expires date.

Now if you find a date in the past that’s an indication that this user is no longer a subscriber anymore.

But if you find a date in the future that’s an indication that this user is in a valid subscription period.

So let’s look at how this works in the example that we’re going through.

Grab that original transaction ID associated with this user and pull out all the transactions associated with this subscription.

Then sort those transactions by that expires date field and grab the one that has the latest expires date.

Now you can take that expires date and update that latest expires date field associated with this user.

And when you do that you’re effectively extending that user’s subscription by another length of time and your server-side logic now knows that the user is in a valid subscription window.

Of course when you’re dealing with renewal transactions like this that have come through StoreKit you still need to inform the device that it passed those validation checks.

And have your device, your app call Finish Transaction back down in StoreKit again.

So let’s say you have this set up and working correctly.

The App Store is charging the user’s credit card in the background and you’re using StoreKit to process these transactions as they come in through the app.

And then your server is updating and maintaining this latest expires date field on your server.

So you’ve got that server-side source of truth as to whether or not a user is subscribed.

Let’s now introduce a slightly more complex example though to the mix, which is maybe that you offer your service through a website as well.

Now when the user accesses your subscription service through a website you know based on that latest expires date field that the user is a subscriber.

But as much as we’d like to think that people use our apps all the time let’s say that the user doesn’t use your app for a number of days.

And then during this time the App Store actually successfully renews a user’s subscription in the background.

When the user tries to access your servers through your website that latest expires date is now out of date because your server hasn’t learned about that new transaction.

So how can your server know about this transaction that’s occurred on the App Store?

You use a technique for this called status polling, this allows you to discover these transactions directly from your server.

In order to get set up to be able to status poll from your server you just save a latest version of that encoded receipt data that you send up associated with each user.

And what you can do is you can treat that encoded data just like a token.

The reason why it can treat it like a token is every time you send up that encoded receipt data to the verify receipt endpoint the verify receipt endpoint doesn’t just respond with a decoded version of that receipt data, it also includes any new transactions that have occurred for this particular user’s subscription.

It’s located in a field called the latest receipt info field in that JSON response.

And you can use that info to unlock those new subscription periods for this particular user without the app having to launch at all.

Let’s see how this works.

So when you’re verifying transactions just like we saw before you’re sending up that receipt data.

Now once you’ve determined that this transaction in question has passed those same checks that you did before you can take that receipt data and store that in a field called latest receipt data against the current user.

And now that you have that latest receipt data stored, that’s a base64Encode string with the user.

When it comes time to answer that question, does my user have an active subscription you can just take that latest receipt data from your server directly and send it to the verify receipt endpoint.

You can also include here an optional flag that’s the exclude old transactions flag, this is just telling verify receipt that you don’t even want to know about that decoded version of the receipt you just want to find out about any new transactions.

Verify receipt will respond with that particular object, the latest receipt info object.

And inside this object is those new transactions that have occurred before this receipt data was actually generated.

And you can take the expires date directly from the response of the latest receipt info object and update it against the current user, again extending them access to that next subscription window.

And so the user who is on your website trying to access your content can now access that next subscription period all without the app having to launch with that new transaction.

If you are using this technique though status polling it’s important to remember one thing and that’s that when your app does come back online again transactions will still appear through StoreKit in the updated transactions callback.

And you still should handle these transactions passing them up to your server for verification and finishing them back down on the user’s device again, even if your server already knows about them through a status poll.

We’d encourage you to use that as an opportunity just to send up that new latest receipt data for storage against the current user on the server.

Now status polling works great when the user’s credit card is able to be charged, but what if some subscription period the user’s credit card has some kind of billing issue and the App Store isn’t able to charge it for the next subscription period.

Is this user destined to remain unsubscribed involuntarily?

Not at all.

When reacting to billing issues like this you can do three simple steps.

Firstly, you can observe that there’s been no renewal transaction for this particular user, their subscription has now lapsed.

Secondly, you can direct that particular user to go and update their billing info.

And then the third step, when a renewal transaction occurs unblock that user immediately after it happens.

Now, steps one and two are pretty straightforward using the status polling techniques that we just went through, but step number three here uses a feature that we actually launched last year server-to-server notifications.

Let’s walk through this example, let’s say that one subscription period the App Store has an error when trying to charge this user’s credit card.

And then you find out about the fact that there is no new renewal transaction for this user through a status poll.

Your server correctly makes the calculation that this user is not a subscriber anymore and so when the user comes along to access your service through the website you give them some appropriate error message about the fact that their subscription wasn’t able to be renewed.

And you can direct that user to go and update their billing info in the App Store.

Now when the user does update their billing info, maybe they just need to update an expiry date or something, two things happen.

Firstly, the App Store will actually immediately charge that user’s credit card and make a successful transaction.

And when the App Store does do this the second part of what it does is it sends your server a direct HTTP post upon the successful renewal.

And then the payload of this post includes the new transaction info for the transaction that’s just occurred.

You can use that original transaction ID field located in the payload to locate which user this notification is for.

Once you find out which user you’re talking about, well you can grab that latest expires date and update it for this new user giving them access again to that next subscription period.

And then the user who’s probably still sitting on your website trying to get access to your content will be able to immediately be unlocked since your server received that push straight from the App Store.

It’s really important to unlock users in a speedy manner when this sort of thing happens, especially when they go to all that effort for updating their credit card info manually and waiting for access to the servers.

But there’s one thing to note here and that’s the notification is only sent if the subscription actually lapsed like we just saw.

To discover successful renewal transactions you still need to rely on status polling techniques we just outlined before.

But it’s really easy to set up to use server-to-server notifications.

All you do is enter a URL in App Store Connect.

This is just an endpoint on your own server and if you enter it into App Store Connect the App Store will begin to send your server HTTPS posts for these status change events.

And as we saw included in the post is that latest transaction info for the transaction that triggered it.

You do make sure your server is ATS in order to receive these, but it’s a really simple step that you can do to give a bunch of users a much better experience.

So those are some tips and tricks around how to build your app and server architecture.

Let’s talk about three tips that you can use in your in-app experience to really enhance the user’s experience.

Firstly, we made the assumption earlier that the user was signed in to an account on your own service.

In order to keep track of each subscription ID you need this particular user table located on your server.

Now when it comes to actually creating accounts we think it’s best to offer in-app purchase before an account creation step.

Why is that?

Well as the user it’s a much better experience, you can just open the app for the first time and buy the subscription immediately getting access to the content you’re after.

For you it’s better because you get higher conversion rates, users don’t have to go through funnels of entering emails and passwords to be able to get access to giving you money.

Now you can use the techniques that we just went through by using an anonymous account in these instances.

And you can rely on the original transaction ID if you need to associate multiple devices.

If you’re using anonymous accounts like this, when it comes time down the road to actually give the user an account creation flow, you can just simply take them through a deanonymization step where you update those email fields and maybe other personally identifiable fields.

So that’s tip number one.

Tip number two is around selling your in-app purchase.

When it comes to selling your subscription, you can use a feature that we launched last year Introductory Pricing.

Now there’s an important step with Introductory Pricing which is that you need to know at runtime whether or not a user is actually eligible for an introductory price.

And the reason you need to know this is you have to know which price to render to your user, whether to render the normal StoreKit price or the introductory offer that you want to offer a user to get them in the door.

Now you can set yourself up to actually know this ahead of time by monitoring the transactions that are occurring in the background as they come in.

Let’s see how this works.

When you’re verifying transactions like we just saw earlier keep an eye out for these two fields.

The is trial period field and the is in intro offer period field.

If either of these fields are true that’s an indication that an introductory offer or a free trial was used for this particular transaction.

And if it was you should keep track of the product ID in question against this current user.

You might store them in a field called consumedProductDiscounts.

Now if you’re keeping track of which products were used with introductory offers, when it comes time to render the price of some new subscription product that you want to show your user this is how you can do it.

You can take those consumed product discounts saved against the current user and execute an SKProductsRequest with them.

And the reason why is that the response from SKProductRequest now in iOS 12 includes the subscription group identifier so you know which subscription group this particular product is from.

And now armed with this subscription group identifier you can keep track of that in a set of consumed group discounts for this particular user.

So you know which subscription groups this user has used offers for.

Now when you want to render the price string of say product A it becomes a simpler check.

You can check to see if this user’s list of consumed group discounts contains the group identifier for the one you want to sell them, in this case product A.

And if it does, that’s an indication that this user has actually used an introductory offer here before, so you can render the normal price string to this particular user.

But if not, they’re still eligible for that introductory offer so you can use that introductory price located on the SKProductObject.

Now when rendering the price string nothing’s really changed here it’s the same techniques that you use for rendering any in-app purchase and I’m not going to go into too much more detail here, but I’d encourage you to check out the video of the session just before this one where they talked about rendering these price strings a little more dynamically.

For more information about setting up introductory offers I’d also recommend you go to the What’s New in App Store Connect session on Wednesday at 5 p.m.

in hall three.

So that’s tip number two around introductory pricing.

The third tip here is around subscription management.

You can offer the user upgrades and downgrades between subscription tiers right in your app’s UI.

And to do this you can actually treat it just like selling an initial subscription.

Now if the subscription you’re selling the user is part of the same subscription group, so it’s a different tier than the one the user has already subscribed to you can basically just create an SKPayment just like you would if you were selling them an initial subscription.

And when you do this StoreKit actually handles the fact that it’s an upgrade or downgrade for you.

So you don’t have to worry about that user being subscribed twice.

Now if you don’t want to provide your own UI for upgrades and downgrades in your app you can also just provide a link out to the App Store subscription management screen.

We provide a link for you to be able to get to this screen directly from your app and here the user can upgrade, downgrade or even cancel their subscription.

Now your app is often the first place a user will go for subscription management, to be able to upgrade, downgrade or cancel.

So it’s a really good idea to give some kind of link maybe in your app’s settings for a user to be able to do this.

To actually get to this screen there’s a link available on our In-App Purchase Programming Guide and here it is if you enjoy writing down links.

So these are some simple techniques you can implement in your app to give a user a pleasant experience using subscriptions.

Next, I’m going to hand it over to my colleague Michael who’s going to go over some great techniques for reducing your subscriber churn.


[ Applause ]

Good afternoon, my name is Michael Gargas and I’m a technical advocate on the App Store operations team.

Today I want to talk about reducing subscriber churn inside your applications by using some of the tactics and methods that Pete just walked you through.

Today we’ll cover involuntary churn and voluntary churn, the two types of churn you’ll see inside of your subscription applications, as well as some ways to win back those subscribers that you potentially may have lost or will lose.

First, let’s talk about involuntary churn.

Involuntary churn is the loss of a subscriber due to a failed payment or billing issue on the platform.

Now last year at WWDC we walked you through what we’re doing in order to minimize involuntary churn inside of your applications.

We announced our updated Billing Retry service where we expanded our retry duration from 24 hours to up to 60 days.

We also implemented new retry strategies and we tuned them over time to recover more and more of your subscriptions.

A date to remember is July 13th, 2017 because this is the day that Apple actively started recovering subscriptions for you.

If we look at the performance of Billing Retry since launch we can see that our recovery rate has more than doubled.

And when we look at involuntary churn we’ve cut this by over 2% platform-wide.

[ Applause ]

Now if we look at how our tuning has impacted the recovery of subscriptions we can see that quarter over quarter we’ve been able to continue to recover more and more of your subscriptions.

Now the net result of this has been 12 million of your subscriptions recovered since the launch of Billing Retry.

[ Applause ]

So that’s what Apple’s doing to minimize involuntary churn for you.

But there’s also some tactics that you as a developer can do to minimize voluntary churn inside of your subscription applications.

You can leverage some of the subscription specific receipt fields that Pete mentioned earlier.

You can implement grace periods and during that time you can deploy some effective customer messaging.

So let’s take a look at an example subscription.

Here we can see that our subscriber was set to renew on April 26th, however, they encountered a billing issue.

So in order to let you know that Apple is actively attempting to collect funds from that user via the Billing Retry service we are going to surface a field in the JSON response aptly titled is in billing retry period.

A value of one signifying that we’re attempting to collect funds for that subscriber.

If we go back to our example of subscription you can see that this has been added to JSON response.

And when you see this in conjunction with the expires date this is your signal as a developer to implement what we call a grace period.

You may ask yourself what is a grace period.

A grace period is free subscription access while in a billing retry state, however it’s before you have lost that subscriber, before they’ve churned out.

The goal of the grace period is to improve recovery.

So let’s take a look at how we can do that leveraging some of the information in the receipt response.

If we flip back to our example subscription you can see that our subscriber is in a billing retry state and was set to renew on April 26th.

Here we want to add some server-side logic to use that expires date field and the is in billing retry period in order to add a period of time, in this example three days where that user will continue to have access to the service and technically stay subscribed.

Now why would you do this?

Well it’s an opportune time to deploy some effective customer messaging to contextually communicate with your subscribers and let them know that there may be an issue with their subscription.

You may want to do things like ask them to update their payment method or have them restate the value proposition of the subscription offering that they’re subscribed to.

And during this time you can offer limited service as well, such as a browse but not watch experience in an entertainment app.

Here we can see Peak, a subscription developer on the App Store.

Peak is leveraging the Billing Retry status fields in order to surface a contextual message to their subscribers letting them know that there’s been an issue with their subscription.

When engaged upon they’re taken to an additional screen which clearly states what the issue is and how they can resolve it.

But it would be really effective if from this screen you could drive that user or subscriber directly to our systems to update their payment details.

So I’m excited today to announce that we have two new URLs coming shortly after WWDC this year, one to drive users directly to update their billing information and the others to take them to manage their subscriptions as Pete alluded to earlier for upgrades, downgrades and crossgrades.

[ Applause ]

With that being said, a lot of developers will ask well when are we seeing our users be recovered.

And on average we see the majority of users recovered within the first seven days of entering a Billing Retry state on the platform.

This might be a time to offer a full access grace period because we see a lot of users self-recovering during this time.

You may want to deploy that customer messaging towards the tail end of this to bring in some of those subscribers that might’ve taken longer to come back into your application.

Let’s flip back to our example subscription.

What happens if this is effective and we’re able to recover these customers?

When a retry attempt is successful the date of the retry or recovery becomes the new subscription anniversary date moving forward and this will be reflected in JSON response when validating that successful transaction and finishing it.

But we’re not going to stop there we’re also going to deploy our server-to-server notifications so that you can immediately unlock access on all platforms and close the loop with your customer letting them know hey you’re all set.

So that’s involuntary churn where the customer didn’t technically make a choice to unsubscribe from your application.

But what is voluntary churn?

Voluntary churn is the loss of a subscriber due to customer choice, either cancellation or refund requests.

To be clear, this user actively made the choice to leave your subscription offering.

So what can you do as a developer to minimize voluntary churn inside of your applications?

Well Pete walked you through how to status poll earlier and you can implement that to get some key subscription details about your users and when you get that information you can use it to offer attractive alternative subscription offerings to potentially save that user.

So let’s talk a little bit more about status polling.

With the release of server-to-server notifications there’s really only two key reasons that you still need to status poll.

The first is understanding will my subscriber churn in the subsequent subscription period and the second being has my subscriber renewed.

We often get asked when should I status poll as a developer, when should I try to catch those users and see their subscription state changes.

The most effective times that we see are doing that status poll at the beginning or end of a subscription period.

By deploying this responsibly you’re most likely going to catch most users that may want to voluntarily churn from your subscription offering.

But when you status poll you’ll also get access to some additional subscriber status fields.

And you may want to take those fields and save the decoded JSON response from the verify receipt call in the user tables on your databases.

Or alternatively, you can parse out specific fields such as the Billing Retry Status in order to segment your customers and maybe understand those that are in a retry state and those that are not.

But the signal to understand if a customer will voluntarily churn is shown via a field called auto renew status.

Auto renew status will let you know with a value of one that that subscriber will return in the subsequent subscription period and a value of zero letting you know that they will voluntarily churn at their next subscription anniversary date.

Let’s see what this would look like in our example subscription.

Here we have a subscriber purchased on March 26th and they’ve made the choice to disable auto renew via the manage subscription setting screen.

Now coincidentally, we status polled shortly after this happened and we were able to see via the receipt response that the auto renew status has changed to zero.

It’s at this point that you can update your user tables on your database or on your server and segment that customer as a potential voluntary churning customer.

If we go back to the example subscription we’ve status polled, we’ve captured this customer might leave, so what should we do?

As a developer this is your opportunity to present an attractive downgrade offer potentially in the same subscription group.

Here we can see Peak trying to save that user by potentially having them take a lesser term or a lesser price subscription of a different duration or maybe a different offering.

If that subscriber decides to engage with this the same way that Pete showed you by surfacing upgrades and downgrades within your applications we want to let you know what product they will renew on in the subsequent period.

We do this via the auto renew product ID field in the JSON response.

So this differs from product ID in that this is what the next offering will be after that subscriber renews.

Here we can see in the example subscription our subscriber has elected to downgrade instead of churn out.

We’ve changed the auto renew status to one and we’ve added auto renew product ID.

It would also be beneficial to be notified of this change immediately, so for this we’ll also send a server-to-server notification letting you know that your subscriber did change their renewal preference.

This is useful if you want to maybe state the differing service levels between what they’re currently subscribed to and what they will have in the subsequent period.

Now it’s impossible to run a 100% retention subscription business, so it’s important to understand how you could possibly win back some of those subscribers that you might have lost.

A win back is reengaging subscribers after they’ve churned by showing them resubscription offers or potentially surveying them to understand why they’ve left.

If we look at our example subscription let’s see what a voluntary cancellation would look like and how we can leverage that inside of our app.

Here our user has elected to cancel via AppleCare.

In order to let you know we’re going to surface a cancellation date field in the JSON response.

This is your signal to understand this customer has contacted AppleCare and either canceled or requested a refund.

But as a developer you would want to know this information immediately.

For this we’ll deploy a server-to-server notification.

This is important because you’ll immediately want to shut off access to those users on all platforms and potentially prompting them to see alternative subscription offers.

Now after this user has churned it would be important to easily segment between those that have voluntarily chosen to unsubscribe and those that have involuntarily been unsubscribed due to a payment issue or billing issue.

So for that we have the field expiration intent in the JSON response.

Now to be clear, this will only show up after the subscription has lapsed.

And we really want to focus on two key values, the first being value one which signifies voluntary churn, the second signifying involuntary churn with a value of two.

If we flip back to our example subscription where our customer canceled via AppleCare you can see that we’ve added the expiration intent field to the receipt response with a value of one.

So what do you do as a developer when you see your subscribers in this state after they’ve already churned and you’ve segmented between those that have voluntarily and involuntarily churned?

Well for voluntary you may want to survey those subscribers who have opted into an account on your system.

You can ask them maybe why the service wasn’t appropriate for them and what you can do to tailor it moving forward to provide a better experience for them or other users.

Additionally, you can always just show alternative subscription products potentially within the same group because if they re-subscribe you want to continue accruing your time towards that 85/15 revenue share.

Here we can see Peak when a user has voluntarily churned being shown a resubscription offer, in this case a 60% discount.

For involuntary churn since the user did not actively make the choice to unsubscribe it’s appropriate to just show the same or alternative subscription products.

You may want to leave some persistent messaging if that user is logged in inside the application experience letting them know that they have lapsed, but that they can always come back.

And you may also want to deploy a limited subscription experience, such as a browse but not watch experience in an entertainment app.

Here we can see Tinder, when users interact with pro level or subscription level features they are continually prompted to subscribe.

So in summary, if you take anything away from this section on reducing subscriber churn it’s that you should be leveraging these subscription receipt fields effectively.

You can then implement status polling to understand when your subscribers may voluntarily churn.

You can then use that status polling to deploy some targeted and effective customer messaging.

And then to close it out, presenting contextual subscription offers to these users to hopefully win them back or prevent them from churning in the first place.

So with that I’d like to hand it back to my colleague Pete to discuss analytics and reporting.

Thank you.

[ Applause ]

Thanks Michael.

If you haven’t implemented handling of these JSON fields yet we highly recommend you try it and watch the great effect it has on your retention rates.

It’s not often as engineers we get the chance to make such simple architectural tweaks that can have such a big effect on a business’ revenue, so take a look.

Now we’ve got some great new updates today in the areas of analytics and reporting.

Located in App Store Connect the sales and trends section contains a huge amount of useful information.

Now you can get even more insight into your app’s performance.

This existing subscription summary dashboard now includes monitoring for subscriptions that are in those Billing Retry windows.

This is great for gaining insight into your subscriber behavior and to determine the most effective amount of time to offer those grace periods for like Michael just talked about.

This year we’re also introducing a brand-new dashboard for subscription retention.

This page offers a glance at how your introductory prices are going, as well as how many of the users are subscribed to the higher proceeds rate, that’s that 85 to 15% split you get when a user has been subscribed for a year.

The dashboard includes new graphs to help you quickly identify which subscription cohorts are the highest performing and you can monitor your subscription performance over time and compare your app’s performance across different territories.

Now all this new information is not just available inside the App Store Connect report, but it’s now available through the new App Store Connect API.

The report data here is available to you daily and you can script your own setups to import this maybe into your own data warehouses for further analysis.

We’re not going to go into any more information on the App Store Connect API here, but I really recommend you check out the automating App Store Connect session in the hall three on Thursday at 3 p.m., there’s some really exciting enhancements in the area of automation.

Now we’ve talked a bit about what you can get from receipts and what you can get from these App Store Connect reports.

So as a bit of a summary, App Store receipts are useful for validating those StoreKit transactions and updating user subscription states, maintaining the state on your server.

And you can also use them to understand individual subscriber behavior just like Michael showed you.

For App Store Connect reports it’s for a slightly different reason, they’re better at that macrolevel analysis, maybe understanding subscription pathways of users of your app and maybe most importantly, understanding how much money is going to get deposited into your bank account for your subscriptions.

Now we’ve covered a lot of topics here today, but as a bit of a summary remember that server-side state management offers you much more flexibility when it comes to managing subscriptions.

If you haven’t done it yet add that URL to receive notifications from the App Store.

Consider offering an introductory price in your app, it’s a great way to get users in the door to your own subscriptions.

Add some simple messaging to reduce subscriber churn, using those fields that Michael walked us through.

And for users that have actually lapsed offer some alternative subscription options maybe to win them back.

Finally, remember to check out these new reporting tools available in App Store Connect.

For more information on this session and for the video, this has been session 705.

We’re also going to be in the labs this week right after this and also on Thursday at 9 a.m.

we’ll have engineers from the StoreKit team and for App Store Connect ready to answer any questions that you might have about engineering subscriptions.

Thanks a lot.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US