StoreKit2 Repro Notes: the latest renewal appears in Transaction.unfinished after restore (2026-04-05)
1. Issue Summary
In the current project, during a normal cold launch:
Transaction.latest(for:)returns a value for the weekly subscriptionTransaction.allreturns the full subscription history chainTransaction.unfinishedis empty
However, after tapping Restore Purchases and calling AppStore.sync(), one "latest renewal" transaction appears in Transaction.unfinished.
This behavior looks more like a system-side replay triggered by AppStore.sync() than a consistently unfinished transaction during a normal launch.
2. Affected Product
Product:
do.i.iapc.vip.week
Transaction chain characteristics:
- All transactions belong to the same auto-renewable subscription chain
originalTransactionID = 2000001143446796- The transaction that appears in
unfinishedis usually the latest or last renewal in the chain
3. Current Code Path
During app startup:
loadProducts()- Debug snapshot for
Transaction.latest(for:) - Debug snapshot for
Transaction.all - Scan
Transaction.unfinished refreshEntitlements()
During restore purchases:
- Call
AppStore.sync() - Scan
Transaction.unfinished refreshEntitlements()
4. Preconditions
- A Sandbox test account is used
- The weekly subscription
do.i.iapc.vip.weekalready has multiple historical renewal transactions - The subscription is already expired, so
entitlements = 0during a normal launch - The issue is easier to reproduce on an
iOS 26.4device - The issue was not consistently reproduced on another
iOS 18.2device
5. Reproduction Steps
Path A: Normal cold launch
- Launch the app
- Observe the logs:
LatestTransaction snapshotAllTransaction snapshot summaryunfinished processing result
Observed result:
latesthas a valueallcontains the full history chainunfinishedHandledCount = 0
Path B: Tap Restore Purchases
- Launch the app
- Tap Restore Purchases
- Trigger
AppStore.sync() - Observe the logs:
restore startedunfinished processing startedunfinished transaction received
Observed result:
- After restore, one "latest renewal" transaction appears in
unfinished - That same transaction does not necessarily appear during a normal cold launch
6. Expected Result
If a transaction has already been successfully finished in the past, it should not appear again as unfinished after Restore Purchases.
A stricter expectation is:
- During a normal cold launch,
unfinished = 0 - After tapping Restore Purchases,
unfinishedshould still remain0
7. Actual Result
Actual behavior:
- Normal cold launch:
unfinished = 0 - After Restore Purchases: one "latest renewal" transaction appears again in
unfinished
This suggests that AppStore.sync() may replay the most recent historical subscription transaction.
8. Current Assessment
Based on the current logs, the issue is more likely to be:
- Related to
AppStore.sync()/ StoreKit / Sandbox replay behavior on the system side - Easier to reproduce on
iOS 26.4 - Less likely to be caused by a persistent app-side bug where
finish()is missed during a normal startup flow
Reasons:
- During a normal launch,
unfinished = 0 - The behavior is inconsistent across devices and OS versions, even with the same Sandbox account
latest,all, andunfinishedcan be clearly separated during a normal cold launch
9. Suggested Engineering Position
Suggested wording for internal or external communication:
In the
iOS 26.4 + Sandboxenvironment, callingAppStore.sync()may cause StoreKit to replay the latest historical subscription transaction intoTransaction.unfinished. Since the same transaction does not necessarily appear during a normal cold launch, the issue currently looks more like a system/environment-specific behavior difference than an app-side bug wherefinish()is consistently missed during the regular startup path.
10. Additional Evidence That Can Be Collected
If this needs to be escalated to the team or to Apple, the following would strengthen the report:
- Full log comparison before and after tapping Restore Purchases
- The same
transactionIdcompared between normal launch and post-restore behavior - Cross-device comparison on different iOS versions
- A minimal reproducible sample project and Sandbox test record