[Closed] Google Play IAP Button no response.

Description of issue:
I’ve put my project into Alpha publication on the Google Play store, and I’m not able to get the codeless IAP buttons to respond. In the editor, they do respond and send me to Fake Store as expected (Although I should note that in editor I am having THIS separate issue, that maybe is related???).

Purchasing Script:

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

// Placing the Purchaser class in the CompleteProject namespace allows it to interact with other scripts.
namespace CompleteProject
{
    // Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
    public class Purchaser : MonoBehaviour, IStoreListener
    {
        private static IStoreController m_StoreController;          // The Unity Purchasing system.
        private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.

        // Product identifiers for all products capable of being purchased:
        // "convenience" general identifiers for use with Purchasing, and their store-specific identifier
        // counterparts for use with and outside of Unity Purchasing. Define store-specific identifiers
        // also on each platform's publisher dashboard (iTunes Connect, Google Play Developer Console, etc.)

        // General product identifiers for the consumable, non-consumable, and subscription products.
        // Use these handles in the code to reference which product to purchase. Also use these values
        // when defining the Product Identifiers on the store. Except, for illustration purposes, the
        // kProductIDSubscription - it has custom Apple and Google identifiers. We declare their store-
        // specific mapping to Unity Purchasing's AddProduct, below.
        public static string Coins150 = "150coins";
        public static string Coins900 = "900coins";
        public static string Coins2000 = "2000coins";
        public static string Coins5000 = "5000coins";
        public static string Coins30000 = "30000coins";

        // Apple App Store-specific product identifier.

        // Google Play Store-specific product identifier.


        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(Coins150, ProductType.Consumable);
            builder.AddProduct(Coins900, ProductType.Consumable);
            builder.AddProduct(Coins2000, ProductType.Consumable);
            builder.AddProduct(Coins5000, ProductType.Consumable);
            builder.AddProduct(Coins30000, ProductType.Consumable);
            // Continue adding the non-consumable product.

            // And finish adding the subscription product.
            // if the Product ID was configured differently between Apple and Google stores note that
            // the store-specific IDs must only be referenced here.

            // 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);
        }


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


        public void Buy150Coins()
        {
            // Buy the consumable product using its general identifier. Expect a response either
            // through ProcessPurchase or OnPurchaseFailed asynchronously.
            BuyProductID(Coins150);
        }
        public void Buy900Coins()
        {
            // Buy the consumable product using its general identifier. Expect a response either
            // through ProcessPurchase or OnPurchaseFailed asynchronously.
            BuyProductID(Coins900);
        }
        public void Buy2000Coins()
        {
            // Buy the consumable product using its general identifier. Expect a response either
            // through ProcessPurchase or OnPurchaseFailed asynchronously.
            BuyProductID(Coins2000);
        }
        public void Buy5000Coins()
        {
            // Buy the consumable product using its general identifier. Expect a response either
            // through ProcessPurchase or OnPurchaseFailed asynchronously.
            BuyProductID(Coins5000);
        }
        public void Buy30000Coins()
        {
            // Buy the consumable product using its general identifier. Expect a response either
            // through ProcessPurchase or OnPurchaseFailed asynchronously.
            BuyProductID(Coins30000);
        }


        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, Coins150, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                // The consumable item has been successfully purchased, optional, add code here to grant item.
            }
            if (String.Equals(args.purchasedProduct.definition.id, Coins900, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                // The consumable item has been successfully purchased, optional, add code here to grant item.
            }
            if (String.Equals(args.purchasedProduct.definition.id, Coins2000, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                // The consumable item has been successfully purchased, optional, add code here to grant item.
            }
            if (String.Equals(args.purchasedProduct.definition.id, Coins5000, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                // The consumable item has been successfully purchased, optional, add code here to grant item.
            }
            if (String.Equals(args.purchasedProduct.definition.id, Coins30000, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                // The consumable item has been successfully purchased, optional, add code here to grant item.
            }
            // Or ... an unknown product has been purchased by this user.
            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));
        }

        public void BankPurchase (int howMany){
            int currentCoins = Keepers.keepers.bank;
            Keepers.keepers.bank = currentCoins + howMany;
            Keepers.keepers.Save ();
        }
    }
}

Device Logs:

01-26 16:06:07.829  1358  2150 I InputDispatcher: Delivering touch to (28673): a
ction: 0x1, toolType: 1
01-26 16:06:07.829 28673 28673 D ViewRootImpl: ViewPostImeInputStage processPoin
ter 1
01-26 16:06:07.859 28673 28686 I Unity   : IAPButton.PurchaseProduct() with prod
uct ID: 150coins
01-26 16:06:07.859 28673 28686 I Unity   :
01-26 16:06:07.859 28673 28686 I Unity   : (Filename: ./artifacts/generated/comm
on/runtime/DebugBindings.gen.cpp Line: 51)
01-26 16:06:07.859 28673 28686 I Unity   :
01-26 16:06:07.869 28673 28686 I Unity   : NullReferenceException: Object refere
nce not set to an instance of an object
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.Purchasing.IAPButton
+IAPButtonStoreManager.InitiatePurchase (System.String productID) [0x00000] in <
filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.Purchasing.IAPButton
.PurchaseProduct () [0x00000] in <filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.Events.InvokableCall
.Invoke (System.Object[] args) [0x00000] in <filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.Events.InvokableCall
List.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.Events.UnityEventBas
e.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.Events.UnityEvent.In
voke () [0x00000] in <filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.UI.Button.Press () [
0x00000] in <filename unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.UI.Button.OnPointerC
lick (UnityEngine.EventSystems.PointerEventData eventData) [0x00000] in <filenam
e unknown>:0
01-26 16:06:07.869 28673 28686 I Unity   :   at UnityEngine.EventSystems.Execute
Events.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.

Unity Version: 5.5.0f3

Unity IAP Version: 1.9.3

Platforms/Stores targeting: Android right now, iOS next.

Sorry to reply to my own thread, but reading and reading to find my mistake, I came across the following, and think it may be my mistake… can anyone confirm that the behavior described above would be caused by this:

SOURCE

EDIT: Nope, not my problem, non publisher account does same thing.

@rogaroga ,

Your log is showing a Null Reference Exception when clicking the button. Could you double check that all of your references are set properly.

In the editor pressing the button pops open the Fake Store with the option to buy or cancel. How can that be happening and then still have a null reference in game after building?

I found my problem with the references. In my above code, I didn’t specifically reference my googleplay ID for each product, just the general handler for each product. Updated code that does work:

    public static string Coins150 = "150coins";
        public static string Coins900 = "900coins";
        public static string Coins2000 = "2000coins";
        public static string Coins5000 = "5000coins";
        public static string Coins30000 = "30000coins";

        // Apple App Store-specific product identifier.

        // Google Play Store-specific product identifier.
        private static string kProductNameGooglePlayCoins150 = "150coins";
        private static string kProductNameGooglePlayCoins900 = "900coins";
        private static string kProductNameGooglePlayCoins2000 = "2000coins";
        private static string kProductNameGooglePlayCoins5000 = "5000coins";
        private static string kProductNameGooglePlayCoins30000 = "30000coins";

        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(Coins150, ProductType.Consumable, new IDs(){ {kProductNameGooglePlayCoins150, GooglePlay.Name} });
            builder.AddProduct(Coins900, ProductType.Consumable, new IDs(){ {kProductNameGooglePlayCoins900, GooglePlay.Name} });
            builder.AddProduct(Coins2000, ProductType.Consumable, new IDs(){ {kProductNameGooglePlayCoins2000, GooglePlay.Name} });
            builder.AddProduct(Coins5000, ProductType.Consumable, new IDs(){ {kProductNameGooglePlayCoins5000, GooglePlay.Name} });
            builder.AddProduct(Coins30000, ProductType.Consumable, new IDs(){ {kProductNameGooglePlayCoins30000, GooglePlay.Name} });

@rogaroga ,

This doesn’t look like any error I’ve seen before. Would you be able to send a sample project where this happens?
https://analytics.cloud.unity3d.com/support/

The FakeStore only works in the editor. The FakeStore is meant to simulate the platform stores that aren’t available in the Editor. Once you create a build for Android and iOS, you must use the product IDs that you created in the store.
https://docs.unity3d.com/Manual/UnityIAPAppleConfiguration.html
https://docs.unity3d.com/Manual/UnityIAPGoogleConfiguration.html

Another thing I didn’t notice before, if you are using Codeless IAP, then you don’t need to create a purchasing script. That is all handled by setting up the IAP Catalog and creating individual buttons.

This shouldn’t be necessary. The store-specific IDs are only needed in cases where you don’t have the same product ID across stores. For example, if your 500 gold coin product is called “500gold” in Google Play but “gold500” in iTunes, then you would need a store-specific ID to reconcile this. In your example, there should be no benefit to adding them.

If you are using the Codeless IAP, then you can see the purchasing script that is used in:
Plugins/UnityPurchasing/script/IAPButton.cs