In-App Purchases and Using Server-to-Server Notifications

Session 302 WWDC 2019

Learn about the latest updates in StoreKit and dive deep into best practices for using server-to-server notifications to manage your subscribers.

[ Music ]

[ Applause ]

Good morning!

Welcome to In-App Purchases and Using Server-to-Server Notifications.

My name is Dana DuBois, and I'm an App Store engineering manager.

We'll talk about a number of things today.

First I'm going to start off about what's new in StoreKit.

What have we changed since we were here last year?

Then I'm going to hand it off to my colleague Tori, and she's going to give you everything you need to know about Server-to-Server Notifications, and how you can make sure you have the latest information about your subscription customers on your backend.

Next up, Manjeet is going to walk through the different billing events during the subscription lifecycle.

And then, finally, he's going to walk you through what you need to know to reduce involuntary churn, and keep your subscription customers in your service.

So, first.

What's new in StoreKit?

Well, this spring we introduced subscription offers.

Subscription offers is a feature that we introduced that gives you a tool to retain existing subscribers as well as win back subscribers who used to be in the service.

You can achieve this by having up to 10 different active offers per subscription, that will give you a discount or free service that you can give to your customers.

Your app decides what to present and to whom.

It's completely up to you.

This is such a great feature, and such a big feature, that we actually have a dedicated session to that later today, right here at 2 o'clock.

If you have subscriptions in your service, I highly recommend you check that out.

So, what else is new in StoreKit?

Well, this summer, we're announcing that we've introduced the SKStorefront.

SKStorefront is how we're actually exposing to you, the developer, the storefront that the user has set their App Store to.

This allows you to present the right content to your customers.

What do you want to merchandise to your customers all around the world?

You can use the SKStorefront to get specific territory for that customer on their device.

This is the same way that the App Store exposes content.

And, this API gives you the storefront that the App Store is currently set to.

One thing to keep in mind is that this API returns a device-specific cached value for that storefront.

And, it can change over time, so there's some stuff you need to think about while you're interfacing with the SKStorefront.

So, let's get into the code, and see how this will all work.

So, if you're interfacing with StoreKit today, you're already have a delegate on the SKPaymentQueue.

We've added on that, the ability to get a parameter that returns the current cached value for the storefront.

Just .storefront, and that'll give you the value.

Because it can change, and because it's device-specific, it is possible, though unlikely, that it will be nil.

So, you need to check for that in your app, and make sure you do the right thing.

Once you have that storefront, right on that API, is a country code.

And, that's a 3-letter, ISO-standard value for territories and countries all around the world.

And, that'll tell you exactly what the App Store is set to on that device.

And, that's really it.

That'll allow you then to take that country code, and merchandise the right content to your customers.

But, as I said, it can change over time.

So, let's go a little deeper into the code, and see what else you need to think about.

Here we have an example of some code where you're taking your product identifiers that you fetch from your backend, passing it in and then trying to determine if you should fetch the metadata, do an SKProduct request to determine if you should show that content or not.

So, what the developer has done there is, fetch that metadata from their backend, and did a call to say, hey, is this storefront valid for that code?

And then, you can see here, right before you fetch product info, you would call shouldShow.

If that returns true, add that identifier to the list of things you're going to make an SKProduct request on.

You should do that before you make the SKProduct request, because if you're not going to merchandise that product, it's more efficient not to actually fetch the product the product metadata.

But, as I said before, it can change over time.

The users could actually switch accounts, or even it's even possible within the same account to go into the App Store and browse different storefronts.

Developers will do this a lot when they actually want to see their app in different territories around the world, see what it looks like.

See how it's doing.

There's ways of doing that in the App Store.

So, we want to make sure you have the latest information in your running app.

So, on the SKPayment transaction observer, we added a new paymentQueueDidChange event that you can listen for, right inside your app.

That passes in the payment queue.

When you get that, you go queue.storefront, and get the new value for what that storefront is.

And, again, it's a new storefront.

So, you want to reload all the content specific to that new store front.

You might actually have different content that you want to merchandise based on where that user is in the world.

So, again, call shouldShow.

Determine if you should, and fetch product information for it.

So, what happens when you're doing a purchase?

You've merchandised some content, the user's about to go buy it, and normally everything will go fine.

But, if the user is actually in a different storefront from what their device was set to, it is possible that the payment queue will change storefronts in the middle of that transaction.

So, we've given you a paymentQueue delegate that allows you to listen to paymentQueue: shouldContinue: in newStorefront.

This is your chance to double-check, should I allow this purchase to happen in this new storefront?

And, again use that same shouldShow functionality, where you've listed all your product identifiers, and you know all the territories that those products are available in.

Just if it returns true, allow the payment to continue.

To make sure that it's the best user experience, we want this to return fast, so you shouldn't make a server-side call in the middle of paymentQueue: shouldContinue: newStorefront.

You should have this information cached on your device, what's available in the territories, ready to go when the payment's happening.

That way, you can return real quick, the user can continue their purchase, or, if you're returning no, you can actually inform the user why it's not valid in the new storefront.

So, as I said, maybe it's not available in the new territory.

So, what do you do then?

Well, when that happens, in your paymentQueue: updatedTransactions delegate call, we'll return an SKStoreProductNotAvailable error.

This will inform you that you've just told us that you shouldn't allow this transaction to happen in that new storefront.

And, here's your chance to present a dialog.

Or, merchandise some other piece of content that might be equivalent in the new storefront.

Show an alert, update the UI, do what you need to do right then and there.

So, that's SKStorefront.

What else are we introducing?

Well, in iOS 11, 11.2 and tvOS 11.2, and macOS 10.13.2, we introduced app pre-orders.

This has been a great feature that developers have used all, you know, around the world, to actually get interest in their apps before they're available in the store.

And, we're excited to announce that we're introducing that with watchOS 6.0 this year, so you can market your apps right to the Watch, and gain interest ahead of time, like having them be available for pre-order.

But, we're also doing one more thing this year.

And, coming soon, we're actually going to signal inside the app receipt, if the app was purchased as a pre-order.

So, you'll know which of your customers have actually pre-ordered the app.

And, you can use that to, you know, give them great messages, saying thank you for pre-ordering my app.

Or, if you want to unlock some additional content as a thank you to some of your best customers, you can use that information.

That'll be available in the receipt.

And, the great thing about the receipt is that this will go back to iOS 11, 12, all the way back in time.

It's available in the receipt.

[ Applause ]

So, those are some of the things that have changed with StoreKit, and in-app purchases since we were here last year.

Next up, I'm excited to hand it off to my colleague Tori, and she's going to talk all about Server-to-Server Notifications.

Tori?

[ Applause ]

Hi everyone.

My name's Tori, and I'm very excited to be here today to talk to you about Server-to-Server Notifications.

We have several new features that we want to bring to the server-to-server notification, and I want to do an in-depth talk with you on how you can use these to effectively monitor your subscription events.

But, before we get into all that, let's first take a look at what Server-to-Server Notifications are, and how you can set up your servers to receive them.

So, Server-to-Server Notifications are an HTTP POST we send from our server to yours, with a JSON body.

You might recognize these by their previous name, statusUpdateNotifications.

Server-to-server notifications are incredibly useful for getting of-the-moment updates on your subscription events, and for using them so win back customers in cases like subscription offers.

Once you've determined which endpoint you want to receive your Server-to-Server Notifications at, all you have to do is return a 200 response from that endpoint to indicate a successfully received message.

However, should you not return a 200 response, we will retry up to three times to resend the notification to you.

Once you have determined this endpoint, you first have to set it up in App Store Connect.

You can find this place on your app's App Information page, in the Subscription Status URL section.

In addition to setting up your endpoint in App Store Connect, there are certain security requirements which the connection must adhere to in order for you to successfully receive these notifications.

Basically, this all sums up to, the connection must be App Transport Security, or ATS, compliant.

Now, this means a few things.

First, the certificate must be issued by a trusted certificate authority.

The Transport Layer Security version, or TLS version, must be TLS 1.2.

You must use one of the provided symmetric ciphers, and the certificate must be signed and hashed using an algorithm which is SHA-256 or greater.

I hope with all this information, you have a better understanding of what Server-to-Server Notifications are, and how you can set them up.

Should you need more information, you can look up the documentation for statusUpdateNotifications on developer.apple.com.

So, now that we've looked into what Server-to-Server Notifications are, and how you can receive them, I'm very excited to talk to you about the new features and new notification types we are bringing to Server-to-Server Notifications.

So, as we were thinking about some new features we could add to the notification, we were taking a look at the receipt fields currently in the notification, latest receipt, and latest receipt info.

We noticed that these receipts, while useful, only give you information about the latest in-app purchase.

So, we were thinking how much more useful it would be if we could give you your entire subscription history when we send you the server-to-server notification.

For this reason, we are bringing the unified receipt to the server-to-server notification.

[ Applause ]

So, as a review, the unified receipt contains the history of subscription purchases for your subscription.

Previously, this incredibly valuable information could only be obtained through hitting verifyReceipt.

The two receipt fields in the notification right now, latest receipt, and latest receipt info provide an encoded and decoded transactional receipt about the latest in-app purchase.

Starting in the fall, you'll begin to see one new field in the server-to-server notification.

We have decided to call this unified receipt, and it will contain almost exactly what you expect to get from verified receipt.

With the addition of unified receipt to the server-to-server notification, in most cases, this will make latest receipt and latest receipt info no longer needed.

However, there is one important caveat we must call out here.

This receipt that we are generating for you is not tied to a specific install of an app on a device, like the receipts you are used to receiving as the result of a buy.

For this reason, the receipt should always be stored on your server, and never locally on a device.

So, with that out of the way, let's take a look at what you can expect to find in the unified receipt in the server-to-server notification.

The first field you will find in this JSON object is the latest receipt.

This is an encoded unified receipt, which we have just generated for you, and you can use this to hit verified receipt later, if you should need it.

You will also find the latest receipt info.

This contains an array of subscription purchases for your subscription, with metadata about them to help you track what has been going on with your subscriber.

You will also find the pending renewal info.

This contains information about the upcoming renewal for your subscription, such as if the customer is in a price increase flow, or they have entered a billing retry period.

We will also include the status of the receipt, and the environment the receipt was produced in, either sandbox, or production.

We chose to name the fields this way because this mirrors what you expect to receive from verify receipt.

So, hopefully you can reuse your parsing logic there to make the transition easier here.

However, latest receipt info will be limited to the 100 latest in-app purchases.

So, should you need more information that this, you can always hit verify receipt with the provided encoded receipt.

So, let's look at the notification types we currently have.

There are currently four existing notification types.

INITIAL BUY, INTERACTIVE RENEWAL, DID CHANGE RENEWAL PREFERENCES, and CANCEL.

And, we are adding four more.

DID CHANGE RENEWAL STATUS, DID FAIL TO RENEW, DID RECOVER, and PRICE INCREASE CONSENT.

[ Applause ]

Now, let's take a quick look at each of those four new notification types, so we can get an idea of why they are sent.

First, let's look at DID CHANGE RENEWAL STATUS.

This is sent when the user toggles auto-renew on or off.

You should actually be receiving this notification right now, so make sure that you're looking for it if you are currently using our Server-to-Server Notifications.

We will soon be adding a notification type called DID FAIL TO RENEW.

We will send this to you when a subscription fails auto-renew at the first attempt to renew in a subscription period.

You will start to see this notification in the fall.

Hand-in-hand with DID FAIL TO RENEW comes DID RECOVER.

DID RECOVER will be sent when we recover billing of your subscription during the billing retry period.

This will also start to appear in the fall.

Should you receive DID RECOVER, you should have recently received DID FAIL TO RENEW.

And, you can know that billing of your subscription was successfully recovered.

If you are currently using our Server-to-Server Notifications, you may notice that we will be sending DID RECOVER when we currently send our renewal notification type.

The plan is for DID RECOVER to eventually replace RENEWAL as it is a bit more aptly named, but for a period, when we start sending DID RECOVER, we will send you both DID RECOVER and RENEWAL to give you time to adjust to the change.

Finally, we're adding a fourth notification type, PRICE INCREASE CONSENT.

PRICE INCREASE CONSENT will be sent to you when we detect that one of your subscribers has entered a price increase flow, which requires their consent in order for them to continue renewing their subscription.

With this notification, comes a new field in the JSON payload, price increase effective date.

This is the date by which the customer must agree to the price increase in order for them to continue renewing.

You may also expect to see this notification in the fall.

So now that we've taken a look into what's new in Server-to-Server Notifications, I'm really excited to talk to you about how you can handle all eight of our notification types so you are getting the most out of each notification when you receive it.

So, first, let's take a quick overview of our existing notification types.

INITIAL BUY, INTERACTIVE RENEWAL, DID CHANGE RENEWAL PREFERENCES, and CANCEL.

We're going to take a moment to take a deep-dive into each of these notification types, but before we do that, I want you to notice one trend up here on this chart.

You'll notice that in the JSON payload, we're asking you to look for the original transaction id for each notification type.

This is because the original transaction id is considered a unique identifier for your subscription.

And, keeping track of this will help you to link subsequent events back to the initial purchase of your subscription.

Now, let's imagine for a moment that you have a potential subscriber.

Let's call him John, and he is interested in purchasing your subscription.

Let's take a walk through decisions that John makes concerning his subscription, and what notifications you will receive along the way.

So, the first notification type you can expect to receive for a subscription is the INITIAL BUY.

When John first purchases his subscription, we will send you an INITIAL BUY notification.

Upon receiving this notification, you can update the customer status to something like "active" or "subscribed" on your server, and provide service for the newly purchased subscription.

In this notification type, there are four fields that I want you to look for in the JSON payload.

The first of these is the purchase date.

This is in milliseconds since epoch, and it will tell you the exact date and time that your customer has purchased your subscription.

You should next look for the original transaction id.

As I mentioned, this is a unique identifier for your subscription, and keeping track of this now will let you link subsequent notifications back to this initial buy.

You should also check for the web order line item id.

This is considered a unique identifier for each subscription period and should you need to hit verify receipt after receiving this notification, it will link this notification to an entry in the verifyReceipt array.

Finally, you should look for the product id.

The product id will tell you exactly which product your new customer has subscribed to.

So, after John has been using his subscription for a while, he decides that he wants to upgrade his service to a higher tier.

We consider this a renewal in the foreground, so we will send you an INTERACTIVE RENEWAL notification type.

Because an upgrade gives the customer access to the higher tier immediately, we will also send you a CANCEL notification type for the lower tier subscription, which we have canceled.

However, should a customer resubscribe after churn, you will receive only an INTERACTIVE RENEWAL notification type.

In this notification, there are four more fields you should look for in the JSON payload.

The first of these will be the purchase date.

This will tell you the exact date and time that your customer either resubscribed to this subscription or upgraded their service.

You should again be checking for the original transaction id to link this back to the original subscription, as well as the web order line item id.

This is the unique identifier for each subscription period, and it will help you to link this notification to an entry in the verifyReceipt array.

Finally, check for the product id.

This will tell you the exact product that your customer has re-subscribed to or upgraded their service to.

A little later down the line, John decides to downgrade his subscription to a more basic tier.

In this case, we send you a DID CHANGE RENEWAL PREFERENCES notification type.

Upon receiving this notification, you can update the customer's subscription status on your server to a more basic tier.

In this notification type, there are two fields that I want you to look out for.

The first of these is the auto renew product id.

Because a subscription downgrade does not take place until the end of the subscription period, this will tell you exactly which product your customer will auto-renew at when it comes time for renewal.

You should again be checking for the original transaction id to link this notification back to the original subscription.

Now, unfortunately, a little later, John gets on the phone with customer support and decides to cancel his subscription.

In this case, we will send you a CANCEL notification type.

As I mentioned earlier, when a customer upgrades their subscription, you will also receive a CANCEL plus an INTERACTIVE RENEWAL, where the CANCEL signifies the lower tier subscription which was canceled.

In this notification type, I want you to look for three fields.

First, you should look for the cancellation date.

This will tell you the exact date and time that your customer decided to cancel their subscription.

You should still be looking for the original transaction id to link this back to the original subscription purchase, as well as the product id, to know exactly which product your customer has canceled.

So, now that we've looked at all of our existing notification types, let's give the same treatment to the four new notification types that I introduced earlier.

These are DID CHANGE RENEWAL STATUS, DID FAIL TO RENEW, DID RECOVER, and PRICE INCREASE CONSENT.

In these notification types, you still want to be looking for the original transaction id in each of the JSON payloads.

As I mentioned earlier, this is because it is a unique identifier for your subscription and will help you to link all of your notifications together.

Now, let's revisit John.

One day he is scrolling through his managed subscriptions page, and decides to turn auto-renew back on for your subscription.

In this case, we will send you a DID CHANGE RENEWAL STATUS notification type.

You will also receive this notification if a customer decides to turn auto-renew off.

Upon receiving this notification, you can update the customer subscription status on your end to reflect the changes.

You can optionally deploy retention strategies to keep the customer, if you see that they have turned auto-renew off.

In this notification type, there are four more fields that you should look for and mark down.

The first of these is the auto renew status change date.

This tells you the exact date and time that your customer's auto-renew status changed.

You should check for auto-renew status.

This will tell you the direction of the auto-renew toggle.

Should you see the auto renew status has a value of true, you can know that your customer has turned auto-renew back on, and intends to continue buying your subscription.

You should again be checking for the original transaction id, to link this back to the original subscription purchase, as well as the product id, to see exactly which product your customer has turned auto-renew on or off for.

As we are trying to bill John for his subscription during the auto-renew period, we unfortunately encounter a billing error.

In this case, we will send you a DID FAIL TO RENEW on our first attempt to renew for that subscription period.

Upon receiving this notification, you can optionally choose to suspend service for your customer.

You can also update the customer subscription status to something like "active" or "billing re-try," depending on the value of the fields you see in the JSON payload.

So now, let's take a look at that.

The first field you should be looking for here is is in billing retry period.

This has a value of 0 or 1, and will tell you if we are actively trying to recover billing of this subscription for you.

Should you see that this field has a value of 1, you can know that we are trying to recover billing of your subscription in the billing re-try period.

You should still be checking for the original transaction id to link this back to the original subscription, as well as the expires date.

This will tell you the exact date and time that we attempted to auto-renew your subscription, and it failed.

Fortunately, during the billing re-try period, the billing issue with John's subscription was resolved, and we are able to recover billing of his subscription.

In this case, we send you a DID RECOVER notification type.

As a reminder, DID RECOVER is replacing our RENEWAL notification type, which we currently send at this time.

Upon receiving this notification type, you can restore service for the recovered subscription, and update your customer's status to something like "active" or "subscribed," or whatever you use to denote an active subscriber.

In this notification type, there are a few more fields that I want you to look for in the payload.

You should first check for the purchase date.

This will tell you exactly when we were able to recover billing of this subscription.

You should again check for the original transaction id, as this will tell you exactly which subscription we recovered billing for, as it is the unique identifier for the subscription.

Finally, look for the expires date.

This will tell you the date and time that this new subscription period will expire.

And, you can expect us to attempt to auto-renew again.

Now, let's suppose that you, the developer, decide to increase the price of your subscription.

When we check for a price increase for a subscription, 7 days before a weekly subscription renewal, 10 days before a monthly subscription renewal, and 30 days before an annual subscription renewal, and we see that you wish to raise the price, we will send you a PRICE INCREASE CONSENT notification for your customer.

Upon receiving this notification, you can update the user status on your end to something like "price increase."

And, optionally, you can deploy in-app messaging to prompt your customer to consent to the price increase.

Note that we will also email and send push notifications to the customer to prompt them to consent to this price increase.

In this notification type, there are a few more fields that I want you to look for.

First, you should look for the price consent status.

This will tell you if your customer has already consented to your price increase.

However, because we are sending this notification to you, almost as soon as we detect the price increase, you should expect in most cases that this value will be 0, or that the customer has not yet consented.

You should again be checking for the original transaction id to link this notification back to the original subscription.

Finally, I want you to look for the expires date.

This will tell you the date and time that this subscription period will expire.

And, by that time, your customer will have had to consented to the price increase.

So, now that we've taken a look at what's new to Server-to-Server Notifications, and what you can do to handle these notifications, when you receive them, I'm going to invite Manjeet Chawla onstage to talk to you about subscription lifecycle, and reducing churn.

[ Applause ]

Good morning.

My name is Manjeet Chawla, and I'm on the App Store commerce scene.

I'm really excited to be here today, and talk to you guys about how you can use the new and improved Server-to-Server Notifications to identify billing events through a subscription lifecycle that may impact your customer's subscription status.

So, what does the lifecycle of a subscription look like?

Now, you may start acquiring new customers by offering them a free trial, or an introductory price to attract them to your service, and let the users try your service before paying the full price.

Next, you keep them engaged to your service by providing them constant updates to your content.

And, finally, you try to retain them as active subscribers by minimizing churn.

Now, we all know that during this lifecycle, there's a number of different billing events that can occur for most of your subscribers.

And, today, you're probably calling the verifyReceipt endpoint for all your subscribers to get these billing event changes.

Now, we know that this is not efficient, and can be costly over time, as your subscription business grows.

But, the improvements that we saw earlier to Server-to-Server Notifications, you no longer have to rely on the receipts to reflect these billing event changes.

So, let's talk more about how you can use the new Server-to-Server Notifications to detect these billing events, and build the best subscription experience for your customers.

So, let's start with the initial purchase of a subscription.

Now, we all know that this initial purchase unlocks content over a set period of time.

When the customer purchases their subscription in your app, you will receive a transaction through StoreKit on the device, and a receipt associated to that transaction.

At the same time, you'll receive a new notification called INITIAL BUY for this newly purchased subscription.

Using this notification, you can identify a newly purchased subscriber that has never purchased your subscription before.

Now, you hang onto that initial buy notification JSON, and at the same time, you send your receipt from the device to your server over a secure connection.

And then, to the verifyReceipt endpoint to validate the contents of the receipt.

In the response that you get back from the App Store, you then check for the contents of the JSON, and you update your user database by looking for information for the latest purchase that the user made.

Finally, that notification that you stored previously, you can link the response that you got from the App Store through this verifyReceipt, and link that to the initial buy notification by using original transaction id, and the web order line item id.

Now, when the subscription is ready for renewal, the App Store will automatically renew the subscription for you in the background.

And, the next time the user launches their app on the device, you'll receive a new transaction, and a receipt associated to that transaction.

You, again, base64-encode that receipt, and send that securely to your server.

And, let's say your user is consuming their service on a different platform, and you don't want to rely on the user launching the app to detect this renewal.

You can also use the receipt data that you stored previously from the initial purchase, and send that to your server.

Next, you send that to the verifyReceipt endpoint, and check for the renewal transaction in the JSON response.

In the response, you can validate the latest renewal for that user, and you update the latest expires date, based on that subscription.

And, finally, you keep service on for the customer as the subscription successfully renewed.

Note that for this event, there is no server-to-server notification.

So you have to call verifyReceipt based on the information that you have from the initial purchase to check for the renewal transaction.

Now, let's say that the customer has been loving your service, and they enjoy their basic subscription service, and they decide to upgrade your service to your premium product, maybe a higher tier of service.

Now, for this upgrade, you'll receive a CANCEL notification from the App Store on your server.

And, in the content of the JSON, there will be a cancellation date notifying you that the App Store has canceled the previous subscription for the user.

Followed by the CANCEL, you'll also receive an INTERACTIVE RENEWAL notification on your server.

Use this notification to update your users' database with the date of the upgrade.

So, now you know that this user has upgraded their subscription to a higher tier service, or a premium product.

And, finally, you unlock the premium content for the user in the app.

Now, at some point, the customer decides that this content isn't right for them, and they want to cancel their subscription.

They do this by turning off auto-renew inside Manage Subscription settings on the device.

Now, today, you might be relying on the receipt to reflect the latest renewal status.

You may even be calling verifyReceipt for all your customers, just to get the renewal status of their latest subscription.

Now, we talked about this earlier.

Tori mentioned the new notification, by using that you no longer have to rely on verifyReceipt for that renewal status change.

And now, you get the DID CHANGE RENEWAL STATUS notification any time a user turns off or on their auto-renew status from the Manage Subscriptions settings.

You use this event to update the renewal status of the subscription to false, because in this case the user turned off auto-renew.

And, you update their subscription status.

Now, let's say that you don't receive any other notifications for this user until the end of their subscription period.

You can safely assume that their subscription has now churned at the end of their current subscription period.

So, you can update the subscription status for your user as "inactive" or "churned."

Now, over time, as your subscribers will churn out, you want to try and win them back by giving them an attractive offer, either a discounted price, or maybe even a free trial.

We launched a new feature recently called Subscription Offers that allows you to give that discounted price, or a free trial, to keep existing subscribers, or win back previously churned subscribers.

We have a session on Subscription Offers later today, where my colleagues will go through best practices in implementing subscription offers.

And, the different use cases on how you can use subscription offers to improve your retention numbers.

Now, let's take a look at a slightly different case, where the user had no intention to cancel your subscription, but the App Store was unable to recover or renew the subscription on the user's behalf.

Maybe the user's credit card was invalid, or maybe they did not have sufficient funds in their account.

For this event, the App Store will now send you a DID FAIL TO RENEW notification.

Using this notification, you know that the user's subscription failed to renew due to a billing issue.

And, you can update your subscription status for that customer to "inactive" or "churned."

You use this notification to show them a message inside the app, letting them know that their subscription has expired.

Now, for billing-related issues, the App Store automatically makes several attempts to renew the user's subscription over time.

If an attempt succeeds, and if you're able to charge the customer for that subscription, you'll now receive a DID RECOVER notification.

So, using this notification, you update the user database for that customer to "subscribed," and you note the new expires date for that newly renewed subscription.

And, you re-enable service for that customer.

So, now we saw in the last example, that the App Store makes several attempts to renew subscriptions over a period of time.

Now, this happens automatically for you, but how can you as a developer respond to these attempts, and what can you do on your end to reduce involuntary churn?

Last year, in Engineering Subscriptions, we talked about our extended billing re-try logic that attempts to renew the subscription over a period of time.

This is deployed when a subscription fails to renew due to a billing issue.

And, since launch, we have been making constant updates and tuning to our strategies to optimize recoveries.

We're also looking at advanced machine learning models to improve how we recover subscriptions across the platform.

Now, as we look at performance since launch, you can see that we're recovering more than 77% of subscriptions that failed due to a billing issue.

And, by recovering these subscriptions, we have been able to reduce the overall involuntary churn to less than 2% across the platform.

[ Applause ]

And, with all these updates and improvements that we've made to our billing re-try strategies, we have been able to recover over 46 million of your subscriptions.

[ Applause ]

Now, these subscriptions would have churned otherwise, and users had no intention to cancel their service.

Users were renewing and enjoying their service, and they did not want to cancel their subscription.

And, we see across the platform that more than half of these subscriptions are still active.

Now, as we look at how we are recovering these subscriptions over time, over the period of 60 days, you can see that we recover more than 77% of subscriptions that failed due to a billing issue over the period of 60 days.

And, more than 80% of these recoveries happen in the first 16 days.

So, what happens when we recover these subscriptions?

First, you reenable service, as Tori mentioned earlier, with the new notifications.

You may re-enable service for the customers next time that they try to access their service.

We start a new billing cycle for that subscription from the date of recovery.

And, the days of paid service that the customers were accumulating toward that higher revenue share, 85/15, resumes from that day of recovery.

So, if you were offering service to customers during this period from the day they started billing re-try to the day of recovery, you're probably missing out on revenue for those days.

How can we improve that?

So, this fall, I'm excited to announce that we're launching a new feature that will allow you to offer a grace period, a billing grace period, that allows additional time for customers while they're enjoying their service, they have access to that paid content.

And, it allows you, the developer to get additional revenue for any service that you provide during those days.

[ Applause ]

Now, this creates a better experience for all your customers, who recovered naturally over a period of time, because they never expressed an intent to cancel.

This also recovers customers who maybe had a temporary credit card hold on their account.

Now, let's take a look at how you can implement this feature.

Well, it's really easy.

It takes three simple steps to implement a billing grace period.

First, you opt-in via App Store Connect to offer pre-configured durations for grace period.

Now, based on the recovery data that we saw earlier, we're going to start with 6 days for weekly subscriptions, and 16 days for all other durations.

Next, you look for a new field in the verifyReceipt response, or in the notification.

This will allow you to know the latest expiry date for your service.

And, you keep service on for customers during the period.

Now, you may be thinking, as a developer, why should you opt-in to offer a billing grace period?

Well, there's a number of benefits.

First, your customers never intended to cancel your subscription, so they enjoy accessing your service without any interruptions.

This also allows you, or your customers to maintain their existing billing cycle, as we recover their subscription during this billing grace period.

And it enables you, the developer, to earn additional revenue for the service that you provide during this billing grace period.

And, as we saw earlier, it allows customers to accumulate those 85/15 higher revenue share days of paid service at a faster rate.

I highly encourage you to look into grace period for your subscriptions when we launch this later this fall.

Now, in addition to offering a grace period, what are some of the other things that you can do to reduce overall involuntary churn, and improve recoveries?

Here we see an example, where inside the app, by showing contextual messaging to your customers who are in the billing re-try state, you can let them know that their subscription has failed to renew due to a billing issue.

And, if you were offering a grace period, you can focus your messaging on any grace period service that you are providing to your customers.

Now, you can be creative with how you devise this messaging.

It can be towards the end of the grace period.

Maybe you are providing premium content, and they will lose content at the end of that grace period.

So, highlighting the premium content that they're going to lose at the end of the grace period will help you recover those subscriptions.

Here we see an app, Foodvisor, showing a message to customers in billing re-try, and showing them the remaining duration for the premium content.

Now, within this message, you can also embed a deep link to the payment details page, where the customers can then go and resolve any billing issues.

As you can see, we've recently updated this Payment Details page to allow a customer to have up to 10 different payment methods on account.

Now, in addition to providing easier payment management options to customers, this also helps the App Store to reduce overall involuntary churn by charging alternate payment methods on file.

Now, we covered a lot of topics today in this session.

But, just to summarize, we talked about Subscription Offers, our new feature that we launched recently that allows you to reduce voluntary churn by giving customers a discounted price, or a free trial to retain their subscription.

Dana walked us through the code to implement a new API called SKStorefront which you can use to display the right content to your customers around the world.

We talked about some receipt changes that are coming soon, that allow you to reward your loyal customers who pre-ordered the app, maybe by giving them a starting balance of a game.

And, if you haven't already done this, check out the Server-to-Server Notifications, and add that URL in App Store Connect to get notified for the billing events from the App Store.

We talked about grace period, and how you can provide a billing grace period to improve recoveries for customers that their subscriptions failed to renew because of a billing issue.

And, we talked about contextual messaging, that you can display inside the app to recover more of your subscriptions.

For more information on this session, and the video for this session, look up Session 302.

And, later this afternoon, my colleagues will talk about subscription offers best practices.

They'll tell you how to implement subscription offers, and the different use cases for using subscription offers to reduce voluntary churn.

And, we'll be around in the lab to answer any questions you may have about these features.

Thank you and have a great rest of your afternoon.

[ Applause ]

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