Unity IAP is not working on Android Device

I tried to implement the Unity IAP and in the editor everything seems to work. On Android it´s not working… And it´s driving me mad…
The Logcat says the products are unavailable… No Products Available… Though they all are active and the spelling should be correct as well. I´m also not using my developer account for testing (I did a factory reset before changing). Here is the screenshot of my store setup:

Here are my variables:

 private static IStoreController m_StoreController;          // The Unity Purchasing system.
    private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.

    [HideInInspector]
    public string Coin_200 = "com.epicsauerkraut.loch.coin200"; 
    [HideInInspector]
    public string Coin_500 = "com.epicsauerkraut.loch.coin_500";
    [HideInInspector]
    public string Coin_5000 = "com.epicsauerkraut.loch.coin_5000";
    [HideInInspector]
    public string No_Ads = "com.epicsauerkraut.loch.no_ads";

And here is the log:
01-29 15:46:32.624 18620 18655 I Unity : UnityIAP Version: 1.23.1
01-29 15:46:32.624 18620 18655 I Unity : UnityEngine.Purchasing.StandardPurchasingModule:Instance(AppStore)
01-29 15:46:32.624 18620 18655 I Unity : IAPManager:InitializePurchasing()
01-29 15:46:32.624 18620 18655 I Unity :
01-29 15:46:32.624 18620 18655 I Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51)
01-29 15:46:32.624 18620 18655 I Unity :
01-29 15:46:32.808 18620 18655 I Unity : Using configuration builder objects
01-29 15:46:32.808 18620 18655 I Unity : UnityEngine.Purchasing.StoreCatalogImpl:handleCachedCatalog(Action1) 01-29 15:46:32.808 18620 18655 I Unity : UnityEngine.Purchasing.<Process>d__4:MoveNext() 01-29 15:46:32.808 18620 18655 I Unity : UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr) 01-29 15:46:32.808 18620 18655 I Unity : 01-29 15:46:32.808 18620 18655 I Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51) 01-29 15:46:32.808 18620 18655 I Unity : 01-29 15:46:33.420 18620 18655 W Unity : Unavailable product Coin_200 -Coin_200 01-29 15:46:33.420 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:CheckForInitialization() 01-29 15:46:33.420 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List1)
01-29 15:46:33.420 18620 18655 W Unity : UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String)
01-29 15:46:33.420 18620 18655 W Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update()
01-29 15:46:33.420 18620 18655 W Unity :
01-29 15:46:33.420 18620 18655 W Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51)
01-29 15:46:33.420 18620 18655 W Unity :
01-29 15:46:33.423 18620 18655 W Unity : Unavailable product Coin_500 -Coin_500
01-29 15:46:33.423 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:CheckForInitialization()
01-29 15:46:33.423 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List1) 01-29 15:46:33.423 18620 18655 W Unity : UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String) 01-29 15:46:33.423 18620 18655 W Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update() 01-29 15:46:33.423 18620 18655 W Unity : 01-29 15:46:33.423 18620 18655 W Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51) 01-29 15:46:33.423 18620 18655 W Unity : 01-29 15:46:33.426 18620 18655 W Unity : Unavailable product coin_5000 -coin_5000 01-29 15:46:33.426 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:CheckForInitialization() 01-29 15:46:33.426 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List1)
01-29 15:46:33.426 18620 18655 W Unity : UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String)
01-29 15:46:33.426 18620 18655 W Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update()
01-29 15:46:33.426 18620 18655 W Unity :
01-29 15:46:33.426 18620 18655 W Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51)
01-29 15:46:33.426 18620 18655 W Unity :
01-29 15:46:33.429 18620 18655 W Unity : Unavailable product No_Ads -No_Ads
01-29 15:46:33.429 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:CheckForInitialization()
01-29 15:46:33.429 18620 18655 W Unity : UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List1) 01-29 15:46:33.429 18620 18655 W Unity : UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String) 01-29 15:46:33.429 18620 18655 W Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update() 01-29 15:46:33.429 18620 18655 W Unity : 01-29 15:46:33.429 18620 18655 W Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51) 01-29 15:46:33.429 18620 18655 W Unity : 01-29 15:46:33.433 18620 18655 I Unity : OnInitializeFailed InitializationFailureReason:NoProductsAvailable 01-29 15:46:33.433 18620 18655 I Unity : IAPManager:OnInitializeFailed(InitializationFailureReason) 01-29 15:46:33.433 18620 18655 I Unity : UnityEngine.Purchasing.PurchasingManager:CheckForInitialization() 01-29 15:46:33.433 18620 18655 I Unity : UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List1)
01-29 15:46:33.433 18620 18655 I Unity : UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String)
01-29 15:46:33.433 18620 18655 I Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update()
01-29 15:46:33.433 18620 18655 I Unity :
01-29 15:46:33.433 18620 18655 I Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51)
01-29 15:46:33.433 18620 18655 I Unity :
01-29 15:46:33.436 18620 18655 I Unity : UnityIAP Promo: Clearing promo product metadata
01-29 15:46:33.436 18620 18655 I Unity : UnityEngine.Purchasing.Promo:ProvideProductsToAds(HashSet`1)
01-29 15:46:33.436 18620 18655 I Unity : UnityEngine.Purchasing.Extension.UnityUtil:Update()
01-29 15:46:33.436 18620 18655 I Unity :
01-29 15:46:33.436 18620 18655 I Unity : (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/Android/runtime/DebugBindings.gen.cpp Line: 51)

What am I missing? Can anyone help? Thank you very much in advance! :slight_smile:

Can anyone help?

Please show your code where you are adding your products. The error says No Product Available for “Coin_200 -Coin_200” which is true. That is not what you have shown on your Dashboard and is not the correct product name.

1 Like

hey thank you for your reply! :slight_smile: Coin_200 is a variable “com.epicsauerkraut.loch.coin200”:

public class IAPManager : Singleton<IAPManager>, IStoreListener

{
    private static IStoreController m_StoreController;          // The Unity Purchasing system.
    private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.

    [HideInInspector]
    public string Coin_200 = "com.epicsauerkraut.loch.coin200"; //muss eindeutig sein und kleinbuchstaben -> am besten zuerst google play store einrichten
    [HideInInspector]
    public string Coin_500 = "com.epicsauerkraut.loch.coin_500";
    [HideInInspector]
    public string Coin_5000 = "com.epicsauerkraut.loch.coin_5000";
    [HideInInspector]
    public string No_Ads = "com.epicsauerkraut.loch.no_ads";

    private Shop_Manager shopManagerScript;
    private Game_Manager gameManagerScript;

    void Start()
    {
        // If we haven't set up the Unity Purchasing reference
        if (m_StoreController == null)
        {
            // Begin to configure our connection to Purchasing
            InitializePurchasing();
        }
    }

    public void InitializePurchasing()
    {
        // If we have already connected to Purchasing ...
        if (IsInitialized())
        {
            // ... we are done here.
            return;
        }

        // Create a builder, first passing in a suite of Unity provided stores.
        var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());

        // Add a product to sell / restore by way of its identifier, associating the general identifier
        // with its store-specific identifiers.
        builder.AddProduct(Coin_200, ProductType.Consumable);
        builder.AddProduct(Coin_500, ProductType.Consumable);
        builder.AddProduct(Coin_5000, ProductType.Consumable);
        // Continue adding the non-consumable product.
        builder.AddProduct(No_Ads, ProductType.NonConsumable);

        // Kick off the remainder of the set-up with an asynchrounous call, passing the configuration
        // and this class' instance. Expect a response either in OnInitialized or OnInitializeFailed.
        UnityPurchasing.Initialize(this, builder);
    }


    public bool IsInitialized()
    {
        // Only say we are initialized if both the Purchasing references are set.
        return m_StoreController != null && m_StoreExtensionProvider != null;
    }


    public void Buy200Coins()
    {
        BuyProductID(Coin_200);
    }

    public void Buy500Coins()
    {
        BuyProductID(Coin_500);
    }

    public void Buy5000Coins()
    {
        BuyProductID(Coin_5000);
    }

    public void BuyNoAds()
    {
        BuyProductID(No_Ads);
    }

    public string GetProductPriceFromStore(string id)
    {
        if (m_StoreController != null && m_StoreController.products != null) {
            return m_StoreController.products.WithID (id).metadata.localizedPriceString;
        } else {
            return "";
        }
    }

    void BuyProductID(string productId)
    {
        // If Purchasing has been initialized ...
        if (IsInitialized())
        {
            // ... look up the Product reference with the general product identifier and the Purchasing
            // system's products collection.
            Product product = m_StoreController.products.WithID(productId);

            // If the look up found a product for this device's store and that product is ready to be sold ...
            if (product != null && product.availableToPurchase)
            {
                Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
                // ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed
                // asynchronously.
                m_StoreController.InitiatePurchase(product);
            }
            // Otherwise ...
            else
            {
                // ... report the product look-up failure situation
                Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
            }
        }
        // Otherwise ...
        else
        {
            // ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
            // retrying initiailization.
            Debug.Log("BuyProductID FAIL. Not initialized.");
        }
    }


    // Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
    // Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
    public void RestorePurchases()
    {
        // If Purchasing has not yet been set up ...
        if (!IsInitialized())
        {
            // ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
            Debug.Log("RestorePurchases FAIL. Not initialized.");
            return;
        }

        // If we are running on an Apple device ...
        if (Application.platform == RuntimePlatform.IPhonePlayer ||
            Application.platform == RuntimePlatform.OSXPlayer)
        {
            // ... begin restoring purchases
            Debug.Log("RestorePurchases started ...");

            // Fetch the Apple store-specific subsystem.
            var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
            // Begin the asynchronous process of restoring purchases. Expect a confirmation response in
            // the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore.
            apple.RestoreTransactions((result) => {
                // The first phase of restoration. If no more responses are received on ProcessPurchase then
                // no purchases are available to be restored.
                Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
            });
        }
        // Otherwise ...
        else
        {
            // We are not running on an Apple device. No work is necessary to restore purchases.
            Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
        }
    }


    //
    // --- IStoreListener
    //

    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        // Purchasing has succeeded initializing. Collect our Purchasing references.
        Debug.Log("OnInitialized: PASS");

        // Overall Purchasing system, configured with products for this application.
        m_StoreController = controller;
        // Store specific subsystem, for accessing device-specific store features.
        m_StoreExtensionProvider = extensions;
    }


    public void OnInitializeFailed(InitializationFailureReason error)
    {
        // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
        Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    }


    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        // A consumable product has been purchased by this user.
        if (String.Equals(args.purchasedProduct.definition.id, Coin_200, StringComparison.Ordinal))
        {
            Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
            gameManagerScript = FindObjectOfType<Game_Manager>();
            gameManagerScript.CoinsWasBought(200);
        }
        // A consumable product has been purchased by this user.
        else if (String.Equals(args.purchasedProduct.definition.id, Coin_500, StringComparison.Ordinal))
        {
            Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
            gameManagerScript = FindObjectOfType<Game_Manager>();
            gameManagerScript.CoinsWasBought(500);
        }
        // A consumable product has been purchased by this user.
        else if (String.Equals(args.purchasedProduct.definition.id, Coin_5000, StringComparison.Ordinal))
        {
            Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
            gameManagerScript = FindObjectOfType<Game_Manager>();
            gameManagerScript.CoinsWasBought(5000);
        }
        // Or ... a non-consumable product has been purchased by this user.
        else if (String.Equals(args.purchasedProduct.definition.id, No_Ads, StringComparison.Ordinal))
        {
            Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
            shopManagerScript = FindObjectOfType<Shop_Manager>();
            shopManagerScript.RemoveAds();
        }
        // Or ... an unknown product has been purchased by this user. Fill in additional products here....
        else
        {
            Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
        }

        // Return a flag indicating whether this product has completely been received, or if the application needs
        // to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still
        // saving purchased products to the cloud, and when that save is delayed.
        return PurchaseProcessingResult.Complete;
    }


    public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        // A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing
        // this reason with the user to guide their troubleshooting actions.
        Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    }
}

You might want to confirm the value, it believes it to be a literal. I don’t believe you need it to be Public, you may be overriding the value in the Inspector.

Debug.Log("The value of Coin_200 is " + Coin_200.ToString());

You are right! Debug says: The value of Coin_200 is Coin_200
That’s not what I wanted… I need the variables to be public because I want to change the pricetext of the shop items in another script. I had to reset the script component and now the log finally says: The value of Coin_200 is com.epicsauerkraut.loch.coin200

Do you have any idea how this happened? I put the [HideInInspector] Attribute specially to prevent an overriding by mistake.

I will check on a new build and will keep you updated! Thank you already :slight_smile:

Perhaps you had set the values in the Inspector, Public variables are exposed there. And just noticed your HideInInspector mention, I haven’t tried that, but still wouldn’t fully explain what may have happened. Was it changed from the other script perhaps?

No I didn´t set the values in the inspector and no other script changed the values… Maybe at first I set the variables to Public - saved the script - and then changed the variables to Private. May that override the values?

If you can reproduce, we could probably figure it out.

OK, I could reproduce the problem. It has to do with the [HideInInspector].
To reproduce: I set the string Coin_200 = “XX” to Pubilc, saved and switched to Unity. Everything is fine. Then I go back to the script and put the [HideInInspector] attribute above Coin_200. Save and change the string to “YY” and save again. I go back to Unity and play the game. The debug still says “XX”…

So once a variable was set and public it overrides the later [HideInInspector] Attribute and the later change of the value in the script! Maybe to avoid this problem: Debug all [HideInInspector] values or reset the script after adding the attribute. @JeffDUnity3D do you have any other tipps to avoid this?

Glad you found it! Not really my area, but sounds like you are unblocked now. Good luck on your project!

1 Like

Yes thank you. And I tested the new build and its working (on Android)!!! Thank you very much for your help @JeffDUnity3D :slight_smile: