If buying an iOS consumable a second time fails with DuplicateTransaction or an "Already purchased" dialog, the first transaction was never finished. Call ConfirmPurchase() for every consumable as soon as OnPurchasePending fires, using the order that callback gives you. This shows up most often in TestFlight, and some iOS versions before iOS 26 also have an Apple bug that causes the same symptom.
How do I know if this is affecting me?
- After successfully purchasing a consumable product and calling ConfirmPurchase, attempting to buy the same product again fails.
- In TestFlight on specific devices, OnPurchaseFailed is triggered with FailureReason = DuplicateTransaction and Apple’s dialog shows “Already purchased,” blocking repurchase.
- When running the same build from Xcode (sandbox), repurchase works and OnPurchasePending is called again.
- Purchase history and transaction IDs differ between TestFlight and Xcode / other devices.
Why does this happen?
- Consumable transactions are not consistently or immediately confirmed in OnPurchasePending, leaving Apple StoreKit transactions in a pending/unfinished state.
- TestFlight enforces stricter, production-like behavior, so unfinished consumable transactions block subsequent purchases and are treated as duplicate transactions.
- The Unity IAP plugin re-detects previously recorded transaction IDs and classifies them as ConfirmedOrder, returning DuplicateTransaction instead of treating them as new purchases.
- On some iOS versions (prior to an iOS 26 fix), there is a known Apple-side issue where transactions might not finish, causing subsequent purchases of the same product to fail.
How do I fix it?
1. Confirm consumable purchases immediately and automatically inside the OnPurchasePending callback instead of deferring confirmation to a later user action (e.g., a button).
2. Ensure ConfirmPurchase is always called for consumables, regardless of additional receipt validation results, so transactions are removed from the pending state.
3. Review the purchase flow to confirm that the same pending order (e.g., m_LastPendingOrder) is used for confirmation and that GetPurchases() is not misused for confirming unrelated orders.
Test on devices running newer iOS versions (e.g., iOS 26 or later), where Apple has fixed an issue causing transactions not to finish and subsequent purchases to fail.
When possible, test by building directly to a device instead of relying exclusively on TestFlight, as TestFlight can exhibit stricter or more pronounced behaviour regarding unfinished transactions.