Check If an Auto-Renewable Subscription Is Still Valid

Check if an Auto-Renewable Subscription is still valid

Here is several ways to do receipt validation to check is user granted to subscription. Here is two ways of doing it correctly:

  1. Do receipt validation locally as it is written here.
  2. Do receipt validation remotely as it is written here. It is mentioned that receipt should not be sent to App Store within an app. Short summary:

    • Your app sends receipt to your backend.
    • Your backend sends receipt to Apple backend for validation.
    • Your backend gets response from the apple.
    • Your backend sends result back to your app is receipt valid or invalid.

In both ways you will get list of in-app purchases. It will contain expired subscriptions as well. You would need to go through all subscriptions and check expiration dates. If it is still valid you must grant user with subscription.

As I understand you are using SwiftyStoreKit and here is open task for local receipt validation.

How to check In App Purchase Auto Renewable Subscription is valid

IF you want to check on it from a web server, you ping their API and it returns the status of the auto-renewable subscription and info about the last payment. link

If you are on the device then you probably have to call restoreCompletedTransactions which I guess asks for the password.

I don't see any other method. I suppose from the device you could verify the subscription by contacting the same web service used on the server side? I don't know how the pros and cons of that.

How to detect and verify a renewal for an auto-renewable subscription?

My experience. Let's assume, we always send initial receipt to Apple's server.

In any case, you'll get JSON with at least two fields: status (no comments) and receipt (information about receipt that you've send).

Additionally to that:

1) If the subscription is still active, you'll additionally get latest_receipt (base64-encoded string) and latest_receipt_info (information about that receipt).

2) If the subscription is already expired, you'll additionally get latest_expired_receipt_info (information about last renewing receipt). Yes, you get only information about it, no base64-encoded string.

And yes, AFAIK, that's not documented anywhere. Hope that helps.

How to check purchase status of In-App Purchase (Auto Renewing Subscription)?

The only way to check the subscription status is to verify the receipt with Apple to see if it's still valid using their /verifyReceipt endpoint - docs.

What you could do is cache some expiration date after the purchase and use that to check if the subscription is valid. If it's passed the expiration date you can re-check the receipt with Apple to see if it's renewed. There are also edge cases where a user is refunded and their subscription is cancelled before the expiration date - you should update your receipts periodically with Apple to check this case. Ideally, this should all be happening server side as well to avoid piracy.

Here's a great post that summarizes the nauces of Apple subscriptions very well: iOS Subscriptions are Hard

Swift How to handle Auto-renewable Subscription receipt and validation

If you do not have the possibility to use a server, you need to validate locally. Since you are already included TPInAppReceipt library, this is relatively easy.

To check if the user has an active premium product and what type it has, you can use the following code:

// Get all active purchases which are convertible to `PurchaseType`.
let premiumPurchases = receipt.activeAutoRenewableSubscriptionPurchases.filter({ PurchaseType(rawValue: $0.productIdentifier) != nil })

// It depends on how your premium access works, but if it doesn't matter what kind of premium the user has, it is enough to take one of the available active premium products.
// Note: with the possibility to share subscriptions via family sharing, the receipt can contain multiple active subscriptions.
guard let product = premiumPurchases.first else {
// User has no active premium product => lock all premium features
return
}

// To be safe you can use a "guard" or a "if let", but since we filtered for products conforming to PurchaseType, this shouldn't fail
let purchaseType = PurchaseType(rawValue: product.productIdentifier)!

// => Setup app corresponding to active premium product type

One point I notice in your code, which could lead to problems, is that you constantly add a new SKPaymentTransactionObserver. You should have one class conforming to SKPaymentTransactionObserver and add this only once on app start and not on every public call. Also, you need to remove it when you no longer need it (if you created it only once, you would do it in the deinit of your class, conforming to the observer protocol.

I assume this is the reason for point 2.

Technically, the behavior described in point 3 is correct because the method you are using asks the payment queue to restore all previously completed purchases (see here).

Apple states restoreCompletedTransactions() should only be used for the following scenarios (see here):

  • If you use Apple-hosted content, restoring completed transactions gives your app the transaction objects it uses to download the content.
  • If you need to support versions of iOS earlier than iOS 7, where the app receipt isn’t available, restore completed transactions instead.
  • If your app uses non-renewing subscriptions, your app is responsible for the restoration process.

For your case, it is recommended to use a SKReceiptRefreshRequest, which requests to update the current receipt.

Auto-Renewable Subscription never expires in Sandbox

The only way to determine if the subscription is still active for the user is through receipt validation.

  1. The best way to do this is to store the receipt file on your server and periodically refresh it with Apple's /verifyReceipt endpoint to see if anything has changed. (See iOS Subscriptions Are Hard for more detail).

  2. If you are just looking for something quick and dirty, you can do the validation locally, and store the expiration date of the subscription in User Defaults. On launch check if the device time is before the expiration date. You'd only need to revalidate if the device time is after the expiration date to check if the subscription renewed.

The second approach is an insecure, non-scalable way to work with subscriptions, but it would "work" for a little side project. The recommended way is through server-side receipt validation and status tracking mentioned in #1.



Related Topics



Leave a reply



Submit