Unity IAP 5.0.1 FetchProducts fetches a list of 0 products

I have an urgent issue with the new mandatory version of the new unity IAP package version 5.0.1 for Android.

My specific problem is that the function _storeController.FetchProducts(catalog.GetProducts()); returns a list with 0 products inside the OnProductsFetched callback. The “catalog” variable is a var catalog = new CatalogProvider();
I have tried every possible solution and fix that I can find but it does not work (IAP package is enabled in project settings, unity services properly connected to online project, product IDs match with google play store, the products are active in the play console etc). Also, the project previously worked with version 4.x of the unity IAP package and the list of products hasn’t changed since then. Yes, I am calling this code after unity game services have been sucessfully initialized.

Here is the full code

// ... after having initialized unity game services
_storeController = UnityIAPServices.StoreController();
_storeController.OnStoreDisconnected += OnStoreDisconnected;
_storeController.OnProductsFetched += OnProductsFetched;
_storeController.OnProductsFetchFailed += OnProductsFetchFailed;
_storeController.OnPurchasesFetched += OnPurchasesFetched;
_storeController.OnPurchasesFetchFailed += OnPurchaseFetchFailed;
_storeController.OnPurchasePending += OnPurchasePending;
_storeController.OnPurchaseFailed += OnPurchaseFailed; 
await _storeController.Connect();

var catalog = new CatalogProvider();
foreach (AbstractIAPOffer iapo in _rewardCatalog) // _rewardCatalog is a list of scriptableobjects that contain the IDs of the various In-App products. I rely on this instead of the built-in catalog
{
    StoreSpecificIds customIDs = null;
    if (iapo.HasCustomGooglePlayStoreID)
    {
        if (customIDs == null)
            customIDs = new StoreSpecificIds();
        customIDs.Add(iapo.GooglePlayStoreID, GooglePlay.Name);
    }
    if (iapo.HasCustomAppleAppStoreID)
    {
        if (customIDs == null)
            customIDs = new StoreSpecificIds();
        customIDs.Add(iapo.AppleAppStoreID, AppleAppStore.Name);
    }
    ProductType consumability = iapo.Consumability == AbstractIAPOffer.ProductType.Consumable ? ProductType.Consumable : ProductType.NonConsumable;
    if (customIDs != null)
        catalog.AddProduct(iapo.ProductID, consumability, customIDs);
    else
        catalog.AddProduct(iapo.ProductID, consumability);
}

//catalog.FetchProducts(productList=>_storeController.FetchProducts(productList)); // I don't know if this is the equivalent of the following. Documentation doesn't help and it also sucks
Debug.Log($"[{nameof(IAPManager)}] before fetch: catalog.Getproducts() = {catalog.GetProducts().Count}");
_storeController.FetchProducts(catalog.GetProducts());

and

private void OnProductsFetched(List<Product> products)
{
    if (products.Count <= 0)
    {
// THIS IS INVOKED. BAD! BAD!
        Debug.LogWarning($"[{nameof(IAPManager)}] The backend fetched {products.Count} products"); // this message is a bit redundant but just to be sure
        return; // there was an error. The store controller will invoke the OnProductsFetchFailed next
    }

    // Good. now handle fetched products  
    _productsFetched = true;
    Debug.Log($"[{nameof(IAPManager)}] Products fetched. The backend fetched {products.Count} products");
    _storeController.FetchPurchases();
}

private void OnProductsFetchFailed(ProductFetchFailed failed)
{
    if (_productsFetched)
        Debug.Log($"[{nameof(IAPManager)}] Products fetch failed, but it was previously successfull. Ignoring failure. (Failure reason: {failed.FailureReason})");
    else
        OnInitializationFailed("Products fetch failed", failed.FailureReason); // THIS IS ALSO INVOKED. BAD! BAD!
}

Hi @spicysparks

I’m taking a look at this. Could you provide the FailureReason you log on line 19 or 21?

I can address some of your BAD! BAD! comments, though :sweat_smile: IAP 5.0.1 invokes both the success and failure callbacks when FetchProducts returns an empty list, which is not the intended behaviour. We’re working on a fix right now that should be included in the 5.0.2 patch release.

Hello. The failure reason is Retrieve Products failed, and could not retrieve any products.

I have tried building both an APK and .AAB and installing the app via both, and the error persists in both. This is also true if I install the app via Google Play store with an internal release. Could it be possible that the C# code is correct, but the project is misconfigured in the unity dashboard/not linked correctly to the google play console? Are there some additional error messages I should look for when inspecting the Android Logcat, for example “could not connect to Google Play Store” or something like that?

I already mentioned it, but the project worked fine when it used the 4.x version of the package, and I didn’t add new IAP products or change the unity dashboard configuration since then

I’m having the same problem, where all I see is “ProductFetchFailed” with no additional details. Have you found anything helpful about it? It was working previously with IAP 4.

Nope, and I’m honestly out of ideas. I’ve tried every random solution I found on internet, tried asking 3 different AI agents and none seem to help, I’ve asked on unity discord and they ignored me, ignored on reddit as well

I am having this same issue with In-App Purchasing 5.02. OnProductsFetched is not being triggered. OnProductsFetchedFailed is being triggered and the failure reason is “Retrieve Products failed, and could not retrieve any products.”

Edit: I should clarify that this fails when running on Android. It works when running in the Unity Editor with the fake store.

Here is the code:

        store_controller = UnityIAPServices.StoreController();

        store_controller.OnPurchasePending += OnPurchasePending;
        store_controller.OnPurchaseFailed += OnPurchaseFailed;
        store_controller.OnStoreDisconnected += OnStoreDisconnected;
        store_controller.OnProductsFetched += OnProductsFetched;
        store_controller.OnProductsFetchFailed += OnProductsFetchedFailed;
        store_controller.OnPurchasesFetched += OnPurchasesFetched;
        store_controller.OnPurchaseConfirmed += OnPurchaseConfirmed;

        await store_controller.Connect();

        var initialProductsToFetch = BuildProductDefinitionsFromEconomy();
        if (initialProductsToFetch.Count == 0)
        {
            Debug.LogWarning("[IAPManager] No real-money products found in Economy config.");
            return;
        }

        store_controller.FetchProducts(initialProductsToFetch);
    private List<ProductDefinition> BuildProductDefinitionsFromEconomy()
    {
        var productDefinitions = new List<ProductDefinition>();
        var realMoney = EconomyService.Instance.Configuration.GetRealMoneyPurchases();

        foreach (var purchase in realMoney)
        {
            var def = new ProductDefinition(id: purchase.Id, type: ProductType.Consumable);
            productDefinitions.Add(def);
            Debug.Log("IAPManager: Product definition added. ID: " + def.id + " StoreSpecificID: " + def.storeSpecificId);
        }

        Debug.Log($"[IAPManager] Prepared {productDefinitions.Count} ProductDefinitions for fetch.");
        return productDefinitions;
    }
    private void OnProductsFetched(List<Product> products)
    {
        Debug.Log("OnProductsFetched - Products: count: " + products.Count);
        foreach (var product in products) { Debug.Log(product); }
        store_controller.FetchPurchases();
    }

    private void OnProductsFetchedFailed(ProductFetchFailed failed)
    {
        Debug.Log("OnProductsFetchedFailed - Reason: " + failed.FailureReason);
    }

Same issue here with iOS. Would love to hear from anyone at Unity staff.
@SashaM maybe there is some debug mode we can enable? Right now we are hard-stuck on this issue and there is no going around it…

@Halvus

Can you check that you are calling await EconomyService.Instance.Configuration.SyncConfigurationAsync(); before attempting to BuildProductDefinitionsFromEconomy(), and add some debugging so you can validate the product IDs in your List<ProductDefinition> is a 100% match for the available products in your Google Play Store > IAP configuration.

@amoswazana

Similar advice, put some debugging in to check the product IDs in your List<ProductDefinition> perfectly match available, enabled, products in your Apple Connect Store configuration.

Hi Laurie

EDIT: read my latest post below - I found the issue after all. I’ll leave this here to showcase my mistake!

Yes, I’ve double-checked and I can confirm that I call await EconomyService.Instance.Configuration.SyncConfigurationAsync(); before attempting BuildProductDefinitionsFromEconomy()

I have tested with IAP 5.0.1 and 5.0.2 in both the Unity client (with fake store) and on a Pixel 7 Pro running Android 16 (using Google Play Store). All the products on the Google Play console match the UGS Economy configuration.

Here are the debug outputs from the console and Android Logcat for each test:

Test 1: 5.0.1 - Unity Client console output

Test 2: 5.0.1 - Android Logcat output (filtered for IAPManager logs)

Test3: 5.0.2 Unity Client console output

Test 4: 5.0.2 Android Logcat output (filtered for IAPManager logs)

Also, here are screenshots of one of the economy item config setups

UGS configuration

Google Play configuration

As you can see, the Google Play product id configured in the UGS economy item matches the item in the Google Play console.

Note that the UGS Resource ID (“GEMS80”) has to be uppercase, whereas the Google Play item id (“gems80”) has to be lower case. I’m making an assumption that the UGS Resource Name (“Gems80” in this case) is not relevant to the match-up.

OK, so having written the last two posts I realised my issue.

I changed one line of code to fix the problem:

var def = new ProductDefinition(id: purchase.Id, storeSpecificId: purchase.StoreIdentifiers.GooglePlayStore, type: ProductType.Consumable);

And here is the debug logs from Android Logcat showing that OnProductsFetched is now being triggered:

The issue was that the StoreSpecific ID was defaulting to the UGS Product ID, which is all uppercase and therefore it didn’t match the lower-case Google Play product ID.

For anyone having the same issue, I managed to find the culprit in my case too. And the culprit is the CatalogProvider class. Apparently, when you invoke

catalog.AddProduct(productId, consumability, customIDs);

and “customIDs” contains custom store identifiers for both google and apple, and those identifiers are different, the CatalogProvider will add a ProductDefinition in its internal list whose “storeSpecificID” is the apple ID, even if you are currently running on android.
This way, if you invoke

var products = catalog.GetProducts();

you will get a list of ProductDefinition whose “storeSpecificID” is the Apple ID even if you are on Android (or even the unity editor!). I think this causes the backend to fail to fetch the products, if the apple ID is different from the ID you used in android.

The solution is to create the list of ProductDefinition by hand, this is the code I used for my specific scenario

List<ProductDefinition> catalog = new List<ProductDefinition>();
foreach (AbstractIAPOffer iapo in _rewardCatalog)
{
    ProductDefinition pd;
    ProductType consumability = iapo.Consumability == AbstractIAPOffer.ProductType.Consumable ? ProductType.Consumable : ProductType.NonConsumable;
    if (Application.platform == RuntimePlatform.Android && iapo.HasCustomGooglePlayStoreID)
        pd = new ProductDefinition(iapo.ProductID, iapo.GooglePlayStoreID, consumability);
    else if (Application.platform == RuntimePlatform.IPhonePlayer && iapo.HasCustomAppleAppStoreID)
        pd = new ProductDefinition(iapo.ProductID, iapo.AppleAppStoreID, consumability);
    else
        pd = new ProductDefinition(iapo.ProductID, consumability);
    catalog.Add(pd);
}
Debug.Log($"[{nameof(IAPManager)}] Before fetch: catalog.Count = {catalog.Count}");
_storeController.FetchProducts(catalog);

@Laurie-Unity @SashaM I’m sure this is not the intended behaviour of the CatalogProvider class and you may want to investigate. I’ve investigated the CatalogProvider source code myself and I think the culprit is

// Extract our store specific ID if present, according to the current store.
var specificId = AddStoreSpecificIds(id, storeIDs);

at line 77 of the CatalogProvider.cs class. That function should return the android Id if you’re on android, and the apple ID if youre on’ apple. Instead, by looking at what the function does, it seems that it simply returns the last customStoreID of the product (which is the Apple ID in my case).

Hello @spicysparks

Thank you for the detailed report, in the case where GetProducts is called without specifying the store’s name, this does return the last store specific id which is incorrect.

A workaround for your case is to invoke the GetProducts with the store name:
var products = catalog.GetProducts(DefaultStoreHelper.GetDefaultStoreName());

We will be fixing the issue with the AddStoreSpecificIds and review how we can make this easier to avoid having to specify the store name when using the default.