Search Unity

Purchase not correctly processed

Discussion in 'Unity IAP' started by drordoit, Oct 11, 2018.

  1. drordoit

    drordoit

    Joined:
    Sep 7, 2013
    Posts:
    36
    Hi I'm having some trouble with the IAP.
    When I run the app on android, I'm getting the following error:
    "Purchase not correctly processed for product "myProductName". Add an active IAPButton to process this purchase, or add an IAPListener to receive any unhandled purchase events."
    Now, i know what you thinking, "Its telling you what to do!", but it's not so cleare.
    I have in my app a "product manager" with the IAP code (and it works great ... I think).
    The product manager hold all the data that i need for my store and my app (products name and prices), should i add to it an IAP button ? Why? I added a IAP listener but the error keep showing up.
    What can be the problem?
    Thanks Dror
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Please show your code. If you are not using Codeless IAP, then an IAP Listener would not be necessary (and would not be expected to work). You don't want to mix Codeless and scripting. Do you have a product named "myProductName"?
     
  3. drordoit

    drordoit

    Joined:
    Sep 7, 2013
    Posts:
    36
    The IAP works great, I bought a product and thing seem to work ok. I'm getting the error on android (but the store still works fine).
    That my code:
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Purchasing;
    5. using System.Globalization;
    6. using UnityEngine.Purchasing.Security;
    7.  
    8.  
    9. public class productManagerScript : MonoBehaviour, IStoreListener {
    10.  
    11.    
    12.     private IStoreController m_StoreController;
    13.     private IExtensionProvider m_StoreExtensionProvider;
    14.  
    15.     static string book2 = "book2";
    16.     static string book3 = "book3";
    17.     static string book4 = "book4";
    18.     static string books234 = "books234";
    19.  
    20.     public bool book2hasRecite;
    21.     public bool book3hasRecite;
    22.     public bool book4hasRecite;
    23.     public bool dealHasRecite;
    24.  
    25.     public bool noAds;
    26.  
    27.     public string book2Price;
    28.     public string book3Price;
    29.     public string book4Price;
    30.     public string dealPrice;
    31.  
    32.  
    33.     [Space(10)]
    34.     public bool newPicInGallary;
    35.     [Space(5)]
    36.     public int countToRate;
    37.    
    38.  
    39.  
    40.  
    41.     private static bool created = false;
    42.  
    43.     void Awake()
    44.     {
    45.         if (!created)
    46.         {
    47.             DontDestroyOnLoad(this.gameObject);
    48.             created = true;
    49.            
    50.         }
    51.     }
    52.     void Start()
    53.     {
    54.         if (m_StoreController == null)
    55.         {
    56.  
    57.             InitializePurchasing();
    58.         }
    59.  
    60.         if (m_StoreController != null)
    61.         {
    62.             Debug.Log(m_StoreController.products.WithID(book2).metadata.isoCurrencyCode + " CurrencyCode");
    63.            
    64.  
    65.             if (m_StoreController.products.WithID("book2").hasReceipt)
    66.             {
    67.                 book2hasRecite = true;
    68.             }
    69.             if (m_StoreController.products.WithID("book3").hasReceipt)
    70.             {
    71.                 book3hasRecite = true;
    72.             }
    73.             if (m_StoreController.products.WithID("book4").hasReceipt)
    74.             {
    75.                 book4hasRecite = true;
    76.             }
    77.             if (m_StoreController.products.WithID("books234").hasReceipt)
    78.             {
    79.                 dealHasRecite = true;
    80.                 noAds=true;
    81.                
    82.             }
    83.         }
    84.  
    85.      
    86.  
    87.     }
    88.     public void InitializePurchasing()
    89.     {
    90.  
    91.         if (IsInitialized())
    92.         {
    93.             return;
    94.         }
    95.         ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    96.         builder.AddProduct("book2", ProductType.NonConsumable);
    97.         builder.AddProduct("book3", ProductType.NonConsumable);
    98.         builder.AddProduct("book4", ProductType.NonConsumable);
    99.         builder.AddProduct("books234", ProductType.NonConsumable);
    100.         UnityPurchasing.Initialize(this, builder);
    101.     }
    102.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    103.     {
    104.         m_StoreController = controller;
    105.         m_StoreExtensionProvider = extensions;
    106.  
    107.      
    108.         Debug.Log("Start controller initialiesed");
    109.         //string cur = NumberFormatInfo.CurrentInfo.CurrencySymbol;
    110.         book2Price =  m_StoreController.products.WithID("book2").metadata.localizedPriceString;
    111.         book3Price =  m_StoreController.products.WithID("book3").metadata.localizedPriceString;
    112.         book4Price =  m_StoreController.products.WithID("book4").metadata.localizedPriceString;
    113.         dealPrice =  m_StoreController.products.WithID("books234").metadata.localizedPriceString;
    114.  
    115.  
    116.     }
    117.  
    118.     public void OnInitializeFailed(InitializationFailureReason error) { }
    119.     public void OnPurchaseFailed(Product item, PurchaseFailureReason r)
    120.     {
    121.         Debug.Log(item + " <==Item");
    122.         Debug.Log(r + " <==reason");
    123.     }
    124.  
    125.  
    126.  
    127.  
    128.  
    129.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    130.     {
    131.  
    132.  
    133.       bool validPurchase = true; // Presume valid for platforms with no R.V.
    134. #if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
    135.      
    136.         var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
    137.             AppleTangle.Data(), Application.identifier);
    138.  
    139.         try
    140.         {
    141.             var result = validator.Validate(e.purchasedProduct.receipt);
    142.             Debug.Log("Receipt is valid. Contents:");
    143.             foreach (IPurchaseReceipt productReceipt in result)
    144.             {
    145.                 Debug.Log(productReceipt.productID);
    146.                 Debug.Log(productReceipt.purchaseDate);
    147.                 Debug.Log(productReceipt.transactionID);
    148.  
    149.                 GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
    150.                 if (null != google)
    151.                 {
    152.                     Debug.Log(google.purchaseState);
    153.                     Debug.Log(google.purchaseToken);
    154.                 }
    155.  
    156.                 AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
    157.                 if (null != apple)
    158.                 {
    159.                     Debug.Log(apple.originalTransactionIdentifier);
    160.                     Debug.Log(apple.subscriptionExpirationDate);
    161.                     Debug.Log(apple.cancellationDate);
    162.                     Debug.Log(apple.quantity);
    163.                     Debug.Log(" The upper debugs sepposed to be the product with the resipt");
    164.                 }
    165.             }
    166.         }
    167.         catch (IAPSecurityException)
    168.         {
    169.             Debug.Log("Invalid receipt, not unlocking content");
    170.             return PurchaseProcessingResult.Complete;
    171.         }
    172.    
    173. #endif
    174.  
    175.  
    176.  
    177.         if (String.Equals(e.purchasedProduct.definition.id, book2, StringComparison.Ordinal))
    178.         {
    179.             book2hasRecite = true;
    180.             if (GameObject.Find("paid"))
    181.             {
    182.                 GameObject.Find("paid").GetComponent<unlockScript>().bookPayedColoect(book2);
    183.                 GameObject.Find("book2").GetComponent<bookStatus>().isPayedFor = true;
    184.             }
    185.             if (GameObject.Find("storePanel(Clone)"))
    186.             {
    187.                 GameObject.Find("storePanel(Clone)").GetComponent<storePanelScript_v2>().book2Cover.SetActive(true);
    188.             }
    189.         }
    190.         if (String.Equals(e.purchasedProduct.definition.id, book3, StringComparison.Ordinal))
    191.         {
    192.             book3hasRecite = true;
    193.             if (GameObject.Find("paid"))
    194.             {
    195.                 GameObject.Find("paid").GetComponent<unlockScript>().bookPayedColoect(book3);
    196.                 GameObject.Find("book3").GetComponent<bookStatus>().isPayedFor = true;
    197.             }
    198.             if (GameObject.Find("storePanel(Clone)"))
    199.             {
    200.                 GameObject.Find("storePanel(Clone)").GetComponent<storePanelScript_v2>().book3Cover.SetActive(true);
    201.             }
    202.         }
    203.         if (String.Equals(e.purchasedProduct.definition.id, book4, StringComparison.Ordinal))
    204.         {
    205.             book4hasRecite = true;
    206.             if (GameObject.Find("paid"))
    207.             {
    208.                 GameObject.Find("paid").GetComponent<unlockScript>().bookPayedColoect(book4);
    209.                 GameObject.Find("book4").GetComponent<bookStatus>().isPayedFor = true;
    210.             }
    211.             if (GameObject.Find("storePanel(Clone)"))
    212.             {
    213.                 GameObject.Find("storePanel(Clone)").GetComponent<storePanelScript_v2>().book4Cover.SetActive(true);
    214.             }
    215.         }
    216.         if (String.Equals(e.purchasedProduct.definition.id, books234, StringComparison.Ordinal))
    217.         {
    218.             book2hasRecite = true;
    219.             book3hasRecite = true;
    220.             book4hasRecite = true;
    221.             dealHasRecite = true;
    222.             GameObject.Find("storePanel(Clone)").GetComponent<storePanelScript_v2>().closeStorePanel();
    223.            
    224.  
    225.             if (GameObject.Find("paid"))
    226.             {
    227.                 GameObject.Find("paid").GetComponent<unlockScript>().bookPayedColoect(book2);
    228.                 GameObject.Find("book2").GetComponent<bookStatus>().isPayedFor = true;
    229.                 GameObject.Find("paid").GetComponent<unlockScript>().bookPayedColoect(book3);
    230.                 GameObject.Find("book3").GetComponent<bookStatus>().isPayedFor = true;
    231.                 GameObject.Find("paid").GetComponent<unlockScript>().bookPayedColoect(book4);
    232.                 GameObject.Find("book4").GetComponent<bookStatus>().isPayedFor = true;
    233.             }
    234.         }
    235.  
    236.         return PurchaseProcessingResult.Complete;
    237.  
    238.     }
    239.    
    240.     private bool IsInitialized()
    241.     {
    242.         return m_StoreController != null && m_StoreExtensionProvider != null;
    243.     }
    244.  
    245.     void buyProduct(string productId)
    246.     {
    247.         if (m_StoreController != null)
    248.         {
    249.             Product product = m_StoreController.products.WithID(productId);
    250.             if (product != null && product.availableToPurchase)
    251.             {
    252.                 Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));  
    253.                 m_StoreController.InitiatePurchase(product);
    254.             }
    255.         }
    256.        
    257.         else
    258.         {
    259.             Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    260.         }
    261.  
    262.  
    263.     }
    264.     public void BuyBook2()
    265.     {
    266.         buyProduct(book2);
    267.     }
    268.     public void BuyBook3()
    269.     {
    270.         buyProduct(book3);
    271.     }
    272.     public void BuyBook4()
    273.     {
    274.         buyProduct(book4);
    275.     }
    276.     public void BuyDeal()
    277.     {
    278.         buyProduct(books234);
    279.         noAds = true;
    280.     }
    281.  
    282.  
    283. }
    284.  
     
  4. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi,
    That error message was from 'CodelessIAPStoreListener.cs'.
    If you are not using Codeless IAP, please don't check the 'Automatically initialize UnityPurchasing(recommended)' checkbox in IAPCatalog window.
    upload_2018-10-12_11-9-48.png
     
  5. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I'm not seeing where you define a product called "myProductName" . Can you search your project for this string?
     
  6. drordoit

    drordoit

    Joined:
    Sep 7, 2013
    Posts:
    36
    I used "myProductName" instead of "books234" to make the post clear for all.
    I removed the checkbox from the 'Automatically initialize UnityPurchasing(recommended)' checkbox and the errors are gone, but now :
    Code (CSharp):
    1. if (m_StoreController.products.WithID("books234").hasReceipt)
    Don't work. Is "hasReceipt" is the best practice to retrieve a purchase? Should it be working with my code ?
    (p.s - The connection to the store is working)
     
  7. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    "Product.hasReceipt" can't retrieve a purchase, it only indicates if you have purchased this product.
    Owned Non-Consumables and Subscriptions should always have receipts. Consumable's receipts are not persisted between app restarts.
    You should use this API after IAP initialization. In your case, you can check it in "OnInitialized".
     
  8. ziadgapp

    ziadgapp

    Joined:
    Aug 17, 2018
    Posts:
    4
    @drordoit 6 years later... how did you manage to solve this? haha :D
     
  9. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Maybe you meant 6 months. What is your current issue?
     
  10. JoshExterna

    JoshExterna

    Joined:
    Apr 26, 2019
    Posts:
    2
    yeah plus its confusing when you guys say dont mix codeless with scripting, because codeless does not have any of the receipt verification handling that is required by apple. So here we are, required to mix them without a good guide or standard. pretty frustrating when according to every single codelss IAP tutorial and guide, i'm all set, but apple denies my app for not having receipt verification.
     
    Dodd85 likes this.
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, codeless is typically for prototyping and learning. It's more appropriate to use scripting for a commercial app IMHO, it's much more flexible. And would likely save you much time in the long run.
     
  12. Venture87

    Venture87

    Joined:
    Dec 22, 2014
    Posts:
    19
    It might be advised to explicitly state this everywhere -.-
     
    harryjbushell likes this.
  13. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    This might be a new policy from apple, I have not heard of this requirement in the past. If it is accurate, it would make sense that Codeless should be updated to meet the requirement, if so. I'll let the IAP team know.
     
  14. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Note my IMHO, but the documentation does state basic functionality.
     
  15. hippogames

    hippogames

    Joined:
    Feb 5, 2015
    Posts:
    233
    The problem in my case is that ProcessPurchase is called for CodelessIAPStoreListener : IStoreListener
    but not for my GameShop : IStoreListener.

    My GameShop is active on scene load but it doesn't receive ProcessPurchase events. At the same time it receives ProcessPurchase event when purchasing. I'm using CodelessIAP with simple callbacks to GameShop (I need to set some booleans to player's profile).

    Can you confirm that only CodelessIAPStoreListener can receive restore purchase events? So my workaround is to make a custom event inside CodelessIAPStoreListener.ProcessPurchase and then subscribe for it inside GameShop.

    BTW I was wondering a few years why Unity IAP doesn't work before trying to debug =)))))))

    upload_2019-10-5_15-38-54.png
     
  16. hippogames

    hippogames

    Joined:
    Feb 5, 2015
    Posts:
    233
    Finally got it working. Try my workaround)
     
  17. Cytanic

    Cytanic

    Joined:
    Jun 6, 2019
    Posts:
    5
    hi 4 year after , i have the same problem , i use IAP codeless and i purchase a noADS nice but when i reinstal the aplication i dont have the items that I buy , how i can obtain if the item was purchased ? , i know that if i use IAP codeless i dont need use code in scripts , or how can i do that ?
     
  18. Cytanic

    Cytanic

    Joined:
    Jun 6, 2019
    Posts:
    5
    Hi 4 years later , if i use IAP Codeless what i should do to obtain if mi NO ADS PURCHASE was bought ? , if i buy works ok , but if i reinstall my game and i use codeless , the buy button should be disabled and the ads disabled, that's what I set to happen when you buy it, but nothing happend in the android studio debug say this



    2023-01-03 16:58:22.840 3019-3042/? E/Unity: Purchase not correctly processed for product "com.threetreesgames.crystal.removeads". Add an active IAPButton to process this purchase, or add an IAPListener to receive any unhandled purchase events.
    UnityEngine.Purchasing.CodelessIAPStoreListener:processPurchase(PurchaseEventArgs)
    UnityEngine.Purchasing.PurchasingManager:processPurchaseIfNew(Product)
    UnityEngine.Purchasing.PurchasingManager:processPurchaseOnStart()
    UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List`1)
    UnityEngine.Purchasing.Extension.UnityUtil:Update()


    and i dont know what happend
     
  19. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
  20. Dodd85

    Dodd85

    Joined:
    Nov 21, 2016
    Posts:
    2

    Is there an update on the Receipt verification for apple? I have been searching for a fix and I have arrived here after what feels like years.

    I am working with assets from the unity store but for the life of me cannot figure this out. My game was published to android and no matter what change I make, Apple rejects it because of the receipt issue.

    The developer I bought this from said it is a unity issue, but all in all it seems like the IAP unity package is not compatible with generating the IAPlisteners and or how to add them when the store draws on a series of cloned scripts that do not let you add individual IAP buttons to call from the IAP catalog, has anyone solved the iOS receipt rejection? been working on this for 3 weeks now.

    Purchase not correctly processed for product " com.patpod.nekoneko.candymatch3.coinspack1". Add an active IAPButton to process this purchase, or add an IAPListener to receive any unhandled purchase events.
    UnityEngine.Purchasing.PurchasingManager:InitiatePurchase (string)
    GameVanilla.Game.UI.IapRow:OnPurchaseButtonPressed () (at Assets/CandyMatch3Kit/Scripts/Game/UI/IapRow.cs:119)
    UnityEngine.Events.UnityEvent:Invoke ()
    GameVanilla.Core.AnimatedButton/<InvokeOnClickAction>d__11:MoveNext () (at Assets/CandyMatch3Kit/Scripts/Core/AnimatedButton.cs:89)
    UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
     
  21. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
    Hello Dodd85,

    First, are you using Codeless IAP or Coded IAP?

    If you are using Coded IAP, then Codeless IAP shouldn't be active at the same time. You can open the IAP Catalog and untick "Automatically initialize UnityPurchasing".

    If you are using Codeless IAP, based on the error you are getting, it seems like either your IAP Buttons or your IAP Listener are not active when the purchase attempt is made.
    These should be active when UnityPurchasing.Initialize is called.
    If you have these in your project, double check that they are active and that they do not have anything added to the OnClick().
    If you don't have these in your project, you will need to add them. See the manual for instructions on how to do this:
    https://docs.unity3d.com/2020.3/Documentation/Manual/UnityIAPCodelessIAP.html

    If the issue is dealing with the IAP Buttons/Listener, then Coded IAP might be more appropriate for your project and would avoid this specific issue.

    If this still doesn't work, could you provide more information on how you are initializing IAP and your buttons/listener?