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?
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,
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
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?
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!
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store
StoreKit
App Store Server API
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.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
Advanced Commerce API
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
Hi everyone,
After updating to Xcode 16.4, my StoreKit configuration stopped working.
Whenever I run the app with a .storekit file set as the active scheme, I immediately get this alert:
“The file has been changed. Do you want to save your changes or revert to the file on disk?”
No matter if I choose Save Anyway or Revert, StoreKit testing does not work - the purchases are not simulated, and the scheme is basically broken.
This issue didn’t exist in Xcode 15.4 - the same StoreKit configuration file works fine there.
What I tried so far:
Clearing Derived Data - no effect
Making sure no scripts/tools modify the .storekit file - still happens
Restarting Xcode and macOS - no change
Environment:
Xcode 16.4
Happens in both Simulator and on device
Reproducible 100%
Has anyone else seen this in 16.4? Any known workarounds until Apple fixes it?
Thanks!
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?
We’ve recently encountered an increased rate of purchase errors StoreKit.InvalidRequestError error 1 (https://developer.apple.com/documentation/storekit/invalidrequesterror) specifically on iOS 18.4.
We suspect this might be related to the new Advanced Commerce API introduced by Apple, although we haven’t made any changes related to this API in our app.
Have you experienced similar issues since the release of iOS 18.4 or when integrating the Advanced Commerce API? Any insights or suggestions would be greatly appreciated.
Thanks!
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
In-App Purchase
Advanced Commerce API
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.
We’re testing SKAN postbacks via AdAttributionKit but aren’t receiving any requests on our server even after generating development impressions and triggering a postback.
Setup:
Domain: https://linkrunner-skan.com
Configured in Info.plist as:
<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://linkrunner-skan.com</string>
<key>AttributionCopyEndpoint</key>
<string>https://linkrunner-skan.com</string>
Apple automatically appends the .well-known paths:
/.well-known/private-click-measurement/report-attribution/
/.well-known/skadnetwork/report-attribution/
ATS diagnostics for the domain: PASS for all tests (TLS 1.0–1.3, PFS disabled, arbitrary loads allowed, etc.)
Both .well-known paths are publicly accessible and return 200 OK
Testing Flow:
Enabled Developer → AdAttributionKit Developer Mode on iOS (15+)
Followed Apple’s official guide: Testing AdAttributionKit with Developer Mode
Generated test impression using:
createAdAttributionKitDevelopmentImpression implemented in SKANManager.swift
Called Postback.updateConversionValue with lockPostback: true
Created Development Postback from Developer Settings
Waited 30+ minutes while intercepting server requests (proxy + backend logs)
What We’ve Tried So Far:
Confirmed ATS compliance with nscurl --ats-diagnostics (all PASS)
Verified .well-known paths are accessible publicly without redirects
Tested endpoints manually with a POST request – server responds 200 OK
Confirmed Info.plist entries exactly match Apple’s required keys
Double-checked iOS device is running iOS 15+ with Developer Mode enabled
Repeated test flow multiple times with fresh impressions and postbacks
Waited up to 1 hour for postback (in case of delays)
Issue:
No POST requests are being received from Apple to either .well-known endpoint, even though the setup appears correct and ATS tests pass.
References Used:
Configuring an Advertised App
Generating JWS Impressions
Question:
Has anyone faced a similar issue where AdAttributionKit Development Postbacks are not firing despite correct Info.plist setup, ATS compliance, and reachable .well-known endpoints?
Any insight into possible missing configuration steps or testing nuances would be greatly appreciated.
I’m seeing an issue with subscriptions in TestFlight builds on iOS 26. Running from Xcode works as expected, and the App Store build looks fine. But when I install the same build via TestFlight, transaction.subscriptionStatus is nil.
The identical binary behaves correctly on an iOS 18 device.
Is this expected behavior on iOS 26 TestFlight, or am I missing something?
Thanks!
Dear Apple Technical Support Team,
We have encountered a potential issue related to transaction handling while using StoreKit v2, and would greatly appreciate your assistance in confirming the behavior or providing any relevant guidance.
Issue Description:
When calling Transaction.unfinished and listening to Transaction.updates on the client side, we noticed that some transactions—despite having already been processed and successfully completed with finish()—are being returned again upon the next app launch, which results in duplicate receipt uploads.
Current Handling Flow:
1. Upon app launch:
• Iterate over Transaction.unfinished to retrieve unfinished transactions;
• Simultaneously listen for transaction changes via Transaction.updates (e.g., renewals, refunds);
2. For each verified transaction, we immediately call await transaction.finish();
3. We then construct a transaction model, store it locally, and report it to our backend for receipt verification;
4. After the server successfully verifies the receipt, the client deletes the corresponding local record;
5. On every app launch, the client checks for any locally stored receipts that haven’t been uploaded, and re-uploads them if necessary.
Key Code Snippets:
private static func verifyReceipt(receiptResult: VerificationResult) -> Transaction? {
switch receiptResult {
case .unverified(_, _):
return nil
case .verified(let signedType):
return signedType
}
}
public static func handleUnfinishedTransactions(payConfig: YCStoreKitPayConfig, complete: ((YCStoreKitReceiptModel?) -> Void)?) {
Task.detached {
for await unfinishedResult in Transaction.unfinished {
let transaction = YCStoreKitV2Manager.verifyReceipt(receiptResult: unfinishedResult)
if let transaction {
await transaction.finish()
if transaction.revocationDate == nil {
let receipt = YCStoreKitV2Manager.createStoreKitReceiptModel(
transation: transaction,
jwsString: unfinishedResult.jwsRepresentation,
payConfig: payConfig,
isRenew: false
)
complete?(receipt)
}
}
}
}
}
private func observeTransactionUpdates() -> Task<Void, Never> {
return Task {
for await updateResult in Transaction.updates {
let transaction = YCStoreKitV2Manager.verifyReceipt(receiptResult: updateResult)
if let transaction {
await transaction.finish()
if transaction.revocationDate == nil {
let receipt = YCStoreKitV2Manager.createStoreKitReceiptModel(
transation: transaction,
jwsString: updateResult.jwsRepresentation,
payConfig: self.payConfig,
isRenew: false
)
self.callProgressChanged(.receiptPrepared, receiptModel: receipt, errorType: .none, error: nil)
}
}
}
}
}
Our Questions:
1. Is it possible for Transaction.unfinished or Transaction.updates to return transactions that have already been finished?
Specifically, if a transaction was successfully finished in a previous app launch, could it still be returned again during the next launch?
2. Are there any flaws in our current handling process?
Our current sequence is: finish() → construct model → local save → report to server → delete after verification. Could this order lead to timing issues where StoreKit considers a transaction unfinished?
3. If we need your assistance in investigating specific user transaction records or logs, what key information should we provide?
We greatly appreciate your support and look forward to your response to help us further optimize our transaction processing logic.
I use [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]. Works on my App which is in the store (compiled pre-iOS 26).
If I compile the same App now, same codebase with Xcode Version 26.0, restore does not work. Nothing happens. Tested on real device (iOS 26).
Documentation says its deprecated, but my deployment target is iOS 12.
Anyone has similar issues? Any recommendations?
Topic:
App & System Services
SubTopic:
StoreKit
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?
Whether using Storefront.current?.countryCode or SKPaymentQueue.default().storefront?.countryCode, both are returning "USA" only.
(It used to return the correct country code before the update.)
In the sandbox environment, the country code is returned correctly,
but in the TestFlight environment, it always returns "USA".
There's no mention of this behavior in the beta release notes, so I'm posting it here for visibility.
Over the past two days, we've observed an unusual spike in requests from some iOS users to our server endpoint responsible for verifying App Store purchase receipts.
After sampling and analyzing the data, we found that the cause is related to the behavior of StoreKit2.Transaction.updates. Specifically, when listening for transaction updates, the system returns a large number of historical transactions — some dating back as far as one year. These callbacks are interpreted as "new" transactions, which in turn trigger repeated calls to Apple’s receipt verification servers, leading to an abnormal surge in traffic and putting pressure on our backend services.
This behavior is ongoing and is something we've never encountered in our previous experience. It appears to be outside of expected behavior, and we suspect it may be due to some kind of abnormality or unintended usage scenario.
We would appreciate guidance on the following:
Is this a known behavior or issue with StoreKit2?
Are there specific device states or conditions that could cause the system to emit historical transactions in bulk?
Are there any recommended practices for mitigating or filtering such transaction floods?
We have attached logs for reference. Any help identifying the root cause or suggestions for investigation would be greatly appreciated.
2025-07-24 12:39:58.594 +0400 listenForTransactions :{
"appTransactionId" : "704289572311513293",
"environment" : "Production",
"inAppOwnershipType" : "PURCHASED",
"originalPurchaseDate" : 1713445834000,
"originalTransactionId" : "430001791317037",
"purchaseDate" : 1713445834000,
"quantity" : 1,
"signedDate" : 1753346396278,
"storefrontId" : "143481",
}
2025-07-24 12:39:58.594 +0400 listenForTransactions :{
"appTransactionId" : "704289572311513293",
"deviceVerificationNonce" : "c4f79de2-a027-4b34-b777-6851f83f7e64",
"environment" : "Production",
"inAppOwnershipType" : "PURCHASED",
"originalPurchaseDate" : 1713445849000,
"originalTransactionId" : "430001791317270",
"purchaseDate" : 1713445849000,
"quantity" : 1,
"signedDate" : 1753346396278,
"storefrontId" : "143481",
"transactionId" : "430001791317270",
}
2025-07-24 12:39:58.594 +0400 listenForTransactions :{
"appTransactionId" : "704289572311513293",
"deviceVerificationNonce" : "02f305d7-0b2d-4d55-b427-192e61b99024",
"environment" : "Production",
"inAppOwnershipType" : "PURCHASED",
"originalPurchaseDate" : 1713511999000,
"originalTransactionId" : "430001792218708",
"purchaseDate" : 1713511999000,
"quantity" : 1,
"signedDate" : 1753346396278,
"storefrontId" : "143481",
"transactionId" : "430001792218708",
}
2025-07-24 12:39:58.598 +0400 [INFO] [MKPaymentService:23]: [XLPay] listenForTransactions :{
"appTransactionId" : "704289572311513293",
"deviceVerificationNonce" : "5ca85907-1ab6-4160-828e-8ab6d3574d6f",
"environment" : "Production",
"inAppOwnershipType" : "PURCHASED",
"originalPurchaseDate" : 1713512034000,
"originalTransactionId" : "430001792219189",
"purchaseDate" : 1713512034000,
"quantity" : 1,
"signedDate" : 1753346396278,
"storefrontId" : "143481",
"transactionId" : "430001792219189",
}
2025-07-24 12:39:58.599 +0400 listenForTransactions :{
"appTransactionId" : "704289572311513293",
"deviceVerificationNonce" : "04869b50-b181-4b69-b4ff-025175e9cf14",
"environment" : "Production",
"inAppOwnershipType" : "PURCHASED",
"originalPurchaseDate" : 1713512049000,
"originalTransactionId" : "430001792219440",
"purchaseDate" : 1713512049000,
"quantity" : 1,
"signedDate" : 1753346396278,
"storefrontId" : "143481",
"transactionId" : "430001792219440",
}
We are launching a new app with a subscription in-app purchase (IAP).
The Status of the IAP is "Waiting for Review". We want to submit the app to app for review, but the section to select an IAP with the app is not appearing. According to online sources, if we submit a new app for review, without selecting the IAP that goes with it, then the app may be released in the App Store without users being able to purchase the IAP. This actually happened to us years ago and I can't remember how we got around it, but it was painful. It seems strange that Apple would allow this problem to persist for years. Why not just hire an intern to fix it? Maybe because they are too busy running around in circles at the new spaceship headquarters.
Regardless, our IAP has been sitting in "Waiting for Review" status for a while now and I'm concerned it may never be approved.
Any advice would be appreciated.
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?
Hello,
For In App Purchases with a renewable subscription, does the originalTransactionId change in the following scenarios?
Case 1:
A user subscribes to a subscription A within a Subscription Group SG1.
The user then cancels it at the end of the month.
Comes back later to subscribe to the same subscription A within the same Subscription Group SG1.
Case 2:
A user subscribes to a subscription A within a Subscription Group SG1.
The user then cancels it at the end of the month.
Comes back later to subscribe to subscription B within the same Subscription Group SG1.