We have encountered an issue when verifying transactions using the Get Transaction Info API.
We tested the behavior in both the sandbox and production environments and observed the following results.
When calling the production endpoint:
https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}
with a transactionId generated in the sandbox environment, the API returns HTTP 401 Unauthorized.
However, based on the documentation and common understanding, we expected HTTP 404 Not Found in this case.
Using the same JWT token, if we call the sandbox endpoint:
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId},
we receive HTTP 200 OK with the expected response body.
We have also confirmed that the same behavior occurs when using the Get Transaction History API — it works correctly in the sandbox environment but returns 401 in production.
Could you please confirm whether this behavior (receiving 401 instead of 404) is expected by design, or if it indicates a potential issue?
If this is not the intended behavior, we would appreciate any guidance or instructions to resolve it.
Thank you very much for your technical support.
「Get Transaction Info」APIを用いてトランザクションの検証を行ったところ、以下の問題が発生しました。
サンドボックス環境および本番環境の両方で検証を行い、次の結果を確認しています。
本番環境エンドポイント https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}
に対して サンドボックス環境で生成された transactionId を使用すると、HTTP 401 Unauthorized が返却されます。
(一般的には、この場合 404 Not Found が返る想定であると理解しています。)
同一のJWTトークン を用いて サンドボックス環境のエンドポイント
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId}
を呼び出した場合は、HTTP 200 OK が返り、期待通りのレスポンスボディを受け取ることができています。
また、同様の挙動が Get Transaction History を使用した場合にも発生することを確認しています。
サンドボックス環境では正常に動作しますが、本番環境では401が返却されます。
この挙動(401が返却されること)は仕様上想定されたものか、または何らかの問題によるものかご確認をお願いいたします。
もし想定外の挙動である場合は、解決に向けたご案内をいただけますと幸いです。
本件について、技術的なサポートをお願いいたします。
よろしくお願いいたします。
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
We were facing the same error yesterday and tried many different solutions, but none of them worked. However, today the issue resolved itself and everything is working properly now.
The issue we were facing was:
#1. not getting any product in didReceive method of storekit.
#2. it was working sometimes (i.e. if we try 3 times then 1 time we were getting the product
Hi everybody 👋 ! Just as the title says, for some reason I can no longer enter my Sandbox account credentials, because the section is gone from the developer settings. I tried reenabling the Developer mode, but with no result. Not a lot of information is available on this topic for the latest iOS versions. Can somebody assist, please?
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?
The same store kit configuration file works in iOS and iPadOS, but not in macOS for the same multi platform application project with a single scheme.
Here’s a more detailed write up with the sample code and screenshots. When the simple app is run on
https://www.reddit.com/r/SwiftUI/s/KJsYcggWOa
EDIT: I’m using Xcode 16.4
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
Hello all,
Posting here before I put in a support ticket to see if there are any ideas.
The previous beta issues seem to have been resolved, but now we are having intermittent problems with sandbox purchases. We do not know if this will affect real purchases. This is happening on beta 9 in both public and dev channels for us, most often on iPad Pro 4th. gen (Though idk if that is relevant).
Sometimes running TestFlight builds on iOS 26 beta 9 devices we will have attempts to make sandbox purchases just go into a black hole.
We do not get a "Do you want to buy this" popup, or the credentials screen. It just pauses for a bit in the section of our code that would be akin to:
let result = try await product.purchase( options: [.appAccountToken(accountUUID) ] )
Then wait a couple seconds, and then nothing. The game returns to normal flow as if it was a pending purchase, but nothing more ever happens. We have not been able to get a local debug build to do this, so it's hard for us to tell if it is going into the pending bucket, the userCancelled bucket, or the unverified bucket, etc.
If we take a device in this state and remove the app and reinstall from TestFlight we will get a credentials popup on the first attempt after install to buy, and after putting in our info we will get the " "You've already purchased this In-App Purchase...", but nothing ever his our listener and we return to the broken state.
Has anyone else seen issues like this?
P.S. Our StoreKit logic code is currently widely distributed, so if it was reproducible in the live version on iOS 18 we would know about it.
Thanks, Chris
Topic:
App & System Services
SubTopic:
StoreKit
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.
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
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?
I am experiencing an issue with my iOS app where StoreKit 2 in-app subscriptions are not appearing when using Product.products(for:). I have done all the necessary configurations, but the products still return an empty array ([]).
Here are the details of my setup:
• App ID / Bundle ID: com.tstore.vocabely
• Product IDs:
• com.tstore.vocabely.base
• com.tstore.vocabely.pro
• com.tstore.vocabely.ultimate
• Paid Apps Agreement: Active
• IAP Status in App Store Connect: Ready to Submit / Approved
• Testing Environment:
• Device: iPhone (real device)
• iOS Version: [insert your iOS version]
• Xcode Version: [insert your Xcode version]
• Sandbox Tester Account used
• Code Snippet: Using Product.products(for: productIDs) in StoreKitManager to fetch products
I have verified:
• Product IDs match exactly with App Store Connect
• Paid Apps Agreement is Active
• Using a real device, not a simulator
• Sandbox account is properly signed in
Despite all of this, fetchProducts() still returns an empty array.
Could you please assist me in troubleshooting why my subscriptions are not appearing in the Sandbox environment?
Thank you for your support.
Topic:
App & System Services
SubTopic:
StoreKit
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
New subscriptions have been failing to renew in the sandbox for 3 days. I am seeing multiple posts and comments from people that appear to be experiencing the same issue. But I haven't seen any feedback from Apple representatives.
I really do not want to launch a new app without seeing functioning renewals in the sandbox.
Is there somewhere else we are intended to seek assistance?
I'm getting really frustrated with emails from my App users who believe they've been charged for a free in-app purchase when they haven't.
My App offers many in-app purchases of digital items and I give 4 of these away for free to let users get comfortable with how it works in-app.
Over the last couple of years I've had a steady increase in angry emails from users who accuse me of fraud by charging them for a free item. I couldn't figure out for a while what this was as they would leave a 1 star rating, delete the app and ignore my emails for more information.
Recently I had someone a bit more patient engage and explain it to me.
The purchase for some reason popped up on my notifications right when I bought the [Free Item in my app]. It was from a movie I bought and the bill was delayed.
The timing of that notification is what is misleading users about the free in-app purchase.
Can someone take note of this please and perhaps delay any payment notifications so they aren't sent when the in-app purchase is for FREE?
Thanks!
I’m implementing StoreKit External Purchase Custom Links (EU) and so far it is really painful. I am running into a strange, device-specific issue. On 3/4 devices it works. On one device I never get a token at launch nor before a transaction. isEligible is true everywhere. All devices have versions 18.5 and are located in Germany.
Info.plist: SKExternalPurchaseCustomLinkRegions is set to EU storefront codes and I have followed every step in the documentation: https://developer.apple.com/documentation/storekit/externalpurchasecustomlink
Good device: At launch → ACQUISITION = nil, SERVICES = token present. Works consistently.
Faulty device: At launch → ACQUISITION = nil, SERVICES = nil. Same before transaction. No token ever reaches my server from this device.
isEligible is true on both devices.
Any experts or help on the matter?
Topic:
App & System Services
SubTopic:
StoreKit
I have implemented IAP. The purchases are successful. The refresh receipt is working fine, which then calls the requestDidFinish(_ request: SKRequest) delegate. I'm fetching the receipt url through 'Bundle.main.appStoreReceiptURL'. When I convert the receipt data in base64 string and send it to app store's sandbox api and try to validate the receipt, it fails giving status code : 21002.
Yesterday I noticed that if I purchase a free trial subscription in my app using a sandbox account, when the subscription expires I see a “Billing Problem” message every time I open the app.
”allow purchases & renewals“ setting is ON so this shouldn’t happen.
has anyone else seen this or knows how to resolve?
observed on iOS 18.3.2 & 16.7.10
thanks
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 implemented consumable in-app purchases in an iPhone app using StoreKit's ProductView().
When I tap the payment button in ProductView(), I am taken to the payment screen and once the payment is completed, the desired code appears to be executed, so there doesn't seem to be a problem, but when I tap the payment button in ProductView() again, the desired code is executed without being taken to the payment screen.
So one payment can be used any number of times.
I thought I wrote it exactly according to the reference, but
will it be okay in a production environment?
Is there any code that is necessary?
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)
}
}