On Android platform with Google Play as the default store, if the Google Play Services is not available on the device, there won’t be any callback after calling UnityPurchasing.Initialize()
Neither OnInitialized nor OnInitializeFailed will be called.
The expected behavior should be calling OnInitializeFailed so that we can handle the failure case.
The easiest way to reproduce is to start an Android emulator using an image without Google APIs, and then build and run the basic IAP sample.
The workaround that we are using is to call OnInitializeFailed if we detected that the device does not have Google Play Services, and also add a timeout coroutine if UnityPurchasing.Initialize does not trigger any callback within the time limit.
I believe that it is a bug or at least should be documented somewhere. There are quite a lot of Android that does not come with Google Play services, particularly some of the Chinese brands. Therefore it is important that we could handle them properly. It would be great if the IAP team can address this issue. Thank you~
Good point. What version of IAP are you using? The latest is 4.1.3. So you know, IAP is never expected to work on an emulator for security reasons (requires an actual credit card), but your point is taken. To support Chinese and other global markets, you would want to use UDP instead https://unity.com/products/unity-distribution-portal Also, where are users installing your game from? So users without Google Play are downloading from the Google Play store? Perhaps I’m not following.
Thanks for the reply!
I can reproduce this issue on both 4.1.3 and 4.0.0.
Actually, I do not intend to publish the app to the Chinese market, but I would like to make the game works for those who are using Chinese phones at least. They are most likely installing the game with apk directly.
It is perfectly fine if IAP doesn’t work for them, but I would expect that if IAP failed to init, it will throw an exception or call the OnInitializeFailed callback, not failing silently.
How are they installing the game directly? You do have a point, but I’m trying to understand how a game published on Google Play with IAP would be installed on phones without the Google services. What device are you testing on? (not an emulator)
Oh I see. Although we only publish it on Google Play, it is pretty easy to extract the apk and obb from an Android phone and distribute it elsewhere. There are a lot of third-party services that do that, such as https://www.taptap.com/, https://apps.qoo-app.com/, and https://apkpure.com/, etc. Taptap and Qoo-App are particularly popular among gamers in Southeast Asia I believe. They even provide reviews, time tracking, and a lot of other features for mobile gamers. They also provide an app for installing games and automatically updating them, essentially a third-party market for games.
Originally they are used to play region-locked Japanese games mostly, as a lot of Japanese games are only available in the Japan Google Play. Now they are also used for other games as well. They are popular in China as there is no google play and these services are the only (convenient) way to access foreign games.
Do I answer your question on how the game is installed?
We do not have a device that we can test this issue, so I can only use an emulator for it… But we have quite a lot of reports from Chinese players claiming that they would be stuck loading the store in our games. After investigation, we found that the reason that they are stuck is due to IAP init not calling any callback.
I believe that the result from the emulator test is helpful. I test the IAP sample(01 Buying Consumables) on 2 emulator images.
The first one has Google APIs, and it is able to call the initialized failed callback.
The second one has no Google APIs, and it is stuck without any callback.
So your game was pirated! Quite common on Android unfortunately. I don’t believe you would expect to see IAP revenue from those sources unless you use our UDP platform. Regardless, we are aware of this issue and working on a solution. You should see the same behavior if the user is not logged into Google Play on the device, can you confirm?
Thank you! I can confirm the same behavior if the user is not logged into Google Play on the device. There is no callback if the device does not have any Google account.
We are unfortunately running into this issue too, using Unity IAP 4.10.0:
Unity IAP fails to fire off any failure events after player signs out of all their google accounts, and this ends up preventing our in-game store from loading up, so pretty nasty.
A workaround I did find though was to import Unity IAP locally, and in GooglePlayStoreRetrieveProductsService.cs, change the following method:
void OnRetrieveProductsFailed(GoogleRetrieveProductsFailureReason reason, GoogleBillingResponseCode responseCode)
{
// Code here manually changed to fix issue where no failure callbacks fired after initializing unity purchasing and player signs out of all google accounts.
if (reason == GoogleRetrieveProductsFailureReason.BillingServiceUnavailable /*&& !m_HasInitiallyRetrievedProducts && !m_RetrieveProductsFailed*/)
{
if(!m_HasInitiallyRetrievedProducts && !m_RetrieveProductsFailed)
{
m_GooglePlayConfigurationInternal.NotifyInitializationConnectionFailed();
m_RetrieveProductsFailed = true;
}
m_StoreCallback?.OnSetupFailed(InitializationFailureReason.PurchasingUnavailable, $"GoogleBillingResponseCode: {responseCode.ToString()}");
}
}
After some testing on device, it seems this allows OnInitializeFailed(InitializationFailureReason error, string message), along with our fetch products failed functionality, to be called as expected. This in turn ensures our in-game store is able to load up rather than just getting stuck due to the player not being signed into any google accounts.
My thought process is perhaps the folks over at Unity only intended to lock NotifyInitializationConnectionFailed() behind the m_RetrieveProductsFailed and m_HasInitiallyRetrievedProducts flags, and not m_StoreCallback?.OnSetupFailed() as well?
@JeffDUnity3D It would be great if you’d be able to give some confirmation as to whether this is a safe change to make? I’m a bit nervous making changes to this code since it’s a codebase i’m not too familiar with (even though the change does seem to fix the issue as far as I can tell), or perhaps an official fix from Unity is near?