Transaction.finish() is a no-op on iOS 27 beta 5; purchase() then replays the same transaction forever

Transaction.finish() doesn't clear a transaction on iOS 27 beta 5 — worked correctly through beta 4 — every repeat purchase after the first replays the same transaction with no confirmation sheet and no charge.

StoreKit.Transaction.finish() does not clear a transaction on iOS 27 beta 5. The transaction remains in Transaction.unfinished indefinitely, and every subsequent Product.purchase() call for that product returns the same stale transaction instead of starting a new purchase — with no confirmation sheet, no charge, and no way for the app to tell it apart from a genuine purchase.

Because the replayed result is .success carrying a VerificationResult that verifies normally, an app has no supported signal that nothing was bought. The only distinguishing traits are that the id and purchaseDate are unchanged and the call returns in ~10ms instead of making a server round-trip.

Consequence: a consumable product can be purchased exactly once per device. Every later attempt silently no-ops while appearing to succeed.

Steps to Reproduce

On a device running iOS 27 beta 5, sign in to a newly created Sandbox Apple Account (Settings → Apps → App Store → Sandbox Account) with no prior purchase history. Install and launch a development build of an app offering a consumable IAP. Confirm Transaction.unfinished is empty. Purchase the consumable. The confirmation sheet appears and the purchase completes normally. await transaction.finish() on the returned transaction. Enumerate Transaction.unfinished again. Purchase the same consumable a second time. Expected Results Step 6: Transaction.unfinished is empty — the transaction was finished. Step 7: a confirmation sheet appears and a new transaction is created, with a new id and a current purchaseDate. Actual Results Step 6: the just-finished transaction is still listed in Transaction.unfinished. Step 7: no confirmation sheet appears. purchase() returns .success in ~0.01s carrying the same transaction — identical id and identical purchaseDate — and nothing is charged. This repeats indefinitely. Re-fetching the transaction from Transaction.unfinished and calling finish() on that instance does not clear it either, so there is no app-side way to drain the queue.

Diagnostic Log

Virgin sandbox account, empty queue, three consecutive taps on one product:

unfinished before tip.small: []
purchase() returned after 18.10s
tip.small: id=2000001221113013 purchaseDate=2026-08-13 22:36:26 +0000
--- await transaction.finish() ---
unfinished after finishing 2000001221113013: [small#2000001221113013]
unfinished before tip.small: [small#2000001221113013]
purchase() returned after 0.01s
tip.small: id=2000001221113013 purchaseDate=2026-08-13 22:36:26 +0000
unfinished before tip.small: [small#2000001221113013]
purchase() returned after 0.01s
tip.small: id=2000001221113013 purchaseDate=2026-08-13 22:36:26 +0000

The first call is a genuine purchase (18s round-trip, sheet shown). The transaction survives its own finish(). Calls two and three are replays of it.

Notes

Not reproducible against a local .storekit configuration in the Simulator, which always presents the confirmation sheet. Requires Apple's sandbox. Also reproduces on a TestFlight build billed to a real Apple ID, where it is worse: TestFlight purchase history cannot be reset, so the affected products stay permanently stuck for that account. It survives deleting and reinstalling the app, and a device reboot. Possibly the same underlying issue as the unanswered report at https://developer.apple.com/forums/thread/808648 (iOS 26/18, Nov 2025).

Configuration

  • Device: iPhone 16 Pro Max
  • OS: iOS 27 beta 5
  • Products: consumable in-app purchases
  • API: StoreKit 2 (Product.purchase(), Transaction.finish(), Transaction.unfinished)
  • Device: iPhone 16 Pro Max
  • OS: iOS 27 beta 5

This was my first post ever. I messed up the formatting in most of my post. Let me try that agin 🙂

Description

StoreKit.Transaction.finish() does not clear a transaction on iOS 27 beta 5. The transaction remains in Transaction.unfinished indefinitely, and every subsequent Product.purchase() call for that product returns the same stale transaction instead of starting a new purchase — with no confirmation sheet, no charge, and no way for the app to tell it apart from a genuine purchase.

Because the replayed result is .success carrying a VerificationResult that verifies normally, an app has no supported signal that nothing was bought. The only distinguishing traits are that the id and purchaseDate are unchanged and the call returns in ~10ms instead of making a server round-trip.

Consequence: a consumable product can be purchased exactly once per device. Every later attempt silently no-ops while appearing to succeed.

Steps to Reproduce

  1. On a device running iOS 27 beta 5, sign in to a newly created Sandbox Apple Account (Settings → Apps → App Store → Sandbox Account) with no prior purchase history.
  2. Install and launch a development build of an app offering a consumable IAP.
  3. Confirm Transaction.unfinished is empty.
  4. Purchase the consumable. The confirmation sheet appears and the purchase completes normally.
  5. await transaction.finish() on the returned transaction.
  6. Enumerate Transaction.unfinished again.
  7. Purchase the same consumable a second time.

Expected Results

  • Step 6: Transaction.unfinished is empty — the transaction was finished.

  • Step 7: a confirmation sheet appears and a new transaction is created, with a new id and a current purchaseDate.

Actual Results

  • Step 6: the just-finished transaction is still listed in Transaction.unfinished.
  • Step 7: no confirmation sheet appears. purchase() returns .success in ~0.01s carrying the same transaction — identical id and identical purchaseDate — and nothing is charged. This repeats indefinitely.

Re-fetching the transaction from Transaction.unfinished and calling finish() on that instance does not clear it either, so there is no app-side way to drain the queue.

Diagnostic Log

Virgin sandbox account, empty queue, three consecutive taps on one product:

unfinished before tip.small: []
purchase() returned after 18.10s
tip.small: id=2000001221113013 purchaseDate=2026-08-13 22:36:26 +0000
--- await transaction.finish() ---
unfinished after finishing 2000001221113013: [small#2000001221113013]

unfinished before tip.small: [small#2000001221113013]
purchase() returned after 0.01s
tip.small: id=2000001221113013 purchaseDate=2026-08-13 22:36:26 +0000

unfinished before tip.small: [small#2000001221113013]
purchase() returned after 0.01s
tip.small: id=2000001221113013 purchaseDate=2026-08-13 22:36:26 +0000

The first call is a genuine purchase (18s round-trip, sheet shown). The transaction survives its own finish(). Calls two and three are replays of it.

Notes

  • Not reproducible against a local .storekit configuration in the Simulator, which always presents the confirmation sheet. Requires Apple's sandbox.

  • Also reproduces on a TestFlight build billed to a real Apple ID, where it is worse: TestFlight purchase history cannot be reset, so the affected products stay permanently stuck for that account. It survives deleting and reinstalling the app, and a device reboot.

  • Possibly the same underlying issue as the unanswered report at https://developer.apple.com/forums/thread/808648 (iOS 26/18, Nov 2025).

  • A functionally identical report — finish() not clearing a transaction, repeat purchases breaking — was filed against iOS 26 beta 3 in July 2025: https://developer.apple.com/forums/thread/792594. Also unresolved on record. This looks like a StoreKit regression class that recurs across beta cycles rather than a one-off.

  • Every StoreKit "Resolved Issue" in the iOS 27 beta 1–5 release notes is scoped to StoreKit Testing in Xcode (SKTestSession / Transaction Manager / local .storekit config) — none touch the real sandbox purchase path this report is about. If fix verification for this cycle has been leaning on that local-testing path, that would explain why a regression here could ship across multiple betas unnoticed.

Configuration

  • Device: iPhone 16 Pro Max
  • OS: iOS 27 beta 5
  • Products: consumable in-app purchases
  • API: StoreKit 2 (Product.purchase(), Transaction.finish(), Transaction.unfinished)

Same issue here!

Also reproduces on StoreKit 1 — with daemon logs and server-side confirmation

Same behaviour here through a different API. We use StoreKit 1 (SKPaymentQueue.finishTransaction:), not StoreKit 2, so the defect looks like it sits below both API surfaces. Device iPhone 17 Pro, iOS 27.0 from Aug 14, sandbox account, consumables. Transactions survive their own finish, are redelivered on every launch, and repeat buys replay the same transaction id with no sheet and no charge.

We had a device log running, so here is where it fails — 8 finish attempts in the capture, 8 identical failures, 0 successes:

storekitd  [7ae43896_SK1] Starting request FinishTransactionRequest (accountRequirement: required(signIn: false, …), client: eu.nordeus.TopEleven Sandbox, destination: AMSBagKey(p2-in-app-transaction-done), …)

storekitd  [7ae43896_SK1] Account is required for request.

error      [7ae43896_SK1] Failed to encode request parameters (Never): Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set"

error      [7ae43896_SK1] Error finishing transaction: requestEncodeFailed

The request to p2-in-app-transaction-done is never sent, so the server side of the finish never happens. The parameter type is Never — nothing to encode — yet it fails with a JSON parse error on what looks like empty data. Possibly related: the same storekitd logged 22 × AMSErrorDomain Code=204 "Bag Value Missing" while adding mescal headers in the same 63-second window. An incomplete bag would explain both.

Our purchases are validated server-to-server, which confirms from outside the device that nothing is charged. Transaction 2000001222597497 produced exactly one purchase notification to our backend at 12:14:21.920Z; the repeat completed on device at 12:14:36Z with the same id and produced none. The App Store does not treat the replay as a new purchase either.

Negative control: same binary, same products, several iOS 26.x devices with different sandbox accounts — repeat purchase works every time, distinct transaction ids, separate server notification for each:

account A, product X:  2000001222675678 (14:48:25.881Z), 2000001222675727 (14:48:41.310Z)
account B, product Y:  2000001222675941 (14:49:07.446Z), 2000001222676023 (14:49:19.243Z)

Only the OS version differs. Varying device and sandbox account changed nothing.

This appears to be fixed on iOS 27 beta 6.

Transaction.finish() is a no-op on iOS 27 beta 5; purchase() then replays the same transaction forever
 
 
Q