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

StoreKit 2 AppTransaction failing
We have had a small number of users of our mac app complaining that the app suddenly can't detect their subscription or previous purchase history. These users are not new, and have been using the app successfully for some time. In the app we do this using the following (very standard) code at app startup: let result: VerificationResult<AppTransaction> = try await AppTransaction.shared For those users experiencing the failure, the result is coming back as unverified. So far we've been unable to find the cause or a solution, but it seems to have become worse with the release of macOS 15.4. We've tried resetting, rebooting and reinstalling the app. It's worth adding the (probably obvious) that it's impossible to test or fault-find with this, because we can't replicate the issue in a development environment. Any suggestions gratefully received.
19
3
440
Apr ’25
StoreKit2
Hello, I use Storekit2 to test the purchase of subscription products. After purchasing a subscription product in the sandbox, it will automatically renew 12 times, and then it will no longer automatically renew. When I click to purchase again, calling the try await product.purchase() method does not pop up the purchase pop-up window. In fact, it will directly go to the case let .success(.verified(transaction)): step, and the Transaction.currentEntitlements is empty
1
3
248
Jun ’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
0
2
45
3w
Unknown Error Occurring During Sandbox Purchase Testing
We are experiencing an Unknown Error (error code 0) when testing purchases in the Apple Sandbox environment. The error does not occur every time, but it fails with an Unknown Error in approximately 8 out of 20 attempts. Due to this issue, our app is failing to pass Apple’s review. Issue Details This issue only occurs in the Apple Sandbox environment. After attempting a purchase, the Apple payment API returns SKPaymentTransaction Unknown Error. Returned error code: SKErrorUnknown (error.code 0). This problem is preventing us from conducting proper payment tests and getting approval from Apple. Would it be possible to receive support or any solutions for this issue?
0
2
221
Mar ’25
Why does a purchase result in success unverified?
A purchase can result in success with verificationResult .unverified. Is there a list of reasons for which the transaction might be unverified and how should i handle it in my app? From my understanding, a successful unverified transaction means the user has already paid for the purchase. So, do i just ignore the unverified transaction or do i provide content to the user anyways?
1
2
106
Jul ’25
Is wallet extension an mandatory implementation for Apple Pay and Add to wallet?
I am working on a banking application (includes iPhone and iPad) which includes add to wallet feature. During the implementation I saw one document it is mentioned that for iPad app, the app must be extended to support Apple pay functionality. Details from document says "Card Issuers with an iOS mobile banking app must support Card Issuer iOS Wallet extension functionality to enable Card issuer mobile app customers to provision new cards directly from the iOS Wallet app with all eligible Apple iOS devices. If the Card Issuer has a dedicated iPad App, that App must be extended to support Apple Pay functionality. " Is wallet extension implementation required for Apple pay to work in iPhone and iPad? Is wallet extension a mandatory implementation for Add to Apple Wallet feature to work and approved by Apple? I am little confused in this. Anyone who integrated Apple pay or done add to Apple Wallet feature recently without wallet extension faced any rejection? Any help would be much appreciated.
4
2
3.7k
Feb ’25
Not receiving Sandbox Server-to-Server Notifications for In-App Purchases (App Store Server Notifications v2)
I’m testing auto-renewable subscription purchases in the sandbox environment. When I buy a subscription package using a sandbox test user, I don’t receive any App Store Server Notifications from the sandbox. However, when I use the “Request Test Notification” option in App Store Connect, the notification is received successfully. My sandbox server notification URL is configured correctly and publicly accessible. I also call finishTransaction() after purchase, and the receipt is verified successfully with the sandbox verification endpoint. To further debug, I used the getNotificationHistory API to fetch notifications for yesterday and today (Nov 3–4, 2025). Apple’s API response does not include any notifications or transaction IDs for my today’s purchases (Nov 4, 2025) — even though I can confirm from logs that those transactions were completed and verified successfully. It looks like sandbox purchase notifications are not being sent or logged, even though test notifications work fine. Could someone from Apple please confirm if there’s currently an issue with sandbox server notifications for auto-renewable subscriptions?
7
2
213
Nov ’25
【iOS18.2~18.3.2 bug】After switching sandbox accounts in Settings, the value of SKStorefront.countryCode is not synchronized
Problem Description: 1、I have two sandbox accounts from different countries. Account A is from Mainland China (CHN), and Account B is from the United States (USA). When I switch the sandbox account from Account A (CHN) to Account B (USA) in the system settings and restart the app, the value of SKPaymentQueue.defaultQueue.storefront.countryCode always returns "CHN" instead of the expected "USA". 2、This issue only occurs on iOS 18.2 and above. 3、On the same device running iOS 17.5.1, the behavior is correct. However, after upgrading the system to iOS 18.3.2, the error occurs. 4、In the sandbox environment, although SKStorefront.countryCode returns the wrong value, the currency type for Apple's in-app purchase is correct when initiating the payment. 5、The issue only exists in the sandbox environment and does not occur in the App Store-downloaded package. Demo Code: - (IBAction)clickButton:(id)sender { NSString *appStoreCountryCode3 = SKPaymentQueue.defaultQueue.storefront.countryCode; NSLog(@"%@",appStoreCountryCode3); } Demo Testing Steps and Results: 1、Sandbox Account A (China) will print "CHN". 2、Go to Settings - Developer - (at the bottom) SANDBOX APPLE ACCOUNT, and switch to another sandbox account B (USA). 3、Restart the Demo App. 4、Print results: iOS 17.5.1: "USA" ✅ → Upgrade the system of the same device to iOS 18.3.2 → "CHN" ❌ iOS 18.2.1: "CHN" ❌ iOS 18.3.1: "CHN" ❌ iOS 18.3.2: "CHN" ❌ Possible Clues: Starting with iOS 18.2, Apple changed the entry point for setting up sandbox accounts, which introduced this bug. It seems that when users switch sandbox accounts on iOS 18.2, Apple engineers forgot to notify the SKStorefront class to update the countryCode value. Before iOS 18.2: Settings - App Store - Sandbox Account iOS 18.2 and later: Settings - Developer - (at the bottom) Sandbox Account Although it doesn't affect the App Store package, it does impact our development and testing process. We hope this issue can be fixed in future versions. Thank you!
5
2
863
Oct ’25
App Store Server Notifications and API Client - Toggling Sandbox vs Production Environment
The documentation mentions the following: Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store. This way, you can use one server environment to handle both Sandbox and Production environments. It is necessary to pass App Review. However, I'm not manually hitting these URLs - I'm using Apple's libraries. Specifically, the environment is used in SignedDataVerifier and AppStoreServerAPIClient. (I can't link to these because, for some reason, the domain apple.github.io is not allowed. The documentation for these is only found there. You can find it quickly by searching these terms and the domain.) Here is how SignedDataVerifier is being used: const verifier = new SignedDataVerifier( appleRootCertificates, APPLE_ENABLE_ONLINE_CHECKS, APPLE_ENVIRONMENT, APPLE_BUNDLE_ID, APPLE_APP_ID ) const verifiedNotification: ResponseBodyV2DecodedPayload = await verifier.verifyAndDecodeNotification(signedPayload) if (!verifiedNotification) { // Failure return } Here is how AppStoreServerAPIClient is being used: const appStoreServerAPIClient = new AppStoreServerAPIClient( SIGNING_KEY, APPLE_IAP_KEY_ID, APPLE_IAP_ISSUER_ID, APPLE_BUNDLE_ID, APPLE_ENVIRONMENT ) const statusResponse: StatusResponse = await appStoreServerAPIClient.getAllSubscriptionStatuses(originalTransactionId, [Status.ACTIVE]) In the source code for SignedDataVerifier.verifyAndDecodeNotification, I can see that it throws a VerificationException(VerificationStatus.INVALID_ENVIRONMENT) error . So for SignedDataVerifier is it as simple as wrapping my code in a try/catch and checking that the error's status code is 21007? I'm unsure about this because if you scroll to the bottom of the linked source code file, you can see the enumeration VerificationStatus, but it's unclear if this member has a value of 21007. The source code for AppStoreServerAPIClient only says that it throws an APIException if a response could not be processed, so I'm not too sure about how to handle this one.
2
2
886
Sep ’25
Unauthorized for Advanced Commerce API Purchase
Hi! My product SKU has been approved for Advanced Commerce API. I successfully receive a purchase pop-up with the correct information. However, I am still having issues with completing the purchase. I always receive Unauthorize error when I confirm the purchase (subscription in my case; see the screenshot). I am using the node.js server library to sign the request. I made sure that the account is a valid account enabled for Sandbox. Logs unfortunately don't indicate any further detail. Thanks for your advice! We've been stuck on this for a while now and would appreciate your help. Marek
6
2
247
Jul ’25
What happens on device after RESCIND_CONSENT notification?
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
0
2
71
Nov ’25
Unable to Authenticate with App Store Server API in Production (401 Error)
Our application is currently under review, and we are still facing issues because we receive a 401 Unauthorized response from the App Store Connect API when using the production environment. Our app integrates with Chargebee for subscription management, and in production, Chargebee is unable to authenticate with the App Store Server API. This results in a 401 Unauthorized error, preventing the user’s subscription from being synced correctly into our system. Interestingly, the same configuration works in the sandbox environment, but fails in production. We’ve tried authenticating using JWTs generated from multiple keys (including App Store Connect API / Team Keys with both Admin and App Manager access, and also In-App Purchase keys), all with the same result — sandbox access works, production does not. Here is our example code for testing with JWT token: const jwt = require('jsonwebtoken'); const fs = require('fs'); const https = require('https'); const config = { keyId: '<key_id>', issuerId: 'issuer_id', bundleId: 'bundle_id', privateKey: fs.readFileSync('path_to_key') }; const { keyId, issuerId, bundleId, privateKey } = config; const now = Math.floor(Date.now() / 1000); const jwtToken = jwt.sign( { iss: issuerId, iat: now, exp: now + 60 * 10, // 10 minutes is fine for test aud: 'appstoreconnect-v1', bid: bundleId }, privateKey, { algorithm: 'ES256', header: { alg: 'ES256', kid: keyId, typ: 'JWT' } } ); console.log('Generated JWT:\n', jwtToken); // prod const originalTransactionId = '<prod_transaction_id>'; const hostname = 'api.storekit.itunes.apple.com'; // sandbox // const originalTransactionId = '<sandbox_transaction_id>'; // const hostname = 'api.storekit-sandbox.itunes.apple.com' const options = { hostname, port: 443, path: `/inApps/v1/history/${originalTransactionId}`, method: 'GET', headers: { Authorization: `Bearer ${jwtToken}`, 'Content-Type': 'application/json', }, }; const callAppStoreConnectApi = async () => { const req = https.request(options, (res) => { console.log(`\nStatus Code: ${res.statusCode}`); let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { console.log('Response Body:\n', data || '[Empty]'); }); }); req.on('error', (e) => { console.error('Request Error:', e); }); req.end(); }; callAppStoreConnectApi(); With this code, we were able to authenticate successfully in the sandbox environment, but not in production. I read in this discussion: https://developer.apple.com/forums/thread/711801 that the issue was resolved once the app was published to the App Store, but I haven’t found any official documentation confirming this. Does anyone know what the issue could be?
3
2
274
Oct ’25
Advanced Commerce API returns 404 not found
We got access into Advanced Commerce API and trying out the server APIs. I was trying out the Migrate a Subscription to Advanced Commerce API but the API was just simply returning not found to me with a generic error code 4040000 (this is undocumented in the API doc). Here is the request body { "descriptors": { "description": "User migrated from old plan to Essential", "displayName": "Essential Plan" }, "items": [ { "sku": "com.company.essential", "description": "A new subscription description after migration", "displayName": "Essential" } ], "requestInfo": { "requestReferenceId": "11aa3174-9aeb-41a6-996d-fc655a793c06" }, "storefront": "HKG", "targetProductId": "com.company.subscription.base", "taxCode": "C003-00-1" } Headers Authorization: Bearer <REQUEST_TOKEN> And the response { "errorCode": 4040000, "errorMessage": "Not found." } Am I doing something wrong or there will be additional configuration needed?
1
0
146
Jul ’25
Xcode 26 beta 3: StoreKit Testing broken
It seems that beta 3 broke StoreKit Testing when running against an iOS 26 simulator device. Specifically, when validating product IDs, the debug console displays messages like the following: [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) [492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue) In addition, the SKProductsResponse (I am using the original StoreKit API), lists all requested product IDs in invalidProductIdentifiers. The products array is empty. StoreKit Testing behaves as expected when Xcode 26 beta 3 is run against an iOS 18.4 simulator device.
4
0
332
Aug ’25
DID_FAIL_TO_RENEW Notification with a null gracePeriodExpiresDate
We are seeking clarification on the behavior of App Store Server Notifications V2. Summary In our production environment, we received a notification with notificationType: DID_FAIL_TO_RENEW and subtype: GRACE_PERIOD. However, the gracePeriodExpiresDate field in the payload was null. We understand this notification indicates that a user's subscription has entered a grace period. The null value for its expiration date is unexpected, and we are looking for an official explanation of this behavior and the correct way to handle it. The Scenario Here are the details of the notification we received: Notification Type: DID_FAIL_TO_RENEW Notification Subtype: GRACE_PERIOD Environment: Production Upon decoding the signedRenewalInfo JWS from the responseBodyV2, we found that the gracePeriodExpiresDate field inside the JWSRenewalInfoDecodedPayload was null. The notificationUUID for this event was in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Our Implementation and its Impact Our backend is designed to ensure service continuity during a grace period, as recommended in the documentation. Current Logic: Receive the DID_FAIL_TO_RENEW / GRACE_PERIOD notification. Extract the gracePeriodExpiresDate. Extend the user's subscription expiration date in our database to match this date. Because the gracePeriodExpiresDate was null in this case, our logic failed, creating a risk of service interruption for the user. Context and Investigation We have performed the following checks: App Store Connect Settings: We have confirmed that Billing Grace Period is enabled for the relevant subscription group. Sandbox Environment: We have been unable to reproduce this scenario in the Sandbox. User Context: We believe the user in this case was experiencing a failed payment when attempting to renew for the first time after a free trial period. Questions To ensure we handle this scenario correctly, we would appreciate clarification on the following points: Conditions for Null: Under what specific conditions does a DID_FAIL_TO_RENEW notification with a GRACE_PERIOD subtype contain a null gracePeriodExpiresDate? Expected Behavior: Is this null value an expected behavior for certain scenarios, such as the first failed renewal after a free trial? Best Practice: If this is an expected behavior, what is the correct way to handle it? How should our backend interpret a null gracePeriodExpiresDate to ensure service continuity for the user?
2
2
179
Jul ’25
Issues with Testing Promotional Offers for Auto-Renewable Subscriptions (StoreKit 2)
We are in the process of implementing promotional offers for auto-renewable subscriptions in our app using StoreKit 2. For testing, we use a sandbox user alongside a new user on our platform. I can successfully purchase an Introductory Offer through the app. Once the user is eligible for a Promotional Offer (based on a previous purchase), we retrieve the Promotional Offer identifier and signature from our backend and display the offer. After initiating the purchase and having the user enter their Sandbox password, the transaction is added to the Payment Queue. However, it fails with the following error: Purchase failed error: invalidOfferSignature Additionally, the error returned is: "Purchase did not return a transaction: Error Domain=ASDServerErrorDomain Code=3903 "Unable to Purchase" UserInfo={NSLocalizedFailureReason=Unable to Purchase, client-environment-type=Sandbox, AMSServerErrorCode=3903, storefront-country-code=IND}" We are using StoreKit 2 APIs for this process. Has anyone encountered this issue when working with StoreKit 2, or found a solution to resolve it?
2
2
424
Mar ’25
Apple Computer, Inc Root certificate expiring on Monday, 10 February 2025
Hi apple team, I'm using Apple Root Certificates from https://www.apple.com/certificateauthority/ for communicating with App Store Server Library for receipt validation API. Apple Computer, Inc Root certificate from the website is Not Valid After: Monday, 10 February 2025 at 01:18:14 Central European Standard Time. When we can expect update of this certificate. Thank you
2
2
376
Feb ’25
How to change promotional image of in-app purchase
My apps stuck on review stage. Reason of it is Guideline 2.3.2 - Performance - Accurate Metadata We noticed that your promotional image to be displayed on the App Store does not sufficiently represent the associated promoted in-app purchase. Specifically, we found the following issue with your promotional image: – Your promotional image is the same as your app’s icon. The problem is i can't change this image. My subscription is still in 'in review' stage and I don't have option like delete image or change image. I replied to the review explaining that I cannot change it as long as the subscription is under review, but I haven’t received any meaningful reply, except that I need to change promotion image to be eligible for further review(which i cant do because i haven't option to change this image). Has anyone had such a problem before?
4
2
1.9k
Oct ’25