Error when trying Unity IAP on Android

Hi guys,

I’ve got a “Authentication is required. You need to sign into your Google Account.” error when I tried testing on a device. I tried all of the steps in this link: How to fix ''Google Play authentication is required'' error .But it’s still there. Can anybody help me here?

Thanks.

FYI, other games’s IAP works perfectly fine. The only one that has the problem is my game.

This is the code that I use to do the IAP. It’s based on the IAP tutorial so it should work.right?

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine.Purchasing;

public enum CurrencyType
{
    RUBY,
    GOLD
}


public class CurrencyManager : MonoBehaviour, IStoreListener {

    #region Static Delegates
    public static Action OnRubyValueChanged = delegate {};
    public static Action OnGoldValueChanged = delegate {};
    public static Action<bool, int, int> OnRetrieveCurrencyData = delegate {};

    public static Action<bool> OnBuyGold = delegate { };
    #endregion

    #region Constants
    public const string RUBY_100_PACK_PRODUCT_ID = "rubies_1"; //Real id in the Google Store / App Store
    public const string RUBY_250_PACK_PRODUCT_ID = "rubies_2"; //Real id in the Google Store / App Store
    public const string RUBY_1500_PACK_PRODUCT_ID = "rubies_3"; //Real id in the Google Store / App Store
    public const string RUBY_10_PACK_PRODUCT_ID = "rubies_4"; //Real id in the Google Store / App Store

    public const string RUBY_100_PACK_ITEM_ID = "rubies_100";
    public const string RUBY_250_PACK_ITEM_ID = "rubies_250";
    public const string RUBY_1500_PACK_ITEM_ID = "rubies_1500";
    public const string RUBY_10_PACK_ITEM_ID = "rubies_10";

    public const string GOLD_1000_PACK_ITEM_ID = "gold_1000";
    public const string GOLD_2750_PACK_ITEM_ID = "gold_2750";
    public const string GOLD_17500_PACK_ITEM_ID = "gold_17500";

    //Test values for editor because in the editor we don't use In App and just use the TEST API.
    public const int RUBY_100_VALUE = 100; //Real value
    public const int RUBY_250_VALUE = 250; //Real value
    public const int RUBY_1500_VALUE = 1500; //Real value
    public const int RUBY_10_VALUE = 10; //Real value

    public const int GOLD_1000_VALUE = 1000; //Real value
    public const int GOLD_2750_VALUE = 2750; //Real value
    public const int GOLD_17500_VALUE = 17500; //Real value
    #endregion

    #region Variables
    private static CurrencyManager _instance = null;
    private static int _currentCharId = 0;

    private static IStoreController m_StoreController;                                                                  // Reference to the Purchasing system.
    private static IExtensionProvider m_StoreExtensionProvider;                                                         // Reference to store-specific Purchasing subsystems.

    private IPurchaseVerifier _verifier = null;

    private int _rubies = 0;
    private int _gold = 0;

    private Dictionary<string, int> rubyRealValues = new Dictionary<string, int>();
    private Dictionary<string, int> goldRealValues = new Dictionary<string, int>();

    #endregion

    #region Properties

    public static int Ruby
    {
        get{ return _instance._rubies; }
        private set
        {
            _instance._rubies = value;
            OnRubyValueChanged();
        }
    }
   
    public static int Gold
    {
        get{ return _instance._gold; }
        //Setter should only be used once per play to get the current data form the server.
        //Use VirtualCurrency.Give() / VirtualCurrency.Take() to do it afterwards.
        private set
        {
            _instance._gold = value;
            OnGoldValueChanged();
        }
    }

    #endregion

    #region Monobehaviour Methods
    void Awake ()
    {
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(this.gameObject);
            UILobbyController.Instance.dontDestroyObjects.Add(this.gameObject);

#if UNITY_ANDROID
            _verifier = new AndroidPurchaseVerifier();
#elif UNITY_IOS
            _verifier = new iOSPurchaseVerifier();
#else
   
#endif

            SetRubiesRealValues();
            SetGoldRealValues();

        }
        else if (_instance != this)
        {
            DestroyImmediate(this.gameObject);
            return;
        }
       
    }

    /// <summary>
    /// Start this instance.
    /// </summary>
    void Start()
    {

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

            //EraDB.OnAuth -= OnUserAuth;
        }
    }

#endregion

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

    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)
    {
        _verifier.Set(args);
        _verifier.Verify();
        //CurrencyManager.VerifyInApp(extra["productId"], extra["token"]);
        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.
        Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    }
    #endregion

    #region Methods

    public void SetRubiesRealValues()
    {
        rubyRealValues.Add(RUBY_100_PACK_ITEM_ID, RUBY_100_VALUE);
        rubyRealValues.Add(RUBY_250_PACK_ITEM_ID, RUBY_250_VALUE);
        rubyRealValues.Add(RUBY_1500_PACK_ITEM_ID, RUBY_1500_VALUE);
        rubyRealValues.Add(RUBY_10_PACK_ITEM_ID, RUBY_10_VALUE);
    }

    public void SetGoldRealValues()
    {
        goldRealValues.Add(GOLD_1000_PACK_ITEM_ID, GOLD_1000_VALUE);
        goldRealValues.Add(GOLD_2750_PACK_ITEM_ID, GOLD_2750_VALUE);
        goldRealValues.Add(GOLD_17500_PACK_ITEM_ID, GOLD_17500_VALUE);
    }

    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(RUBY_100_PACK_ITEM_ID, ProductType.Consumable, new IDs() { { RUBY_100_PACK_PRODUCT_ID, AppleAppStore.Name }, { RUBY_100_PACK_PRODUCT_ID, GooglePlay.Name }, });// Continue adding the consumable product.
        builder.AddProduct(RUBY_250_PACK_ITEM_ID, ProductType.Consumable, new IDs() { { RUBY_250_PACK_PRODUCT_ID, AppleAppStore.Name }, { RUBY_250_PACK_PRODUCT_ID, GooglePlay.Name }, });// Continue adding the consumable product.
        builder.AddProduct(RUBY_1500_PACK_ITEM_ID, ProductType.Consumable, new IDs() { { RUBY_1500_PACK_PRODUCT_ID, AppleAppStore.Name }, { RUBY_1500_PACK_PRODUCT_ID, GooglePlay.Name }, });// Continue adding the consumable product.
        builder.AddProduct(RUBY_10_PACK_ITEM_ID, ProductType.Consumable, new IDs() { { RUBY_10_PACK_PRODUCT_ID, AppleAppStore.Name }, { RUBY_10_PACK_PRODUCT_ID, GooglePlay.Name }, });// Continue adding the consumable product.
        UnityPurchasing.Initialize(this, builder);
    }

    public void OnUserAuth()
    {
        RetrieveCurrencyData();
    }

    void BuyProductID(string productId)
    {
        // If the stores throw an unexpected exception, use try..catch to protect my logic here.
        try
        {
            // 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.");
            }
        }
        // Complete the unexpected exception handling ...
        catch (Exception e)
        {
            // ... by reporting any unexpected exception for later diagnosis.
            Debug.Log("BuyProductID: FAIL. Exception during purchase. " + e);
        }
    }


    // Restore purchases previously made by this customer. Some platforms automatically restore purchases. Apple currently requires explicit purchase restoration for IAP.
    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);
        }
    }

    public string GetLocalizedPrice(string productID)
    {
        // ... 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 product is found
        if (product != null )
        {
            return product.metadata.localizedPriceString;
        }
        else
        {
            Debug.Log("Cannot found product with ID = " + productID );
        }

        return "";
    }

    public void OnInAppConfirmation(string productID, string purchaseToken)
    {
        //GameUI.LoadScreen.Instance.On("Verifying In App...");
        string request = JsonConvert.SerializeObject(new StoreVerifyInAppRequest( productID, purchaseToken ));
        Debug.Log("Request = " + request);
       
        string address = EraDB.StoreVerifyInAppAddress;
       
        Debug.Log("Address = " + address);
       
        EraDB.GameDatabase.Post(address, request, resp =>
                                                       {
            if (resp.exception == null)
            {
                if (resp.response.message == "OK")
                {
                    string statusMsg = "Buy Rubies Successful! Getting the new data from the server! " + resp.response.Text;
                    Debug.Log(statusMsg);

                    CurrencyManager.RetrieveCurrencyDataFromServer();

                    Mixpanel.SendEvent("BuyRubies", new Dictionary<string, object> {
                                                    {"ID",  productID}
                                                });
                }
                else
                {
                    ErrorResponse errorResp = JsonConvert.DeserializeObject<ErrorResponse>(resp.response.Text);
                    string statusMsg = "Buy Rubies Failed! " + errorResp.errorMessage;

                    Debug.Log(statusMsg);

                    UINotification.Add(statusMsg, Color.red);

                    CurrencyManager.RetrieveCurrencyDataFromServer();
                }
            }
            else
            {
                string statusMsg = "Buy Rubies Failed! " + resp.exception.Message;
                Debug.Log(statusMsg);
               
                UINotification.Add(statusMsg, Color.red);
                //_shopPanel.statusLabel.PopStatus(Color.red, statusMsg);
           
                CurrencyManager.RetrieveCurrencyDataFromServer();
                //GameUI.LoadScreen.Instance.Off();
            }
        }, true);
    }

    public void RetrieveCurrencyData()
    {
            //Debug.Log("Char ID = " + PlayerPrefs.GetInt("char_id", 0).ToString());
            //string address = EraDB.GameDatabase.StoreCurrencyGetAddress + "/" + PlayerPrefs.GetInt("char_id", 0);
            string address = EraDB.StoreCurrencyGetAddress + "/" + HeroData.Instance.charId;

            EraDB.GameDatabase.Get(address, resp =>
            //StartCoroutine(EraDB.GameDatabase.hGetRequest(EraDB.GameDatabase.UserCurrencyGetAddress, request, resp =>
            //StartCoroutine(EraDB.GameDatabase.hPostRequest(EraDB.GameDatabase.UserCurrencyGetAddress, request, resp =>
            {
                if (resp.exception == null)
                {
                    if (resp.response.message == "OK")
                    {
                        Debug.Log("Get Currency Successful! " + resp.response.Text);

                        CurrencyGetResponse currency = JsonConvert.DeserializeObject<CurrencyGetResponse>(resp.response.Text);

                        Ruby = currency.rubies;
                        Gold = currency.gold;

                        _currentCharId = HeroData.Instance.charId;

                        OnRetrieveCurrencyData(true, currency.rubies, currency.gold);
                    }
                    else
                    {
                        ErrorResponse errorResp = JsonConvert.DeserializeObject<ErrorResponse>(resp.response.Text);

                        if (!string.IsNullOrEmpty(errorResp.errorMessage))
                            Debug.Log("Get Currency Failed! " + errorResp.errorMessage);

                        OnRetrieveCurrencyData(false, Ruby, Gold);
                    }
                }
                else
                {
                    Debug.Log("Get Currency Failed! " + resp.exception.Message);

                    OnRetrieveCurrencyData(false, Ruby, Gold);
                }
            }, false);
        }

    public void BuyGold(int goldValue)
    {
        //string request = JsonConvert.SerializeObject(new StoreGoldBuyRequest( PlayerPrefs.GetInt("char_id", 0), _goldValues[merchandizeEnum] ));
        string request = JsonConvert.SerializeObject(new StoreGoldBuyRequest(HeroData.Instance.charId, goldValue));
        Debug.Log("Request = " + request);

        string address = EraDB.StoreGoldBuyAddress;

        Debug.Log("Address = " + address);

        EraDB.GameDatabase.Post(address, request, resp =>
        {
            if (resp.exception == null)
            {
                if (resp.response.message == "OK")
                {
                    string statusMsg = "Buy " + goldValue.ToString() + " Gold Successful! " + resp.response.Text;
                    Debug.Log(statusMsg);

                    //_shopPanel.statusLabel.PopStatus(Color.green, statusMsg);

                    string respText = resp.response.Text;

                    Mixpanel.SendEvent("BuyGold", new Dictionary<string, object> {
                                                    {"Amount",  goldValue.ToString()}
                                                });

                    CurrencyManager.RetrieveCurrencyDataFromServer();

                    OnBuyGold(true);


                }
                else
                {
                    ErrorResponse errorResp = JsonConvert.DeserializeObject<ErrorResponse>(resp.response.Text);
                    string statusMsg = "Buy " + goldValue.ToString() + " Gold Failed! " + errorResp.errorMessage;

                    //_shopPanel.statusLabel.PopStatus(Color.red, statusMsg);

                    UINotification.Add(statusMsg, Color.red);

                    CurrencyManager.RetrieveCurrencyDataFromServer();

                    OnBuyGold(false);
                }
            }
            else
            {
                string statusMsg = "Buy " + goldValue.ToString() + " Gold Failed! " + resp.exception.Message;
                Debug.Log(statusMsg);

                OnBuyGold(false);

                //_shopPanel.statusLabel.PopStatus(Color.red, statusMsg);
                UINotification.Add(statusMsg, Color.red);
            }
        });
    }

#if UNITY_EDITOR
    void AddGoldFromEditor(int amount)
    {
        GameUI.LoadScreen.Instance.On("Set gold from editor. Not using the production API but using the test API...");

        //int charID = PlayerPrefs.GetInt("char_id", 0);
        int charID = HeroData.Instance.charId;
        string request = JsonConvert.SerializeObject(new TestSetGoldRequest(charID, Gold + amount));
        EraDB.GameDatabase.Post(EraDB.TestSetGoldAddress, request, resp =>
        {
            if (resp.exception == null)
            {

                if (resp.response.message == "OK")
                {

                    string statusMsg = "Set gold from editor successful! " + resp.response.Text;
                    Debug.Log(statusMsg);

                    CurrencyManager.RetrieveCurrencyDataFromServer();


                    OnBuyGold(true);

                    GameUI.LoadScreen.Instance.Off();

                    //_statusLabel.PopStatus(Color.green, statusMsg);
                }
                else
                {
                    string statusMsg = "Set gold from editor failed! " + resp.response.Text;
                    Debug.Log(statusMsg);

                    CurrencyManager.RetrieveCurrencyDataFromServer();

                    OnBuyGold(false);

                    GameUI.LoadScreen.Instance.Off();

                }
            }
            else
            {
                GameUI.LoadScreen.Instance.Off();
                string statusMsg = "Set gold from editor failed!  " + resp.exception.Message;

                OnBuyGold(false);

                Debug.Log(statusMsg);

            }
        });
    }

    void AddRubyFromEditor(int amount)
    {
        GameUI.LoadScreen.Instance.On("Set rubies from editor. Not using the production API but using the test API...");

        //int charID = PlayerPrefs.GetInt("char_id", 0);
        int charID = HeroData.Instance.charId;
        string request = JsonConvert.SerializeObject(new TestSetRubyRequest(Ruby + amount));
        EraDB.GameDatabase.Post(EraDB.TestSetRubyAddress, request, resp =>
        {
            if (resp.exception == null)
            {

                if (resp.response.message == "OK")
                {

                    string statusMsg = "Set rubies from editor successful! " + resp.response.Text;
                    Debug.Log(statusMsg);

                    CurrencyManager.RetrieveCurrencyDataFromServer();

                    GameUI.LoadScreen.Instance.Off();

                    //_statusLabel.PopStatus(Color.green, statusMsg);
                }
                else
                {
                    string statusMsg = "Set rubies from editor failed! " + resp.response.Text;
                    Debug.Log(statusMsg);

                    CurrencyManager.RetrieveCurrencyDataFromServer();


                    GameUI.LoadScreen.Instance.Off();

                }
            }
            else
            {
                GameUI.LoadScreen.Instance.Off();
                string statusMsg = "Set rubies from editor failed!  " + resp.exception.Message;

                Debug.Log(statusMsg);

            }
        });
    }
#endif

    //-------------------------------------------------------------------------------------
    //Static methods

    public static bool Exists()
    {
        return !(_instance == null && FindObjectOfType<CurrencyManager>() == null);
    }
   
    private static CurrencyManager Instance()
    {
        if (!Exists())
        {
            return Instantiate();
        }
       
        return _instance;
    }
   
    private static CurrencyManager Instantiate()
    {
        GameObject go = new GameObject("CurrencyManager");
        CurrencyManager currencyManagerComp = go.AddComponent<CurrencyManager>();
        currencyManagerComp.Awake();
        return currencyManagerComp;
    }

    public static string GetPrice(string productId)
    {
        return Instance().GetLocalizedPrice(productId);
    }

    public static void RetrieveCurrencyDataFromServer()
    {
        Instance().RetrieveCurrencyData();
    }

    public static void ReduceCurrency(CurrencyType type, int amount, bool changeAPI)
    {

        if (type == CurrencyType.RUBY)
            Ruby -= amount;
        else if (type == CurrencyType.GOLD)
            Gold -= amount;

        //TODO: API CALL
        if (changeAPI)
        {

        }
    }

    public static void AddCurrency(CurrencyType type, int amount, bool changeAPI)
    {

        if (type == CurrencyType.RUBY)
            Ruby += amount;
        else if (type == CurrencyType.GOLD)
            Gold += amount;

        //TODO: API CALL
        if (changeAPI)
        {

        }
    }

    public static void VerifyInApp(string productID, string purchaseToken)
    {
        Instance().OnInAppConfirmation(productID, purchaseToken);
    }

    public static void BuyRubies(string rubiesId)
    {
#if UNITY_EDITOR
        Instance().AddRubyFromEditor(Instance().rubyRealValues[rubiesId]);
#else
        Instance().BuyProductID(rubiesId);
#endif
    }

    public static void BuyGold(string goldId)
    {
#if UNITY_EDITOR
        Instance().AddGoldFromEditor(Instance().goldRealValues[goldId]);
#else
        Instance().BuyGold(Instance().goldRealValues[goldId]);
#endif
    }

#endregion
}