I'm on macOS Sequoia Version 15.7.3 (24G419) and using Xcode Version 26.2 (17C52).
In my Xcode project, Transaction.updates and Product.SubscriptionInfo.Status.updates don’t seem to emit updates reliably.
The code below works consistently in a fresh Xcode project using a minimal setup with a local StoreKit Configuration file containing a single auto-renewable subscription.
class InAppPurchaseManager {
static let shared = InAppPurchaseManager()
var transactionTask: Task<Void, Never>?
var subscriptionTask: Task<Void, Never>?
init() {
print("Launched InAppPurchaseManager...")
transactionTask = Task(priority: .background) {
for await result in Transaction.updates {
print("\nReceived transaction update...")
try? await result.payloadValue.finish()
}
}
subscriptionTask = Task(priority: .background) {
for await result in Product.SubscriptionInfo.Status.updates {
print("\nReceived subscription update...")
print("state:", result.state.localizedDescription)
}
}
}
}
I initialise it in:
func applicationDidFinishLaunching(_ aNotification: Notification) {
_ = InAppPurchaseManager.shared
}
I do not build any UI for this test. I open StoreKit Transaction Manager then click Create Transaction → select the product → choose Purchase (Default) → Next → Done. The console shows that it detects the initial purchase, renewals and finishes each transaction.
It also works even if I do not add the In-App Purchase capability.
In my actual project, the initial purchase is detected and finished, but renewals are not detected. Subsequent transactions then appear as unverified, presumably because the updates are not being observed so the transactions are not being finished.
What can I do to make this work reliably in my actual project?
For context, in the actual project:
I have a StoreKit Configuration file that is synced with App Store Connect
The In-App Purchase capability is enabled
The configuration file is selected in the scheme
The products in App Store Connect show “Ready to Submit”
Loading products works:
try await Product.products(for: ...)
Also, I use ProductView for the purchase UI. The first purchase works and is detected and finished, but subsequent renewals are not finished because the updates do not seem to be emitted.
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Posts under StoreKit tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
**Environment
Platform:** iOS
Distribution: TestFlight
Product type: Consumable In-App Purchase
Account used for testing: Real Apple ID (not Sandbox)
StoreKit: StoreKit 1
iOS version: iOS 17+ (also reproduced on earlier versions)
Issue Description
We are encountering an issue when testing consumable in-app purchases in a TestFlight build using a real Apple ID.
Under normal circumstances, consumable products should be purchasable repeatedly. However, in TestFlight, after a successful purchase flow, the same product may become unavailable for repurchase, and the transaction appears to be stuck, even though:
• finishTransaction: is correctly called
• The transaction state is .purchased
• No pending transactions are left in the payment queue
Once this happens, subsequent purchase attempts result in behavior similar to a non-consumable product (e.g. “already purchased” or no purchase UI shown).
I had published an App, and my app has App Clip supported. The issue I faced is that I had received complaints where the user keep seeing the pop up "Apple Media Services Terms and Conditions Have Changed" when user clicked on the "Open" Button in the App Clip.
What we had tried so far:
Let user switch the Apple Id's region to our supported region.
Let user try to log out and log in to Apple Id within the supported region.
I added my first subscription to my app using StoreKit's SubscriptionStoreView. Everything worked as expected in the debug environment and also in TestFlight. So I submitted my app and subscriptions to App Store Connect, got everything Approved and released.
After updating my app through App Store and checking the Subscription View, it just says "Subscription Unavailable. The subscription is unavailable in the current storefront."
I waited around 3 days and still getting the same message. Now the very strange behavior starts. I went to App Store Connect, I made and edit to the subscription description, saved, removed the edit, saved, and submitted to review. 15 minutes later the subscriptions appear in my app and everything works as expected. After getting the edit approved, the Subscription View in my app again only showed the message "Subscription Unavailable. The subscription is unavailable in the current storefront." No user is able to see the subscriptions anymore, even though it worked as expected before the edit was approved.
So I did the same as before. Again, make an edit to the subscription description, save, remove the edit, save, submit to review. 15 minutes later the subscriptions are again available in my app and it works as expected.
This is definitely not the expected behavior and submitting the subscription edits every day is wasting the App Review Team's time as well as mine. I contacted Apple Developer Support but I didn't get any reply back (at least yet).
I am not the only one experiencing this. I found a friend online who has the exact same issue, and is able to temporarily solve it by making an edit to the subscription description as well.
So far it has been a huge headache, and we are losing customers this way. Please if anyone has experience with this problem, or has any suggestions, they will be greatly appreciated.
Thank you so much,
Tomas
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Connect
In-App Purchase
Ever since I have upgraded to iOS 26 (at least, I THINK this was when it started) I have been seeing an email account on Testflight purchase flows that is NOT mine. I have never seen this email in my life, and have not used it during the 10+ years I have been a developer.
I do not know how to make this use my Apple ID I am signed into on my device. However, locally signed builds from Xcode still work ok.
But I need Testflight builds to work as well.
How do I force this account to change?
Topic:
App Store Distribution & Marketing
SubTopic:
TestFlight
Tags:
StoreKit Test
StoreKit
TestFlight
Hello,
In my iOS app, I have a customer center where the user can see some details about its current subscription. I display things like the billing period, the price, the introductory offer state, the renewal date if it's not cancelled or the expiration date if it's cancelled, etc. From this screen, the user can open the subscription management sheet.
I want to detect if the user cancels the subscription from this sheet or from the App Store (when the app is running) so I can refresh the information displayed on my customer center.
I checked the asynchronous sequences provided by StoreKit 2 like Transaction.updates or Product.SubscriptionInfo.Status.updates and tested with a Sandbox account on my physical device with the app debugged using Xcode. But I noticed these sequences don't emit when I cancel the subscription in Sandbox.
Is this the expected behavior?
Is there a way to observe in real time if a user cancels the subscription?
I can still manually check when the sheet is dismissed but it's not ideal because I want to know even if the user cancel from outside of the app with the app running.
Thank you,
Axel
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit Test
StoreKit
In-App Purchase
有一个用户反馈7月8日充值了3笔后一直无法发起新的购买,总是提示已经购买。我们查了是之前的交易无法结束,我们使用StoreKit2,已经调用await transaction.finish()成功结束交易了,但是每次发起新支付时,Transaction.unfinished还会返回之前已完成的交易信息。
是设备或AppleID的问题么?用户尝试了重启设备、重新登录AppleID都没用。有什么办法解决呢?
用户无法结束的苹果交易id为:200002703899379、200002703900716、200002703902023。
A user reported that after making 3 purchases on July 8th, they have been unable to initiate new purchases, always receiving a prompt that the item has already been purchased. Upon investigation, we found that the previous transactions couldn't be finalized. We use StoreKit2 and have successfully called await transaction.finish() to end the transactions. However, every time a new payment is initiated, Transaction.unfinished still returns information about the previously completed transactions.
Could this be an issue with the device or Apple ID? The user has tried restarting the device and re-logging into their Apple ID, but these attempts were unsuccessful. Is there any way to resolve this?
The Apple transaction IDs that the user is unable to finalize are: 200002703899379, 200002703900716, 200002703902023.
Since January 28, 2026, we’ve noticed an increase in StoreKit-related errors during purchase flows.
Specifically, we’re seeing a spike in errors reported as “Unable to Complete Request”, categorized as unknown StoreKit errors. This correlates with a noticeable drop in the overall purchase success rate.
A few observations:
The issue is not limited to the latest app version, it also affects older versions.
It appears to occur only on iOS 17+.
The impact seems country-specific: some regions are affected more heavily, while others show no significant change compared to previous days.
At the moment, there are no related incidents reported on Apple’s System Status page.
Given these symptoms, this looks like a potential StoreKit / Apple API issue, but we haven’t found any official confirmation yet.
Has anyone else observed similar StoreKit behavior recently on iOS 17+?
Any insights or known issues would be greatly appreciated.
Hello everyone,
I’m currently stuck in App Review and would appreciate input from anyone who has dealt with a similar situation.
App setup
iOS app, SwiftUI, StoreKit 2
No user accounts (no login, no username/password, no backend)
No subscriptions
Monetization model:
App was paid before version 1.0.5
Since 1.0.5 the app is free
Unlocking full functionality is done via a non-consumable, one-time IAP (“Pro Unlock”)
Existing users who bought the app when it was paid are automatically granted Pro access
New users get a 24-hour local trial, then must purchase the non-consumable IAP
All state is local / device-based, no server
There is no concept of an account, subscription renewal, or expired subscription in the app.
Rejection from App Review
Apple rejected the app with the following message:
Guideline 2.1 – Information Needed
We are not able to continue our review because we need access to a demo account with an expired subscription to review the entire purchase flow.
Please provide a user name and password for a demo account with expired subscriptions in the App Review Information section of App Store Connect.
This is where I’m confused.
Why this doesn’t seem applicable
The app does not use subscriptions
The app does not have user accounts
The IAP is non-consumable, one-time purchase
Trials are not subscription-based and expire automatically on-device
There is no “expired subscription account” that could exist technically
StoreKit testing is done via sandbox Apple IDs, but those are:
not app-level accounts
not usernames/passwords inside the app
only used in the Apple purchase sheet
Additional complication
Because old paid users are automatically recognized as “Pro” via AppTransaction.originalAppVersion, the Unlock Pro button is hidden once Pro is active.
This means that on some devices (including mine), the purchase sheet never appears anymore because the app already considers the user entitled.
This might be confusing App Review and causing them to assume the app uses subscriptions and gated accounts.
Questions
Has anyone seen App Review request a demo account with expired subscription for an app that uses only non-consumable IAPs?
Is there a correct way to respond other than explicitly explaining that:
there are no accounts
there are no subscriptions
StoreKit sandbox Apple IDs are sufficient?
Would providing a sandbox Apple ID (clearly labeled as such) help, or is that inappropriate since Apple reviewers already have sandbox accounts?
Is this likely just a misclassification by App Review, or is there something in Apple’s guidelines that I’m missing?
What I’ve already tried
Explained the purchase flow step-by-step in App Review notes
Clarified that the Paid Apps Agreement is already accepted
Clarified that Pro access is automatically granted to previous paid users
Verified that the IAP is correctly configured and available in App Store Connect
At this point I’m unsure whether I should:
escalate / push back on the incorrect assumption, or
modify the UI to make the purchase path permanently visible even for entitled users
Any insight from people who have gone through similar review issues would be greatly appreciated.
Thanks in advance.
Hello,
I'm sure I've probably missed a checkbox somewhere.. I have a mulitiplatform app, when building from Xcode, and not using the testing config, both iOS and macOS show the correct App Store currency..
When I distribute a build through TestFlight, my Mac version shows a
different country/currency price (the US one). I can't find anywhere to change this. My Mac is signed into the same sandbox account as my iOS device.
Can anyone help?
My app has in app purchase for subscriptions, available in many countries. When using Sandbox App Store accounts on TestFlight with a locale different from my own in the iOS 26 RC, I'm getting incorrect currency coming back from Product.products(for: identifiers), and so my app displays the wrong price for the locale. However, the actual Apple Pay buy sheet shows the proper currency symbol and currency amount. This did not happen on prior versions of iOS.
Is anyone else experiencing this?
I'm implementing PurchaseIntent.intents for App Store in-app purchase promotions, following Apple's WWDC guidance. The API only works on cold launch (killed→launch), but fails on background→foreground transitions, making App Store promotions unusable.
Sample code as followed from WWDC23 video "What's new in StoreKit 2 and StoreKit Testing in Xcode".
In the StoreKitManager observable class, I have this function which is initialized in a listening task:
func listenForPurchaseIntent() -> Task<Void, Error> {
return Task { [weak self] in
for await purchase in PurchaseIntent.intents {
guard let self else { continue }
let product = purchase.product
await self.purchaseProduct(product)
}
}
}
where purchaseProduct() will perform the call to:
try await product.purchase()
ISSUE:
When the app is in background (after previously launched), and the purchase intent is initiated from Xcode Transaction Manager or using the "itms-services://?action=purchaseIntent" method, the system foregrounds my app but the purchase intent is never delivered to the waiting listener. The intent remains queued until the next cold launch (quit app and relaunch app). This could mean that if a user has installed the app, and has run the app, then tapped the promotional IAP from the App Store, the purchase intent will not show up until the next cold launch.
If the app is in quit state, then the system will foreground the app, and purchase intent is delivered correctly.
STEPS TO REPRODUCE
Launch app (listener starts in StoreKitManager.init())
Background app
Add purchase intent via Xcode Transaction Manager
Foreground app
Result: No purchase sheet appears, no intent delivered
Workaround attempts:
Using this either in a view or the main app:
func checkForPurchaseIntents() async {
for await purchaseIntent in PurchaseIntent.intents {
await storeKit.purchaseProduct(purchaseIntent.product)
}
}
Applied to .onChange(of: scenePhase) - Doesn't work, nothing happens.
Using UIApplication.willEnterForegroundNotification - Only works on the first time the app goes from background to foreground when purchase intent is sent. Doesn't work on second time or third time.
• Attempting to creating fresh listening task on each foreground - Does not work.
The question is:
How are we supposed to implement the PurchaseIntent API?
I have checked Apple sample projects like BackyardBirds, and sample projects from WWDC on StoreKit 2 but they never implemented Purchase Intent.
Hello everyone,
I'm having trouble testing custom offer codes for my auto-renewable subscription in the Sandbox environment.
I am using the modern StoreKit 2 API to display the redemption sheet:
try await StoreKit.AppStore.presentOfferCodeRedeemSheet(in: windowScene)
When I enter a valid custom code that I created in App Store Connect, the system sheet immediately returns the following error:
"Cannot Redeem Code. The code entered is not valid. [environment: sandbox]"
To be clear, I am manually typing the custom code string itself into the redemption sheet's text field, not using a one-time code link or URL.
I believe my configuration is correct, and I've already spent a significant amount of time troubleshooting. Here is a list of everything I have checked and tried so far:
App Store Connect Configuration:
The Offer Code is marked as "Active".
The parent subscription product is approved and its status is "Ready for Sale".
The offer's eligibility is set correctly
The start and end dates for the offer are valid.
Despite all these steps, the error persists. At this point, I suspect this might be a temporary issue with the Sandbox environment itself, but I want to make sure I haven't missed any crucial configuration steps.
Has anyone else encountered this issue recently? Is there anything else I should be checking?
Any help would be greatly appreciated.
Thank you.
I updated the price of subscriptions within an app in App Store Connect, however the new price does not show within the test app. I would just like to make sure that if the app is released that the subscription price is set to the most recent price. Is there any way to confirm that and is there a way to see the new price in the test app?
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
Subscriptions
StoreKit
App Store Connect
SKStorefront.countryCode always returns "US" in TestFlight and sandbox,
even when sandbox Apple ID is set to a different country.
Is this expected behavior?
Is SKStorefront guaranteed to return the correct storefront
only for production App Store builds?
Topic:
Developer Tools & Services
SubTopic:
Apple Developer Program
Tags:
StoreKit Test
StoreKit
In-App Purchase
TestFlight
Hello everyone, I achieved my MacOs app and distributed for "App Store Connect" and I already have setup the Product In App Purchase in the App Store connect, and I even tested the purchase flow using the local .storekit file using the "Edit Schema". And now, when I remove the edit scema's storekit file and archieved the app and used the Internal testing and installed the app using Testflight, I see that the product is not showing, an empty product array is being returned and there is no option to purchase.
I don't want to sumit the full app until I test the StoreKit integration in real test
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
StoreKit
App Store Connect
In-App Purchase
TestFlight
I'm using StoreKit 2 with Product.products(for:) to fetch my auto-renewable subscriptions. It works in the Xcode simulator with a local StoreKit configuration file, but returns an empty array (no error) in TestFlight.
iOS 15+, using async/await API
Products are configured in App Store Connect
Paid Apps agreement is active
Sandbox tester account set up
Has anyone experienced this? What am I missing?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit Test
StoreKit
TestFlight
I have a $4 USD, one-time-purchase app (Dash Calc) and sales have been rough. In a crowded category, an paid-upfront app feels like a tough sell without a way to try it first. I’d like to offer a simple 7-day free trial followed by a single lifetime purchase, but App Store Connect still doesn’t officially support trials for paid apps.
In Jan 2023, an App Store Commerce Engineer recommended the $0 non-consumable IAP + paid non-consumable IAP workaround:
https://developer.apple.com/forums/thread/722874
I haven’t implemented it yet, but the subsequent discussion suggests the approach is overly complex. Handling refunds, reinstalls, activation timing, and purchase history requires non-obvious logic, and some developers report customer confusion and drop-off when presented with a $0 trial IAP.
Has anything improved since 2023?
Any new StoreKit APIs or App Store Connect changes that make this simpler or less error-prone?
Is the $0 non-consumable IAP still the recommended approach in 2026?
Any updated guidance for time-limited access on one-time purchases?
I’m happy to use the workaround if it’s still the official path—I just want to confirm there isn’t a better option now.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
StoreKit
In-App Purchase
I’m implementing a subscription purchase flow using promo code redemption via an external App Store URL.
Flow:
User taps “Purchase” in the app (spinner shown)
App opens the promo redemption URL (apps.apple.com/redeem)
User completes redemption in the App Store
User returns to the app
The app must determine whether the subscription was purchased within a reasonable time window
The app listens to Transaction.updates and also checks
Transaction.currentEntitlements when the app returns to the foreground.
Issue:
After redeeming a subscription promo code via the App Store and returning to the
app, the app cannot reliably determine whether the subscription was successfully
purchased within a short, user-acceptable time window.
In many cases, neither Transaction.updates nor
Transaction.currentEntitlements reflects the newly redeemed subscription
immediately after returning to the app. The entitlement may appear only after a
significant delay, or not within a 60-second timeout at all, even though the
promo code redemption succeeded.
Expected:
When the user returns to the app after completing promo code redemption,
StoreKit 2 should report the updated subscription entitlement shortly thereafter
(e.g. within a few seconds) via either Transaction.updates or
Transaction.currentEntitlements.
Below is the minimal interactor used in the sample project. The app considers
the purchase successful if either a verified transaction for the product is received via Transaction.updates, or the product appears in Transaction.currentEntitlements when the app returns to the foreground. Otherwise, the flow fails after a 60-second timeout.
Questions:
Is this entitlement propagation delay expected when redeeming promo codes through the App Store?
Is there a recommended API or flow for immediately determining whether a subscription has been successfully redeemed?
Is there a more reliable way to detect entitlement changes after promo code redemption without triggering user authentication prompts (e.g., from AppStore.sync())?
import UIKit
import StoreKit
final class PromoPurchaseInteractor {
private let timeout: TimeInterval = 60
private struct PendingOfferRedemption {
let productId: String
let completion: (Result<Bool, Error>) -> Void
}
private var pendingRedemption: PendingOfferRedemption?
private var updatesTask: Task<Void, Never>?
private var timeoutTask: Task<Void, Never>?
enum DefaultError: Error {
case generic
case timeout
}
init() {
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
updatesTask?.cancel()
timeoutTask?.cancel()
}
func purchaseProduct(using offerUrl: URL, productId: String, completion: @escaping (Result<Bool, Error>) -> Void) {
guard pendingRedemption == nil else {
completion(.failure(DefaultError.generic))
return
}
pendingRedemption = PendingOfferRedemption(productId: productId, completion: completion)
startPurchase(using: offerUrl)
}
@objc private func willEnterForeground() {
guard let pendingRedemption = pendingRedemption else { return }
startTimeoutObserver()
Task {
if await hasEntitlement(for: pendingRedemption.productId) {
await MainActor.run {
self.completePurchase(result: .success(true))
}
}
}
}
private func startPurchase(using offerURL: URL) {
startTransactionUpdatesObserver()
UIApplication.shared.open(offerURL) { [weak self] success in
guard let self = self else { return }
if !success {
self.completePurchase(result: .failure(DefaultError.generic))
}
}
}
private func completePurchase(result: Result<Bool, Error>) {
stopTransactionUpdatesObserver()
stopTimeoutObserver()
guard let _ = pendingRedemption else { return }
pendingRedemption?.completion(result)
pendingRedemption = nil
}
private func startTransactionUpdatesObserver() {
updatesTask?.cancel()
updatesTask = Task {
for await update in Transaction.updates {
guard case .verified(let transaction) = update else { continue }
await MainActor.run { [weak self] in
guard let self = self,
let pending = self.pendingRedemption,
transaction.productID == pending.productId
else { return }
self.completePurchase(result: .success(true))
}
await transaction.finish()
}
}
}
private func stopTransactionUpdatesObserver() {
updatesTask?.cancel()
updatesTask = nil
}
private func startTimeoutObserver() {
guard pendingRedemption != nil else { return }
timeoutTask?.cancel()
timeoutTask = Task {
try? await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
await MainActor.run { [weak self] in
self?.completePurchase(result: .failure(DefaultError.timeout))
}
}
}
private func stopTimeoutObserver() {
timeoutTask?.cancel()
timeoutTask = nil
}
private func hasEntitlement(for productId: String) async -> Bool {
for await result in Transaction.currentEntitlements {
guard case .verified(let transaction) = result else { continue }
if transaction.productID == productId {
return true
}
}
return false
}
}
one of my app users cancelled the subscription from settings. After that, she tried to buy the subscription from app which she could not as I receive same transaction id and previous exp date.