hamobi
hamobi

Reputation: 8130

iOS - IAP problems, confused about app store receipt

I made a game. I didnt expect it to become wildly popular so I didn't care to write complicated cryptography code for receipt validation. My game's IAP (to remove ads) was working perfectly in sandbox mode. The app has been released live for about the last 16 hours and there is a problem. The app is behaving as if everyone has purchased the "remove ads" IAP.

this is my extremely basic receipt validation code:

override init(){
    super.init()

    // storekit delegation
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)

    if SKPaymentQueue.canMakePayments() {

        let request = SKProductsRequest(productIdentifiers: NSSet(object: self.productID))
        request.delegate = self
        request.start()
    }

    self.checkReceipt()

}

func checkReceipt(){
    if let url = NSBundle.mainBundle().appStoreReceiptURL {
        if let receipt = NSData(contentsOfURL: url) {
            self.adsRemoved = true
        }
    }
}

Questions:

  1. in my sandbox environment.. the receipt variable wouldn't get unwrapped unless I had purchased the IAP. self.adsRemoved wasn't being set to true. It seems like on the app store itself, these variables always get unwrapped. What's up with that? What should i do.

  2. I know now that it's standard for IAP to become active a day or two after app submission. Could this be related to that? I know if I hit my restore button nothing happens. In my sandbox environment it asks for credentials.. shows a success message, etc.

Upvotes: 1

Views: 1656

Answers (2)

junjie
junjie

Reputation: 7966

  1. in my sandbox environment.. the receipt variable wouldn't get unwrapped unless I had purchased the IAP. self.adsRemoved wasn't being set to true. It seems like on the app store itself, these variables always get unwrapped. What's up with that? What should i do.

Your checkReceipt() method checks for the presence of the receipt at the URL given by -appStoreReceiptURL.

This is problematic because every app that is downloaded from the App Store will contain a receipt, whether the app is free or paid.

This is documented in Apple's Receipt Validation Programming Guide:

When an application is installed from the App Store, it contains an application receipt that is cryptographically signed, ensuring that only Apple can create valid receipts. The receipt is stored inside the application bundle. Call the appStoreReceiptURL method of the NSBundle class to locate the receipt.

So why did it work for you in sandbox mode? This is because sandbox apps installed via Xcode contains no receipt, until either of the following occurs:

  1. A receipt is requested via SKReceiptRefreshRequest (link)
  2. When an in-app purchase is made, or
  3. When previous transactions are restored via -restoreCompletedTransactions

So in a nut shell, your checkReceipt method will definitely set adsRemoved to true for anyone who has downloaded your game from the App Store, because you're checking for the PRESENCE of a receipt, which for reasons above, does not mean anything at all to you.

What you need to be doing is validating whether the receipt is (1) legit, and (2) whether it indicates that the correct in-app purchase has been made.

  1. I know now that it's standard for IAP to become active a day or two after app submission. Could this be related to that? I know if I hit my restore button nothing happens. In my sandbox environment it asks for credentials.. shows a success message, etc.

Not true that IAP takes a day or two to activate after app approval, and no—the problem occurred because you're checking for the presence of the receipt, and not validating the receipt.

Unfortunately, receipt validation is complex, but necessary if you want to achieve what you want to achieve (i.e. check if an IAP has been made). You'd want to refer to this objc.io article on receipt validation for more information about how to actually validate the receipt. At bare minimum, the steps involved are:

  1. Locate the receipt, which is what you're already doing.
  2. Verify the receipt is legit and not tampered with.
  3. Parse the receipt to figure out the IAP has been made, because a receipt exist even without purchasing the IAP

Upvotes: 4

Deepak
Deepak

Reputation: 31

You have to validate the receipt sending the receipt data for Apple provided url. Later, based on the response you will receive you have to set the value self.adsRemoved = true/false.

Upvotes: 0

Related Questions