StoreKit

RSS for tag

Support in-app purchases and interactions with the App Store using StoreKit.

StoreKit Documentation

Posts under StoreKit subtopic

Post

Replies

Boosts

Views

Activity

Unresolved pending purchases for consumables
In our app we are running into a few issues with pending purchases staying on receipt indefinitely. These are consumable purchases where we received the purchase succeeded from apple but then something went wrong on our servers to validate and confirm the purchase. At this point the purchase stays on the apple receipt indefinitely or until we confirm it. The problem is there are lots of scenarios where we can't confirm purchases anymore (like a game world expired/banned player/etc). So there's a few things I'd like to know to see how this could be handle correctly. 1- Was the user already charged, and if yes would they ever be refunded if the purchase is not confirmed (some sort of expiry)? 2- Is there a way to cancel this sort of pending transaction directly from the app or backend? 3- If one of these users asked for a refund from apple would this clear the purchase from the receipt? Any information would be greatI couldn't find a lot of info on this topic.
0
1
58
Jun ’25
StoreKit 2: Delayed Transaction and Entitlement Updates After Promo Code Subscription Redemption
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 } }
1
1
214
Jan ’26
SubscriptionStoreView - Restoring Subscriptions
I'm using code similar to the following to conditionally show the SubscriptionStoreView and the .storeButton(.visible, for: .restorePurchases) modifier is used to allow the user to restore an existing subscription. How can I listen for events that would allow me to close this view once the subscription is restored? The .onInAppPurchaseCompletion closure does not handle this and it also appears that listening for results in Transaction.currentEntitlements also doesn't handle the fact that a subscription is restored. Any guidance on how to determine if the subscription has been restored would be greatly appreciated. Finally, how can this be tested effectively in both TestFlight and in Xcode with the simulator. if subscriptionManager.subscription == .none { SubscriptionStoreView(groupID: "1234567") { SubscriptionMarketingView(transparency: false) .containerBackground(for: .subscriptionStoreFullHeight) { GradientBackground() } } .backgroundStyle(.clear) .storeButton(.visible, for: .restorePurchases) .storeButton(.visible, for: .redeemCode) .onInAppPurchaseCompletion { product, result in Task { await subscriptionManager.entitlements() } } }
0
1
131
Nov ’25
SKTestSession.setSimulatedError() not working for .loadProducts API in iOS 26.2
Description SKTestSession.setSimulatedError() does not throw the configured error when testing StoreKit with the .loadProducts API in iOS 26.2. The simulated error is ignored, and products load successfully instead. Environment iOS: 26.2 (Simulator) Xcode: 26.2 beta 2 (Build 17C5038g) macOS: 15.6.1 Framework: StoreKitTest Testing Framework: Swift Testing base project: https://developer.apple.com/documentation/StoreKit/implementing-a-store-in-your-app-using-the-storekit-api Expected Behavior After calling session.setSimulatedError(.generic(.notAvailableInStorefront), forAPI: .loadProducts), the subsequent call to Product.products(for:) should throw StoreKitError.notAvailableInStorefront. Actual Behavior The error is not thrown. Products load successfully as if setSimulatedError() was never called. Steps to Reproduce Create an SKTestSession with a StoreKit configuration file Call session.setSimulatedError(.generic(.notAvailableInStorefront), forAPI: .loadProducts) Call Product.products(for:) with a valid product ID Observe that no error is thrown and the product loads successfully Sample Code import StoreKitTest import Testing struct SKDemoTests { let productID: String = "plus.standard" @Test func testSimulatedErrorForLoadProducts() async throws { let storeKitConfigURL = try Self.getStoreKitConfigurationURL() let session = try SKTestSession(contentsOf: storeKitConfigURL) try await session.setSimulatedError( .generic(.notAvailableInStorefront), forAPI: .loadProducts ) try await confirmation("StoreKitError throw") { throwStoreKitError in do { _ = try await Self.execute(productID: productID) } catch let error as StoreKitError { guard case StoreKitError.notAvailableInStorefront = error else { throw error } throwStoreKitError() } catch { Issue.record( "Expect StoreKitError. Error: \(error.localizedDescription)" ) } } #expect(session.allTransactions().isEmpty) } static func execute(productID: String) async throws { guard let product = try await Product.products(for: [productID]).first( where: { $0.id == productID }) else { throw NSError( domain: "SKDemoTests", code: 404, userInfo: [NSLocalizedDescriptionKey: "Product not found for ID: \(productID)"] ) } _ = product } static func getStoreKitConfigurationURL() throws -> URL { guard let bundle = Bundle(identifier: "your.bundle.identifier"), let url = bundle.url(forResource: "Products", withExtension: "storekit") else { fatalError("StoreKit configuration not found") } return url } } Test Result The test fails because the expected StoreKitError.notAvailableInStorefront is never thrown. Question Is this a known issue in iOS 26.2 / Xcode 26.2 beta 2, or is there a different approach required for simulating errors with SKTestSession in this version? Any guidance would be appreciated. Feedback Assistant report: FB21110809
2
2
280
Feb ’26
StoreKit 2 not loading subscription products
Hi everyone, I’m seeing a strange behavior with StoreKit 2 and I’d like to know if anyone else experienced this. My subscription group “ROTA Premium” (Monthly + Annual) is currently Waiting for Review in App Store Connect. What works In Xcode’s StoreKit sandbox, everything loads correctly: Products appear Trial starts Purchases work What doesn’t work In TestFlight and App Review, StoreKit 2 returns zero products, so my paywall shows: “No subscription options found.” There are: No geo restrictions No backend No VPN/IP filtering Paid Apps Agreement is accepted App Review said the device was online, but couldn’t give technical help. My question Has anyone seen StoreKit 2 fail to load subscription products when the subscription group is still in Waiting for Review? Do subscription groups need to be reviewed together with the app version for StoreKit 2 to return them in TestFlight/App Review? Any advice would be appreciated! Thanks.
0
1
94
Nov ’25
In-app purchase for auto-renewals subscription for sandbox
Dear Apple Support Team, We are currently implementing auto-renewable subscriptions in our iOS app and are testing the integration using the sandbox environment. On the iOS app side, the in-app purchase flow completes successfully and displays a "Purchase Successful" message. However, we are not receiving any server notification callbacks on our configured App Store Server Notifications (Sandbox) webhook URL. For your reference, the webhook URL we have set in App Store Connect (Sandbox) is: https://9c0f-182-79-123-254.ngrok-free.app/ios/webhook Despite successfully completing a subscription purchase in the sandbox, there is no evidence that the webhook is being triggered. We would appreciate your guidance in resolving this issue or confirming if there are any additional configurations or steps required on our end.
3
1
322
May ’25
StoreKit sandbox purchase and product fetch not working (IAPError: storekit_no_response)
💬 Post Content Hello everyone, I’m currently testing In-App Purchases (auto-renewable subscriptions) for my iOS app, and I’m experiencing an issue where the product information cannot be fetched from StoreKit. ❓ Questions For sandbox testing, is it absolutely necessary to submit a new app version for review (with the in-app purchase included)? Or can I test subscriptions without submitting a new build to App Review? Under what conditions does the error IAPError(code: storekit_no_response, source: app_store, message: StoreKit: Failed to get response from platform.) 🧪 What I’ve Tried • Confirmed that the bundle identifier matches the App Store Connect record • Verified that In-App Purchase capability is enabled in Xcode • Ensured the sandbox tester account is logged in (Settings → App Store) • Removed the StoreKit configuration file to use the sandbox environment • Tried both real device and simulator (same error) • Accepted the Paid Apps Agreement in App Store Connect • Products are created but currently not yet submitted for review 🔍 Environment • App type: iOS app built with Flutter (using in_app_purchase plugin) • Build type: Archive build installed via Xcode (Run on device) and testFlight • StoreKit Configuration File: Currently removed (for sandbox test); • Status: Ready to submit in App Store Connect
0
1
170
Nov ’25
Mismatch between App Store Server API `expiresDate` (July 23) and iOS UI “Expires on” date (July 22) for 1-month subscription
Hi everyone, I’m seeing a consistent one-day discrepancy between the expiresDate returned by the App Store Server API and the “Expires on” date shown in the iOS Settings / App Store subscription list. I’d like to confirm whether this behavior is expected or if I’m misunderstanding the way Apple rounds dates. Reproduction steps Step Action Result 1 Purchase a 1-month auto-renewable subscription on 23 June 2025 14:00 JST (UTC+9) Transaction succeeds 2 Immediately fetch the transaction with GET /inApps/v1/subscriptions/{transactionId} Response contains "expiresDate": "2025-07-23T05:00:00Z" (= 23 July 2025 14:00 JST) 3 On the same device open Settings › Apple ID › Subscriptions (or App Store › Account › Subscriptions) UI shows Expires on: 22 July 2025 The same happens for every monthly renewal and on multiple devices. Region is Japan, device time zone Asia/Tokyo. What I understand so far (and my hypothesis) Apple’s docs say a monthly subscription renews “on the same calendar date” of the next month, so renewal in this example is 23 July. If the renewal is scheduled for 23 July at 14:00 JST, the subscription is fully usable until the end of 22 July in calendar terms, because the new billing period starts the moment the 23rd begins in Apple’s canonical time zone. Therefore, it might be intentional for the UI to display 22 July—i.e., “you can keep using it through the 22nd; on the 23rd it renews.” This hypothesis makes sense internally, yet it still looks confusing to end users who read “Expires on 22 July” and assume access ends at 00:00 on the 22nd, a whole day earlier than in reality. Questions Is showing the day before the renewal date the official/expected behavior? If so, could Apple clarify that the “Expires on” label represents the last full calendar day rather than the exact expiry timestamp? Which value should we surface in-app when telling users “Your subscription is valid until …”? The server’s expiresDate (precise to the second, converted to user time zone), or A UI-style date that’s one day earlier, matching Settings / App Store? Does Apple have a public document describing this rounding/visual convention? Have other developers encountered user confusion about the apparent 1-day “shortening” and, if so, how did you word your in-app messaging? Any insight from Apple engineers or fellow developers would be greatly appreciated. Thank you!
0
1
298
Jun ’25
Advanced Commerce (Sandbox) – Generic Product Still “Ready to Submit”
Hello, Our app is approved for the Advanced Commerce API and we are currently testing in the Sandbox environment only. We have created generic product identifiers and have already submitted them via the Advanced Commerce API Access form. However, the generic product status in App Store Connect is still “Ready to Submit.” For Sandbox testing, is this status expected, or do we need to submit an app build or the generic product for review before Advanced Commerce works correctly? Thank you.
2
0
255
Jan ’26
StoreKit v2: autoRenewStatus returns 0 right after purchase on iOS 26.1
Hi everyone, I’m implementing subscriptions using StoreKit v2, and I’ve noticed a behavior change starting with iOS 26.1. I’d like to ask if anyone else has experienced the same issue. ■ Issue Immediately after purchasing a new subscription, the value of auto_renew_status (or autoRenewStatus) returned in the receipt is 0 (auto-renew OFF). This issue occurs on iOS 26.1. On iOS 26.0 and earlier, the same parameter returned 1 (auto-renew ON) right after purchase. Sometimes, after executing a “restore” operation, the value changes to 1 later. Since we’ve been using this parameter to determine whether a user’s subscription is active or not, the current behavior is causing some difficulties on our end. ■ Questions Has anyone else observed this issue (where autoRenewStatus is 0 immediately after purchase on iOS 26.1 or later)? How are you handling or working around this behavior in your implementation? If autoRenewStatus is unreliable, we’re considering determining the subscription state based on receipt fields instead. Would this approach be reasonable? "status" is 1 (indicates active subscription) "expire_time" is in the future "deleted_at" is null If anyone has encountered the same behavior or knows of any Apple-recommended implementation approach, I’d really appreciate your insights. Thank you! 🙏
1
1
200
Feb ’26
StoreKit 2 - Is it necessary to finish unverified transactions?
The sample code provided in https://developer.apple.com/wwdc21/10114 doesn't appear to call finish() on unverified transactions, and I haven't been able to find any documentation regarding what to do with unfinished transactions. However, Apple has always emphasized the importance of finishing transactions, and since a transaction object is provided even with the unverified state, I'd love some guidance!
4
1
2.9k
May ’25
StoreKit returning 0 products (IAPError: storekit_no_response)
Hi everyone, I’m facing an issue where StoreKit is returning 0 products from the App Store, even though my auto-renewable subscriptions are approved in App Store Connect. When calling queryProductDetails using Flutter’s in_app_purchase package (which uses StoreKit under the hood), StoreKit reports success but returns an empty list. The logs show the following error: IAPError(code: storekit_no_response, source: app_store, message: "StoreKit: Failed to get response from platform.") InAppPurchase.isAvailable() returns true, but no product details are received. Already verified: • Subscriptions are approved in App Store Connect • Product identifiers in the app match those in App Store Connect exactly • In-App Purchase capability is enabled in Xcode • Paid Applications Agreement, banking, and tax details are active and complete • Using the latest version of the Flutter in_app_purchase package StoreKit should normally return the list of available products in the production environment, but it consistently returns an empty array along with the “storekit_no_response” error. Has anyone else encountered this issue or found any potential causes for StoreKit failing to return products in the production environment? Any insights would be greatly appreciated. Thank you.
2
1
362
Nov ’25
Apple Media Services T&C pop-up
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.
2
0
337
1w
Landscape safe area is incorrect when presenting SKStoreProductViewController
Hi. If the app is in landscape only and when the SKStoreProductViewController is presented, the safeArea changes to what looks like a portrait mode safe area. When the SKStoreProductViewController is dismissed, the safeArea does NOT revert back to the original values. Is there a way to force the safeArea to "reset"? I've submitted some bug tickets through Apple Feedback but I haven't received any response about it. The below code will pop up the SKStoreProductViewController and if you have a UIView that is constrained to the safe area, then you can visibly notice that the safe area is changed and doesn't go back. I have tested this on iPhone 14 Pro, iPhone 15, and iPhone 16 Pro and in the Simulators. The incorrect behavior happens on those and probably more. Thanks. #import "ViewController.h" #import &amp;lt;StoreKit/StoreKit.h&amp;gt; @interface ViewController () @property (nonatomic, strong) SKStoreProductViewController *productViewController; @end @implementation ViewController - (IBAction)buttonTapped:(id)sender { self.productViewController = [[SKStoreProductViewController alloc] init]; NSDictionary *parameters = @{ @"id" : @"6443575749" }; [self.productViewController loadProductWithParameters:parameters completionBlock:^(BOOL result, NSError * _Nullable error) { [self presentViewController:self.productViewController animated:YES completion:^{ // presented // The panel that is constraint to the safe area visibly shows that the safe area is no longer correct. }]; }]; } @end
3
1
737
May ’25
Can SKOverlay be used to prompt updates for the same app?
According to Apple's documentation, SKOverlay is designed to recommend other applications to users. I'm seeking clarification on whether it also supports displaying update prompts for the host application itself. Use case: My app (for example, HelloDeveloper) is live at version 2.0, but some users are still on version 1.0. I want to display a soft update prompt that allows users to remain in the app. Question: Is it possible to use SKOverlay with my app's App Store ID to present an update option without requiring users to leave the app?
1
0
193
Oct ’25
Unable to retrieve data from In App Purchase
I want to add in-app purchasing to my app, but I can't figure out what part of my workflow is wrong. I created a product for my app in iTunes Connect (the product ID is com.mycompany.products.***) and it's in "Ready to submit" status. I created a sandbox test user for this app. I connected to iTunes on a real device using the sandbox AppleID. I went back to XCode and added in-app purchasing to my app. I turned on developer mode on the real device and logged in as the sandbox user. I built the app and ran it on a real device (not the simulator). I tried to get product information (com.mycompany.products.***) but nothing was returned. In-app purchasing is registered in App Store Connect and the status is "Ready to submit". The code only retrieves product information in a simple way, so I don't think there's a problem. inAppPurchase.getProducts(["com.mycompany.products.***"]).then(console.log).catch(console.error); But it only returns an empty array. What could be wrong? Any help would be much appreciated.
2
1
77
Jul ’25
Unresolved pending purchases for consumables
In our app we are running into a few issues with pending purchases staying on receipt indefinitely. These are consumable purchases where we received the purchase succeeded from apple but then something went wrong on our servers to validate and confirm the purchase. At this point the purchase stays on the apple receipt indefinitely or until we confirm it. The problem is there are lots of scenarios where we can't confirm purchases anymore (like a game world expired/banned player/etc). So there's a few things I'd like to know to see how this could be handle correctly. 1- Was the user already charged, and if yes would they ever be refunded if the purchase is not confirmed (some sort of expiry)? 2- Is there a way to cancel this sort of pending transaction directly from the app or backend? 3- If one of these users asked for a refund from apple would this clear the purchase from the receipt? Any information would be greatI couldn't find a lot of info on this topic.
Replies
0
Boosts
1
Views
58
Activity
Jun ’25
StoreKit 2: Delayed Transaction and Entitlement Updates After Promo Code Subscription Redemption
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 } }
Replies
1
Boosts
1
Views
214
Activity
Jan ’26
In-App Rating Prompt can't be dismissed in iOS 26.1
The app review prompt on iOS/iPadOS 26.1 has the "Not Now" button greyed out: On iOS/iPadOS 26.0, this was working: Before filing a radar, is there anything I'm missing? The only way to dismiss it is to tap a star rating and then a Cancel button appears which lets you dismiss it without reviewing.
Replies
1
Boosts
1
Views
218
Activity
Nov ’25
SubscriptionStoreView - Restoring Subscriptions
I'm using code similar to the following to conditionally show the SubscriptionStoreView and the .storeButton(.visible, for: .restorePurchases) modifier is used to allow the user to restore an existing subscription. How can I listen for events that would allow me to close this view once the subscription is restored? The .onInAppPurchaseCompletion closure does not handle this and it also appears that listening for results in Transaction.currentEntitlements also doesn't handle the fact that a subscription is restored. Any guidance on how to determine if the subscription has been restored would be greatly appreciated. Finally, how can this be tested effectively in both TestFlight and in Xcode with the simulator. if subscriptionManager.subscription == .none { SubscriptionStoreView(groupID: "1234567") { SubscriptionMarketingView(transparency: false) .containerBackground(for: .subscriptionStoreFullHeight) { GradientBackground() } } .backgroundStyle(.clear) .storeButton(.visible, for: .restorePurchases) .storeButton(.visible, for: .redeemCode) .onInAppPurchaseCompletion { product, result in Task { await subscriptionManager.entitlements() } } }
Replies
0
Boosts
1
Views
131
Activity
Nov ’25
SKTestSession.setSimulatedError() not working for .loadProducts API in iOS 26.2
Description SKTestSession.setSimulatedError() does not throw the configured error when testing StoreKit with the .loadProducts API in iOS 26.2. The simulated error is ignored, and products load successfully instead. Environment iOS: 26.2 (Simulator) Xcode: 26.2 beta 2 (Build 17C5038g) macOS: 15.6.1 Framework: StoreKitTest Testing Framework: Swift Testing base project: https://developer.apple.com/documentation/StoreKit/implementing-a-store-in-your-app-using-the-storekit-api Expected Behavior After calling session.setSimulatedError(.generic(.notAvailableInStorefront), forAPI: .loadProducts), the subsequent call to Product.products(for:) should throw StoreKitError.notAvailableInStorefront. Actual Behavior The error is not thrown. Products load successfully as if setSimulatedError() was never called. Steps to Reproduce Create an SKTestSession with a StoreKit configuration file Call session.setSimulatedError(.generic(.notAvailableInStorefront), forAPI: .loadProducts) Call Product.products(for:) with a valid product ID Observe that no error is thrown and the product loads successfully Sample Code import StoreKitTest import Testing struct SKDemoTests { let productID: String = "plus.standard" @Test func testSimulatedErrorForLoadProducts() async throws { let storeKitConfigURL = try Self.getStoreKitConfigurationURL() let session = try SKTestSession(contentsOf: storeKitConfigURL) try await session.setSimulatedError( .generic(.notAvailableInStorefront), forAPI: .loadProducts ) try await confirmation("StoreKitError throw") { throwStoreKitError in do { _ = try await Self.execute(productID: productID) } catch let error as StoreKitError { guard case StoreKitError.notAvailableInStorefront = error else { throw error } throwStoreKitError() } catch { Issue.record( "Expect StoreKitError. Error: \(error.localizedDescription)" ) } } #expect(session.allTransactions().isEmpty) } static func execute(productID: String) async throws { guard let product = try await Product.products(for: [productID]).first( where: { $0.id == productID }) else { throw NSError( domain: "SKDemoTests", code: 404, userInfo: [NSLocalizedDescriptionKey: "Product not found for ID: \(productID)"] ) } _ = product } static func getStoreKitConfigurationURL() throws -> URL { guard let bundle = Bundle(identifier: "your.bundle.identifier"), let url = bundle.url(forResource: "Products", withExtension: "storekit") else { fatalError("StoreKit configuration not found") } return url } } Test Result The test fails because the expected StoreKitError.notAvailableInStorefront is never thrown. Question Is this a known issue in iOS 26.2 / Xcode 26.2 beta 2, or is there a different approach required for simulating errors with SKTestSession in this version? Any guidance would be appreciated. Feedback Assistant report: FB21110809
Replies
2
Boosts
2
Views
280
Activity
Feb ’26
StoreKit 2 not loading subscription products
Hi everyone, I’m seeing a strange behavior with StoreKit 2 and I’d like to know if anyone else experienced this. My subscription group “ROTA Premium” (Monthly + Annual) is currently Waiting for Review in App Store Connect. What works In Xcode’s StoreKit sandbox, everything loads correctly: Products appear Trial starts Purchases work What doesn’t work In TestFlight and App Review, StoreKit 2 returns zero products, so my paywall shows: “No subscription options found.” There are: No geo restrictions No backend No VPN/IP filtering Paid Apps Agreement is accepted App Review said the device was online, but couldn’t give technical help. My question Has anyone seen StoreKit 2 fail to load subscription products when the subscription group is still in Waiting for Review? Do subscription groups need to be reviewed together with the app version for StoreKit 2 to return them in TestFlight/App Review? Any advice would be appreciated! Thanks.
Replies
0
Boosts
1
Views
94
Activity
Nov ’25
In-app purchase for auto-renewals subscription for sandbox
Dear Apple Support Team, We are currently implementing auto-renewable subscriptions in our iOS app and are testing the integration using the sandbox environment. On the iOS app side, the in-app purchase flow completes successfully and displays a "Purchase Successful" message. However, we are not receiving any server notification callbacks on our configured App Store Server Notifications (Sandbox) webhook URL. For your reference, the webhook URL we have set in App Store Connect (Sandbox) is: https://9c0f-182-79-123-254.ngrok-free.app/ios/webhook Despite successfully completing a subscription purchase in the sandbox, there is no evidence that the webhook is being triggered. We would appreciate your guidance in resolving this issue or confirming if there are any additional configurations or steps required on our end.
Replies
3
Boosts
1
Views
322
Activity
May ’25
StoreKit sandbox purchase and product fetch not working (IAPError: storekit_no_response)
💬 Post Content Hello everyone, I’m currently testing In-App Purchases (auto-renewable subscriptions) for my iOS app, and I’m experiencing an issue where the product information cannot be fetched from StoreKit. ❓ Questions For sandbox testing, is it absolutely necessary to submit a new app version for review (with the in-app purchase included)? Or can I test subscriptions without submitting a new build to App Review? Under what conditions does the error IAPError(code: storekit_no_response, source: app_store, message: StoreKit: Failed to get response from platform.) 🧪 What I’ve Tried • Confirmed that the bundle identifier matches the App Store Connect record • Verified that In-App Purchase capability is enabled in Xcode • Ensured the sandbox tester account is logged in (Settings → App Store) • Removed the StoreKit configuration file to use the sandbox environment • Tried both real device and simulator (same error) • Accepted the Paid Apps Agreement in App Store Connect • Products are created but currently not yet submitted for review 🔍 Environment • App type: iOS app built with Flutter (using in_app_purchase plugin) • Build type: Archive build installed via Xcode (Run on device) and testFlight • StoreKit Configuration File: Currently removed (for sandbox test); • Status: Ready to submit in App Store Connect
Replies
0
Boosts
1
Views
170
Activity
Nov ’25
Mismatch between App Store Server API `expiresDate` (July 23) and iOS UI “Expires on” date (July 22) for 1-month subscription
Hi everyone, I’m seeing a consistent one-day discrepancy between the expiresDate returned by the App Store Server API and the “Expires on” date shown in the iOS Settings / App Store subscription list. I’d like to confirm whether this behavior is expected or if I’m misunderstanding the way Apple rounds dates. Reproduction steps Step Action Result 1 Purchase a 1-month auto-renewable subscription on 23 June 2025 14:00 JST (UTC+9) Transaction succeeds 2 Immediately fetch the transaction with GET /inApps/v1/subscriptions/{transactionId} Response contains "expiresDate": "2025-07-23T05:00:00Z" (= 23 July 2025 14:00 JST) 3 On the same device open Settings › Apple ID › Subscriptions (or App Store › Account › Subscriptions) UI shows Expires on: 22 July 2025 The same happens for every monthly renewal and on multiple devices. Region is Japan, device time zone Asia/Tokyo. What I understand so far (and my hypothesis) Apple’s docs say a monthly subscription renews “on the same calendar date” of the next month, so renewal in this example is 23 July. If the renewal is scheduled for 23 July at 14:00 JST, the subscription is fully usable until the end of 22 July in calendar terms, because the new billing period starts the moment the 23rd begins in Apple’s canonical time zone. Therefore, it might be intentional for the UI to display 22 July—i.e., “you can keep using it through the 22nd; on the 23rd it renews.” This hypothesis makes sense internally, yet it still looks confusing to end users who read “Expires on 22 July” and assume access ends at 00:00 on the 22nd, a whole day earlier than in reality. Questions Is showing the day before the renewal date the official/expected behavior? If so, could Apple clarify that the “Expires on” label represents the last full calendar day rather than the exact expiry timestamp? Which value should we surface in-app when telling users “Your subscription is valid until …”? The server’s expiresDate (precise to the second, converted to user time zone), or A UI-style date that’s one day earlier, matching Settings / App Store? Does Apple have a public document describing this rounding/visual convention? Have other developers encountered user confusion about the apparent 1-day “shortening” and, if so, how did you word your in-app messaging? Any insight from Apple engineers or fellow developers would be greatly appreciated. Thank you!
Replies
0
Boosts
1
Views
298
Activity
Jun ’25
Advanced Commerce (Sandbox) – Generic Product Still “Ready to Submit”
Hello, Our app is approved for the Advanced Commerce API and we are currently testing in the Sandbox environment only. We have created generic product identifiers and have already submitted them via the Advanced Commerce API Access form. However, the generic product status in App Store Connect is still “Ready to Submit.” For Sandbox testing, is this status expected, or do we need to submit an app build or the generic product for review before Advanced Commerce works correctly? Thank you.
Replies
2
Boosts
0
Views
255
Activity
Jan ’26
StoreKit v2: autoRenewStatus returns 0 right after purchase on iOS 26.1
Hi everyone, I’m implementing subscriptions using StoreKit v2, and I’ve noticed a behavior change starting with iOS 26.1. I’d like to ask if anyone else has experienced the same issue. ■ Issue Immediately after purchasing a new subscription, the value of auto_renew_status (or autoRenewStatus) returned in the receipt is 0 (auto-renew OFF). This issue occurs on iOS 26.1. On iOS 26.0 and earlier, the same parameter returned 1 (auto-renew ON) right after purchase. Sometimes, after executing a “restore” operation, the value changes to 1 later. Since we’ve been using this parameter to determine whether a user’s subscription is active or not, the current behavior is causing some difficulties on our end. ■ Questions Has anyone else observed this issue (where autoRenewStatus is 0 immediately after purchase on iOS 26.1 or later)? How are you handling or working around this behavior in your implementation? If autoRenewStatus is unreliable, we’re considering determining the subscription state based on receipt fields instead. Would this approach be reasonable? "status" is 1 (indicates active subscription) "expire_time" is in the future "deleted_at" is null If anyone has encountered the same behavior or knows of any Apple-recommended implementation approach, I’d really appreciate your insights. Thank you! 🙏
Replies
1
Boosts
1
Views
200
Activity
Feb ’26
StoreKit 2 - Is it necessary to finish unverified transactions?
The sample code provided in https://developer.apple.com/wwdc21/10114 doesn't appear to call finish() on unverified transactions, and I haven't been able to find any documentation regarding what to do with unfinished transactions. However, Apple has always emphasized the importance of finishing transactions, and since a transaction object is provided even with the unverified state, I'd love some guidance!
Replies
4
Boosts
1
Views
2.9k
Activity
May ’25
App is on App Store but Subscription is in review
App is approved and on App Store but Subscription is in review and localizations rejected. no way to edit. anyone here that go this flow resolved and how?
Replies
2
Boosts
0
Views
621
Activity
Jan ’26
StoreKit returning 0 products (IAPError: storekit_no_response)
Hi everyone, I’m facing an issue where StoreKit is returning 0 products from the App Store, even though my auto-renewable subscriptions are approved in App Store Connect. When calling queryProductDetails using Flutter’s in_app_purchase package (which uses StoreKit under the hood), StoreKit reports success but returns an empty list. The logs show the following error: IAPError(code: storekit_no_response, source: app_store, message: "StoreKit: Failed to get response from platform.") InAppPurchase.isAvailable() returns true, but no product details are received. Already verified: • Subscriptions are approved in App Store Connect • Product identifiers in the app match those in App Store Connect exactly • In-App Purchase capability is enabled in Xcode • Paid Applications Agreement, banking, and tax details are active and complete • Using the latest version of the Flutter in_app_purchase package StoreKit should normally return the list of available products in the production environment, but it consistently returns an empty array along with the “storekit_no_response” error. Has anyone else encountered this issue or found any potential causes for StoreKit failing to return products in the production environment? Any insights would be greatly appreciated. Thank you.
Replies
2
Boosts
1
Views
362
Activity
Nov ’25
Apple Media Services T&C pop-up
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.
Replies
2
Boosts
0
Views
337
Activity
1w
Landscape safe area is incorrect when presenting SKStoreProductViewController
Hi. If the app is in landscape only and when the SKStoreProductViewController is presented, the safeArea changes to what looks like a portrait mode safe area. When the SKStoreProductViewController is dismissed, the safeArea does NOT revert back to the original values. Is there a way to force the safeArea to "reset"? I've submitted some bug tickets through Apple Feedback but I haven't received any response about it. The below code will pop up the SKStoreProductViewController and if you have a UIView that is constrained to the safe area, then you can visibly notice that the safe area is changed and doesn't go back. I have tested this on iPhone 14 Pro, iPhone 15, and iPhone 16 Pro and in the Simulators. The incorrect behavior happens on those and probably more. Thanks. #import "ViewController.h" #import &amp;lt;StoreKit/StoreKit.h&amp;gt; @interface ViewController () @property (nonatomic, strong) SKStoreProductViewController *productViewController; @end @implementation ViewController - (IBAction)buttonTapped:(id)sender { self.productViewController = [[SKStoreProductViewController alloc] init]; NSDictionary *parameters = @{ @"id" : @"6443575749" }; [self.productViewController loadProductWithParameters:parameters completionBlock:^(BOOL result, NSError * _Nullable error) { [self presentViewController:self.productViewController animated:YES completion:^{ // presented // The panel that is constraint to the safe area visibly shows that the safe area is no longer correct. }]; }]; } @end
Replies
3
Boosts
1
Views
737
Activity
May ’25
Can SKOverlay be used to prompt updates for the same app?
According to Apple's documentation, SKOverlay is designed to recommend other applications to users. I'm seeking clarification on whether it also supports displaying update prompts for the host application itself. Use case: My app (for example, HelloDeveloper) is live at version 2.0, but some users are still on version 1.0. I want to display a soft update prompt that allows users to remain in the app. Question: Is it possible to use SKOverlay with my app's App Store ID to present an update option without requiring users to leave the app?
Replies
1
Boosts
0
Views
193
Activity
Oct ’25
Age Verification: what is a sandbox account?
On this page: https://developer.apple.com/documentation/storekit/testing-age-assurance-in-sandbox It says: Start with a Sandbox account. What is a Sandbox account and how does one set one up? Just it just simply mean an Apple account id used for testing, or something else?
Replies
1
Boosts
0
Views
154
Activity
Nov ’25
Promotional offer is not showing in In-app subscription sheet.
Hello, I have 2 subscription group. Each group have different plans and promotional offer. I was set 1 month free promotional offer in all plans. While I subscribe any plan IAP sheet is not showing 1 month free promotional offer. I already used with new sandbox account. How can I get promotional offer in IAP sheet?
Replies
4
Boosts
1
Views
940
Activity
Sep ’25
Unable to retrieve data from In App Purchase
I want to add in-app purchasing to my app, but I can't figure out what part of my workflow is wrong. I created a product for my app in iTunes Connect (the product ID is com.mycompany.products.***) and it's in "Ready to submit" status. I created a sandbox test user for this app. I connected to iTunes on a real device using the sandbox AppleID. I went back to XCode and added in-app purchasing to my app. I turned on developer mode on the real device and logged in as the sandbox user. I built the app and ran it on a real device (not the simulator). I tried to get product information (com.mycompany.products.***) but nothing was returned. In-app purchasing is registered in App Store Connect and the status is "Ready to submit". The code only retrieves product information in a simple way, so I don't think there's a problem. inAppPurchase.getProducts(["com.mycompany.products.***"]).then(console.log).catch(console.error); But it only returns an empty array. What could be wrong? Any help would be much appreciated.
Replies
2
Boosts
1
Views
77
Activity
Jul ’25