41

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.

rigdonmr
  • 2,662
  • 3
  • 26
  • 40

4 Answers4

37

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)")
        }
}
Theo
  • 3,826
  • 30
  • 59
rigdonmr
  • 2,662
  • 3
  • 26
  • 40
  • 3
    I do agree with your great answer however, For security concerns, Its good idea to check expiration date on YOUR server side with that of apple. ` let currentTime = NSDate().timeIntervalSince1970 let expired = currentTime > expiresTime` If user changes the device's date to past, that check might pass, – abdimuna Mar 28 '17 at 13:46
  • good point. Suppose my server side is nothing but Firebase though? – rigdonmr Apr 04 '17 at 07:18
  • I haven't worked with firebase in that scenario, but its cool if you cold control the validation on your back end. – abdimuna Apr 04 '17 at 09:59
  • How to get a single user payment information/receipt after successful in app purchase or recurring subscription, or cancel subscription I need to maintain this information to my server. any approach? – Amit Kumar Sahu May 12 '17 at 08:23
  • 13
    Warning from Apple: "Important: Do not call the App Store server /verifyReceipt endpoint from your app" – adib Jul 08 '18 at 09:24
  • @adib Why...... – Eric May 30 '22 at 19:42
  • Apple's documentation clearly advises against using this endpoint from within the application: https://developer.apple.com/documentation/appstorereceipts/validating_receipts_with_the_app_store – Nicolas Prugne Dec 12 '22 at 10:26
2

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
    }
}
enc_life
  • 4,973
  • 1
  • 15
  • 27
1

What are you trying to achieve in particular? Do you want to check for a specific Apple ID?

I highly doubt that this is possible through the SDK. Referring to Is it possible to get the user's apple ID through the SDK? you can see that you can't even ask for the ID directly but rather services attached to it.

What would work is caching all transactions on your own server and search its database locally but that would require the app to ask for the user's Apple ID so the app could update the subscription state whenever it launches as it can check for IAP of the ID associated with the device.

However, the user could just type whatever he wanted - and it's unlikely to get this through Apple's app review process.

Community
  • 1
  • 1
user2875404
  • 3,048
  • 3
  • 25
  • 47
  • 1
    Okay so Apple ID was a bad example. I was just trying to convey that I'm looking for a way to check if a subscription is active. Say a user subscribes, then deletes their subscription right after - I need a way to detect that. – rigdonmr Aug 10 '16 at 15:07
  • @rigdonmr do you mean cancels the subscription or cancels auto-renew? if the former, the process is described under cancellation [here](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html) – Lesleh Aug 10 '16 at 15:16
  • 1
    Need to handle both cases I guess. My overarching question is, is there an API that lets me send _something specific to the user's subscription_ (receipt, transaction ID, etc.) and I get a status back of something like "Active" "Cancelled Auto Renewal" "Cancelled Completely". See what I'm going for? – rigdonmr Aug 10 '16 at 15:26
1

I am using MKSoreKit https://github.com/MugunthKumar/MKStoreKit for auto-renew subscriptions.but it is in objective c you can check the library code for solution.I am using it in my code and it is working fine.

using below method you can easily check subscription status..

if([MKStoreManager isProductPurchased:productIdentifier]) {
 //unlock it
}

It gets the apple id from device and I think that is user specific

Rishabh
  • 391
  • 3
  • 13