OneShot

All packsApp Store MachinerySolve

storekit 2 transaction.updates listener missed renewals

Forgetting the updates listener misses renewals, refunds, and offer codes

Transaction.updates delivers renewals, Ask to Buy approvals, cross-device purchases, and offer-code redemptions -- not just the purchase you just made. Skipping it misses all of them.

3 of 3 runs passedOne decision inside App Store Machinery (swiftui-storekit2-node).

What goes wrong

A purchase flow that only reads the transaction StoreKit hands back from Product.purchase() correctly handles that one purchase, but misses everything that arrives afterward through a different path: subscription renewals, Ask to Buy parental approvals that complete later, purchases restored on another device, and offer-code redemptions specifically arrive through the Transaction.updates async stream, not the purchase() call's return value. An app without that listener running silently stops learning about any of these events.

Why agents get it wrong

The purchase() call's return value looks like the complete answer to "did the purchase work" -- it returns a transaction, the transaction verifies, the code moves on. Nothing about that call signature hints that a second, separate, long-running listener is required to catch everything that isn't the immediate purchase itself; it's a StoreKit 2 architectural requirement that has to be known in advance, not inferred from any one API's shape.

What App Store Machinery pre-decides

Decision 9 has the app start the Transaction.updates listener in its init, before any UI renders -- explicitly documented as "the #1 StoreKit 2 bug" to forget. Offer-code redemption specifically is called out as arriving through this listener, not the purchase return path, which is exactly why the listener has to already be running before a user could possibly redeem a code from the paywall.

Source: ARCHITECTURE.md decision 9

Coming soon — $129Full receipt and details for App Store Machinery

Related problems