[Solved] DuplicateTransaction error on relaunching app

Hi

I’m using Unity 5.5.2.
My issue is present on Android and iOS.
I’m using UnityIAP StandardPurchasingModule Version: 1.15.0.
I have one Non-consumable product configured.
I can successfully purchase the product and the purchase is successfully restored on reinstalling the app.

However, on simply relaunching the app, I get a DuplicateTransaction error in the IStoreListener.OnPurchaseFailed callback.

I was initially expecting IStoreListener.ProcessPurchase to be called on relaunching, but then on further investigation, there was nothing implying that this should happen. But, why is IStoreListener.OnPurchaseFailed getting called? I expected that that would be called only if an actual purchase process failed.

If IStoreListener.ProcessPurchase is not supposed to be called on relaunching the app, should I be caching the purchased state myself, or using another Unity IAP API to detemine what has been purchased?

Full Android log is attached.

Thanks.

Colin

3342857–261098–iaplog.txt (11.1 KB)

It sounds like the previous transaction did not complete. Try clearing your Google Play cache as a test. If that doesn’t help, we would need to see your purchasing logic.

1 Like

Thanks Jeff. I’ll try that but I don’t think it will work. The issue seems to be 100% repeatable on multiple devices. The same account was used on the multiple devices, but if the issue would be solved by clearing data/cache, then it would be an issue related to the device, not the account.

What “purchasing logic” would you need to see? The implementations of the IStoreListener interface?

…no, it didn’t work.

I cleared Google Play Store and Google Play Services data and cache.
Launch the app…no change.
Cleared Google Play Store and Google Play Services data and cache again, and force stopped Google Play Store (force stopping Google Play Services was not an option).
Launch the app…Unity IAP initialised OK, but the game played like the purchase had not been made.
I checked the purchase history in the Google Play Store app, and the IAP showed there as having been purchased.
Restarted the phone.
Relaunched the app…DuplicateTransaction issue was present again.

Here’s our IStoreListener interface implementation:

    void IStoreListener.OnInitialized( IStoreController controller, IExtensionProvider extensions )
    {
#if DEBUG_PurchaseManager
        Debug.Log( LOG_TAG + "Unity IAP initialized" );
#endif

        _storeController = controller;
        _extensionProvider = extensions;
        _isInitialised = true;

        if ( _onInitialised != null )
        {
            System.Action    cached = _onInitialised;
            _onInitialised = null;
            cached();
        }
    }

    void IStoreListener.OnInitializeFailed( InitializationFailureReason error )
    {
        Debug.LogError( LOG_TAG + "Unity IAP init failed: " + error.ToString() );

        _isInitialised = false;

        if ( _onInitialised != null )
        {
            System.Action    cached = _onInitialised;
            _onInitialised = null;
            cached();
        }
    }

    PurchaseProcessingResult IStoreListener.ProcessPurchase( PurchaseEventArgs e )
    {
        if ( _isPurchasing && _purchasingProductId == e.purchasedProduct.definition.id )
        {
#if DEBUG_PurchaseManager
            Debug.Log( LOG_TAG + "Unity IAP purchase complete: " + e.purchasedProduct.metadata.localizedTitle );
#endif

            EndPurchasing( EPurchaseResult.SUCCESS );
        }
        else
        {
#if DEBUG_PurchaseManager
            Debug.Log( LOG_TAG + "Unity IAP purchase restored: " + e.purchasedProduct.metadata.localizedTitle );
#endif

            if ( _onPurchaseRestored != null )
            {
                _onPurchaseRestored( e.purchasedProduct.definition.id );
            }
        }

        return PurchaseProcessingResult.Complete;
    }

    void IStoreListener.OnPurchaseFailed( Product i, PurchaseFailureReason p )
    {
        Debug.LogError( LOG_TAG + "Unity IAP purchase failed: " + p.ToString() );

        if ( !_isPurchasing )
        {
            Debug.LogError( LOG_TAG + "OnPurchaseFailed called when not purchasing!" );
            return;
        }

        if ( _purchasingProductId != i.definition.id )
        {
            Debug.LogError( LOG_TAG + "OnPurchaseFailed product id '" + i.definition.id + "' does not match the product id being purchased '" + _purchasingProductId + "'!" );
            return;
        }

        EPurchaseResult    result = EPurchaseResult.FAIL_GENERAL;

        if ( _sFailedPurchaseResultDict.ContainsKey( p ) )
        {
            result = _sFailedPurchaseResultDict[ p ];
        }

        EndPurchasing( result );
    }
1 Like

Sorry, I meant your purchase script

As in the script from which I call IStoreController.InitiatePurchase?

Yes, generally this is stored in a single script, like purchase.cs. Specifically where you call ProcessPurchase, this is where any possible duplicate purchases may be occurring. To be clear, this is not something we are aware of, nor heard reports of from other users. So it would likely be occurring in your code, and this is what I want to look at.

In particular, I’ve seen this behavior when the developer added an explicit On_Click method to a Codeless IAP button, which would call purchase twice. So I wanted to look at your overall logic.

Ok, I’ve attached our “PurchaseManager.cs” script. The PurchaseManager is instantiated elsewhere.

I’m not calling ProcessPurchase at all. I assumed that was only intended to be called by the underlying Unity IAP system.

I’m not using Codeless IAP.

3346269–261520–PurchaseManager.cs (8.14 KB)

It looks like it is called MakePurchase in your script. I’m looking for the logic that handles purchases and calls MakePurchase. I see you’ve implemented your own listener Initialise, in this code it is named Initialize

Instead of this back and forth, it might be best if you supplied your project in a support ticket at Unity Cloud

Thanks Jeff. I’ll make a support ticket tomorrrow.

Sounds good. Also, what is AmuzoEngine?

AmuzoEngine is the namespace for our internal tech (our company is called Amuzo). It was inspired by the UnityEngine namespace :slight_smile:

For future reference, this turned out to be a bug that has since been fixed.You can find a list of the versions with the fix here: