We are experiencing a critical issue where StoreKit 2 is returning empty products when using Product.products(for:), specifically on devices running iOS 18.4.
This issue does not occur on iOS 18.3 or earlier.
Steps:
Created a subscription product (e.g. "upm1") in App Store Connect
Confirmed the product is active, localised, and part of a valid subscription group
Call the following Swift code using StoreKit 2:
Task {
do {
let products = try await Product.products(for: ["upm1"])
print(products)
} catch {
print("Error: (error)")
}
}
4. Result: products is an empty list.
This regression is blocking subscription testing on iOS 18.4.
Kindly someone please advise on a potential fix or workaround.
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,
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
One of our apps has 85% stuck in Billing Retry -- We are so confused. All the users are from the US, and have a one-week free trial.
We had 1,000 subscriptions expire from this issue.
So any help would be so appreciated.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Connect
App Store Server API
Regarding App Store Server Notifications V2,we are currently using Notifications V2 in a production environment.
It is set up so that if the server receives the notification successfully, it returns 200 after about 30 seconds, and if an error occurs, it returns 400 or 500.
However, the notification is being resent multiple times from Apple's server, at 1 hour, 12 hours, and 24 hours.
Is it necessary to return the notification using Apple's API?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
App Store Server Notifications
App Store Server API
SKProductsRequest always returns as USD not local currency for debug environment and even some time it fails. This is only happening for debug or TestFlight build.
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?
I have an iOS app with a main target and an app extension. I want the app extension to write data to the SwiftData in App Groups, while the main target reads the data. However, currently, the app extension only has permission to read data, not to write it.Is the issue due to my incorrect configuration or an Apple-imposed restriction?
Hi everyone,
I'm experiencing an issue with APNs server notifications where I receive a 404 error when trying to validate the signedPayload from Apple's notification. Below is a sanitized version of my code:
class ServerNotificationAppleController extends Controller
{
// URL for StoreKit keys (Sandbox environment)
private $storeKitKeysUrl = 'https://api.storekit-sandbox.itunes.apple.com/inApps/v1/keys';
public function handleNotification(Request $request)
{
\Log::info($request);
$signedPayload = $request->input('signedPayload');
if (!$signedPayload) {
return response()->json(['error' => 'signedPayload not provided'], 400);
}
// Step 1: Create your JWT token (token creation logic can be in a separate service)
$jwtToken = $this->generateAppleJWT();
// Step 2: Send a request to the StoreKit keys endpoint
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $jwtToken,
])->get($this->storeKitKeysUrl);
Log::info('Apple Keys Status:', ['status' => $response->status()]);
Log::info('Apple Keys Body:', ['body' => $response->body()]);
if ($response->status() !== 200) {
return response()->json(['error' => "Apple public keys couldn't be retrieved"], 401);
}
$keysData = $response->json();
// Step 3: Validate the signedPayload
$validatedPayload = $this->validateSignedPayload($signedPayload, $keysData);
if (!$validatedPayload) {
return response()->json(['error' => 'Invalid signedPayload'], 400);
}
// Process the validated data as needed
Log::info("Apple Purchase Data:", (array)$validatedPayload);
return response()->json(['message' => 'Notification processed successfully'], 200);
}
private function generateAppleJWT()
{
// API key details (replace placeholders with actual values)
$keyId = config('services.apple.key_id'); // e.g., <YOUR_KEY_ID>
$issuerId = config('services.apple.issuer_id'); // e.g., <YOUR_ISSUER_ID>
$privateKey = file_get_contents(storage_path(config('services.apple.private_key')));
// Set current UTC time and expiration time (20 minutes later)
$nowUtc = Carbon::now('UTC');
$expirationUtc = $nowUtc->copy()->addMinutes(20);
// Create the payload with UTC timestamps
$payload = [
'iss' => $issuerId,
'iat' => $nowUtc->timestamp,
'exp' => $expirationUtc->timestamp,
'aud' => 'appstoreconnect-v1',
'bid' => 'com.example.app', // Replace with your Bundle ID if necessary
];
// Generate the JWT token
return JWT::encode($payload, $privateKey, 'ES256', $keyId);
}
private function validateSignedPayload($signedPayload, $keysData)
{
try {
$jwkKeys = JWK::parseKeySet($keysData);
return JWT::decode($signedPayload, $jwkKeys, ['RS256']);
} catch (\Exception $e) {
Log::error("Apple Purchase Validation Error: " . $e->getMessage());
return null;
}
}
}
I’m particularly puzzled by the fact that I receive a 404 error when trying to retrieve the public keys from the StoreKit keys endpoint. Has anyone encountered this issue or can provide insight into what might be causing the error?
Any help or suggestions would be greatly appreciated. Thanks!
I've been testing the offer code feature for my non consumable in app purchase using a sandbox account, with sandbox offer codes and in the sandbox environment. However, the codes don't appear to work despite everything being in the sandbox.
Any idea what I'm missing?
I'm currently working on transitioning to StoreKit 2. In order to see if my users are legacy users who purchased the app before I implemented an in-app purchase, I am trying to use the original purchase date for the app. Unfortunately, it's returning 0 seconds since 1970.
func updateOriginalPurchaseStatus() async throws {
let transaction = try await checkVerified(AppTransaction.shared)
self.originalPurchaseVersion = transaction.originalAppVersion
self.originalPurchaseDate = transaction.originalPurchaseDate
}
This is from the transaction:
[3] = {
key = "originalPurchaseDate"
value = number (number = 0)
}
Currently trying to figure out when I actually purchased the app, but it might be as early as 2012. And I likely used a download code.
Looking to update one of my apps that uses SKStoreReviewController +requestReview (deprecated) to
AppStore.requestReview(in:)
umm...I have a few of questions...
Why is an NSViewController parameter required? It's really not so uncommon for an AppKit app to just use NSWindowController with a window that does not use NSViewController...
It should be possible to present the review request in a standalone alert (attached to a window is preferred IMO but it still should be possible to ask in separate window).
3)...why Swift..(err nevermind)
Ideally:
AppStore requestReview should take a NSWindow parameter but that parameter should be optional. If nil the request should be presented in a standalone window (like an alert). If non nil..present as a sheet on the window.
Why a view controller? Maybe I'm missing something.
We got Advanced Commerce API and the generic product identifiers approved. When I was try to migrate a sandbox subscription to ACA enabled subscription I hit an error
Request payload
(Hid some info but the requestReferenceId is the real)
{
"descriptors": {
"description": "Migrated",
"displayName": "Migration"
},
"items": [
{
"sku": "product_1mo_999",
"description": "description",
"displayName": "Product"
}
],
"requestInfo": {
"requestReferenceId": "3b0b8e67-d8a0-45f4-8f6d-06bffa9a2c08"
},
"storefront": "USA",
"targetProductId": "com.company.generic.subscription",
"taxCode": "C003-00-1"
}
Response
{
"errorCode": 5000000,
"errorMessage": "An unknown error occurred."
}
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
App Store Server API
Advanced Commerce API
I has sandbox account with Japanese local. When i build app directly to check, price is displayed in Japanese Currency. But when I install app from the Test Flight, price is always displayed in USD Currency.
the issue is appear in iOS 18.5
How can i fix this issue ?
Hi there 👋🏻
We are facing an issue that started on 24 June 2025 where some users that have an active subscription with an offer are not being able to use/restore their subscription since Transaction.currentEntitlements is empty.
We have tried to call the server with this endpoint https://developer.apple.com/documentation/appstoreserverapi/get-transaction-history and it's returning the correct transactions correctly.
Any idea what is happening?
My app has a couple of consumable IAP items. I have tested this extensively and it works in all test scenarios including loads of beta testers using testflight. However, Apple's production reviewer reports that loading of the products hangs in their setup.
This is very frustrating as I have no means of recreating the problem.
My first product was tested ok an all my IAP items are approved for release. However, I did not explicitly assign them to my build. I read somewhere that you need to do that but could not find in App Store Connect after my first product was approved.
Below is the relevant code section. What am I missing?
class DonationManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
@Published var products: [SKProduct] = [] // This is observed by a view. But apparently that view never gets populated in Apple's production review setup
@Published var isPurchasing: Bool = false
@Published var purchaseMessage: String? = nil
let productIDs: Set<String> = ["Donation_5", "Donation_10", "Donation_25", "Donation_50"]
override init() {
super.init()
SKPaymentQueue.default().add(self)
fetchProducts()
}
deinit {
SKPaymentQueue.default().remove(self)
}
func fetchProducts() {
print("Attempting to fetch products with IDs: \(productIDs)")
let request = SKProductsRequest(productIdentifiers: productIDs)
request.delegate = self
request.start()
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
DispatchQueue.main.async {
self.products = response.products.sorted { $0.price.compare($1.price) == .orderedAscending }
print("Successfully fetched \(self.products.count) products.")
if !response.invalidProductIdentifiers.isEmpty {
print("Invalid Product Identifiers: \(response.invalidProductIdentifiers)")
self.purchaseMessage = NSLocalizedString("Some products could not be loaded. Please check App Store Connect.", comment: "")
} else if self.products.isEmpty {
print("No products were fetched. This could indicate a problem with App Store Connect configuration or network.")
self.purchaseMessage = NSLocalizedString("No products available. Please try again later.", comment: "")
}
}
}
...and the view showing the items:
@StateObject private var donationManager = DonationManager()
var body: some View {
VStack(spacing: 24) {
Spacer()
// Donation options -------------------
if donationManager.products.isEmpty {
ProgressView(NSLocalizedString("Loading donation options...", comment: ""))
.foregroundColor(DARK_BROWN)
.italic()
.font(.title3)
.padding(.top, 16)
} else {
ForEach(donationManager.products, id: \.self) { product in
Button(action: {
donationManager.buy(product: product)
}) {
HStack {
Image(systemName: "cup.and.saucer.fill")
.foregroundColor(.pink)
Text("\(product.localizedTitle) \(product.priceLocale.currencySymbol ?? "$")\(product.price)")
}
.buttonStyle()
}
.disabled(donationManager.isPurchasing)
}
}
Dear my friends,
I have set up two subscriptions (monthly and annual) in App Store Connect. By configuring with StoreKit, I can import the configurations from App Store Connect into the local .storekit file, and the program can
run normally. However, when the StoreKit configuration is set to NONE, I am unable to retrieve product information from App Store Connect.
The code for fetching the product list is as follows. Could you please provide suggestions on how to proceed? Thank you very much!
private let productIds: [String] = ["subscription.year", "subscription.monthly"]
subscriptions = try await Product.products(for: productIds)
Hey!
We're implementing In-App Purchase Subscriptions and we were able to receive "App Store Server Notifications" on our "Sandbox Server URL".
But the last event we received 22 hours ago. We are able to verify transactions and finish them, but receive no webhooks.
We changed nothing on our server or its configurations but the notifications stoped to come.
We consulted the API (https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/history) and it says the same as we see - the last event was 22hrs ago.
I checked all the advices from here as well (https://developer.apple.com/forums/thread/805806?answerId=864483022#864483022).
Is there any Status page for the Store Kit Sandbox services? Was there any outage?
Sincerely,
Konstantin
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Server Notifications
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
I am shown as being subscribed to our service in the Subscriptions list in settings yet when going to the Storekit2 page in my app it shows me as NOT being subscribed and is unresponsive. I select Restore Subscription, that grays briefly, asks for a password, then returns to blue and nothing else happens. Bouncing back and forth between monthly and yearly likewise gives no response.
The Transaction.currentEntitlements seems to be empty so it thinks the user is not subscribed.
I have unsubscribed, and resubscribed via the Settings page to no avail.
Topic:
App & System Services
SubTopic:
StoreKit
Unsure if this has been reported before, but I'm seeing a specific scenario where Apple is sending back faulty information in App Store Server Notifications.
When users have payment failures that result in the membership expiring, and then reactivate later on with a different subscription, they are given a new originalTransactionId, yet we have three cases where we receive messages about the old originalTransactionId, indicating that after the user recovers their subscription, Apple is resurrecting the old subscription and treating it as if it is active BUT ultimately decides to deactivate it.
Here is a sample screenshot of an impacted user. We only have three month and annual plans.
Timeline:
March 21st:
This user signed up for a 3 Month Plan
June 21st:
This user fails to pay
July 7th:
After our 16 day grace period, this user loses access
July 29th:
This user sees they lost access and repurchase
Augsut 19th:
Their membership is deactivated (long before their 3 Month Subscription is up)
For further verification I've also attached a record of all the payloads we've received from Apple for this user.
Please let me know if you need any more details to fix this bug OR if it has already been fixed! Thank you.
user_webhook_data_sanitized.csv