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

iOS misleading users with payment notification timing
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!
2
0
934
Jun ’25
Transitioning a freemium app to StoreKit 2
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.
2
0
253
Mar ’25
RequestReviewAction never triggering rating dialog
Hello, We are having an issue with the RequestReview API and were hoping to get some help. We know that there is no guarantee that the in-app review modal will show and we know that there are 3 circumstances in which it will definitely not appear: if the user has turned off in-app review/ratings in their settings if the user has submitted a review for that app on that device within the last 365 days if the user has been asked for a review >3 times in the last 365 days When testing our implementation, every single one of our testers did not receive the rating modal despite the fact that we had all our testers turn on the app rating setting and that we have never asked for reviews from our app before. So that seems suspicious. While it is possible that something is up with our code (and I have provided some snippets below) we are also concerned that apple maybe is suppressing it for another reason. We really want to go live with our app review code but unfortunately we are not able to get confidence that it will ever appear for the user. Can you please help us understand why this isn't working. The code: We are using the SwiftUI approach to requesting review. Here are some relevant code snippets Important to note, we have a modal that appears when the user is in our list of active, targeted users. If they tap yes on this modal, it should show the in app rate the app system modal. If they tap no, we present them with an airship survey so that they can give feedback. Here is the code for the Yes button action: @Environment(\.requestReview) private var requestReview private var yesButton: some View { Button( action: { dismiss() requestReview() }, label: { Text(Lingua.General.appRateFirstButton) .regularParagraph() .frame(width: 180, height: 35) } ) .customButtonStyle( foregroundColor: .black, backgroundColor: Color(.powderBlue), radius: 36 ) } and this is the logic we use to determine whether we want to show them the modal in the first place. Obviously, a lot of this code leads to deeper areas in our logic and code but to give an idea... private func showAppRateModalIfNeeded() { if preferencesManager.appRateReviewShown == nil, accountManager.userAccount?.permissions.rateTheApp == true { let appReviewModalVC = UIHostingController(rootView: AppReviewModal()) appReviewModalVC.view.backgroundColor = .init(white: 0, alpha: 0.6) appReviewModalVC.modalPresentationStyle = .overFullScreen appReviewModalVC.modalTransitionStyle = .crossDissolve parentVC?.navigationController?.present(appReviewModalVC, animated: true) preferencesManager.appRateReviewShown = true } } When testing in debug, we do find that the modal appears and works as expected. However, on release builds nobody is able to trigger it. Why? Are we doing something wrong here or is Apple just suppressing it. We are thinking about implementing the button taking the user directly into the app store review but we'd prefer to do the lower-friction dialog in-app if we can get it work so the user doesn't get sent out of the app.
2
0
112
3w
Unable to retrieve data from In App Purchase
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.
2
1
71
Jul ’25
No subscriptions showed to buy in release phase
I have auto-renewable subscriptions, and in Xcode everything works fine. It shows a list of subscriptions where I can make a test purchase. But when I send it for review, the review team, as well as TestFlight, simply do not have subscriptions. If the problem was in the code, it would not work in the sandbox as I think. But I think that I configured everything correctly in the subscription settings. The only thing: it shows there for the subscription in appstoreconnect that it is preparing for review, but nothing can be done about it, because it will be solved with the first release of the application. But I do not know where else to look and what to do. The problem is probably not in the code, but I also redirected the subscription config in appstoreconnect a bunch of times. I asked help on review team, no way. Tried to google and chat GPT, no ideas where to find a solution.
2
0
341
Mar ’25
Advanced commerce API - dynamic subscriptions
Hello, We have been approved for the Advanced commerce API and we are trying to implement dynamically created subscriptions via the SubscriptionCreateRequest. We followed the Sending Advanced Commerce API requests from your app (https://developer.apple.com/documentation/storekit/sending-advanced-commerce-api-requests-from-your-app) documentation but we are not able to make it work correctly. We created a generic subscription in the Appstore connect, product ID: com.example.subscription Then in the app we load the subscription: try await Product.products(for: ["com.example.subscription"]) We do the JWS serialization on our backend and then we wrap the jwt and convert it to Data in the app as this: let request = """ { "signatureInfo": { "token": "\(result.signedPayload)" } } """ let advancedCommerceRequestData = Data(request.utf8) Lastly, we apply the purchase options on the generic product as this: try await product.purchase( options: [ Product.PurchaseOption.custom( key: "advancedCommerceData", value: advancedCommerceRequestData ) ] ) It doesn't show any error, but on the payment sheet it shows the data from the generic subscription and not the data that was in the SubscriptionCreateRequest. Here is an example of the generated jwt: eyJraWQiOiI4V0tNQjhLWTI0IiwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiI0MDZkYmEyOS04ZjIyLTQ3ZDUtYWI1Mi1kY2M2NTQ5OTE1Y2MiLCJiaWQiOiJjby5oZXJvaGVyby5IZXJvaGVybyIsImlhdCI6MTc0NjQzNTcxNCwiYXVkIjoiYWR2YW5jZWQtY29tbWVyY2UtYXBpIiwibm9uY2UiOiJhMzY2MGIwMS1kMDcyLTRlZDYtYmYyMS01MWU1Y2U5MDRmYTUiLCJyZXF1ZXN0IjoiZXlKdmNHVnlZWFJwYjI0aU9pSkRVa1ZCVkVWZlUxVkNVME5TU1ZCVVNVOU9JaXdpY21WeGRXVnpkRWx1Wm04aU9uc2ljbVZ4ZFdWemRGSmxabVZ5Wlc1alpVbGtJam9pTVdSaVlqZG1ZbVl0WWpFNE55MDBZMlJoTFRrNE16WXRNalUzTTJZeU1UaGpOekZpSW4wc0luTjBiM0psWm5KdmJuUWlPaUpEV2tVaUxDSjJaWEp6YVc5dUlqb2lNU0lzSW1OMWNuSmxibU41SWpvaVExcExJaXdpZEdGNFEyOWtaU0k2SWxNd01qRXRNRGd0TVNJc0ltUmxjMk55YVhCMGIzSnpJanA3SW1ScGMzQnNZWGxPWVcxbElqb2lVM1ZpYzJOeWFYQjBhVzl1SUZCbGRISWc0b0tzSURVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWxOMVluTmpjbWx3ZEdsdmJpQlFaWFJ5SU9LQ3JDQTFJbjBzSW5CbGNtbHZaQ0k2SWxBeFRTSXNJbWwwWlcxeklqcGJleUprYVhOd2JHRjVUbUZ0WlNJNklsTjFZbk5qY21sd2RHbHZiaUJRWlhSeUlPS0NyQ0ExSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpUZFdKelkzSnBjSFJwYjI0Z1VHVjBjaURpZ3F3Z05TSXNJbkJ5YVdObElqb3hOVEF3TUN3aWMydDFJam9pY1dkeGIzUnNlSEY1WVdGaFlsOTRiV3RvWlhWdGFHWjJhbXhtWDBWVlVqQTFJbjFkZlE9PSJ9.kJ0f_q2A11Mn9OBmvX6SRmtW5P--volFTVcq_Gohs3N51ECfZqS3WHOxOZc7aojq_qiUHGFp_evmHP51f3LzSw
2
0
203
May ’25
Is it possible to generate a new receipt for the same transaction on device?
Hi, all! I am wondering about something about App Receipts. I'm using App Receipt hash to key some information server-side. I'm curious however, if that doesn't actually have a possible flaw. Is it possible to get a new receipt that would yield a different hash for the same transaction? Reinstalling the app, perhaps? Installing the app on a new phone? Basically, I want to make sure this hash is something I can rely on. If the user can get a new hash for the same purchase, that's obviously problematic. Thanks!
2
0
349
Feb ’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
435
Mar ’25
Facing the same error
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
2
3
162
May ’25
StoreKit returning 0 products (IAPError: storekit_no_response)
Hi everyone, I’m facing an issue where StoreKit is returning 0 products from the App Store, even though my auto-renewable subscriptions are approved in App Store Connect. When calling queryProductDetails using Flutter’s in_app_purchase package (which uses StoreKit under the hood), StoreKit reports success but returns an empty list. The logs show the following error: IAPError(code: storekit_no_response, source: app_store, message: "StoreKit: Failed to get response from platform.") InAppPurchase.isAvailable() returns true, but no product details are received. Already verified: • Subscriptions are approved in App Store Connect • Product identifiers in the app match those in App Store Connect exactly • In-App Purchase capability is enabled in Xcode • Paid Applications Agreement, banking, and tax details are active and complete • Using the latest version of the Flutter in_app_purchase package StoreKit should normally return the list of available products in the production environment, but it consistently returns an empty array along with the “storekit_no_response” error. Has anyone else encountered this issue or found any potential causes for StoreKit failing to return products in the production environment? Any insights would be greatly appreciated. Thank you.
2
1
296
Nov ’25
App Store StoreKit web hooks doesn't work o=in the Sandbox env.
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
2
2
145
Nov ’25
Migration Subscription API returns 500 error
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." }
2
0
270
Nov ’25
The currency information obtained from [priceLocale] differs between the TestFlight version and the official release version.
When creating a subscription charging system for an iOS app, I am trying to change the display to yen, dollars, or euros depending on the user's country. I am using [priceLocale] of [SKProduct] in [StoreKit] to obtain currency information linked to the Apple account from the App Store and change the display. The smartphone I am testing on uses an Apple account created in Japan, and the nationality of the App Store is also set to Japan, so I expect the display to be in yen. As a result, the TestFligh version displayed dollars, but the official release version displayed yen. Why doesn't the TestFligh version display yen?
2
0
139
Apr ’25
Unexpected 401 Unauthorized response from production endpoint when using sandbox transactionId with Get Transaction Info API
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が返却されること)は仕様上想定されたものか、または何らかの問題によるものかご確認をお願いいたします。 もし想定外の挙動である場合は、解決に向けたご案内をいただけますと幸いです。 本件について、技術的なサポートをお願いいたします。 よろしくお願いいたします。
2
0
221
Nov ’25
Why does AppStore.requestReview(in:) require NSViewController Parameter?
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.
2
0
142
Oct ’25
Need Objective C code to validate App Store SHA-256 receipt on device
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 !
2
1
433
Jan ’25
StoreKit 2 Fails to Load Subscription Products
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.
2
2
268
Sep ’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
905
Sep ’25