In Getting started with In-App Purchase using StoreKit views and the corresponding sample project, Store simultaneously enumerates Transaction.unfinished and Transaction.updates.
Since, "if your app has unfinished transactions, the updates listener receives them once, immediately after the app launches," it appears that Transaction.unfinished would also receive the same unfinished transactions causing handle(updatedTransaction:) to be called for twice for each transaction, causing consumables to be double-counted.
Is this a bug in the sample? Is there more information on concurrent execution of unfinished and updates?
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Posts under StoreKit tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello,
My app uses auto-renewable subscriptions.
I submitted an update and at first the subscription products appeared properly under the monetization section in App Store Connect.
However, after resubmitting the binary for review, the subscription product attachment disappeared, and now it is not possible to re-attach them.
Even sandbox testing cannot retrieve the subscription identifiers anymore, because the monetization section no longer shows the products.
Questions:
Why did the subscription products disappear after a resubmission?
How can I re-attach subscription products to the new version?
Do I need to remove the current version submission to make subscriptions appear again?
Does App Review need to finish processing subscription approval separately?
Notes:
The first version binary showed subscription products.
After internal rejection and resubmission, they disappeared.
Using StoreKit 2 restore flow in production code.
Sandbox accounts cannot fetch product identifiers.
Any guidance would be appreciated.
Thanks.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
Subscriptions
StoreKit
App Store Connect
In-App Purchase
In the XCode 16 beta, it isn't possible to create a new StoreKit configuration file because the templates list doesn't contain that option.
Adding the storekit extension to a file crashes XCode or doesn't have an effect at all. But, loading an existing file still works.
Need to vent a bit before relaxing for Christmas...
WatchOS IAP using Storekit 2 is such a mess...is nobody actually using this or does Apple just not care for the user experience here?
Lots of users experience after the purchase confirmation double tap on the side button an instant return to the purchase screen with nothing actually happening. No error message whatsoever. There is just one remedy: users need to unpair and re-pair their watch, including restoring a backup and setting up their wallet again. Nobody really wants to do this, or doesn't believe me and think this is just typical support BS, because their watch is paired and most things just work as they expect. And it turns away a customer, often leaving a bad review. And I can't do anything about it.
Other errors in the purchase process are reported, but like "process interrupted" in case the payment is not setup correctly (credit car no longer valid or sth.). How should the user know? There must be better ways of letting him know what exactly the problem is.
You need to implement a "Restore Purchase" function, otherwise you're not passing the review. But it really asks every time for the AppStore password, and users with crazy passwords -- that they rightfully should have! -- have almost no chance of typing them successfully on the tiny AW keyboard. Why is it not also just a side button double tap like for purchase? At the very least you would need access to the keychain PWs or allow pasting of sth. copied on the paired iPhone.
Promo Codes for IAP on AW-only apps just don't work. AW has no redemption at all, and on the iPhone the AppStore will try to talk to a companion app (which AW-only doesn't have) and the end up in a dead-end installation effort.
This all feels like never really tested in the field, and people are of course blaming the 3rd party dev. for all these issues. And opening a ticket is just leading nowhere -- at best it's closed after months with the hint "duplicate" but w/o any chance for me to see that one that they then actually work on and track progress.
It's all so frustratingly broken...
Hello everyone, I achieved my MacOs app and distributed for "App Store Connect" and I already have setup the Product In App Purchase in the App Store connect, and I even tested the purchase flow using the local .storekit file using the "Edit Schema". And now, when I remove the edit scema's storekit file and archieved the app and used the Internal testing and installed the app using Testflight, I see that the product is not showing, an empty product array is being returned and there is no option to purchase.
I don't want to sumit the full app until I test the StoreKit integration in real test
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
StoreKit
App Store Connect
In-App Purchase
TestFlight
SKStorefront.countryCode always returns "US" in TestFlight and sandbox,
even when sandbox Apple ID is set to a different country.
Is this expected behavior?
Is SKStorefront guaranteed to return the correct storefront
only for production App Store builds?
Topic:
Developer Tools & Services
SubTopic:
Apple Developer Program
Tags:
StoreKit Test
StoreKit
In-App Purchase
TestFlight
I am implementing In App Purchase and integrating App Store Server Notifications version 2.
I am able to receive the JWSSignedPayload from Apple. The payload is Base64 encoded and contains header, payload, and signature. The documentation clearly explains how to decode the payload and extract claims such as notificationType and data.
What is unclear is how to verify the JWS signature itself.
I could not find concrete documentation that explains:
Which Apple public key or certificate should be used for verification
How to retrieve or rotate the key used to sign the payload
The exact steps required to validate the JWS signature before trusting the payload
Signature verification is critical for security, and simply decoding the claims without validation is not sufficient.
I am looking for official guidance or a recommended approach to verify the JWSSignedPayload signature for App Store Server Notifications v2.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
StoreKit
App Store Connect
In-App Purchase
App Store Server API
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
}
}
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?
Try this simple code:
import SwiftUI
import StoreKit
struct ReviewView: View {
@Environment(\.requestReview) var requestReview
var body: some View {
Button("Leave a review") {
requestReview()
}
}
}
When the Review Alert shows, the "Not Now" button is disabled for some reason!? It was always tappable in all iOS versions that I remember. And there is no way to opt out, unless the user taps on the stars first. Is it a bug or a feature?
Thanks for looking into it!
Hello,
I am currently implementing External Purchase Link and External Purchase Custom Link and am encountering an issue where both
ExternalPurchaseLink.canOpen and ExternalPurchaseCustomLink.isEligible always return false under all test conditions.
I would like to confirm whether my setup is missing any required steps or whether this behavior is expected.
Below are the details of my current environment and configuration:
🔧 1. Development Environment
Xcode: 16.3, 16.4, 26.0 beta 4
Devices:
iPhone running iOS 26.2 beta
iPhone running iOS 16.7.12
macOS 15.5 (real device testing)
Simulator iOS 18.0
Build Type: Local development build using a Developer Provisioning Profile
Sandbox account signed in during testing
🔑 2. Entitlements (Developer site & Xcode)
In Certificates → Identifiers → App ID, both capabilities are enabled:
StoreKit External Purchase
StoreKit External Purchase Link
The .entitlements file in Xcode includes:
com.apple.developer.storekit.external-purchase = YES
com.apple.developer.storekit.external-purchase-link = YES
The Provisioning Profile also contains both entitlements (confirmed via codesign -d --entitlements :-).
📄 3. Info.plist Configuration
Both keys are configured with correct region codes according to documentation:
SKExternalPurchase
SKExternalPurchaseCustomLinkRegions
🌍 4. Test Storefront
Device storefront verified as United States (US) or Portugal (PT) (US = target region for External Purchase Link, PT = EU region)
But despite all the above configuration,
both API calls consistently return false:
ExternalPurchaseLink.canOpen // false
ExternalPurchaseCustomLink.isEligible // false
So I cannot proceed to testing the remaining flow (token retrieval, link opening, etc.)
------ Questions ------
❓ Q1) Local Development Build Limitation
Is it expected behavior that Developer-signed local builds always return
canOpen = false / isEligible = false
for External Purchase Link & Custom Link?
Is there a technical or policy restriction that prevents eligibility in local dev builds?
❓ Q2) App Store Connect Configuration Requirement
Are there mandatory App Store Connect settings (such as external purchase URLs, support URL, disclosures, or country configuration) that must be enabled before eligibility becomes true?
Currently, no External Purchase Link or Custom Link menu is visible in my App Store Connect app settings.
Is this menu only available after certain approvals or under specific conditions?
❓ Q3) TestFlight Requirement
Do External Purchase Link and Custom Link only return eligibility = true on:
TestFlight builds, or
Distribution-signed builds?
Or should eligibility also work on developer builds?
Formal confirmation would be helpful.
❓ Q4) Developer Account Type Limitation
We are using an Individual Developer Account (not Organization).
Can Individual accounts fully request, test, and ship apps using:
External Purchase Link
External Purchase Custom Link
Or are there limitations on account type?
🙏 Request
We have completed all documented setup steps (Entitlements → Provisioning → Info.plist),
but eligibility remains false, blocking feature validation.
Please clarify which of the following is the cause:
Local development builds do not support eligibility
Missing App Store Connect configuration (not visible to us)
Account type restriction
Region rollout or entitlement approval requirement
Any additional setup not documented publicly
Thank you for your assistance.
There is a project that has been running online for years. A few months ago, a player reported that after making their first successful IAP at a specific purchase point, any subsequent attempts to purchase the same item do not trigger the payment window. Instead, they get the error:"This in-app purchase has already been bought".After contacting Apple Support once, the player was able to make a payment, but the issue reappeared on the next attempt. So far, this is the only user reporting the problem, other people can purchase normally.
Question1:
Here’s what I’ve tried:
I reviewed the code and ensured that TransactionObserveris correctly called.
I’ve also added **[[SKPaymentQueue defaultQueue] finishTransaction:transaction]**in all possible places, but the issue persists.
According to the logs, after the user’s first purchase, every subsequent IAP attempt returns the same receipt from the initial successful transaction, even though I’m certain finishTransactionis being called. It seems like this method isn’t having the intended effect.
Question2:
I asked the player to manually trigger the Restore Purchases button by calling [[SKPaymentQueue defaultQueue] restoreCompletedTransactions].
the restoreCompletedTransactionsFailedWithErrorcallback returned the following error:
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."
The player has already checked their device time and tried switching between Wi-Fi and 4G, but the error remains. Is this SSL error related to the "already bought" error? Note that this SSL issue occurred during a separate restore process, not during a purchase attempt.
Question: 3:
I noticed that I’m not calling finishTransaction inside the restoreCompletedTransactionsFailedWithErrorcallback. Should I add it there?
Purchase Logs:
The player clicked "Restore Purchases" and then attempted another purchase. The purchase flow appears normal, but the IAP returns an old, already-used receipt.
[2025-12-10 17:41:38:995] Restore transaction failed: Error > Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a > secure connection to the server cannot be made."
[2025-12-10 17:41:40:010] Restore transaction failed: Error > Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a > secure connection to the server cannot be made."
[2025-12-10 17:41:42:011] buy method called... productID: > huoxiancj_648 orderID: 22674511
[2025-12-10 17:41:42:107] ----Log Observers ID----
[2025-12-10 17:41:42:108] ObserverID: 0x109968890
[2025-12-10 17:41:42:108] Processing unfinished transactions...
[2025-12-10 17:41:42:108] Finished processing unfinished > transactions.
[2025-12-10 17:41:42:108] Allowing in-app purchase...
[2025-12-10 17:41:42:215] Requesting product info...
[2025-12-10 17:41:42:989] productsRequest didReceiveResponse:
[2025-12-10 17:41:43:066] Invalid Product ID: (
[2025-12-10 17:41:43:066] Purchase quantity: 1
[2025-12-10 17:41:43:066] Product info:
[2025-12-10 17:41:43:067] Price: 648
[2025-12-10 17:41:43:067] Product ID: huoxiancj_648
[2025-12-10 17:41:43:067] Validating product info...
[2025-12-10 17:41:43:067] Sending payment request...
[2025-12-10 17:41:43:067] requestDidFinish
[2025-12-10 17:41:43:132] paymentQueue updatedTransactions.
[2025-12-10 17:41:43:133] updatedTransactions case > SKPaymentTransactionStatePurchasing
[2025-12-10 17:41:43:208] [payment.applicationUsername] > userid=50306496 appid=1045 instid=12844 reserve=xxxx > productID=22674511
[2025-12-10 17:43:16:008] paymentQueue updatedTransactions.
[2025-12-10 17:43:16:008] updatedTransactions case > SKPaymentTransactionStatePurchased
[2025-12-10 17:43:16:008] productIdentifier= huoxiancj_648
[2025-12-10 17:43:16:113] Sending receipt to server for validation.
[2025-12-10 17:43:16:113] Transaction completed.
Any help or suggestions would be greatly appreciated! Thanks in advance.
Hi everyone,
I’m experiencing an issue with In-App Purchases during App Review.
What works
My consumable IAP products load correctly using StoreKit2.
TestFlight (sandbox) purchases work perfectly.
Localizations are filled in and valid.
Paid Apps Agreement, banking, and tax forms are active.
IAP products are properly created in App Store Connect and marked as “Developer Action Needed” only because they wait for approval with the new binary.
What fails
During review I received:
“We found that your in-app purchase products exhibited one or more bugs which create a poor user experience.
Specifically, we were not able to complete a purchase.”
They didn’t provide any more technical details.
Additional context
The StoreKit configuration file is not included in the app archive.
Product identifiers perfectly match those in App Store Connect.
StoreKit2 purchase() works as expected on TestFlight.
The app does not use server-side receipt validation - purchases are handled purely through StoreKit2 APIs, as recommended.
My questions
What could cause a situation where TestFlight purchases work but App Review cannot complete a purchase?
Does Apple expect server-side receipt validation even for simple one-time consumables?
Could there be a delay or sync issue causing IAP products to not be available to the reviewer yet?
Is there anything I should check on the App Store Connect side beyond what I already verified?
Any help or hints would be greatly appreciated - I’m stuck because everything works in sandbox but fails only for reviewers.
Thanks!
I'm experiencing an issue where monthly and family monthly subscriptions fail immediately with "purchase cancelled" in TestFlight, while yearly and family yearly subscriptions work correctly.
Setup:
All 4 products in same subscription group
All show "Ready to Submit" status
Using react-native-iap with StoreKit 2
Testing in TestFlight sandbox
The Problem:
Yearly subscriptions work fine
Monthly subscriptions fail immediately with E_USER_CANCELLED (no dialog appears, user didn't cancel)
What I've verified:
No active subscriptions (getAvailablePurchases() returns empty)
No pending transactions
Same subscription group for all products
Product IDs match App Store Connect
Code is identical for all products
Error: E_USER_CANCELLED fires immediately when calling requestPurchase() for monthly products, but yearly products proceed normally.
Questions:
Why would monthly fail while yearly works (same group, same config)?
Could this be sandbox state corruption for monthly products?
Should I delete/recreate the monthly products?
Any insights would be appreciated!
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
Subscriptions
StoreKit
In-App Purchase
TestFlight
会员等级进行升级?需要按天计算费用,像爱奇艺这样是怎么做的?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit
In-App Purchase
App Store Server API
Advanced Commerce API
I have an iOS and iPadOS app that also runs on macOS Catalyst. The user is able to view their subscription using the SubscriptionStoreView with two SubscriptionOptionGroups.
The documentation does not mention these are supported on macOS Catalyst and the app crashes when attempting to show the SubscriptionStoreView on macOS Catalyst.
If not supported, how can the user manage their subscription on macOS?
When I try to archive an app in order to submit it to the App Store I receive the following errors I do not know how to fix:
error: Framework /Users/fbartolom/Library/Developer/Xcode/DerivedData/Virtual_Tags-apzduassdiglhcapscsllvzbfgid/Build/Intermediates.noindex/ArchiveIntermediates/Virtual Tags/InstallationBuildProductsLocation/Applications/VirtualTags.app/Frameworks/StoreKit.framework did not contain an Info.plist (in target 'VirtualTags' from project 'Virtual Tags') error: Framework /Users/fbartolom/Library/Developer/Xcode/DerivedData/Virtual_Tags-apzduassdiglhcapscsllvzbfgid/Build/Intermediates.noindex/ArchiveIntermediates/Virtual Tags/InstallationBuildProductsLocation/Applications/VirtualTags.app/Frameworks/Security.framework did not contain an Info.plist (in target 'VirtualTags' from project 'Virtual Tags') error: Framework /Users/fbartolom/Library/Developer/Xcode/DerivedData/Virtual_Tags-apzduassdiglhcapscsllvzbfgid/Build/Intermediates.noindex/ArchiveIntermediates/Virtual Tags/InstallationBuildProductsLocation/Applications/VirtualTags.app/Frameworks/CloudKit.framework did not contain an Info.plist (in target 'VirtualTags' from project 'Virtual Tags')
MacBook Pro M5, Tahoe 26.1, Xcode 26.1.1
Dear all,
This is my first post in this forum - and, in fact, my first app, too! I'm glad to be here, and thanks in advance for your help.
I'm looking to offer an app for a one-time payment. I'd also like people to be able to try the app for a week.
It seems that the "Pricing and Availability" section in App Store Connect is not the right place to configure this kind of offer. It does allow me to set a one-time price, but I cannot find a trial-period there (or am I missing something?)
Two different strategies seem possible here:
Using in-app-purchases: make the actual app free, but ask users after a week to buy a non-consumable IAP. The problem with that: I need to verify that the app has been installed for seven days ... even if it has been uninstalled at some point.
Using subscriptions: There is a "free trial" option for subscriptions. But after that free trial, subscriptions are being payed periodically. I'd rather have the user only pay once for lifetime-access.
Some apps seem to use strategy 1 - I believe the "Lap swim" app does. But still it seems like a bit of a hack - is there a more elegant way to achieve this?
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
In-App Purchase
App Store Connect
StoreKit
Subscriptions
StoreKit ask to buy should have more data in pending state. When user try to purchase ask to buy, we should get at least transactionID, product itself, and time that user start the request. So we can keep track of the whole transaction flow
jwsRepresentation should always available for every state, actually even failing state. And should attach state inside of it. Instead of only available after verified purchase. So we can use transactionID and everything relate to transaction for both waiting for purchase and clearing up the cancel or invalid purchase
Currently we only have jwsRepresentation after complete purchase, which is very limited its usage
We feel like we're at the end of the long and treacherous process of migrating to StoreKit2. But we've hit a small snag. When testing in the sandbox environment, we've found that if we don't finish a transactions, no subsequent purchase (invoked via call to purchase or the other purchase) will produce the confirmation sheet. Is this the expected behavior? The behavior is observed on iOS26 and 18.
Our app will only attempt to finish the transaction if it successfully uploads the receipt to our API. If it fails to do so for whatever reason, the transaction is left unfinished. Whilst the user is informed about this, users will commonly try again. Our concern is that since the confirmation sheet will not be shown again, users will not know they are actually paying again - most certainly not the UX we want to have. We'd much rather have our users be fully aware when they're paying us money.
The reason we're choosing not to finish the transaction until our backend has received it and confirmed the receipt to be valid is that the only way the user can get their product is if the server side is aware of this and add more time to the users account. When finishing the transaction via finish immediately after the purchase() call, the confirmation sheet is shown every time after subsequent calls to purchase().
Again, is this the expected behavior both in the sandbox and the production environments? Are we doing something wrong or misusing the product API? We are somewhat stumped because technically, we could get the first confirmation for a product purchase, and then finish it only after an arbitrary amount of calls to purchase() have been made - the user will believe they will have paid only once, but we will receive however much money we can drain from their account - most certainly not the kind of app we want to develop.
Please advise and best regards,
Emīls