Search Unity

IAP process purchase callbacks getting destroyed?

Discussion in 'Unity IAP' started by mukulabdagiri, Mar 15, 2018.

  1. mukulabdagiri

    mukulabdagiri

    Joined:
    Jul 1, 2016
    Posts:
    23
    Hi,

    I am alpha testing my game with Google play transactions on Android phone. When I make a purchase the processpurchase callback function is not getting called until I restart the app and it gets called repeatedly every time I restart the app.

    How can I make sure that the call backs are called immediately and only once after the purchase is complete?

    I am looking at this solution:
    https://support.unity3d.com/hc/en-u...allbacks-on-our-IStoreListener-implementation
    where he suggests adding an exception in the app’s hierarchy cleanup script for the “IAPUtil” GameObject. But I am not leaving any scene when the transaction takes place. It's just the Google play pop up window opening up on top of my existing scene.
    How and where should I be adding the exception for IAPUTIL gameobject?
    Any code snippet elaborating this would be very helpful.

    Thanks,
    Mukul
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Can you show your purchase script? You should not need to add an exception handler. What type of product have you configured? Are you using Codeless IAP Buttons?
     
  3. mukulabdagiri

    mukulabdagiri

    Joined:
    Jul 1, 2016
    Posts:
    23
    I am using Consumable products. This is how my purchase script looks like:


    public class IAPManager : MonoBehaviour, IStoreListener
    {
    public static IAPManager instance = null;
    private static IStoreController m_StoreController; // The Unity Purchasing system.
    private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.

    public static string PRODUCT_ID1 = "PRODUCTID1";
    public static string PRODUCT_ID2 = "PRODUCTID2";

    //Awake is always called before any Start functions
    void Awake()
    {
    //Check if instance already exists
    if (instance == null)

    //if not, set instance to this
    instance = this;

    //If instance already exists and it's not this:
    else if (instance != this)

    //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
    Destroy(gameObject);

    //Sets this to not be destroyed when reloading scene
    DontDestroyOnLoad(gameObject);
    }

    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());

    //testing: auto approves all transactions by Samsung
    builder.Configure<ISamsungAppsConfiguration>().SetMode(SamsungAppsMode.AlwaysSucceed);

    builder.AddProduct(PRODUCT_ID1, ProductType.Consumable);
    builder.AddProduct(PRODUCT_ID2, ProductType.Consumable);

    // 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 BuyProductId1()
    {
    BuyProductID(PRODUCT_ID1);
    }

    public void BuyProductId2()
    {
    BuyProductID(PRODUCT_ID2);
    }

    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.");
    }
    }

    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, PRODUCT_ID1, StringComparison.Ordinal))
    {
    Debug.Log ("User bought Product 1");
    }
    else if (String.Equals(args.purchasedProduct.definition.id, PRODUCT_ID2, StringComparison.Ordinal))
    {
    Debug.Log ("User bought Product 2");
    }
    // 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));
    }
    }


    A UI panel for shop contains the buttons for shop items. These are the functions which get triggered on button click:

    public class ShopManager : MonoBehaviour {

    public void OnButton1Clicked()
    {
    IAPManager.instance.BuyProduct1 ();
    }

    public void OnButton2Clicked()
    {
    IAPManager.instance.BuyProduct2 ();
    }
    }
     
    Last edited: Mar 16, 2018
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Your code is exactly like mine which works correctly. The only difference I see are the Destroy methods, can you test without these in place and test with a new purchase? Once we rule it out, you could add them back.
     
  5. mukulabdagiri

    mukulabdagiri

    Joined:
    Jul 1, 2016
    Posts:
    23
    I removed the Destroy(gameobject) function in Awake() but that didn't make any difference. Like mentioned in other similar threads is it the IAPUtil gameobject which is the culprit? If so how can I prevent it from getting destroyed?

    Btw I am just using the Samsung IAP code as it is.
     
  6. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi @mukulabdagiri
    You don't need to handle the situation which the link points out since you are not leaving any scene.
    I tested your script and it works well on my side.
    Could you please send me the full log so that I can investigate further? Thank you.
     
  7. GameChangerShan

    GameChangerShan

    Joined:
    May 31, 2017
    Posts:
    2
    Hello,

    I am also having this same issue. Was there a fix found for this?

    Thanks!
     
  8. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446