I would like to inquire about the originalTransactionId of StoreKit2.
Users who purchase auto-renewal subscription products
To re-purchase the same subscription item after cancellation and prior to refund
If you receive a refund after cancellation and re-purchase the same subscription item
If you do not renew immediately after expiration and re-purchase the same subscription after a long period of time
I would like to ask if 1, 2, and 3 all use the same value as the original Transaction Id at the initial subscription.
In the case of 3, if you re-purchase more than a few days after the last subscription purchase, please let me know if there are any detailed conditions such as the original Transaction Id not maintained.
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello,
My app "MyCourses" (bundle id: com.ahmedbaqer.mycourses) was rejected under Guideline 2.1 because "No action followed when we tapped the button to subscribe to a course" on iPadOS 26.1.
When I run the same code (version 1.0.0 (11)) from Xcode on a real device using a StoreKit configuration file (In App Purchase.storekit), the purchase flow works correctly:
When I tap the "Subscribe via Apple" button, the App Store purchase sheet appears.
The purchase completes successfully and unlocks the course.
I use the in_app_purchase Flutter plugin and queryProductDetails to load products.
However, when I install the build via TestFlight (and in App Review), tapping the same "Subscribe via Apple" button does nothing – which matches the behavior described by App Review. From my logs it looks like queryProductDetails is returning an empty productDetails list in that environment.
For In‑App Purchases:
I created 7 non‑consumable products in App Store Connect.
Their Product IDs exactly match the IDs in my In App Purchase.storekit file (used only for local Xcode testing).
All IAPs are now in "Waiting for Review" status and are linked to the iOS app version 1.0.0 (11) in the “In‑App Purchases and Subscriptions” section.
At the time of the original review, some IAPs were in "Developer Action Needed / Rejected" state, so I suspect queryProductDetails may have returned no products and the reviewer saw no action after tapping the button.
My questions:
When IAP products are in "Waiting for Review" and linked to the app version, should queryProductDetails return them during App Review / TestFlight, or do they need to be fully approved first?
Is there any additional configuration required so that the subscribe button reliably shows the App Store purchase sheet for reviewers (for example, any specific StoreKit / sandbox settings)?
Are there recommended best practices to show a clearer error state when queryProductDetails returns no products, so that App Review understands this is a configuration / IAP-status issue rather than a UI bug?
Any guidance from Apple engineers or other developers who faced a similar situation would be greatly appreciated.
Thanks in advance.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit Test
StoreKit
In-App Purchase
TestFlight
Hello,
I noticed the Product.SubscriptionInfo subscriptionPeriod (of type Product.SubscriptionPeriod) is different for the same product between StoreKit Testing in Xcode and the sandbox/App Store (production) environment.
For a “1 week” auto-renewable subscription, we get the following:
StoreKit Testing in Xcode: 1 week gives a subscriptionPeriod with value of 1 and a unit of Product.SubscriptionPeriod.Unit.week
Sandbox/App Store: 1 week gives a subscriptionPeriod with value of 7 and a unit of Product.SubscriptionPeriod.Unit.day
This created issues in my app because I used the localizedDescription of a Product.SubscriptionPeriod to display a text similar to “$4.99 per week”. This is what I obtain with the StoreKit Testing in Xcode, but in the Sandbox/App Store environment, it displays “$4.99 per day” (because the subscriptionPeriod is “7 Days” and the unit is then .day). Obviously, this is not what I wanted to display.
Other periods like “1 month”, “2 months”, “3 months”, “6 months, and “1 year”, the period provided by both StoreKit Testing and Sandbox/App Store correspond to the period unit specified in App Store Connect.
In addition, I want to report that for a weekly subscription/offer or a 2 weeks offer, Product.SubscriptionInfo.subscriptionPeriod or Product.SubscriptionOffer.period == .weekly or .everyTwoWeeks is always false.
We observe the following:
With Sandbox or App Store live production:
1 week, Product.SubscriptionInfo.subscriptionPeriod == .weekly is false (because it’s “7 days”)
1 week, Product.SubscriptionOffer.period == .weekly is false (because it’s “7 days”)
2 weeks (offer), Product.SubscriptionInfo.subscriptionPeriod == .everyTwoWeeks is false (because it’s “14 days”)
2 weeks (offer), Product.SubscriptionOffer.period == .everyTwoWeeks is false (because it’s “14 days”)
But with an Xcode StoreKit configuration file:
1 week, Product.SubscriptionInfo.subscriptionPeriod == .weekly is true (because it’s “1 week”)
1 week, Product.SubscriptionOffer.period == .weekly is true (because it’s “1 week”)
2 weeks, Product.SubscriptionInfo.subscriptionPeriod == . everyTwoWeeks is true (because it’s “2 weeks”)
2 weeks, Product.SubscriptionOffer.period == . everyTwoWeeks is true (because it’s “2 weeks”)
So in sandbox and production, .weekly and .everyTwoWeeks is never possible.
If someone from Apple could check the feedback FB19605865 🙂
Thank you
Regards,
Axel, @alpennec
Code:
do {
let productIDs: [String] = ["revenueSocks_weekly_trial"]
let products: [StoreKit.Product] = try await Product.products (for: productIDs)
let weeklySubscription: StoreKit.Product = products.first!
let displayPrice: String = weeklySubscription.displayPrice
// For a weekly subscription in App Store Connect
// With an Xcode StoreKit configuration file: subscriptionPeriod unit is Week (week), value is 1 → "1 Week"
// With the Sandbox + App Store: subscriptionPeriod unit is Day (.day), value is 7 → "7 Days"
let unitString: String = weeklySubscription.subscription!.subscriptionPeriod.unit.localizedDescription
print("\(displayPrice) per \(unitString.localizedLowercase)")
// StoreKit configuration file → "$4.99 per week"
// Sandbox + App Store → "$4.99 per day"
} catch {
print(error)
}
Trying to test IAP in sandbox. I created the test group and tester accounts. Accepted the invite downloaded the app. Signed into to sandbox in settings with the tester account. In app the purchases are failing and throwing my catch error message product couldn't be found. I decided to test it from settings/ sandbox/ manage/ initiate purchase/ but I've been getting "can't complete transaction. Something went wrong, ant this transaction couldn't be completed. Try again later" since last week. I reached out to dev support over the phone then email and they couldn't or wouldn't provide assistance. I asked my senior at work she took a look at it and confirmed I created the IAP correctly and that my sandbox account could make test purchases in apps she make but couldn't get mine to work. The storekit test work fine in xcode I just don't know what to do now.
If an app on the App Store still uses StoreKit 1 (a.k.a. the Original StoreKit) to handle In-App Purchases, would IAPs work for users who download such app on iOS 26.2?
Would the app allow the users to purchase an IAP via StoreKit 1 or would it be impossible to buy the IAP on iOS 26?
The iOS Documentation says that SKPaymentQueue (which is a part of StoreKit 1) is "Deprecated" and "No longer supported.", with the support being for iOS 3.0–18.0.
Does this mean that apps using StoreKit 1 won't be able to make IAP purchases when running on iOS 26?
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.
Topic:
App & System Services
SubTopic:
StoreKit
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
I tried deleting the configuration file, but I ended up with an alert asking me to sign in with my sandbox ID. I did, and it returned an error: "Request Canceled".
It is as if the purchase function doesn't update the currentEntitlements. This is my purchase function. Am I doing something wrong?
func purchase(_ product: Product) async throws {
let result = try await product.purchase()
switch result {
case .success(let verificationResult):
switch verificationResult {
case .verified(let transaction):
// Successful purchase - deliver content
await updatePurchasedProducts()
await transaction.finish()
// Go back to the map
integratePlanAhead()
case .unverified(_, let error):
// Purchase failed verification
throw error
}
case .userCancelled:
// User cancelled the purchase
break
case .pending:
// Purchase is pending (e.g., parental approval needed)
break
@unknown default:
break
}
}
It looks like purchases are never updated for my sandbox user...
Thanks for any help :)
Topic:
App & System Services
SubTopic:
StoreKit
Hello,
I’m experiencing an issue with StoreKit 2 when passing a new appAccountToken for each purchase request.
Case-ID: 15948169 (for DTS reference)
Description of the Problem
When initiating a purchase, I generate a new UUID to use as the appAccountToken:
let serverTransactionId = UUID()
let options: Set<Product.PurchaseOption> = [
.appAccountToken(serverTransactionId)
]
let result = try await product.purchase(options: options)
Expected Behavior:
Each new purchase should return the updated appAccountToken that I pass into the purchase options.
Actual Behavior:
The payload response after success always contains the same appAccountToken from the very first transaction. It ignores subsequent UUIDs I pass and keeps reusing the original one.
This causes issues because the same identifier is being reused across multiple transactions, making it difficult to map purchases to the correct user session.
Steps to Reproduce
Generate a fresh UUID using UUID().
Pass it as .appAccountToken when calling purchase().
Complete the transaction in the sandbox environment.
Inspect the payload response → The appAccountToken value is always the same as the first one used, not the newly provided one.
Additional Info
I do have a focused test project that reproduces this issue.
The issue appears specific to appAccountToken persistence across multiple transactions.
Has anyone else experienced this behavior with StoreKit 2? Is this expected (Apple caching the first token) or could this be a bug?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
In-App Purchase
App Store Receipts
Hi folks,
How can I check the URL we have configured for SKAdNetwork install postback requests? Sadly we've lost any record of this via email on our end, and Apple developer support have asked that I reach out via the forums.
Topic:
App & System Services
SubTopic:
StoreKit
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
I need help understanding how the In-App purchase subscription works within the same subscription group and how the Order plays a role at the time of auto-renewal.
In my case, I have 5 in-app products in the same group as given below with their Order,
Order Product
----------------------
1 Gold Annual
2 Gold Monthly
3 Silver Annual Old,
3 Silver Annual New
4 Silver Monthly
One user purchased Silver annual New, at the time of expiry, after a year, it auto-renewed to Silver annual Old.
My understanding is that if a user has purchased Silver annual New, at the time of renewal, it should renew with the same product, not with different product id.
Is this behaviour expected? Is there any official document which can explain this behaviour?
Note: There was no Order given before, recently we have changed the Order to manage Upgrade and Downgrade for In-App subscriptions.
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.
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()
}
}
}
Hello.
I have setup a StoreKit testing in the app that was and still is perfectly working on iOS 18.
Unfortunately when run on iOS 26 the following error gets printed in the console after calling Transaction.currentEntitlement(for:) method:
Failed to verify certificate chain due to client recoverable failure:
Error Domain=NSOSStatusErrorDomain Code=-67843 "“StoreKit Testing in Xcode” certificate is not trusted" UserInfo={NSLocalizedDescription=“StoreKit Testing in Xcode” certificate is not trusted, NSUnderlyingError=0x109de7e10 {Error Domain=NSOSStatusErrorDomain Code=-67843 "Certificate 0 “StoreKit Testing in Xcode” has errors: Root is not trusted;" UserInfo={NSLocalizedDescription=Certificate 0 “StoreKit Testing in Xcode” has errors: Root is not trusted;}}}
I'm not seeting any StoreKit Testing certificates in phone's certificate trust settings.
This test was performed on iOS 26.0 (23A341) with app built in Xcode 16.4.
FB20339145
My app offers auto renewable subscriptions,
I have couple of questions regarding how to model the subscriptions at server side,
Is originalTransactionId will ever change for a subscription? Will it stay same in case of subscription upgrade/downgrade/crossgrade? If it changes on product change then how do i keep track that the subscription was a continuous subscription and not a new one?
Is the webhook payload contains any identifier of which apple account purchase the subscription?
I am having issues maintaining sync between the app account's current subscription and the local apple account's subscription, is there any doc regarding this? Will be really helpful
I am finding that the verifyReceipt endpoint used to verify a receipt is failing on occassion starting around October 27, 2015. I realize this API is deprecated. I am using cURL to call verifyReceipt. The specific errors are 52 - Empty reply from server and 56 - Connection closed abruptly. This indicates an issue with Apple's server as I understand it.
Is anyone else experiencing this as well?
In a recent update to developer news, Apple posted more details about compliance tools for the new laws coming into effect in Texas on Jan 1.
https://developer.apple.com/news/?id=2ezb6jhj
It is not explicitly stated in the news update or documentation but when the new RESCIND_CONSENT notification is sent to the developer, what happens on the child account devices? Does the app just disappear. Does the developer need to take any action in the App itself?
https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
Thanks,
Eric
Topic:
App & System Services
SubTopic:
StoreKit
I did an in app purchase in my development app and now I cannot get rid of it. It is a "monthly" subscription that seems to renew every 1 day.
I can see the subscription when I go to settings then tap on Subscriptions.
Then I tap the item and choose "Cancel Subscription", revealing a new modal sheet saying "Confirm Cancellation".
When I "Confirm", I get the popup:
"Your request is temporarily unable to be processed, please try again later".
However, this is anything BUT temporary, has gone on for a couple weeks now.
As such, I am unable to test subscriptions in my development app.
I've tried logging out, restarting, different devices, etc.
The phone is logged in under my primary user account, and I may not have been logged into sandbox email when I did the purchase.
Can someone forcibly remove it for me?
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! 🙏