Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

IAP non-consumable buttons keeps purchasing on every click

Discussion in 'Unity IAP' started by islamagdy111, Jul 3, 2021.

  1. islamagdy111

    islamagdy111

    Joined:
    Oct 29, 2019
    Posts:
    23
    hey,

    using unity 2020.3.12f1 , android platform.

    I'm new on using IAP, I made 3 non-consumable buttons and by following some tutorials I have made the IAP script.. I wanted to unlock a scene when someone buy (or click) any of these buttons. when I click one of them the purchasing popup shows and I can buy it..
    the problems are:
    1- if I completed the purchase it keeps popping up when I click again, but this time says (you already bought this item).. I wanted to stop popping up and instead change scene
    2- when I change store scene and return back to it everything resets too. how to keep changes of IAP data when I change scenes or close the app and open it
    3- I know how to change scenes on click, but can't find how to do it after purchasing complete.

    here is my script:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Purchasing;
    5.  
    6.  
    7. namespace CompleteProject
    8. {
    9.     public class Purchaser : MonoBehaviour, IStoreListener
    10.     {
    11.         private static IStoreController m_StoreController;          // The Unity Purchasing system.
    12.         private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
    13.  
    14.         public string animalscategory = "com.idgames.slidepuzzle.animalscategory";
    15.         public string carscategory = "com.idgames.slidepuzzle.carscategory";
    16.         public string pharaohscategory = "com.idgames.slidepuzzle.pharaohscategory";
    17.  
    18.         void Start()
    19.         {
    20.             // If we haven't set up the Unity Purchasing reference
    21.             if (m_StoreController == null)
    22.             {
    23.                 // Begin to configure our connection to Purchasing
    24.                 InitializePurchasing();
    25.             }
    26.         }
    27.  
    28.         public void InitializePurchasing()
    29.         {
    30.             // If we have already connected to Purchasing ...
    31.             if (IsInitialized())
    32.             {
    33.                 // ... we are done here.
    34.                 return;
    35.             }
    36.  
    37.             var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    38.  
    39.             builder.AddProduct(animalscategory, ProductType.NonConsumable);
    40.             builder.AddProduct(carscategory, ProductType.NonConsumable);
    41.             builder.AddProduct(pharaohscategory, ProductType.NonConsumable);
    42.  
    43.             UnityPurchasing.Initialize(this, builder);
    44.         }
    45.  
    46.  
    47.         public bool IsInitialized()
    48.         {
    49.             return m_StoreController != null && m_StoreExtensionProvider != null;
    50.         }
    51.  
    52.  
    53.         public void BuyAnimalscategory()
    54.         {
    55.             BuyProductID(animalscategory);
    56.         }
    57.  
    58.         public void BuyCarscategory()
    59.         {
    60.             BuyProductID(carscategory);
    61.         }
    62.  
    63.         public void BuyPharaohscategory()
    64.         {
    65.             BuyProductID(pharaohscategory);
    66.         }
    67.  
    68.  
    69.         void BuyProductID(string productId)
    70.         {
    71.             // If Purchasing has been initialized ...
    72.             if (IsInitialized())
    73.             {
    74.                 // ... look up the Product reference with the general product identifier and the Purchasing
    75.                 // system's products collection.
    76.                 Product product = m_StoreController.products.WithID(productId);
    77.  
    78.                 // If the look up found a product for this device's store and that product is ready to be sold ...
    79.                 if (product != null && product.availableToPurchase)
    80.                 {
    81.                     Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
    82.                     // ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed
    83.                     // asynchronously.
    84.                     m_StoreController.InitiatePurchase(product);
    85.                 }
    86.                 // Otherwise ...
    87.                 else
    88.                 {
    89.                     // ... report the product look-up failure situation
    90.                     Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    91.                 }
    92.             }
    93.             // Otherwise ...
    94.             else
    95.             {
    96.                 // ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
    97.                 // retrying initiailization.
    98.                 Debug.Log("BuyProductID FAIL. Not initialized.");
    99.             }
    100.         }
    101.  
    102.  
    103.         // Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
    104.         // Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
    105.         public void RestorePurchases()
    106.         {
    107.             // If Purchasing has not yet been set up ...
    108.             if (!IsInitialized())
    109.             {
    110.                 // ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
    111.                 Debug.Log("RestorePurchases FAIL. Not initialized.");
    112.                 return;
    113.             }
    114.  
    115.             // If we are running on an Apple device ...
    116.             if (Application.platform == RuntimePlatform.IPhonePlayer ||
    117.                 Application.platform == RuntimePlatform.OSXPlayer)
    118.             {
    119.                 // ... begin restoring purchases
    120.                 Debug.Log("RestorePurchases started ...");
    121.  
    122.                 // Fetch the Apple store-specific subsystem.
    123.                 var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
    124.                 // Begin the asynchronous process of restoring purchases. Expect a confirmation response in
    125.                 // the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore.
    126.                 apple.RestoreTransactions((result) => {
    127.                     // The first phase of restoration. If no more responses are received on ProcessPurchase then
    128.                     // no purchases are available to be restored.
    129.                     Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    130.                 });
    131.             }
    132.             // Otherwise ...
    133.             else
    134.             {
    135.                 // We are not running on an Apple device. No work is necessary to restore purchases.
    136.                 Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
    137.             }
    138.         }
    139.  
    140.  
    141.         //
    142.         // --- IStoreListener
    143.         //
    144.  
    145.         public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    146.         {
    147.             // Purchasing has succeeded initializing. Collect our Purchasing references.
    148.             Debug.Log("OnInitialized: PASS");
    149.  
    150.             // Overall Purchasing system, configured with products for this application.
    151.             m_StoreController = controller;
    152.             // Store specific subsystem, for accessing device-specific store features.
    153.             m_StoreExtensionProvider = extensions;
    154.         }
    155.  
    156.  
    157.         public void OnInitializeFailed(InitializationFailureReason error)
    158.         {
    159.             // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    160.             Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    161.         }
    162.  
    163.  
    164.         public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    165.         {
    166.             // Or ... a non-consumable product has been purchased by this user.
    167.             if (String.Equals(args.purchasedProduct.definition.id, animalscategory, StringComparison.Ordinal))
    168.             {
    169.                 Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    170.                 // TODO: The non-consumable item has been successfully purchased, grant this item to the player.
    171.             }
    172.             // Or ... an unknown product has been purchased by this user. Fill in additional products here....
    173.             else
    174.             {
    175.                 Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
    176.             }
    177.  
    178.             // Return a flag indicating whether this product has completely been received, or if the application needs
    179.             // to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still
    180.             // saving purchased products to the cloud, and when that save is delayed.
    181.             return PurchaseProcessingResult.Complete;
    182.         }
    183.  
    184.  
    185.         public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    186.         {
    187.             // A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing
    188.             // this reason with the user to guide their troubleshooting actions.
    189.             Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    190.         }
    191.     }
    192. }

    thanks...
     
  2. islamagdy111

    islamagdy111

    Joined:
    Oct 29, 2019
    Posts:
    23
    Solved:
    I made an Indirect solution (I think), I put a normal button with change scene on click, behind the IAP button. and then destroy the IAP button after purchase is complete so I can use the normal button behind, and then save changes by Playerprefs.

    If someone have a better solution please...
     
  3. nicholasr

    nicholasr

    Unity Technologies

    Joined:
    Aug 15, 2015
    Posts:
    183
    For non-consumables, after purchasing they should be "owned" by the player forever. And, "after purchase" == (A) immediately after, and also (B) after a game quit + restart game. Please let me know if this is not a helpful response:

    Detect ownership:

    (A) In the code (congrats on the first-IAP script btw) where "grant this item to the player" is commented, I expect any purchased non-consumable product will be notified: at purchase.

    (B) Then, also in the code where "configured with products for this application" is commented, I expect any owned non-consumable products will be returned inside an array. Example: var product = controller.products.WithID("my.custom.product.id"); var isOwned = product.hasReceipt;

    Use the ownership knowledge:

    So, after the example 'isOwned' is known to be true or false, the UI logic can choose to Display the Buy dialog or Not Display the dialog. Example: if (Purchaser.controller.products.WithID(Purchaser.animalscategory).hasReceipt == false) { Purchaser.BuyAnimalscategory(); } else { DisplayAnimalsScene(); } Like this.

    Am I misunderstanding the question?
     
    FastGamePlay likes this.