Handling Apple Grace Period and Billing Retry state

In this post, we will talk about handling payment errors and using Billing Retry with in-app subscriptions. Based on the data from Qonversion, we can see that on average 20% of payments move to Billing Issue state. Around 15% of them return to active subscription status. Enabling Apple’s Grace Period allows you to improve your customers’ experience and increase the percent of payments returned to the active subscription status.

Some common questions:

  • How does Billing Retry work?
  • What is the Grace Period?
  • Best ways to implement the Grace Period in your product

How does Billing Retry Work?

Payment issues are among the most common reasons for losing subscribers. Let’s have a look at the following example:

At the end of a subscription period, Apple tries to charge a client’s card. If an error occurs during this process, the subscriber’s status changes to the Billing Retry state. He can stay within this state up to 60 days. Apple will try to renew the subscription on the upcoming renewal date while the client is in the Billing Retry status. All this time the clients cannot use add-ons (premium) they purchased (subscribed for) in the app. On the chart above we see that after 10 days when a successful payment was made the recurring payments resume. All of this complicates subscription analytics, subscription LTV estimates and leads to lower customer satisfaction.

The user receipt contains the following fields:

is_in_billing_retry_period and expiration_intent

The fields tell us that there was an unsuccessful attempt to receive payment.

"pending_renewal_info": [
    {
        "expiration_intent": "2",
        "auto_renew_product_id": "product.99.trial.3d",
        "original_transaction_id": "10000000306492965",
        "is_in_billing_retry_period": "1",
        "product_id": "product.99.trial.3d",
        "auto_renew_status": "1"
    }
]

What is the Grace Period?

You can allow access to your app’s premium content even if Apple is unable to collect payment from a client. That is exactly what the  Grace Period was designed for. While Apple is trying to collect a payment, the client is granted full access to the app. The Grace Period can be activated for auto-renewable subscriptions. The length of the grace period depends on the duration of your subscription:

Let’s have a look at 2 examples:

Example 1: successful subscription payment while in the Grace Period

Upon the ending of the first auto-renewable period, Apple was not able to collect the payment. Normally, this would instantly move the client into the Billing Retry status and block their access (same question as above) to the app. However, if the Grace Period has been enabled then the user can still access the premium features for a specified period of time. If Apple collects the payment at one of the next attempts while the user is still in the Grace Period the existing payment cycle is not interrupted.

Example 2: payment did not go through during Grace Period

In the second example, Apple was unable to collect the payment during the Grace Period, which leads to changes in the current billing cycle and the client moves into the Billing Retry status with blocked access to the app. Later, if Apple is able to collect payment a new auto-renewable payment cycle is launched starting from the actual payment date.

After enabling the Grace Period option in the app settings, the JSON response you get when validating the payment receipt will include an extra field: grace_period_expires_date_ms

This field shows the expiration date of the premium content access.

"pending_renewal_info": [
    {
        "expiration_intent": "2",
        "grace_period_expires_date": "2020-09-05 23:41:42 Etc/GMT",
        "auto_renew_product_id": "product.99.trial.3d",
        "original_transaction_id": "10000000306492965",
        "is_in_billing_retry_period": "1",
        "grace_period_expires_date_pst": "2020-09-05 16:41:42 America/Los_Angeles",
        "product_id": "product.99.trial.3d",
        "grace_period_expires_date_ms": "1599349302000",
        "auto_renew_status": "1"
    }
]

Right after a successful payment has been made, this field will disappear together with is_in_billing_retry_period.  The field that will show you when the subscription expires is expires_date_ms which is received from receipt.in_app.

"in_app": [
            {
                "quantity": "1",
                "product_id": "product.99.trial.3d",
                "transaction_id": "0000000306492966",
                "original_transaction_id": "0000000306492965",
                "purchase_date": "2020-08-25 02:53:10 Etc/GMT",
                "purchase_date_ms": "1598323990000",
                "purchase_date_pst": "2020-08-24 19:53:10 America/Los_Angeles",
                "original_purchase_date": "2020-08-25 02:53:12 Etc/GMT",
                "original_purchase_date_ms": "1598323992000",
                "original_purchase_date_pst": "2020-08-24 19:53:12 America/Los_Angeles",
                "expires_date": "2020-09-25 02:53:10 Etc/GMT",
                "expires_date_ms": "1601002390000",
                "expires_date_pst": "2020-09-24 19:53:10 America/Los_Angeles",
                "web_order_line_item_id": "000000003253190",
                "is_trial_period": "false",
                "is_in_intro_offer_period": "false"

If the payment did not go through in 60 days, the fields is_in_billing_retry_period and grace_period_expires_date disappear from the receipt and you will able to use  expires_date_ms, but in this case, the subscription is expired with the field auto_renew_status being equal to 0.

Ways to implement the Grace Period in your product

If a billing issue has been detected, it might be a good idea to notify a user about the issue within the app and remind them to update their billing information with the App Store.

You can validate user receipts on your server to catch billing retry state and check if a user is currently in the Grace Period. You can also try Qonversion for that, and save yourself some time to work on your product. With Qonversion all you’ll need to do is to implement the code below:

Qonversion.checkPermissions { (permissions, error) in
  if let error = error {
    // handle error
    return
  }

  if let premium = permissions["premium"], premium.isActive {
    switch premium.renewState {
       case .billingIssue:
         // Grace period: permission is active, but there was some billing issue.
         // Prompt the user to update the payment method.
         break
       default: break
    }
  }
}