We have a verification server set up that receives purchase receipts from our iOS app over SSL. The receipts are then validated against the iTunes Connect server successfully and all works well. However, if we jailbreak the iOS device and install LocalIAPStore via Cydia, the device can purchase anything without the user being charged. The receipts are still sent to our server and successfully validated, but the user on the device is never charged and no real transaction ever occurs. How could it be that Apple servers successfully verify the receipt?!?
Has anyone ran into this scenario with LocalIAPStore?
You wrote "The receipts are still sent to our server and successfully validated". I suspect that you are being handed the same old receipt over and over again. Your server can log the transaction_id in the receipt and refuse to validate any duplicate transaction_id.
Here is something you can try out even tho this will not prevent them from using it.
if ([[NSFileManager defaultManager] fileExistsAtPath:#"/Library/MobileSubstrate/DynamicLibraries/LocalIAPStore.dylib"]) {
NSLog(#"Local IAP Store detected");
}
This in not very effective, but it might stop someone from doing it. You may want to make your own server (server-sided app) for the app to make it impossible for them to use LocalIAPStore and such.
Related
I integrated in app purchase in my app and i tested it with created test account. It was working fine but when i submitted it to app store for live in app review my app got rejected due to:
Guideline 2.1 - Performance - App Completeness
When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production,” you should validate against the test environment instead.
From WWDC 2013 Session 308 at 44:00:
"The app reviewers are actually testing your production signed, ready-to-go-into-the-store binary, but against the test environment. So your production signed app will see test environment receipts".
What this means is that if you're using a server to validate your receipt, make sure to validate the receipt against the production server first, and if that validation fails, then validate the receipt against the sandbox server.
This is what is instructed to do in the rejection reason you received:
"The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production,” you should validate against the test environment instead."
Please also see the following answers to similar questions:
https://stackoverflow.com/a/49398571/5948155
https://stackoverflow.com/a/21515982/5948155
Actually I am using this function to purchase:
func purchase(_ purchase: RegisteredPurchase) {
self.view.showLoad()
// NetworkActivityIndicatorManager.NetworkOperationStarted()
SwiftyStoreKit.purchaseProduct(bundleId + "." + purchase.rawValue, atomically: true) { result in
// NetworkActivityIndicatorManager.NetworkOperationFinished()
self.view.hideLoad()
print(result)
if case .success(let purchase) = result {
self.refrenceNumber = purchase.transaction.transactionIdentifier!
// print(purchase.transaction.transactionDate)
self.sendRefrence()
if purchase.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
}else{
if let alert = self.alertForPurchaseResult(result) {
self.showAlert(alert)
}
}
}
}
It working fine in testing environment but in review its response is :
paymentInvalid
How can i change environment for purchase.
Actually I understand the issue, but when I am purchasing in app purchase product. I am not validating receipt. I am direct purchasing and after calling purchase function as i updated above it gives error .paymentinvalid. where i have to validate or change or where i have to give sandbox or production url to purchase or check. Because my function is not checking any thing directly gives error.
The documentation for didInvalidatePushTokenForType says its optional to implement and also says this
This method is invoked if a previously provided push token is no
longer valid for use. No action is necessary to request registration.
This feedback can be used to update an app's server to no longer send
push notifications of the specified type to this device.
Why on earth therefore would somebody not want to implement this? If the token is no longer valid then a server will never be able to send Voip pushes to that device again, so doesn't the app on the handset want to know as soon as possible if its invalided so it can send a new token to the server?
I've been trying to search for info and use of didInvalidatePushTokenForType() but it seems everybody just copy and pastes this method into their source code because everybody else has copy and pasted it. But nobody seems to ever do anything with it.
But seems to like it should be a vitally important method to make use of, so why does nobody apparently?
When would the token become invalid?
Thanks for the asking great question.
1) When would the token become invalid?
If we are update app from the App Store then APNS token doesn't change.
reinstalling the OS or Update OS or Reset iOS device then APNS token does change(Upgrade or downgrade OS).
Device token Invalidated or expired after 2 Years.
iOS9 and later device token changes if I reinstall an app.(As per my experience and Knowledge).
Download app from App Store then run your code using X-Code in this case device token will change.
2) Important of didInvalidatePushTokenForType() or Why? didInvalidatePushTokenForType() is optional
Let's clarify about didInvalidatePushTokenForType() method.
Once token got changed then called didInvalidatePushTokenForType() and didUpdatePushCredentials() method, So all code is placed in didUpdatePushCredentials() instead of didInvalidatePushTokenForType().
That's why Developer doesn't give important to didUpdatePushCredentials() method.
Find Reference from Here
When providing consumable In App Purchases on the Windows 10 Store, there are FullfillmentResults when ReportConsumableFullfillmentAsync is called.
The user of my app has had their IAP fullfilled by the time I get this result. This means they have their Coins/Gems/Potatoes.
But if I receive FulfillmentResult.PurchaseReverted, then what happened? How did the user just revert the purchase? Am I meant to withdraw their Coins/Gems/Potatoes?
What are scenarios behind the other error messages?
Note: I'm working with using Windows.ApplicationModel.Store
But if I receive FulfillmentResult.PurchaseReverted, then what
happened? How did the user just revert the purchase? Am I meant to
withdraw their Coins/Gems/Potatoes?
The value PurchaseReverted means the transaction is canceled on the backend and users get their money back. So you should disable the user's access to the cosumable content (withdraw the Coins/Gems/Potatoes) as necessary.
What are scenarios behind the other error messages?
NothingToFulfill : The transaction id has been fulfilled or is otherwise complete
PurchasePending: The purchase is not complete. At this point it is still possible for the transaction to be reversed due to provider failures and/or risk checks. It means the purchase has not yet cleared and could still be revoked.
ServerError: There was an issue receiving fulfillment status. It might be the problem from the Store.
Succeed: The fulfillment is complete and your Coins/Gems/Potatoes can be offered again.
Here is the documentation about FulfillmentResult Enum
I am working on a mobile app using Ionic (with Cordova purchase plugin) but this question is more general. We are using In App Purchases (IAP) and are currently getting an error when we try to finish the consumable purchase. Our current flow is like this:
Get list of our Products from Apple and render on our IAP page
User clicks consumable IAP they want and it fire off a message to StoreKit initiating the purchase
We get a response with a consumable IAP object with the state set to approved.
We initiate the verification procedure with a callback to our own server where we hit apple up to verify the purchase and then log it on our database and send the app a 200 response (not sure if we need to send back the IAP object here with receipt from our server or we just work with the one already inside the app?)
We try finish the purchase where we get an error saying (InAppPurchase[objc]: Cannot finish transaction)
My question is assuming this is the correct flow what does the finish method do? Looking in the source code of the Cordova Purchase Plugin wrapper I can see it sets the state of the object to finished but I am assuming (I couldn't find the code where this happens) it also talks to Apple so that Apple marks the purchase as finished on their side? If we manually set the state to finished the IAP error goes away but the consumable can still not be purchased multiple times which means to me that Apple also need to close it. Is this a correct assumption? Any other tips to getting this to work would also be appreciated.
I'm not sure how cordova handles IAP, but on the apple side, the transaction needs to be finished by calling finishTransaction. It would appear thats the step thats not working.
One thing that might happen due to the delay in going to the server to validate is that the original transaction object has expired, and calling finishTransaction with it does nothing. At this point you might be able to search for your transaction in: [[SKPaymentQueue defaultQueue] transactions]
If you can grab it from there then call finishTransaction then it should work. Not sure how you do this with cordova but I hope this helps.
According to https://developer.apple.com/library/ios/documentation/StoreKit/Reference/StoreKitTypes/Reference/reference.html, SKErrorPaymentCancelled means that user cancelled a payment request. However, in our production app, we are getting a failed transaction with error code = SKErrorPaymentCancelled but there was a charge to the user's account (user later sent it a receipt as a proof). Looking at the error, it said "cannot connect to itunes store". Are we doing something wrong? Why is the user getting charged even though the error is SKErrorPaymentCancelled?
Note that we also listen to transaction when the app starts to capture all missed transaction that comes our way.