Android: Checking if a user already purchased an IAP

Hello!

I’m trying to query Google for IAP the user already purchased and no matter what I do, I don’t get a correct reply

This: (Full image on Imgur)

Is the code I’m currently using. I made sure that I’ve bought an IAP and I want to check if I can tell I did at startup.

This is the relevant part of the log I’m getting:

[2021.11.03-14.16.00:185][271]LogBlueprintUserMessages: [BP_GameMode_Gameplay_C_2147482563] Trying to QUERY FOR OWNED PURCHASES
[2021.11.03-14.16.00:214][271]LogOnlinePurchase: OSS: FOnlinePurchaseGooglePlay::OnQueryExistingPurchasesComplete Response: Ok Num: 1
[2021.11.03-14.16.00:214][271]LogOnlinePurchase: OSS: Adding existing receipt OfferId: basketball_game_product_no_ads TransactionId: [LONG ALPHANUMERIC CODE] ReceiptData: {
	"receiptData": "[LONG ALPHANUMERIC CODE]",
	"signature": "[LONG ALPHANUMERIC CODE]"
}
[2021.11.03-14.16.00:214][271]LogBlueprintUserMessages: [BP_GameMode_Gameplay_C_2147482563] Query form Owned Purchases returned: *Failure* Purchase Status Enum = 'Failed'
[2021.11.03-14.16.00:223][272]LogBlueprintUserMessages: [BP_GameMode_Gameplay_C_2147482563] Query form Owned Purchases returned: *Success* Purchase Status Enum = 'Purchased'
[2021.11.03-14.16.00:224][272]LogBlueprintUserMessages: [BP_GameMode_Gameplay_C_2147482563] Query form Owned Purchases Return an **EMPTY ARRAY**

As you can see, the log starts with printing the message that the event is triggered, it’s this part of the BP:

Then, the internal log of the OnlineSystems says it actually managed to find an IAP the user purchased with this line FOnlinePurchaseGooglePlay::OnQueryExistingPurchasesComplete Response: Ok Num: 1

However then both the On Success and On Failure branches of the node trigger:

And the the success branch confirms that the retrieved array is empty:

This confuses me, it’s clear in the log that it sees the IAP after the query. Why am I getting an empty array as a result?

Also, it’s clearly wrong that both ‘OnSuccess’ and ‘OnFailure’ branches trigger, is it not?

I saw this post Android letting me purchase a non-consumable product more than 1 time - #4 by Ashyankee by @Ashyankee from this question and added his confirmation code to my GooglePlayStoreHelper.java

However as you see, I’m still getting an empty array. Does anyone know how to resolve this issue?

Also, on the above mentioned post, another user @Arturogalaxy suggests a workaround that involves further edits to the GooglePlayStoreHelper.java file. He suggests to inject a string that says ‘owned’ into the Read In App Purchase Information2 node.

My question is this, if the java file knows what IAP are owned then why is this hack necessary? If the Java file knows the user made an IAP, why this information isn’t passed on to the node’s output?

answering to your question the reason why this function does not return correctly the information of the purchased products is because there is a function in the java file that is not implemented correctly, I do not remember with precision what it was, it is described in the other post to which you have made reference, I believe that it has to do with acknowledgePurchase.

For those who have this problem, there is a working solution in that post.

I added @Ashyankee confirmation code. I think this helped, because before I did this I didn’t see the IAP being detected in the log. However, I’m still getting an empty array from the node.

Also, the article you linked in the original post post is from January 2018. Almost 4 years ago. And this problem seems to exist back then as well. I think it’s kind of crazy that a serious problem such as this weren’t addressed in 4 years.

Especially (And I might be wrong) when it seems that the problem isn’t that difficult to fix, because the information is already there in the Java file.

As I read they didn’t have time to get around to it, in any case what you have implemented is the part of @Ashyankee that allows purchases to be registered correctly as non-consumable items. For that function to return the information of the purchased items you have to modify the other function in the java file as I described in the other post. You already have the java file attached in that post so you can simply copy and paste it.

Thank you! I’ll try to integrate it and report back. Though I’m still amazed this isn’t fixed after 4 years. Isn’t the source code for the C++ wrapper over the Java file open? Can’t we fix it ourselves and maybe make a pull request?

EDIT: Also, I wonder if you know, can I do IAP in C++ instead of Blueprints?

As far as I know this did work in 4.24 or similar versions but it stopped working in versions a little less than a year old, sorry I can’t help you regarding c++

@Arturogalaxy ,I have a question, In your code, you removed the ConsumePurchase(String purchaseToken) code, why is that necessary?

EDIT:
Your fix seems to be working as intended:

[2021.11.04-09.52.10:009][185]LogBlueprintUserMessages: [BP_GameMode_Gameplay_C_2147482563] In App Purchase Info:

Offer ID: basketball_game_product_no_ads[OWNED],
Title: Remove Ads (com.pragmaticsystematic.Basketball_CPP,
Description: [ownedSkus:basketball_game_product_no_ads]Removes Ads From The Game,

Though I didn’t remove the ConsumePurchase. I’m still not sure why it’s necessary.

Been working through this and have a couple new things to add to this discussion in case it helps someone. (or I learn more when someone corrects me! :grinning:)

First:
The V2 ReadIAP node fails if the user isn’t logged in to GPS, but that isn’t actually a requirement. The google version of the query function call StoreInterface->QueryOffersById(…) doesn’t even use the ID. It may just be included in the interface for other platforms.

I commented out the IsValid() check on the networkID and it works perfectly. This is obviously a hack, and it seems like the check should be the responsibility of the QueryOffersById function of each platform if some need it and some don’t.

See: UInAppPurchaseQueryCallbackProxy2::TriggerQuery in Engine\Plugins\Online\OnlineSubsystemUtils\Source\OnlineSubsystemUtils\Private\InAppPurchaseQueryCallbackProxy2.cpp

Second:
I found one minor issue with the life saving custom java code, so thought I would add a note here.

The part that adds [Failed] tags can cause the string comparison to fail if there are multiple IAP and the one you are looking for isn’t first in line. (since it adds to the string being compared)

Easy to just remove that, but in case someone gets stuck on it.

Years and UE versions later, does anyone know if these issues have been improved or changed? I’ve been searching through comments like these for days trying to be able to get “restore IAP” functionality working in my game, but most of the info is quite old and there’s no definitive guide to modifying the engine in all of the ways that people have suggested, and no way to know if it would even work with current engine versions (or Android’s current billing system). Any guidance would be so very much appreciated @Beastman and @Arturogalaxy .

I’ve not noticed the problems described while using 5.2. The purchases seem permanent (just don’t set the consumable bool to true when making the purchase).

As for reading purchase info, I’ve had success using “Query for Owned Purcahses”. Bind success/fail events on the output object.