I have 12 consumable items in "Ready To Submit" status and I am trying to purchase 1 of them again and again by using a sandbox tester account. In the initialization process, I can receive the product successfully. I can initiate a purchase flow by giving the name of the product. In the "ProcessPurchase" callback, I can read all required data like transactionId, payload, etc. and completing the process by returning PurchaseProcessingResult.Complete. I expect UnityIAP to complete the transaction and make the current item available to purchase again.
The problem is, when I try to buy the previous item again, it shows "This In-App Purchase has already been bought" popup. Here is the log file :
PurchaseController - ,Purchasing product asychronously: 'mini_coin_pack'
PurchaseController:BuyProductID(String)
UnityEngine.Events.UnityAction:Invoke()
UnityEngine.Events.UnityEvent:Invoke()
UnityEngine.EventSystems.EventFunction`1:Invoke(T1, BaseEventData)
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
UnityEngine.EventSystems.StandaloneInputModule:Process()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
2021-04-15 15:34:14.006798+0300 mygame[1689:850412] UnityIAP: PurchaseProduct: mini_coin_pack
purchase(mini_coin_pack)
UnityEngine.Events.UnityAction:Invoke()
UnityEngine.Events.UnityEvent:Invoke()
UnityEngine.EventSystems.EventFunction`1:Invoke(T1, BaseEventData)
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
UnityEngine.EventSystems.StandaloneInputModule:Process()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
2021-04-15 15:34:14.048335+0300 mygame[1689:850412] UnityIAP: UpdatedTransactions
-> applicationWillResignActive()
-> applicationDidBecomeActive()
2021-04-15 15:34:22.947604+0300 mygame[1689:850412] UnityIAP: UpdatedTransactions
2021-04-15 15:34:22.948039+0300 mygame[1689:850412] UnityIAP: DuplicateTransaction error with product mini_coin_pack and transactionId 1000000801477725
-> applicationWillResignActive()
onPurchaseFailedEvent(productId:mini_coin_pack message:Duplicate transaction occurred)
UnityEngine.Purchasing.PurchasingManager:OnPurchaseFailed(PurchaseFailureDescription)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
PurchaseController - ,OnPurchaseFailed, Product : mini_coin_pack, Reason : DuplicateTransaction
PurchaseController:OnPurchaseFailed(Product, PurchaseFailureReason)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
PurchaseController - ,Store specific error code: Unknown
PurchaseController:OnPurchaseFailed(Product, PurchaseFailureReason)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
PurchaseController - ,Purchase failure description message: Duplicate transaction occurred
PurchaseController:OnPurchaseFailed(Product, PurchaseFailureReason)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
-> applicationDidBecomeActive()
When I kill and relaunch the app, following logs show that there is still a transaction to complete from previous try.
2021-04-15 15:35:03.181235+0300 mygame[1699:852283] UnityIAP UnityEarlyTransactionObserver: Created
2021-04-15 15:35:03.182006+0300 mygame[1699:852283] UnityIAP UnityEarlyTransactionObserver: Registered for lifecycle events
CrashReporter: initialized
2021-04-15 15:35:03.215730+0300 mygame[1699:852283] Built from '2019.4/staging' branch, Version '2019.4.23f1 (3f4e01f1a5ec)', Build type 'Release', Scripting Backend 'il2cpp'
2021-04-15 15:35:03.228051+0300 mygame[1699:852283] MemoryManager: Using 'Default' Allocator.
-> applicationDidFinishLaunching()
2021-04-15 15:35:03.528270+0300 mygame[1699:852283] UnityIAP UnityEarlyTransactionObserver: Added to the payment queue
-> applicationDidBecomeActive()
GfxDevice: creating device client; threaded=1
Initializing Metal device caps: Apple A8 GPU
Initialize engine version: 2019.4.23f1 (3f4e01f1a5ec)
CrashReporter: No pending report exists at /var/mobile/Containers/Data/Application/01EA9697-0BD5-40AF-BB20-967567B5EAE2/Library/Caches/CrashReports/crash-pending.plcrash
UnloadTime: 1.242625 ms
UnityIAP Version: 3.0.1
UnityEngine.Purchasing.StandardPurchasingModule:Instance(AppStore)
PurchaseController:InitializePurchasing()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
2021-04-15 15:35:06.133617+0300 mygame[1699:852283] UnityIAP: Requesting 1 products
2021-04-15 15:35:06.201046+0300 mygame[1699:852283] UnityIAP: Requesting product data...
2021-04-15 15:35:07.031669+0300 mygame[1699:852283] UnityIAP: Received 1 products
PurchaseController - ,OnInitialized
PurchaseController:OnInitialized(IStoreController, IExtensionProvider)
UnityEngine.Purchasing.PurchasingManager:CheckForInitialization()
UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List`1)
UnityEngine.Purchasing.AppleStoreImpl:OnProductsRetrieved(String)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
PurchaseController - ,Mini Coin Pack, is ready to purchase!
PurchaseController:OnInitialized(IStoreController, IExtensionProvider)
UnityEngine.Purchasing.PurchasingManager:CheckForInitialization()
UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List`1)
UnityEngine.Purchasing.AppleStoreImpl:OnProductsRetrieved(String)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
UnityIAP: Initialization complete with 1 items
UnityEngine.Purchasing.Promo:ProvideProductsToAds(HashSet`1, Boolean)
UnityEngine.Purchasing.AppleStoreImpl:OnProductsRetrieved(String)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
2021-04-15 15:35:07.079768+0300 mygame[1699:852283] UnityIAP: Add transaction observer
2021-04-15 15:35:07.080293+0300 mygame[1699:852283] UnityIAP: UpdatedTransactions
2021-04-15 15:35:07.084949+0300 mygame[1699:852283] UnityIAP UnityEarlyTransactionObserver: Request to initiate queued payments
Already recorded transaction 1000000801477725
UnityEngine.Purchasing.PurchasingManager:ProcessPurchaseIfNew(Product)
UnityEngine.Purchasing.JSONStore:OnPurchaseSucceeded(String, String, String)
System.Action:Invoke()
UnityEngine.Purchasing.Extension.UnityUtil:Update()
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)
2021-04-15 15:35:07.095638+0300 mygame[1699:852283] UnityIAP: Finishing transaction 1000000801477725
The last line of the above log, shows that ProcessPurchase callback method is called during app launch and transaction has tried to complete again.
Here is my sandbox code I used during testing :
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.UI;
public class PurchaseController : MonoBehaviour, IStoreListener
{
private IStoreController _storeController;
private IExtensionProvider _extensionProvider;
[SerializeField] private Button _purchaseButton;
private string _miniCoinPack = "mini_coin_pack";
private void Awake()
{
_purchaseButton.onClick.AddListener(BuyConsumable);
DontDestroyOnLoad(gameObject);
}
private void Start()
{
if (_storeController == null)
{
InitializePurchasing();
}
}
private void InitializePurchasing()
{
if (IsInitialized()) return;
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
builder.AddProduct(_miniCoinPack, ProductType.Consumable);
UnityPurchasing.Initialize(this, builder);
}
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
_storeController = controller;
_extensionProvider = extensions;
foreach (var product in _storeController.products.set)
{
if (product.hasReceipt)
{
_storeController.ConfirmPendingPurchase(product);
}
if (product.availableToPurchase)
{
Log(product.metadata.localizedTitle + ", is ready to purchase!");
}
}
}
public void OnInitializeFailed(InitializationFailureReason error)
{
Log("OnInitializeFailed: " + error);
}
private bool IsInitialized()
{
return _storeController != null && _extensionProvider != null;
}
public void BuyConsumable()
{
BuyProductID(_miniCoinPack);
}
private void BuyProductID(string productId)
{
if (IsInitialized())
{
Product product = _storeController.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
_storeController.InitiatePurchase(product, "{\"userID\":\"313131696969\"}");
}
else
{
Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
}
}
else
{
Log("BuyProductID FAIL. Not initialized.");
}
}
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
Log("purchase success for : " + args.purchasedProduct.definition.id);
return PurchaseProcessingResult.Complete;
}
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
Log(string.Format("OnPurchaseFailed, Product : {0}, Reason : {1}", product.definition.storeSpecificId, failureReason));
var transactionHistoryExtensions = _extensionProvider.GetExtension<ITransactionHistoryExtensions>();
Log("Store specific error code: " + transactionHistoryExtensions.GetLastStoreSpecificPurchaseErrorCode());
if (transactionHistoryExtensions.GetLastPurchaseFailureDescription() != null)
{
Log("Purchase failure description message: " + transactionHistoryExtensions.GetLastPurchaseFailureDescription().message);
}
}
private void Log(string message)
{
Debug.Log(string.Concat("PurchaseController - ,", message));
}
}
I have tried different ways to integrate UnityIAP into my sandbox Unity project like :
- using only unity package manager and installing (2.2.7 & 3.0.2 respectively)
- activating In-App Purchasing & Analytics services and installing UnityIAP package from there.
Unity Version : 2019.4.22f1, 2019.4.23f1
Unity IAP Version : 2.2.7, 3.0.2
Test Device : iPhone 6, iOS 12.5.1
Test User : Sandbox Tester (Country : United States, Interrupted Purchases : disabled, Apple Pay: Yes)
App Store Connect App Status : Prepare For Submission (Not submitted yet!)
In App Purchase Items : (Type:Consumable, Status:Ready To Submit)
It seems like this problem is fixed with UnityIAP-2.2.2 version as stated here : https://forum.unity.com/threads/unity-iap-package-3-0-2-is-now-available.415517/page-2 but it still exist in 2.2.7 & 3.0.2 versions.
Is there anyone else who face the same or similar behaviour while implementing UnityIAP?