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?
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
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.
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
We have some users who have upgraded to iOS 26 beta3. Currently, we observe that when these users make in-app purchases, our code calls [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; method, and we clearly receive the successful removal callback in the delegate method - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray<SKPaymentTransaction *> *)transactions. However, when users click on products with the same productId again, the method - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions still returns information about previously removed transactions, preventing users from making further in-app purchases.
the email say this notification type will be available to you in production beginning May 27, 2025, and you can test it in sandbox now. but i didn't receice this notification yet
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.
I've been trying to make my app available on the App Store for a month now, but I can't because the signatures I created don't appear in the sandbox app. I did all the configuration in the store and in the app. I tested the same code in another app with signatures and it was loaded, but the signature for that specific app doesn't appear. I've tried contacting Apple support, but they can't help me. It almost seems like it's on purpose. I'm treated like crap and they don't even give me an explanation about what's happening.
Can anyone help me?
I'm working on a watchOS app that has an iOS counterpart. There will be a subscription required to unlock functionality and I would like the user to be able to make the purchase on either the iPhone or the watch and have both apps unlock.
The first link below says that StoreKit 2's Transaction.currentEntitlements will not work in this case like it does with extensions. The second link says it might work but doesn't in the sandbox.
What is the best way to make this work? Will it just work in the App Store? Should I use WCSession to send the purchase information from one platform to the other and store it in the keychain? Something else?
Via https://www.revenuecat.com/blog/engineering/ios-in-app-subscription-tutorial-with-storekit-2-and-swift/
"Transaction.currentEntitlements can be used in extensions the same way it was used in the previous steps. This works for extensions like Widgets and Intents. However, an iOS app with a companion watchOS app will not work even though Transaction.currentEntitlements can be executed in it. A companion watch app does not stay updated with the same transaction history as its iOS app because they are separate platforms."
Via https://developer.apple.com/forums/thread/739963
"In TestFlight I was able to confirm that the Watch app and IOS app share in-app purchases. It seems the problems confirming this with Storekit and Sandbox are limits of the testing environments."
Hello
We are developers of a long-running game series and now reports have started to come in
that users who install any of our previous games from the Mac App Store on OS X Sequoia
are shown a popup claiming "The exit(173) API is no longer available". It's actually a lie,
the mechanism is still there, the receipt generation still works and the game still runs afterwards.
But the popup is confusing to users therefore we need to update the code.
Apparently the replacement for the old receipt generation mechanism is AppTransaction which
does not exist for Objective C. We have attempted to use it using the Swift/ObjC interoperability
and failed so far. The problem is that we need to call async methods in AppTransaction and
all our attempts to make this work have failed so far. It seems as the actor/@MainActor concept
is not supported by Swift/ObjC interoperability and without those concepts we don't know how
to pass results from the async context to the callers from ObjC.
The lack of usable information and code online regarding this topic is highly frustrating. Apple
really needs to provide better support for developers if they want us to continue to support
the Mac platform with high quality games and applications on the Mac App Store.
We would appreciate if anyone can cook up a working sample code how to use AppTransaction
in ObjC. Thanks in advance!
Topic:
App & System Services
SubTopic:
StoreKit
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.
With the imminent suspension of SHA-1 on App Store receipts, we desperately need an objective C code sample demonstrating how to calculate the same SDH-256 hash on device to compared with the hash from the App Store receipt.
The forced migration to SHA256 for app store receipts this month mean we have to rewrite our on device receipt validation code. However there is no documentation or objC sample code on how to validate the SHA256 hash from MAS receipts. Thje documentation at
https://developer.apple.com/documentation/technotes/tn3138-handling-app-store-receipt-signing-certificate-changes/ does not give any detail on how to validate SHA256. All my 100+ hours of experimentation and trial and error attempting to create a matching SHA256 has on device have failed.
We desperately need some ObjC Sample code to validate the SHA256 hash on device. Our existing SHA1 code is still working but we expect SHA1 hashes to disappear from MAS Receipts any day now.
Tnanks for any advice !
I have two sandbox users in App Store Connect, as I'm trying to test in-app purchases and Family Sharing. They're set up fine; I can make purchases in the app.
The issue is that the refund request sheet in my app sometimes shows properly and lets me request a refund, but I'd say >80% of the time the sheet just shows "Cannot Connect" with a "Retry" button. Hitting that button doesn't ever result in the sheet showing the refund page.
The only fix for this is to delete the app from the device, and restart the device.
This has to be a joke, right? I need to be able to test this IAP, and the sandbox environment is useless most of the time. Why?
Anyone experiencing this sort of issue?
I want to add in-app purchasing to my app, but I can't figure out what part of my workflow is wrong.
I created a product for my app in iTunes Connect (the product ID is com.mycompany.products.***) and it's in "Ready to submit" status.
I created a sandbox test user for this app.
I connected to iTunes on a real device using the sandbox AppleID.
I went back to XCode and added in-app purchasing to my app.
I turned on developer mode on the real device and logged in as the sandbox user.
I built the app and ran it on a real device (not the simulator).
I tried to get product information (com.mycompany.products.***) but nothing was returned.
In-app purchasing is registered in App Store Connect and the status is "Ready to submit".
The code only retrieves product information in a simple way, so I don't think there's a problem.
inAppPurchase.getProducts(["com.mycompany.products.***"]).then(console.log).catch(console.error);
But it only returns an empty array.
What could be wrong?
Any help would be much appreciated.
Hello,
I’m integrating promotional offers for auto-renewable subscriptions using StoreKit 2.
The offer is displayed correctly, the Apple purchase sheet appears, and I can start the payment flow. The sheet shows the correct discounted price and the end date of the offer. However, after confirming the purchase, an alert appears saying “Unable to Purchase - Contact the developer for more information”
When dismissing the alert, Xcode logs the following:
Purchase did not return a transaction:
Error Domain=ASDServerErrorDomain Code=3902
"No se ha podido realizar la compra"
UserInfo={
NSLocalizedFailureReason=No se ha podido realizar la compra,
client-environment-type=Sandbox,
AMSServerErrorCode=3902,
storefront-country-code=ESP
}
Test environment:
App installed from Xcode on a real iPhone
Logged in with a Sandbox Apple ID
Using StoreKit 2
Promotional offer applied using:
Product.PurchaseOption.promotionalOffer(_:compactJWS:)
On the server side, I generate the promotional offer signature exactly as described in Apple’s documentation:
https://developer.apple.com/documentation/storekit/generating-a-signature-for-promotional-offers
The signature is generated using a Subscription Key
Signed with ECDSA + SHA256
Uses the correct invisible separator (U+2063)
The signature is validated locally using the derived public key and verifies correctly
The sandbox user has had previous subscriptions, which is why this promotional offer is eligible and shown.
Given that:
The offer is displayed correctly
The purchase sheet shows the discounted price and duration
The signature validates locally
The error occurs only after confirming the purchase
My question is:
Is this a known limitation or issue with promotional offers in the Sandbox environment?
Should promotional offers be tested exclusively via TestFlight instead of Sandbox?
Any clarification would be greatly appreciated.
Thank you!
What happens after 12 renewals? Does the subscription expire completely? The next day when I try to manage settings for my sandbox account it says it cannot connect.
I cannot see the status of subscription in:
Settings-> App Store -> Sandbox Account -> Manage
Logging out and logging in of my regular account does not fix that.
When I login with a different non-testflight sandbox account I can finally edit those settings. There are some missing details in documentation explaining testflight sandbox accounts. Do these accounts stop working after 12 auto-renewals? I need more specific details in order to ensure subscriptions will work properly during production.
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
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?
iOS Storekit2 Appstore production environment, some user feedback in app purchase faliure, What our log records is StoreKitError.unknown,please How to solve problem, thanks
Hi. If the app is in landscape only and when the SKStoreProductViewController is presented, the safeArea changes to what looks like a portrait mode safe area. When the SKStoreProductViewController is dismissed, the safeArea does NOT revert back to the original values.
Is there a way to force the safeArea to "reset"? I've submitted some bug tickets through Apple Feedback but I haven't received any response about it.
The below code will pop up the SKStoreProductViewController and if you have a UIView that is constrained to the safe area, then you can visibly notice that the safe area is changed and doesn't go back.
I have tested this on iPhone 14 Pro, iPhone 15, and iPhone 16 Pro and in the Simulators. The incorrect behavior happens on those and probably more.
Thanks.
#import "ViewController.h"
#import &lt;StoreKit/StoreKit.h&gt;
@interface ViewController ()
@property (nonatomic, strong) SKStoreProductViewController *productViewController;
@end
@implementation ViewController
- (IBAction)buttonTapped:(id)sender {
self.productViewController = [[SKStoreProductViewController alloc] init];
NSDictionary *parameters = @{
@"id" : @"6443575749"
};
[self.productViewController loadProductWithParameters:parameters completionBlock:^(BOOL result, NSError * _Nullable error) {
[self presentViewController:self.productViewController animated:YES completion:^{
// presented
// The panel that is constraint to the safe area visibly shows that the safe area is no longer correct.
}];
}];
}
@end