Hello,
I am trying to get started with Consumable IAP on Android.
My general flow based on the IAP sample and Unity - Manual: Processing Purchases is
1 I call InitiatePurchase
2 Unity calls ProcessPurchase
2 a) I start a Coroutine that send the receipt to my server
2 b) I return PurchaseProcessingResult.Pending
3 Coroutine sends a request to my server
3 a) Server consumes the purchase using Google’s API
3 b) Server adds the consumable to the user’s account
3 c) Server sends back a response
4 Coroutine calls ConfirmPendingPurchase
The issue I am seeing is that sometimes, somewhere between 3 c) and 4 (or during 4?) I see OnPurchaseFailed
get triggered and the logs say
Error consuming purchase with token. Response code: 8
onPurchaseFailedEvent(productId:premium_credits_10 message:Item is not owned by the user. {code: ItemNotOwned, M: GPSFTS.HFT})
I suspect this is maybe because there is a race and 4 ends up being called before 2 b) happens? Or it may be because I am consuming the purchase on the server side and then ConfirmPendingPurchase
tries to do it again on the client?
So my questions are:
- What is the correct usage of
ConfirmPendingPurchase
in this case? In the IAP sample it’s bound to a button - surely we are not meant to ask the user to tell us this?
- a. Since I am consuming the purchase myself on the server, do I even need to call ConfirmPendingPurchase at all? Unity - Manual: Processing Purchases implies that the unity IAP API will consume the purchase (I don’t want this, since this means that between when I credit the user on the server and when the purchase gets consumed there is a gap during which many bad things can happen, invalidating the transaction).
- How do deferred purchases factor into this? (AFAICT I just have to wait for ProcessPurchase to get called with store extension IsDeferred returning false?
Thank you for the detailed reply!
My followup here is that since my server validation flow happens in an async coroutine, which is started from ProcessPurchase, what should I return from process purchase while it runs? If I return Pending then it expects me to call ConfirmPendingPurchase, so do I just return complete, even though the purchase validation may fail on the server?
Since you do purchase validation on your server, you should return Pending in ProcessPurchase.
When the server validation is successful, you want to call the ConfirmPendingPurchase, otherwise, do nothing.
Right, so this is what I did, and so I was getting the error I mentioned in the original post.
Sorry, I forgot to refer to your initial post with my last reply.
Since you are consuming the purchase on the server side, then you don’t want to call ConfirmPendingPurchase on the client side (it does the same thing) since you can only consume it once.
You also want to return Pending to ProcessPurchase, this makes it not call ConfirmPendingPurchase (this is the only additional call we do when ProcessPurchase returns Complete).
1 Like
Thanks Yannick, so just to confirm, I return Pending and never confirm it in unity (only on my server, directly with the google play/ios stores). There are no repercussions to this (unity won’t revert/refund the purchase or something after some timeout?).
Correct, just return pending and never confirm in Unity, then confirm through your server.
The case you’re worried about seems to be the 72h refund on the Google Play Store, you can test this with the test-card, if something goes wrong, it will be refunded in 3 minutes:
Once you confirmed the purchase on your server, you will be able to see that the transaction was confirmed on your Google Play Console in the Order Management and it won’t get refunded.
Another test you can do is doing a restore transaction or closing/reopening the application. All the pending purchases will be sent to the ProcessPurchase again. If you confirmed the purchase, you won’t see it go through the ProcessPurchase.
1 Like