Reputation: 8130
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:
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.
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
Reputation: 7966
- 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:
SKReceiptRefreshRequest
(link)-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.
- 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:
Upvotes: 4
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