Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: How to detect if a user is subscribed to an auto-renewable subscription

Hopefully the title is self-explanatory. I'm trying to do something like this:

checkIfUserIsSubscribedToProduct(productID, transactionID: "some-unique-transaction-string", completion: { error, status in
    if error == nil {
        if status ==  .Subscribed {
            // do something fun
        }
    }
 }

does anything like the hypothetical code I've provided exist? I feel like I'm taking crazy pills

Edit

In similar questions I keep seeing a generic answer of "oh you gotta validate the receipt" but no explanation on how, or even what a receipt is. Could someone provide me with how to "validate the receipt"? I tried this tutorial but didn't seem to work.

Edit - For Bounty

Please address the following situation: A user subscribes to my auto-renewable subscription and gets more digital content because of it - cool, implemented. But how do I check whether that subscription is still valid (i.e. they did not cancel their subscription) each time they open the app? What is the simplest solution to check this? Is there something like the hypothetical code I provided in my question? Please walk me through this and provide any further details on the subject that may be helpful.

like image 988
rigdonmr Avatar asked Aug 10 '16 02:08

rigdonmr


People also ask

Do Apple Subscriptions automatically renew?

They automatically renew at the end of their duration until the user chooses to cancel. Subscriptions are available on iOS, iPadOS, macOS, watchOS, and tvOS.


2 Answers

I know everyone was very concerned about me and how I was doing on this - fear not, solved my problem. Main problem was that I tried Apple's example code from the documentation, but it wasn't working so I gave up on it. Then I came back to it and implemented it with Alamofire and it works great. Here's the code solution:

Swift 3:

let receiptURL = Bundle.main.appStoreReceiptURL
let receipt = NSData(contentsOf: receiptURL!)
let requestContents: [String: Any] = [
    "receipt-data": receipt!.base64EncodedString(options: []),
    "password": "your iTunes Connect shared secret"
]

let appleServer = receiptURL?.lastPathComponent == "sandboxReceipt" ? "sandbox" : "buy"

let stringURL = "https://\(appleServer).itunes.apple.com/verifyReceipt"

print("Loading user receipt: \(stringURL)...")

Alamofire.request(stringURL, method: .post, parameters: requestContents, encoding: JSONEncoding.default)
    .responseJSON { response in
        if let value = response.result.value as? NSDictionary {
            print(value)
        } else {
            print("Receiving receipt from App Store failed: \(response.result)")
        }
}
like image 127
rigdonmr Avatar answered Oct 09 '22 12:10

rigdonmr


As some comments pointed out there's a couple flaws with these answers.

  1. Calling /verifyReceipt from the client isn't secure.
  2. Comparing expiration dates against the device clock can be spoofed by changing the time (always a fun hack to try after cancelling a free trial :) )

There are some other tutorials of how to set up a server to handle the receipt verification, but this is only part of the problem. Making a network request to unpack and validate a receipt on every app launch can lead to issues, so there should be some caching too to keep things running smoothly.

The RevenueCat SDK provides a good out-of-the box solution for this.

A couple reasons why I like this approach:

  • Validates receipt server side (without requiring me to set up a server)
  • Checks for an "active" subscription with a server timestamp so can't be spoofed by changing the device clock
  • Caches the result so it's super fast and works offline

There's some more details in this question: https://stackoverflow.com/a/55404121/3166209

What it works down to is a simple function that you can call as often as needed and will return synchronously in most cases (since it's cached).

subscriptionStatus { (subscribed) in
    if subscribed {
        // Show that great pro content
    }
}
like image 2
enc_life Avatar answered Oct 09 '22 13:10

enc_life