According to the documentation, regarding SetServiceDisconnectAtInitializeListener(),
For example: a user first installs the app with the Play Store. Then the user removes their Google account from the device. The user launches the game and Unity IAP does not finish initializing, preventing the user from purchasing or restoring any prior purchases. To fix this, the user can add a Google account to their device and return to the game.
What I found when implementing and testing this approach was that I saw both the OnInitializeFailed() and the service disconnect listener called immediately when it attempted to initialize IAP. When I left the app to log into my Google account and returned, IAP did not reinitialize. I had to close the app and restart it in order to get IAP working again.
I later tried implementing a mechanism to restart IAP at a later time by calling UnityPurchasing.Initialize() if OnInitializeFailed() was called. I found that the callbacks weren’t invoked, so it didn’t actually reinitialize. This was true whether I left the app to log into the Google account or not.
I also want to know what we can do in the event that the device has disabled IAP and the user leaves the app to enable it and returns to the app afterwards without restarting it. Can we or should we manually reinitialize IAP in this case? Should we tell our users to restart the app instead?
What is the proper way for IAP to recover from an initialization failure?
Manual restart: Should we manually call a series of functions to reset the state of IAP and restart it? If so, which ones and when?
Auto restart: Should we instead wait for things to recover on their own? If so, can we get any visibility into what is happening? What is an acceptable timeframe to wait, and when would we know if that retry process failed?
App relaunch: Should we just tell our users to restart the app once they’ve resolved the issue?
On IAP 4.12, when OnInitializeFailed gets called, it means IAP tries to initialize with a few retries, but failed.
In this case, you could either let your users relaunch the app or try calling back the UnityPurchasing.Initialize in the OnInitializeFailed, ideally with an exponential backoff retry.
When you tried implementing the mechanism to restart IAP, one of the callbacks should have been called. Did any error occur on the device logs that you could share?
If there wasn’t any, could you share code snippets where you call the UnityPurchasing.Initialize so we can try to reproduce this on our end?
On IAP 5.0.0, which we are still working on, we have made significant changes to the architecture which will allow handling these errors without reinitializing IAP or closing the application.
Thank you for your response. My reinitialization mechanism calls UnityPurchasing.Initialize from a MonoBehaviour component in the scene when it is needed. Is there something special about calling the reinitialize from OnInitializeFailed? Say, if it’s from a different thread?
Here is basically what my restart method does.
public class IAPReInitProcess : MonoBehaviour {
void OnEnable() {
InvokeRepeating("ReInitActions", 10, 10);
}
void OnDisable() {
CancelInvoke("ReInitActions");
}
void ReInitActions() {
if (!(<Initialization Failed>)) {
Debug.Log("Did not ReInitialize IAP");
return;
}
Debug.Log("ReInitialized IAP");
IAPManager.Instance.Initialize();
}
}
IAPManager
public void Initialize() {
// load catalog and create configuration builder, then put products into builder.
...
googlePlayBuilder.SetServiceDisconnectAtInitializeListener(() => {
Debug.LogError($"IAP Initialization Failed: Unable to connect to the Google Billing Service. Please ensure that you are logged into your Google account.");
});
googlePlayBuilder.SetQueryProductDetailsFailedListener((int retryCount) => {
Debug.LogError($"IAP Initialization Failed: Failed to query product details {0} times., retryCount)}");
});
...
Debug.Log("Initializing IAP");
UnityPurchasing.Initialize(this, builder);
}
void IStoreListener.OnInitialized(IStoreController controller, IExtensionProvider extensions) {
Debug.Log("IAP IAPManager::OnInitialized");
...
}
void IStoreListener.OnInitializeFailed(InitializationFailureReason error) {
Debug.LogError($"IAP Initialization Failed: error = {error}");
...
}
void IStoreListener.OnInitializeFailed(InitializationFailureReason error, string message) {
Debug.LogError($"IAP Initialization Failed: error = {error}, message = {message}");
...
}
I can work on making a repro project if necessary. Here is the logcat output, filtered for my application, at around the times of the initialization and reinitialization. It includes a few reinitialize calls back to back. If I leave the app, log into my Google account, and reenter the app, my restart process behaves the same way. I just see my log messages printed repeatedly every 10 seconds (because that was the interval I set for InvokeRepeating).
Your code seems fine.
The main reason for calling it in the OnInitializeFailed is that you’re calling it right away when it’s needed.
Your logs seem filtered on “Unity”, but is there any error that might come from another source after the call to IAPReInitProcess:ReInitActions? (could be IAP or BillingClient)
Before I saw the first time initialization errors, I saw this message from billing client.
2024/10/09 14:44:54.674 12632 13183 Warn BillingClient In-app billing API version 3 is not supported on this device.
About two minutes after restarting IAP, I saw these messages from BillingClient, but nothing more from Unity after my logs telling me that I attempted to reinitialize IAP:
2024/10/09 14:46:15.227 12632 12632 Warn BillingClient Billing service disconnected.
2024/10/09 14:46:15.264 12632 12632 Warn BillingClient Billing service disconnected.
2024/10/09 14:46:15.287 12632 12632 Warn BillingClient Billing service disconnected.
2024/10/09 14:46:17.025 12632 13619 Warn BillingClient In-app billing API version 3 is not supported on this device.
2024/10/09 14:46:17.025 12632 13617 Warn BillingClient In-app billing API version 3 is not supported on this device.
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient Exception while checking if billing is supported; try to reconnect
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient java.lang.NullPointerException: Attempt to invoke interface method ‘int com.google.android.gms.internal.play_billing.j7.m(int, java.lang.String, java.lang.String)’ on a null object reference
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient at com.android.billingclient.api.zzay.zza(Unknown Source:154)
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient at com.android.billingclient.api.zzav.call(Unknown Source:2)
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient at java.util.concurrent.FutureTask.run(FutureTask.java:264)
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
2024/10/09 14:46:17.026 12632 13618 Warn BillingClient at java.lang.Thread.run(Thread.java:1012)
To be clear, this is the case when the user has not attempted to log into the Google account before reinitializing IAP.
After investigating this, it seems we had silenced the callbacks in the specific case where the billing service is unavailable.
This is something that is already changed for IAP 5.0.0 coming out soon where all cases of a failed initialization will be recoverable.
As a workaround, I would recommend retrying the UnityPurchasing.Initialize again as long as you havn’t received the OnInitialized with a few seconds interval between tries.
Regarding log messages after “ReInitialized IAP”, in terms of those belonging to the Unity app (when filtering log cat for my application’s package name), there is nothing other than my log statements. If I don’t filter by my package name, there are too many log messages after “ReInitialized IAP” from all kinds of processes on the device. I’ve attached printouts from both the filtered and unfiltered logs.
Unfortunately, when I tried calling UnityPurchasing.Initialize() repeatedly once every ten seconds, I still see the problem where there are no listeners, and it won’t detect success or failure. after reinitialize logs.txt (21.5 KB) after reinitialize logs unity only.txt (758 Bytes)
When will 5.0.0 be available? Until then, are there temporary changes that I can make so that 4.12 can restart properly?
In order to properly restart it, was calling UnityPurchasing.Initialize() sufficient after conditions have been fixed? Or is there some other function that I should call first to shut it down and force it to be recreated from scratch as if the app has been restarted? Currently, we need to restart the app if an initialization fail condition has been fixed.