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
Sometimes when I call [[FIRMessaging messaging] subscribeToTopic:myTopic] (or unsubscribe) I see errors logged in the console. Sometimes they have error codes, and other times it's just a message like this:
Cannot unsubscribe to topic: /topics/my_topic with token: (null)
This one appears to be because it doesn't think it has a token, though usually when I see this I've already given it a token.
There is, however, no apparent programmatic way to know when an error occurred. There's no callback passed to the subscribe methods, and their return types are void. I think I read somewhere in my Googling of these errors that the library will retry on its own, but I can't find that back now, and it's not in the documentation for FIRMessaging anywhere. The error codes are also not documented anywhere that I can find.
One error code I remember seeing specifically is 5. My implementation used to simplistically re-subscribe / unsubscribe from each topic when the user changed any of them; when I modified this to only update the topic that actually changed, that particular error went away, so maybe it was complaining because I was subscribing to a topic that I was already subscribed to, and vice versa?
How do I deal with these errors? Is it true that the library will retry on its own? And can someone link to an error code listing?
You don't have to deal with errors that result from subscribe and unsubscribe, it is retried by the SDK automatically. See docs for more.
Issues with the current error messages is a known, we will improve them in future releases.
I'm testing in-app-purchase functionality and stuck in receipt validation step.
My receipt validation is customized and based on number of checks I proceed on server-side(calling my server API) and one them is condition for transactionID uniqueness to confirm payment.
So, using RMStore I'm getting receipt successfully and addPayment function return unique transaction to success block. After that I'm running verifyTransaction and call receiptURL inside success block.
Unfortunately, it looks like I'm always get the same receipt using receiptURL and when I send it to my server it responds me with error that transactionID already exists in DB and transactionID is not unique. This error indicates that I send the same receipt even I do new payment. Please, note I use consumable product.
Can someone show me the right workflow for functions I provide below(?):
[RMStore productRequest] - I call it right in init function.
[RMStore addPayment] - It is start of consequence I need to understand.
[[[RMStore defaultStore].transactionPersistor consumeProductOfIdentifier:pID]
-- Do I need to call this function at all? Apple sends me unique transactinID each time I pay and calling this function has no influence.
[[[RMStore] defaultStore].receiptVerificator verifyTransaction:transaction]
-- Do I need to call transaction verification?
[RMStore receiptURL] or [[NSBundle mainBundle] appStoreReceiptURL]
-- are those ways to get receiptURL equal ? Both don't work as I expect.
[NSData dataWithContentOfURL:receiptURL] - here is the main problem I'm experiencing. After different attempts with combination of functions I mentioned upper, this method always return the receipt with the same transactionID. This makes me think that there is a cash I need to refresh before run #5, #6 to get the last receipt, but it is not clear how to do that.
So, RMStore wiki looks good, but I always expect that library will give me a chance do not fully understand what is going on under the hood. Unfortunately for RMStore I have to investigate how Apple in app purchase API works. This can be resolved if a consequence of function calls will be provided, but I cannot find it anywhere(!)
Can someone review the list of functions I provided upper and help me with putting them in right order what let me fix my issue with getting the last receipt after the purchase I do?
I have two answers here to my questions:
The code:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
, works fine(i.e. it return correct receipt body), but for some reason Apple can return TWO(or even array[]) payments where first transactionID can be the same as you previously got. As my server developer said this is unexpected behaviour. So, when I get receipt and send receipt to server it read only first transactionID which could be the same what you got previously.As result, server return FALSE as verification result(it looks like you try to pay twice). So, since it is not clear why Apple send me two or more transaction records in receipt, but as workaround I asked server side developer to check all transactions in receipt for any unique transactionID.
More investigation is needed to understand Apple behaviour but it can take more time. I did not find the answer and go ahead for now.
What is related to RMStore workflow I realised that actually we have just two most important functions: addPayment and verifyTransaction.
The addPayment function will call verifyTransaction implicitly. You should overload verifyTransaction and call serverAPI verification function inside. So, if verification is success you go to success block of addPayment and do what you need for successful payment. If not - do something else. My mistake was to call verifyTransaction inside addPayment successful block what means to call it twice.
In iOS, if the user attempts to purchase IAP, and my server validation of the receipt fails, what is the proper behavior? When testing in sandbox, I just get a ton of pop ups asking for my password. If I call finishTransaction, it stops asking for my password, but I believe this can lead to the user getting charged without receiving a product.
I think that error handling when doing in-app purchases is one of the lesser talked about difficulties of doing in-app purchases. There are several questions here. For example: 1) If you are doing validation of the purchase receipt on the device (i.e., the >= iOS7 style decryption style validation), and that validation fails, what should you do? 2) If you are doing validation with a web-based server, and that fails, what should you do? 3) If you are storing receipts to your own web-based server, and that fails, what should you do? I'll put some ideas here from my own app. It would be great to see what others are doing about this kind of error handling.
1) If you are doing validation of the purchase receipt on the device (i.e., >= iOS7 decryption style validation), and that validation fails, what should you do?
Apple recommends that you do a receipt refresh (SKReceiptRefreshRequest) if your initial validation, on the device, fails. But what if the validation after the refresh fails? You could interpret that situation as there being something seriously wrong (e.g., the user has somehow hacked into your app to intentionally give you a bad receipt), tell the user the purchase has failed with a receipt validation failure, and finish the transaction (SKPaymentQueue finishTransaction:) , failing permanently. However, I don’t like to be so final. Perhaps it’s my confidence, but something could have gone wrong with my programming. I like to give the user more chances. So, my solution is to: 1) Tell the user that validation has failed, 2) put the SKPaymentTransaction into a “holding” state, and 3) call finishTransaction. This idea of a holding state is my invention, and nothing suggested by (or supported by) Apple. I have made a mutable subclass of SKPaymentTransaction, and have a queue of those objects (call them SPASMutablePaymentTransaction), which I store (using NSCoding) into NSUserDefaults. Nearly always, this queue is empty, but if I get a validation failure like I’m talking about here, I create a SPASMutablePaymentTransaction object, copy over the SKPaymentTransaction info (including the receipt, e.g., from the bundle), and save that transaction into my holding state queue. I make a (normally hidden) part of my UI visible, which can allow the user to retry holding state transactions.
Complicated? Yes, a little. However, since we’re dealing with the user giving you money, I am trying to be robust. It seems to work well so far in testing. I don’t have any feedback from users on this yet (or analytics), but it is deployed to the app store.
2) If you are doing validation with a web-based server, and that fails, what should you do?
For me, this is a similar case to 1), above. You have tried to do a validation of the receipt and it failed. You could first try to refresh the receipt (see above), and then re-try your server validation. In my app, since I always do on-device receipt validation first, so it doesn’t make sense for me to refresh the receipt again (since I would have done that if the on-device receipt validation failed). So, I again put the receipt into a “holding” state (as above), and allow the user to retry the holding state transactions at their discretion.
3) If you are storing receipts to your own web-based server, and that fails, what should you do?
(a) Presumably this should definitely not be a case where you permanently fail to give the user their purchase. This is a failure of your services. In my case this can happen, for example, if I get a network error in communicating with my server. I do something a little different here. I don’t immediately give the user access to their purchase, and I don’t put the transaction into a “holding” state. Instead, I set up a timer to retry the process of storing the receipt to my server. It retries again in a couple of minutes. I tell the user what I’m doing. I store the receipt data into NSUserDefaults (again using a SPASMutablePaymentTransaction object), so will persist in this retrying even if the app crashes/is terminated. When I do succeed in saving the receipt to my server, I give the user access to the purchase.
(b) In this case, I do call finishTransaction: and I don't yet give the user access to the purchase. I could avoid some these details by not calling finishTransaction:, and just let my transaction observer deal with this process again (e.g., when the app next starts), but the user would probably have to enter their Apple Id again. My figuring is that this is my problem (i.e., I failed to save the receipt info to my server), so I'm trying to handle it under the covers.
Pop Up A UIAlertView. See Code Below...
self.alert("Purchase Failed", Message: "Please Make Sure You are connected to the internet and try agian")
func alert(title:String, Message:String){
let actionSheet:UIAlertController = UIAlertController(title: "\(title)", message: "\(Message)", preferredStyle: UIAlertControllerStyle.Alert)
// for alert add .Alert instead of .Action Sheet
// start copy
let firstAlertAction:UIAlertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler:
{
(alertAction:UIAlertAction!) in
// action when pressed
})
actionSheet.addAction(firstAlertAction)
// end copy
self.presentViewController(actionSheet, animated: true, completion: nil)
}
The app I'm working on makes calls when the user tells it to, using telprompt to come back to the app when the call is over. Is there any way to tell what caused the call to end? If the call failed, I'd like to do one thing, but if it succeeded and the user just hit "END" I want to do something else. I'm not seeing anything in the documentation about it, though.
TIA
Janene