Problem with IAPs (duplicating on Android and bypassing receipt check on IOS)

Hello, I have a game published on both iOS and Android platform. I am having issues since the beginning with IAP purchases. Purchases are sometimes being awarded twice to the users on Android. iOS users, seem to bypass receipt validation and being able to use cheating apps to fake IAP purchases. I was using Codeless IAP with customized code, but since I read some comments on forum I completely removed it from the project and now Im just using regular IAP plugin (at least I believe I removed it)
The problem in Android seems to happen when user restarts the app, it seems like the purchase got stuck at pending state and is being awarded again upon restarting.
On iOS it seems like receipt validation is not working as intended, since some users complete a purchase correctly (boolean validPurchase from code below returns true) but it is a fake one, using IAP simulator apps.

I believe the problem in Android might be there are two listeners for purchases in the Project but I already removed every script except the needed ones. On iOS I have no idea what might be happening. Can you check the code and see if something is missing or something is not correct?
I removed unrelated code:

public class GemShop : MonoBehaviour, IStoreListener
{
 

    private static IStoreController m_StoreController;          // The Unity Purchasing system.
    private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.



    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(gems100, ProductType.Consumable);
        builder.AddProduct(gems500, ProductType.Consumable);
        builder.AddProduct(gems1200, ProductType.Consumable);
        builder.AddProduct(gems2500, ProductType.Consumable);
        builder.AddProduct(gems5500, ProductType.Consumable);
        builder.AddProduct(gems15000, ProductType.Consumable);

        UnityPurchasing.Initialize(this, builder);
    }


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

//BUTTON FOR BUYING 100 GEMS
    public void Buy100gems()
    {
        if (!buying)
        {
            BuyProductID(gems100);
        }
    
    } 


    void BuyProductID(string productId)
    {
        buying = true;
        // 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");
                buying = false;
            }
        }
        // Otherwise ...
        else
        {
            // ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
            // retrying initiailization.
            Debug.Log("BuyProductID FAIL. Not initialized.");
            buying = false;
        }
    }


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


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


      
        price100.text = m_StoreController.products.WithID(gems100).metadata.localizedPriceString;
        price500.text = m_StoreController.products.WithID(gems500).metadata.localizedPriceString;
        price1200.text = m_StoreController.products.WithID(gems1200).metadata.localizedPriceString;
        price2500.text = m_StoreController.products.WithID(gems2500).metadata.localizedPriceString;
        price5500.text = m_StoreController.products.WithID(gems5500).metadata.localizedPriceString;
        price15000.text = m_StoreController.products.WithID(gems15000).metadata.localizedPriceString;

    }


    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);
        buying = false;
    }


    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        bool validPurchase = true; // Presume valid for platforms with no R.V.
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
        var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
       AppleTangle.Data(), Application.identifier);

        try
        {
            var result = validator.Validate(args.purchasedProduct.receipt);


        }
        catch (IAPSecurityException)
        {
            Debug.Log("Invalid receipt, not unlocking content");
            validPurchase = false;
            buying = false;
        }

#endif
        if (validPurchase)
        {
            // Unlock the appropriate content here.
            if (String.Equals(args.purchasedProduct.definition.id, gems100, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
              
               // HERE USER PURCHASES 100 GEMS CORRECTLY
              
             

            }

          .......

            else
            {
                Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
              
            }





        }

        if (validPurchase)
        {
            buying = false;
            return PurchaseProcessingResult.Complete;
        }
        else
        {
            buying = false;
            return PurchaseProcessingResult.Pending;
          
        }
        // 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.
      

    }









    public string GetproductPrice(string id)
    {
        if (m_StoreController != null && m_StoreController.products != null)
        {
            return m_StoreController.products.WithID(id).metadata.localizedPriceString;
        }
        else
        {
            return "";
        }
    }
    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.
        scripter.GetComponent<Alerts>().ShowAlert(Lang.purchasefail[Lang.lang]);
        buying = false;
        Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    }


}

As long as you are not using an IAPButton, then you are not using Codeless. Typically you should not publish your game unless it is fully working or else you might get bad reviews. On Google, you can do beta testing with a few selected users. On iOS, you can use TestFlight. Please provide the device logs for your issue. On Android, your Debug.Log statements will appear in the device logs. On iOS, you can monitor the logs in XCode. These links should help Unity - Manual: Configuring for Google Play Store and Unity - Manual: Configuring for Apple App Store and Mac App Store and How To - Capturing Device Logs on Android and How To - Capturing Device Logs on iOS

Thanks for your reply, I cannot provide logs, since I am not able to replicate the issue, it happen on specific scenarios which circustamces are unknown to me, I am just guessing what might be happening.

i’d like some feedback regarding the code, since this is the only script handling purchases and its mostly copied from the links you provided, except for a minor tweaks. Thanks

Are you using an IAPButton? You don’t have any listeners in your code (and you shouldn’t have for scripted IAP) Please describe the issue in detail. Are your users complaining about purchases? Why do you think people are cheating?

Hello, first of all pardon my grammar, as english is not my main language.
I have several buttons, (just regular UI buttons), for example one of them for buying 100 gems, just calls this method (it’s all in the code I pasted above)

BuyProductID(gems100);

On Android it works fine, user correctly purchases the 100 gems, but in some cases, after the user restart the app, or the scene gets reloaded, they get another 100 gems automatically, but they only got charged once (the first time, which is correct). This was reported to me several times, and on some occasions I could see it myself in the database, comparing it to Google Console purchases:
for example > user buy 15000 gems > he has 30000 in database, but only purchased 15000 once.
I believe this might be because purchase is still pending for some reason, or is being processed twice, this is just me guessing.

On iOs the issue is worse. Whenever an user complete a purchase (purchase returns Completed, and receipt is validated) I can see a handler on his account on the database, which indicate me that the user made a valid purchase, but that is not always the case. For example I see the user purchased 15000 gems, but often the real ammount is over 100k, which is impossible, also admited by some users that they have cheated.
There are some apps, that I dont want to name here, which simulate an IAP purchase, and they can bypass the receipt validation, So I guess I might be doing something wrong.

This is the code I think is messing up iOS validation:

 public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        bool validPurchase = true; // Presume valid for platforms with no R.V.
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
        var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
       AppleTangle.Data(), Application.identifier);
        try
        {
            var result = validator.Validate(args.purchasedProduct.receipt);
        }
        catch (IAPSecurityException)
        {
            Debug.Log("Invalid receipt, not unlocking content");
            validPurchase = false;
            buying = false;
        }

Understood, I just wanted to make sure you’re not using the Codeless IAPButton prefab. I’ve never heard of fraud on iOS. On Google yes, all the time unfortunately. One thing to test. Try to purchase a product, then cancel. Then restart the app and see if you see the ProcessPurchase. Please provide the device logs from your own testing, I might be able to spot something even if everything is working as expected. And what do you mean, handler on the database? There are no database calls in your code.

Also, you are sometimes returning Pending. These will trigger ProcessPurchase next time you start the app, so you’ll want to test that flow too.

Yes. I removed the unrelated bits from the code, this is everything that happen when for example buy 100 gems, but this dont affect purchase itself, I just made it write a node on the Database:

        if (validPurchase)
        {
            // Unlock the appropriate content here.
            if (String.Equals(args.purchasedProduct.definition.id, gems100, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                Puntos.byer = true;

                Puntos.gems = Puntos.Obf(Puntos.Deobf(Puntos.gems) + 100);
                reference.Child("users").Child(Puntos.fbid).Child("Resources").Child("Gems").SetValueAsync(Puntos.Deobf(Puntos.gems));
                gemtext.text = Puntos.Deobf(Puntos.gems).ToString();
                scripter.GetComponent<Alerts>().ShowAlert(Lang.gemspurchased[Lang.lang].Replace("$gems", "100"));
                buying = false;
               
              

            }

This is the log of what happened when I cancelled the purchase:

2020/10/13 01:44:31.659 8931 9008 Info Unity Purchasing product asychronously: 'gems_100'
2020/10/13 01:44:31.659 8931 9008 Info Unity GemShop:BuyProductID(String)
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.Events.UnityAction:Invoke()
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.Events.UnityEvent:Invoke()
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.EventSystems.EventFunction`1:Invoke(T1, BaseEventData)
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
2020/10/13 01:44:31.659 8931 9008 Info Unity UnityEngine.EventSystems.StandaloneInputModule:Process()
2020/10/13 01:44:31.659 8931 9008 Info Unity 
2020/10/13 01:44:31.659 8931 9008 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:44:31.659 8931 9008 Info Unity
2020/10/13 01:44:31.659 8931 9008 Debug UnityIAP isUnityVrEnabled = false
2020/10/13 01:44:31.660 8931 9008 Info UnityIAP onPurchaseProduct: gems_100
2020/10/13 01:44:31.660 8931 9008 Info UnityIAP ITEM TYPE:inapp
2020/10/13 01:44:31.724 8931 9008 Info Unity purchase({0}): gems_100
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.Events.UnityAction:Invoke()
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.Events.UnityEvent:Invoke()
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.EventSystems.EventFunction`1:Invoke(T1, BaseEventData)
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
2020/10/13 01:44:31.724 8931 9008 Info Unity UnityEngine.EventSystems.StandaloneInputModule:Process()
2020/10/13 01:44:31.724 8931 9008 Info Unity 
2020/10/13 01:44:31.724 8931 9008 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:44:31.724 8931 9008 Info Unity
2020/10/13 01:44:31.751 8931 9008 Debug   PlayerBase::stop() from IPlayer
2020/10/13 01:44:31.751 8931 9008 Debug AudioTrack stop() called with 1155072 frames delivered
2020/10/13 01:44:31.814 8931 8931 Warn ActivityThread handleWindowVisibility: no activity for token android.os.BinderProxy@e4e9fa2
2020/10/13 01:44:31.831 8931 8931 Info UnityIAP Creating purchase activity
2020/10/13 01:44:31.832 8931 8931 Info UnityIAP oldSkuMetadata is null
2020/10/13 01:44:31.832 8931 8931 Debug UnityIAP trackPurchase: gems_100
2020/10/13 01:44:31.833 8931 10764 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:44:31.833 8931 10764 Info UnityIAP Constructing buy intent for gems_100, item type: inapp
2020/10/13 01:44:31.842 8931 8931 Info DecorView createDecorCaptionView >> DecorView@e2e308f[], isFloating: false, isApplication: true, hasWindowDecorCaption: false, hasWindowControllerCallback: true
2020/10/13 01:44:31.845 8931 10764 Info UnityIAP Launching buy intent for gems_100. Request code: 999
2020/10/13 01:44:31.905 8931 8931 Debug InputTransport Input channel constructed: fd=259
2020/10/13 01:44:31.905 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] setView = DecorView@e2e308f[PurchaseActivity] TM=true MM=false
2020/10/13 01:44:31.906 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] MSG_WINDOW_FOCUS_CHANGED 0 1
2020/10/13 01:44:31.907 8931 8931 Debug InputMethodManager prepareNavigationBarInfo() DecorView@ea6be07[UnityPlayerActivity]
2020/10/13 01:44:31.907 8931 8931 Debug InputMethodManager getNavigationBarColor() -855310
2020/10/13 01:44:31.946 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] dispatchAttachedToWindow
2020/10/13 01:44:32.020 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] Relayout returned: old=[0,0][2220,1080] new=[0,0][2220,1080] result=0x7 surface={valid=true 533505060864} changed=true
2020/10/13 01:44:32.058 8931 10453 Debug mali_winsys EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, EGLBoolean) returns 0x3000
2020/10/13 01:44:32.058 8931 10453 Debug OpenGLRenderer eglCreateWindowSurface = 0x7c74aec980, 0x7c37613010
2020/10/13 01:44:32.068 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] MSG_RESIZED: frame=Rect(0, 0 - 2220, 1080) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=2
2020/10/13 01:44:32.391 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] Relayout returned: old=[0,0][2220,1080] new=[0,0][2220,1080] result=0x1 surface={valid=true 536886288384} changed=false
2020/10/13 01:44:32.413 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] MSG_RESIZED: frame=Rect(0, 0 - 2220, 1080) ci=Rect(0, 0 - 144, 0) vi=Rect(0, 0 - 144, 0) or=2
2020/10/13 01:44:32.413 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] MSG_RESIZED: frame=Rect(0, 0 - 2076, 1080) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=2
2020/10/13 01:44:32.442 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] Relayout returned: old=[0,0][2076,1080] new=[0,0][2076,1080] result=0x21 surface={valid=true 533505060864} changed=false
2020/10/13 01:44:32.444 8931 10453 Warn libEGL EGLNativeWindowType 0x7c37613010 disconnect failed
2020/10/13 01:44:32.445 8931 10453 Debug OpenGLRenderer eglDestroySurface = 0x7c74aec980, 0x7c37613000
2020/10/13 01:44:32.447 8931 10453 Debug mali_winsys EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, EGLBoolean) returns 0x3000
2020/10/13 01:44:32.447 8931 10453 Debug OpenGLRenderer eglCreateWindowSurface = 0x7c74aec980, 0x7c37613010
2020/10/13 01:44:32.460 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] MSG_RESIZED: frame=Rect(0, 0 - 2076, 1080) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=2
2020/10/13 01:44:32.563 8931 8931 Debug InputTransport Input channel destroyed: fd=114
2020/10/13 01:44:33.384 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] Relayout returned: old=[0,0][2220,1080] new=[0,0][2220,1080] result=0x1 surface={valid=true 536886288384} changed=false
2020/10/13 01:44:38.999 8931 8931 Info UnityIAP onActivityResult
2020/10/13 01:44:38.999 8931 8931 Info UnityIAP Purchase data: null
2020/10/13 01:44:38.999 8931 8931 Info UnityIAP Data signature: null
2020/10/13 01:44:39.000 8931 8931 Info UnityIAP Purchase canceled - Response: 1:User Canceled
2020/10/13 01:44:39.000 8931 8931 Info UnityIAP onIabPurchaseFinished: false
2020/10/13 01:44:39.000 8931 8931 Info UnityIAP 1:User Canceled (response: 1:User Canceled)
2020/10/13 01:44:39.000 8931 8931 Info UnityIAP Purchase response code:1
2020/10/13 01:44:39.001 8931 8931 Info UnityIAP Received user cancelled response, polling deeply for successful purchases to investigate failure more deeply
2020/10/13 01:44:39.001 8931 8931 Info UnityIAP Reducing depth of failure investigation. aggressivelyRecoverLostPurchases is false.
2020/10/13 01:44:39.020 8931 8931 Debug UnityIAP Starting failure reconciliation for: (gems_100)
2020/10/13 01:44:39.020 8931 11024 Debug UnityIAP Adding failed purchase (gems_100) to list being reconciled
2020/10/13 01:44:39.020 8931 11024 Debug UnityIAP FailedPurchaseReconciler state StateIdle
2020/10/13 01:44:39.020 8931 11024 Debug UnityIAP Querying actual purchases to reconcile against previously failed
2020/10/13 01:44:39.021 8931 10764 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:44:39.021 8931 10764 Info UnityIAP Querying owned items, item type: inapp
2020/10/13 01:44:39.021 8931 10764 Info UnityIAP Package name: com.ariel.zanyants
2020/10/13 01:44:39.021 8931 10764 Info UnityIAP Calling getPurchases with continuation token: null
2020/10/13 01:44:39.023 8931 10764 Info UnityIAP Owned items response: 0
2020/10/13 01:44:39.024 8931 10764 Info UnityIAP Continuation token: null
2020/10/13 01:44:39.065 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] MSG_WINDOW_FOCUS_CHANGED 1 1
2020/10/13 01:44:39.065 8931 8931 Debug InputMethodManager prepareNavigationBarInfo() DecorView@e2e308f[PurchaseActivity]
2020/10/13 01:44:39.065 8931 8931 Debug InputMethodManager getNavigationBarColor() -855310
2020/10/13 01:44:39.066 8931 8931 Debug InputMethodManager prepareNavigationBarInfo() DecorView@e2e308f[PurchaseActivity]
2020/10/13 01:44:39.066 8931 8931 Debug InputMethodManager getNavigationBarColor() -855310
2020/10/13 01:44:39.066 8931 8931 Verbose InputMethodManager Starting input: tba=com.ariel.zanyants ic=null mNaviBarColor -855310 mIsGetNaviBarColorSuccess true , NavVisible : true , NavTrans : false
2020/10/13 01:44:39.066 8931 8931 Debug InputMethodManager startInputInner - Id : 0
2020/10/13 01:44:39.066 8931 8931 Info InputMethodManager startInputInner - mService.startInputOrWindowGainedFocus
2020/10/13 01:44:39.069 8931 8931 Debug InputTransport Input channel constructed: fd=264
2020/10/13 01:44:39.118 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] setWindowStopped(false) old=false
2020/10/13 01:44:39.122 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] MSG_WINDOW_FOCUS_CHANGED 1 1
2020/10/13 01:44:39.123 8931 8931 Debug InputMethodManager prepareNavigationBarInfo() DecorView@ea6be07[UnityPlayerActivity]
2020/10/13 01:44:39.123 8931 8931 Debug InputMethodManager getNavigationBarColor() -855310
2020/10/13 01:44:39.128 8931 8931 Debug InputMethodManager prepareNavigationBarInfo() DecorView@ea6be07[UnityPlayerActivity]
2020/10/13 01:44:39.128 8931 8931 Debug InputMethodManager getNavigationBarColor() -855310
2020/10/13 01:44:39.128 8931 8931 Verbose InputMethodManager Starting input: tba=com.ariel.zanyants ic=null mNaviBarColor -855310 mIsGetNaviBarColorSuccess true , NavVisible : false , NavTrans : false
2020/10/13 01:44:39.128 8931 8931 Debug InputMethodManager startInputInner - Id : 0
2020/10/13 01:44:39.128 8931 8931 Info InputMethodManager startInputInner - mService.startInputOrWindowGainedFocus
2020/10/13 01:44:39.132 8931 8931 Debug InputTransport Input channel constructed: fd=310
2020/10/13 01:44:39.132 8931 8931 Debug InputTransport Input channel destroyed: fd=264
2020/10/13 01:44:39.132 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] MSG_WINDOW_FOCUS_CHANGED 0 1
2020/10/13 01:44:39.132 8931 8931 Debug InputMethodManager prepareNavigationBarInfo() DecorView@e2e308f[PurchaseActivity]
2020/10/13 01:44:39.132 8931 8931 Debug InputMethodManager getNavigationBarColor() -855310
2020/10/13 01:44:39.133 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] MSG_RESIZED: frame=Rect(0, 0 - 2220, 1080) ci=Rect(0, 0 - 144, 0) vi=Rect(0, 0 - 0, 0) or=2
2020/10/13 01:44:39.146 8931 8931 Debug ViewRootImpl@ecabccc[UnityPlayerActivity] Relayout returned: old=[0,0][2220,1080] new=[0,0][2220,1080] result=0x1 surface={valid=true 536886288384} changed=false
2020/10/13 01:44:39.161 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] setWindowStopped(true) old=false
2020/10/13 01:44:39.170 8931 10453 Warn libEGL EGLNativeWindowType 0x7c37613010 disconnect failed
2020/10/13 01:44:39.170 8931 10453 Debug OpenGLRenderer eglDestroySurface = 0x7c74aec980, 0x7c37613000
2020/10/13 01:44:39.170 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] dispatchDetachedFromWindow
2020/10/13 01:44:39.170 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] Surface release. android.view.ViewRootImpl.doDie:7979 android.view.ViewRootImpl.die:7947 android.view.WindowManagerGlobal.removeViewLocked:497 android.view.WindowManagerGlobal.removeView:435 android.view.WindowManagerImpl.removeViewImmediate:124 android.app.ActivityThread.handleDestroyActivity:4753 android.app.servertransaction.DestroyActivityItem.execute:39 android.app.servertransaction.TransactionExecutor.executeLifecycleState:145
2020/10/13 01:44:39.179 8931 8931 Debug InputTransport Input channel destroyed: fd=259
2020/10/13 01:44:39.211 8931 8931 Verbose MediaRouter onRestoreRoute() : route=RouteInfo{ name=Teléfono, description=null, status=null, category=RouteCategory{ name=Sistema types=ROUTE_TYPE_LIVE_AUDIO ROUTE_TYPE_LIVE_VIDEO  groupable=false }, supportedTypes=ROUTE_TYPE_LIVE_AUDIO ROUTE_TYPE_LIVE_VIDEO , presentationDisplay=null }
2020/10/13 01:44:39.211 8931 8931 Verbose MediaRouter Selecting route: RouteInfo{ name=Teléfono, description=null, status=null, category=RouteCategory{ name=Sistema types=ROUTE_TYPE_LIVE_AUDIO ROUTE_TYPE_LIVE_VIDEO  groupable=false }, supportedTypes=ROUTE_TYPE_LIVE_AUDIO ROUTE_TYPE_LIVE_VIDEO , presentationDisplay=null }
2020/10/13 01:44:39.228 8931 9008 Info Unity onPurchaseFailedEvent({0}): gems_100
2020/10/13 01:44:39.228 8931 9008 Info Unity UnityEngine.Purchasing.PurchasingManager:OnPurchaseFailed(PurchaseFailureDescription)
2020/10/13 01:44:39.228 8931 9008 Info Unity System.Action:Invoke()
2020/10/13 01:44:39.228 8931 9008 Info Unity UnityEngine.Purchasing.Extension.UnityUtil:Update()
2020/10/13 01:44:39.228 8931 9008 Info Unity 
2020/10/13 01:44:39.228 8931 9008 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:44:39.228 8931 9008 Info Unity
2020/10/13 01:44:39.231 8931 9008 Info Unity OnPurchaseFailed: FAIL. Product: 'gems_100', PurchaseFailureReason: UserCancelled
2020/10/13 01:44:39.231 8931 9008 Info Unity GemShop:OnPurchaseFailed(Product, PurchaseFailureReason)
2020/10/13 01:44:39.231 8931 9008 Info Unity System.Action:Invoke()
2020/10/13 01:44:39.231 8931 9008 Info Unity UnityEngine.Purchasing.Extension.UnityUtil:Update()
2020/10/13 01:44:39.231 8931 9008 Info Unity 
2020/10/13 01:44:39.231 8931 9008 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:44:39.231 8931 9008 Info Unity
2020/10/13 01:44:39.373 8931 8931 Debug ViewRootImpl@b436108[PurchaseActivity] Surface release. android.view.ViewRootImpl$ViewRootHandler.handleMessage:4825 android.os.Handler.dispatchMessage:106
2020/10/13 01:44:39.521 8931 11024 Debug UnityIAP FailedPurchaseReconciler state StateQuerying
2020/10/13 01:44:39.521 8931 10764 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:44:39.521 8931 10764 Debug UnityIAP hadPurchase false
2020/10/13 01:44:39.521 8931 10764 Debug UnityIAP hasPurchase false
2020/10/13 01:44:40.021 8931 11024 Debug UnityIAP FailedPurchaseReconciler state StateProcessing

This is the log upong restarting, with the purchasing bits:

2020/10/13 01:47:00.225 12043 12100 Info Unity UnityIAP Version: 2.0.0
2020/10/13 01:47:00.225 12043 12100 Info Unity UnityEngine.Purchasing.StandardPurchasingModule:Instance(AppStore)
2020/10/13 01:47:00.225 12043 12100 Info Unity GemShop:InitializePurchasing()
2020/10/13 01:47:00.225 12043 12100 Info Unity GemShop:Start()
2020/10/13 01:47:00.225 12043 12100 Info Unity 
2020/10/13 01:47:00.225 12043 12100 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:47:00.225 12043 12100 Info Unity
2020/10/13 01:47:00.243 12043 12100 Info UnityIAP Starting in-app billing setup.
2020/10/13 01:47:00.252 12043 12043 Info UnityIAP Billing service connected.
2020/10/13 01:47:00.253 12043 12384 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:47:00.253 12043 12384 Info UnityIAP Checking for in-app billing 3 support.
2020/10/13 01:47:00.257 12043 12384 Info UnityIAP In-app billing version 3 supported for com.ariel.zanyants
2020/10/13 01:47:00.258 12043 12384 Info UnityIAP Subscriptions AVAILABLE.
2020/10/13 01:47:00.261 12043 12384 Info UnityIAP Subscription upgrade and downgrade are AVAILABLE.
2020/10/13 01:47:00.263 12043 12384 Info UnityIAP Subscriptions information parse AVAILABLE.
2020/10/13 01:47:00.266 12043 12384 Info UnityIAP VR supported.
2020/10/13 01:47:00.266 12043 12384 Debug UnityIAP onIabSetupFinished: 0
2020/10/13 01:47:00.266 12043 12384 Debug UnityIAP Requesting 6 products
2020/10/13 01:47:00.266 12043 12384 Info UnityIAP QueryInventory: 6
2020/10/13 01:47:00.266 12043 12384 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:47:00.266 12043 12384 Info UnityIAP Querying owned items, item type: inapp
2020/10/13 01:47:00.266 12043 12384 Info UnityIAP Package name: com.ariel.zanyants
2020/10/13 01:47:00.266 12043 12384 Info UnityIAP Calling getPurchases with continuation token: null
2020/10/13 01:47:00.269 12043 12384 Info UnityIAP Owned items response: 0
2020/10/13 01:47:00.269 12043 12384 Info UnityIAP Continuation token: null
2020/10/13 01:47:00.269 12043 12384 Info UnityIAP Querying SKU details.
2020/10/13 01:47:00.275 12043 12384 Info UnityIAP Querying owned items, item type: subs
2020/10/13 01:47:00.275 12043 12384 Info UnityIAP Package name: com.ariel.zanyants
2020/10/13 01:47:00.275 12043 12384 Info UnityIAP Calling getPurchases with continuation token: null
2020/10/13 01:47:00.277 12043 12384 Info UnityIAP Owned items response: 0
2020/10/13 01:47:00.277 12043 12384 Info UnityIAP Continuation token: null
2020/10/13 01:47:00.277 12043 12384 Info UnityIAP Querying SKU details.
2020/10/13 01:47:00.285 12043 12384 Info UnityIAP Querying owned items' purchase history, item type: subs
2020/10/13 01:47:00.285 12043 12384 Info UnityIAP Package name: com.ariel.zanyants
2020/10/13 01:47:00.285 12043 12384 Info UnityIAP Calling getPurchaseHistory with continuation token: null
2020/10/13 01:47:00.400 12043 12384 Info UnityIAP Purchase history response: 0
2020/10/13 01:47:00.400 12043 12384 Info UnityIAP Continuation token: null
2020/10/13 01:47:00.400 12043 12384 Info UnityIAP onQueryInventoryFinished: true
2020/10/13 01:47:00.400 12043 12384 Info UnityIAP Inventory refresh successful. (response: 0:OK)
2020/10/13 01:47:00.406 12043 12384 Info UnityIAP Reducing depth of inventory update failure investigation. aggressivelyRecoverLostPurchases is false.
2020/10/13 01:47:00.406 12043 12384 Debug UnityIAP Starting failure reconciliation for 1 products
2020/10/13 01:47:00.407 12043 12388 Debug UnityIAP Adding failed purchase (gems_100) to list being reconciled
2020/10/13 01:47:00.407 12043 12388 Debug UnityIAP FailedPurchaseReconciler state StateIdle
2020/10/13 01:47:00.407 12043 12388 Debug UnityIAP Querying actual purchases to reconcile against previously failed
2020/10/13 01:47:00.408 12043 12384 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:47:00.408 12043 12384 Info UnityIAP Querying owned items, item type: inapp
2020/10/13 01:47:00.408 12043 12384 Info UnityIAP Package name: com.ariel.zanyants
2020/10/13 01:47:00.408 12043 12384 Info UnityIAP Calling getPurchases with continuation token: null
2020/10/13 01:47:00.410 12043 12384 Info UnityIAP Owned items response: 0
2020/10/13 01:47:00.410 12043 12384 Info UnityIAP Continuation token: null
2020/10/13 01:47:00.618 12043 12043 Info DynamiteModule Considering local module com.google.android.gms.ads.dynamite:0 and remote module com.google.android.gms.ads.dynamite:22000
2020/10/13 01:47:00.618 12043 12043 Info DynamiteModule Selected remote version of com.google.android.gms.ads.dynamite, version >= 22000
2020/10/13 01:47:00.618 12043 12043 Verbose DynamiteModule Dynamite loader version >= 2, using loadModule2NoCrashUtils
2020/10/13 01:47:00.635 12043 12043 Info Ads Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("367AB2886644CAF75295669683FB07C9") to get test ads on this device.
2020/10/13 01:47:00.752 12043 12238 Info .ariel.zanyant The ClassLoaderContext is a special shared library.
2020/10/13 01:47:00.788 12043 12043 Info DynamiteModule Considering local module com.google.android.gms.ads.dynamite:0 and remote module com.google.android.gms.ads.dynamite:22000
2020/10/13 01:47:00.788 12043 12043 Info DynamiteModule Selected remote version of com.google.android.gms.ads.dynamite, version >= 22000
2020/10/13 01:47:00.908 12043 12388 Debug UnityIAP FailedPurchaseReconciler state StateQuerying
2020/10/13 01:47:00.908 12043 12384 Info UnityIAP Billing Service Manager invoking callback 1 of 1
2020/10/13 01:47:00.908 12043 12384 Debug UnityIAP hadPurchase false
2020/10/13 01:47:00.908 12043 12384 Debug UnityIAP hasPurchase false
2020/10/13 01:47:00.919 12043 12053 Info .ariel.zanyant Background concurrent copying GC freed 91278(4MB) AllocSpace objects, 46(1632KB) LOS objects, 49% free, 7MB/15MB, paused 260us total 144.535ms
2020/10/13 01:47:01.072 12043 12100 Info Unity OnInitialized: PASS
2020/10/13 01:47:01.072 12043 12100 Info Unity GemShop:OnInitialized(IStoreController, IExtensionProvider)
2020/10/13 01:47:01.072 12043 12100 Info Unity UnityEngine.Purchasing.PurchasingManager:CheckForInitialization()
2020/10/13 01:47:01.072 12043 12100 Info Unity UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List`1)
2020/10/13 01:47:01.072 12043 12100 Info Unity UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String)
2020/10/13 01:47:01.072 12043 12100 Info Unity System.Action:Invoke()
2020/10/13 01:47:01.072 12043 12100 Info Unity UnityEngine.Purchasing.Extension.UnityUtil:Update()
2020/10/13 01:47:01.072 12043 12100 Info Unity 
2020/10/13 01:47:01.072 12043 12100 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:47:01.072 12043 12100 Info Unity
2020/10/13 01:47:01.075 12043 12100 Info Unity UnityIAP: Initialization complete with  6 items
2020/10/13 01:47:01.075 12043 12100 Info Unity UnityEngine.Purchasing.Promo:ProvideProductsToAds(HashSet`1, Boolean)
2020/10/13 01:47:01.075 12043 12100 Info Unity UnityEngine.Purchasing.JSONStore:OnProductsRetrieved(String)
2020/10/13 01:47:01.075 12043 12100 Info Unity System.Action:Invoke()
2020/10/13 01:47:01.075 12043 12100 Info Unity UnityEngine.Purchasing.Extension.UnityUtil:Update()
2020/10/13 01:47:01.075 12043 12100 Info Unity 
2020/10/13 01:47:01.075 12043 12100 Info Unity (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
2020/10/13 01:47:01.075 12043 12100 Info Unity
2020/10/13 01:47:01.408 12043 12388 Debug UnityIAP FailedPurchaseReconciler state StateProcessing
2020/10/13 01:47:02.725 12043 12043 Info ExoPlayerImpl Init ExoPlayerLib/2.4.2 [dreamlte, SM-G950F, samsung, 28]
2020/10/13 01:47:02.793 12043 12477 Debug MetadataUtil Skipped unknown metadata entry: gsst
2020/10/13 01:47:02.793 12043 12477 Debug MetadataUtil Skipped unknown metadata entry: gstd

Yes, thats something I noticed, but it forces me to return either Completed or Pending. Cant I return something else when purchase is cancelled or failed?

Did it work as expected? Are the Debug.Logs what you expect?

Return Complete but don’t award the product.

I dont notice anything wrong, I just see that there is still a failed purchase appearing upon restarting, but as an user I didnt notice anything, and gems didnt get awarded

Sorry you’ll need to provide more information. So you can provide steps to reproduce?

I mean, i just did what you recommended and pasted the logs. I am not able to reproduce the issue, since its been reported on some occasions by players. Just need to find issues with the code.

You said “there is still a failed purchase appearing upon restarting” and I was looking for specifics.

Yes, i did what you asked: Try to purchase but cancelling before confirming. Upon restarting the app i noticed this on the logs i pasted.

  • 2020/10/13 01:47:00.406 12043 12384 Debug UnityIAP Starting failure reconciliation for 1 products
  • 2020/10/13 01:47:00.407 12043 12388 Debug UnityIAP Adding failed purchase (gems_100) to list being reconciled
  • 2020/10/13 01:47:00.407 12043 12388 Debug UnityIAP FailedPurchaseReconciler state StateIdle
  • 2020/10/13 01:47:00.407 12043 12388 Debug UnityIAP Querying actual purchases to reconcile against previously failed

Got it, thanks. So you are referring to the cancelled purchase. Hopefully you’ll find specific steps to reproduce what your users are reporting. When you do, please post here.