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 Configuration Not Syncing to Xcode
Hello! I am attempting to add Subscriptions to an App that Is already published on the App Store. I cannot get Xcode to actually sync what is in my App Store Connect. When adding the Storekit configuration file, I go through the automatic linking process and select the proper bundleID. The configuration file says 'Synced @ [CurrentTime]' however there are no subscriptions listed in there. I have attempted deleting the file several times, creating a new subscription group. With no success. Do I need to publish the subscriptions without the features first? Upon attempting to write the supporting code that will enable these features within the app, I cannot get Xcode to identify that I have these subscriptions. I have also tried pushing these to TestFlight, still with no success. Thank you.
14
5
1.8k
3w
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.
5
0
484
Apr ’26
Failed to get productIdentifier from StoreKit
I am currently developing an auto-renewal subscription in-app purchase for my app. Currently, the subscription items have already been approved in appStoreConnect, and the .store file is synced with appStoreConnect, so the subscription items are displayed well and the test is also progressing well. However, when I build without using the .store file to perform sandbox testing, the subscription items do not appear and response.invalidProductIdentifiers appears. Is there anything I need to do additionally so that the subscription items can appear in response.products? ps. The bank account item in the contract is in 'processing' status, and the paid app contract status is 'waiting for user information'.
1
0
197
May ’25
in-app purchases
I implemented consumable in-app purchases in an iPhone app using ProductView(). When I tap the payment button in ProductView(), I am taken to the payment screen and once the payment is completed the next code seems to be executed, so there doesn't seem to be a problem, but if I tap the payment button in ProductView() again, the next code is executed without taking me to the payment screen. This means that a single payment can be made multiple times. Can someone help? ProductView(id: "geminiOneMatch") .productViewStyle(.compact) .padding() .onInAppPurchaseCompletion { product, result in if case .success(.success(_)) = result { // 課金が成功した場合の処理 gemini.addOneMatch(amount: 20) popUpVM.geminiOneMatchPopUp = false dataManageVM.generateRespons(locale: locale) } }
0
0
257
Jul ’25
Production review failed because IAP products cannot be loaded
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) } }
2
0
226
Jul ’25
AppDistributor.current never returning
I try to access the AppDistributor.current (using try await) and the property never seem to return nor throw. The code I'm using looks like this: do { print("accessing current") let current = try await AppDistributor.current print("current obtained") switch(current) { case .appStore: return "AppStore" default: return "Unknown" } } catch { return "Exception: \(error)" } But the log only shows the accessing current and never the current obtained. Trying to step in the property starts with some assembly, but at some point, the debugger just never returned. I join a full Swift file of a sample test I'm using: SwiftMarketplaceTests.swift
0
0
155
Jun ’25
integrating In-App Purchases on PWA.
I am trying to launch a PWA as an application on App Store, and got rejected cause of In-App Purchases. Application is about content generation and selling subscriptions for premium content. So thats why needed to implement In-App Purchases. I have created wrapper via PWABuilder. And it is looking great so far, now my question is about How can I implement In-App Purchases in the bundle which is already created. Any suggestions will be appreciated. More clarification:- I have that product running on Web right now, we have integrated Stripe for payment handling for subscriptions. If there is nothing we can do for In-App Purchases then we have to make purchases on website and then user will be able to experience the changes. We tried that, but then we got rejected for showing locked premium content on user's feed. Also can't show any CTA or Subscribe text or button.
0
0
156
Jul ’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
1k
Oct ’25
"In-App Purchases are not allowed" Error Persists After All Troubleshooting Steps
Hello, I am consistently receiving the error message "In-app purchases are not allowed on this device" whenever I try to make an in-app purchase on my iOS device. Despite following all the recommended solutions I could find online, the issue remains unresolved. Here is a list of the steps I have already taken: Checked Screen Time Settings: I navigated to Settings > Screen Time > Content & Privacy Restrictions > iTunes & App Store Purchases. I have confirmed that "In-App Purchases" is set to "Allow." I have also tried toggling this setting off and on again. Signed Out & In of Apple ID: I signed out of my Apple ID via Settings > [Your Name] > Media & Purchases, restarted the device, and then signed back in. Restarted the Device: I have force-restarted my device multiple times. Updated iOS: I have ensured my device is running the latest version of iOS (checked via Settings > General > Software Update). Verified Payment Method: I have confirmed that my payment method on file is valid and up-to-date. Created a New Sandbox Account: I also created a new Sandbox Tester account in App Store Connect and tested with it, but the result was the same. Device Information: Device Model: iPhone 15, iPhone 13 iOS Version: iOS 17.5, iOS 18 Even after performing all of these steps, the problem persists. Has anyone else encountered such a stubborn issue, or does anyone have a different solution I could try? Thank you in advance for your help.
0
0
250
Jul ’25
BUG in implemented StoreKit External Link Account API in a Vue + Capacitor app
I would like to clarify that my app is a Reader APP and a hybrid application built with Vue.js and Capacitor. To comply with Apple’s guidelines, I am not using any third-party SDKs for account management or payments. Instead, I am attempting to use the official StoreKit External Link Account API as required. To achieve this, I created a custom native Capacitor plugin in Swift, which calls the StoreKit 2 classes (SKStoreExternalLinkAccountRequest and SKStoreExternalLinkAccountViewController) to present the required modal before redirecting users to manage their accounts externally. However, I am encountering a technical issue: When building the app in Xcode 16 (with iOS Deployment Target set to 16+), the Swift compiler cannot find the StoreKit 2 classes (SKStoreExternalLinkAccountRequest and SKStoreExternalLinkAccountViewController). I have attached a screenshot showing the error in Xcode. Could you please clarify if there are any additional requirements or steps needed to access these StoreKit 2 APIs in a hybrid (Capacitor/Vue) app? Is there any limitation for hybrid apps, or is there a specific configuration needed in Xcode or the project to make these APIs available? I am committed to fully complying with Apple’s guidelines and want to ensure the best and safest experience for my users. Any guidance or documentation you can provide would be greatly appreciated. my plugin: my app in xcode - build failed I would really appreciate it if someone could help me.
1
0
155
Jun ’25
react native iap not providing the subscription information
I am handling the buy subscription with this function const handleBuySubscription = async (productId) =&gt; { try { await requestSubscription({ sku: productId, }); setLoading(false); } catch (error) { setLoading(false); if (error instanceof PurchaseError) { errorLog({ message: [${error.code}]: ${error.message}, error }); } else { errorLog({ message: "handleBuySubscription", error }); } } }; but the requestSubscription({ sku: productId, }) does not return anything, and it is stuck at await
0
0
92
Aug ’25
Why Non-Consumable product has originalTransactionId?
I try to call Get Transaction Info from App Store Server API, and the transactionId is for a Non-consumable type product, but it is odd that there are so many different transactionId and they have a same originalTransactionId { "bundleId": "${bundleId}", "environment": "Production", "inAppOwnershipType": "PURCHASED", "originalPurchaseDate": 1691220528000, "originalTransactionId": "${originalTransactionId}", "productId": "${productId}", "purchaseDate": 1691220528000, "quantity": 1, "signedDate": 1692590989925, "storefront": "USA", "storefrontId": "143441", "transactionId": "${originalTransactionId}", "transactionReason": "PURCHASE", "type": "Non-Consumable" } the defination of Non-Consumable is can only purchase once for same apple account. But why there would have originalTransactionId?
4
0
1.2k
3w
Consumable in-app purchases
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?
2
0
246
Jul ’25
Purchase Confirmation Letter
Hi all, I've received emails from other apps after making a purchase, with content like: You have purchased {App Name} on {Date & Time} and acknowledged that if you download or use this in-App Purchase within fourteen days of buying it, you will no longer be eligible to cancel this purchase. Do anyone know under what circumstances Apple sends a Purchase Confirmation Letter to the user's email after they purchase our digital products via IAP? Is this something developers can control? Additionally, I've seen pop-up reminders before making a payment in apps, with content similar to the above message. Are these reminders provided by Apple, or can developers create their own guidance to help users avoid accidental purchases? Kindly, Vanto
1
0
99
Jun ’25
Proper way to set up Sandbox iOS for Purchase Testing
I cannot explain how frustrating this is. Not that I want to compare to Android, but in 3 years of QA Testing my app, Android works like a dream, while iOS fights with me EVERY SINGLE STEP OF THE WAY. Hopefully someone here can tell me what I am missing/doing wrong/which god I must appease to get this to work. I have 3 REAL iPhones of varying iOS versions and ages. But they are all proper actual iPhones. We use google accounts at this company, so my primary email is a gmail one. I have created MANY sandbox accounts inside App Store Connect. Currently I have 2, and 2 of my devices (both 14's one of which is a Pro) have my Primary account as the main account for the device. But they both also have a Sandbox account which is simply my main email with a +sandbox in it to make it a new unique email. Here is the problem, nothing works as expected ever. I can install my Staging and Production apps from TestFlight, then I can make a subscription purchase as a customer would and I SHOULD see that subscription in my Sandbox right? That's the point of a Sandbox and TestFlight is it not? But in ALL cases whenever I try to view my 'Sandbox Subscriptions' it tells me I don't have any. Now, sometimes, very occasionally, I get a specific error message inside my app when attempting to make a purchase, this one states something like 'You already have a subscription, please restore it instead...' which makes no sense. Since it clearly states that I have none. But this message has a 'Manage' button to manage my subscriptions, tapping it lads me to a windows which amazing DOES have a subscription in it. But attempting to 'Cancel' it does nothing, just refreshes the screen to be the same. Now I think that this subscription is actually attached to the primary account on the device and NOT the sandbox account. So when this happens I cannot subscribe, I cannot restore, and I cannot manually alter the subscription within iOS. So I am stuck at this point. What am I doing wrong, am I setting this all up in the wrong order? Do I need to install some kind of profile or security cert, do I need to give a pint of blood to Imhotep? What am I missing. I even once sat on the phone for 90 minutes with an Apple Support Rep who took me through it step by step, same result. Also I just noticed that inside 'App Store Connect' when you look at the list of 'Sandbox' accounts there is a column for 'Last Purchase' which is entirely blank, apparently after a year of use I have NEVER purchased on the Sandbox, which is another reason I think my subs are going to the main email, not the sandbox one. I tried using the sandbox email as the main account for the whole device, I can't recall the result but it was worse and didn't work at all. So that's not it. https://developer.apple.com/help/app-store-connect/test-in-app-purchases/create-a-sandbox-apple-account/ The instructions on this page are not detailed enough and were not helpful to me. All I really want to know is how to fully setup a real actual iPhone for TestFlight and Sandbox testing of a app. WHat order do I create accounts, validate emails, attach to devices, login with etc etc etc. Step by step, nothing no matter how mundane missed out. A true idiots guide to making this work for me. Testing this on Android always takes 5 mins. iPhone, I'm lucky if I am done in half a day. Please help and thanks for reading!
1
0
114
Jul ’25
Storekit, how to change and retrieve current user storefront
I've been struggling to work with the Storekit framework and specifically to find the current Storefront used by the user of the app. Context : My app needs to behave differently depending on the country of the user. For me relying on Locale.current.region?.identifier does not seem very reliable, the user can change it really easily. I'm trying to use the Storekit framework like so : if let storefront = await StoreKit.Storefront.current{ return storefront.countryCode } As per Apple's Storekit documentation : Use current to determine a customer's current storefront region and offer in-app products suitable for that region. You maintain your own list of product identifiers and the storefronts in which you make them available. But I just can't find out what I need to change in my current configuration to get another country. The code keeps returning my original storefront (which is France) I've tried login in with a sandbox user defined on another country. Changed all settings on my device to another country. Changed my Apple's account region as described here. Also tried to logout from everything. The only thing that works is setting a local .storekit file as described here and changing the default storefront. Is Xcode overriding the default storefront when building on debug or TestFlight? does anyone know how can I test different storefronts with sandbox users without the local storekit file ? Thank you in advance.
3
2
642
Oct ’25
Updating this code to comply with TN3138: Handling App Store receipt signing certificate changes
My Mac app fails to open for some users with the error: "ABC.app does not support the latest receipt validation requirements." I assume this is due to the update of the App Store receipt signing intermediate certificate with one that uses the SHA-256 algorithm. I cannot reproduce this myself and I have trouble figuring out how to address this issue. Below is the code that decrypts the receipt and verifies its signature. How does this code need to be updated to support the new signing certificate? Thanks a lot in advance! inline static void CheckBundleSignature(void) { NSURL *bundleURL = [[NSBundle mainBundle] bundleURL]; SecStaticCodeRef staticCode = NULL; OSStatus status = SecStaticCodeCreateWithPath((__bridge CFURLRef)bundleURL, kSecCSDefaultFlags, &amp;staticCode); if (status != errSecSuccess) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to validate bundle signature: Create a static code", nil]; } NSString *requirementText = @"anchor apple generic"; SecRequirementRef requirement = NULL; status = SecRequirementCreateWithString((__bridge CFStringRef)requirementText, kSecCSDefaultFlags, &amp;requirement); if (status != errSecSuccess) { if (staticCode) CFRelease(staticCode); [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to validate bundle signature: Create a requirement", nil]; } status = SecStaticCodeCheckValidity(staticCode, kSecCSDefaultFlags, requirement); if (status != errSecSuccess) { if (staticCode) CFRelease(staticCode); if (requirement) CFRelease(requirement); [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to validate bundle signature: Check the static code validity", nil]; } if (staticCode) CFRelease(staticCode); if (requirement) CFRelease(requirement); } static NSData *DecodeReceiptData(NSData *receiptData) { CMSDecoderRef decoder = NULL; SecPolicyRef policyRef = NULL; SecTrustRef trustRef = NULL; @try { OSStatus status = CMSDecoderCreate(&amp;decoder); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Create a decoder", nil]; } status = CMSDecoderUpdateMessage(decoder, receiptData.bytes, receiptData.length); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Update message", nil]; } status = CMSDecoderFinalizeMessage(decoder); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Finalize message", nil]; } NSData *ret = nil; CFDataRef dataRef = NULL; status = CMSDecoderCopyContent(decoder, &amp;dataRef); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Get decrypted content", nil]; } ret = [NSData dataWithData:(__bridge NSData *)dataRef]; CFRelease(dataRef); size_t numSigners; status = CMSDecoderGetNumSigners(decoder, &amp;numSigners); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: Get singer count", nil]; } if (numSigners == 0) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: No signer found", nil]; } policyRef = SecPolicyCreateBasicX509(); CMSSignerStatus signerStatus; OSStatus certVerifyResult; status = CMSDecoderCopySignerStatus(decoder, 0, policyRef, TRUE, &amp;signerStatus, &amp;trustRef, &amp;certVerifyResult); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: Get signer status", nil]; } if (signerStatus != kCMSSignerValid) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: No valid signer", nil]; } return ret; } @catch (NSException *e) { @throw e; } @finally { if (policyRef) CFRelease(policyRef); if (trustRef) CFRelease(trustRef); if (decoder) CFRelease(decoder); } }
1
0
197
Jul ’25
Inconsistent behavior with transactionId and appAccountToken in iOS Sandbox purchases (StoreKit1 & StoreKit2)
Hi, I'm reaching out to report a recurring issue with in-app purchases on iOS that seems to be related to Apple’s transaction handling — not to third-party libraries. In my Flutter application, I use both StoreKit2 and StoreKit1 (for comparison) via different packages, including the official in_app_purchase package. However, in both cases, I’m experiencing unexpected reuse of transactionId and appTransactionId values, even when initiating fresh purchases with unique appAccountToken values. Problem Summary: Purchase Stream Returns Old Purchases When calling buyNonConsumable() with a new product, the purchase stream still returns data for a previously purchased product, despite clearing all Sandbox transactions and using a new applicationUserName for each attempt. Transaction IDs Reused Across Distinct Purchases Even when generating a new UUID for appAccountToken on each purchase, the returned appTransactionId and transactionId are reused — this breaks our server-side logic, which expects these fields to uniquely identify purchases and users. Example Logs: // First purchase { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928154716" } // Second purchase (different user context) { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928429780" } Even when using a different productId, the appTransactionId stays the same. When using StoreKit1, the productId updates properly, but the transactionId still matches the previous one. This behavior also affects App Store Server Notifications (V2): we have observed notifications tied to appAccountTokens from completely different user accounts (based on internal logs), sometimes delayed by days or weeks. I’ve prepared a reproducible example using the official Flutter in_app_purchase sample with minimal changes — you can find it here: Github gist The code is almost identical to the package example. I only added UUID generation for applicationUserName in _getToken(). In the actual app (not in this example), I retrieve the token from an API. Additional Observations from the Community: We’ve also found similar issues reported in other frameworks and languages. For instance, a developer using react-native-iap observed that App Store Server Notifications in TestFlight were tied to previously deleted users, even after signing up with a new user account and generating a new appAccountToken. Details here: User A deleted → User B signs up → receives upgrade event with User A’s token Notification uses appAccountToken from old account, not the new one This strengthens the suspicion that the issue may be related to how Apple associates transactions with Apple IDs in test environments. Questions: Is it expected for transactionId or appTransactionId to persist across purchases within the same Apple ID, even for different user contexts (e.g., separate logins in the same app)? Is there any official recommendation for avoiding this kind of data reuse in Sandbox or TestFlight environments? Should I expect appAccountToken in server notifications to always match the latest value provided during the purchase? Thank you in advance for your assistance. I would appreciate any clarification or advice regarding this issue, as it impacts production logic that relies on these identifiers being unique and consistent.
1
1
238
Jun ’25
StoreKit2.Transaction.updates Returning Large Amounts of Historical Transactions, Causing Verification Traffic Surge
Over the past two days, we've observed an unusual spike in requests from some iOS users to our server endpoint responsible for verifying App Store purchase receipts. After sampling and analyzing the data, we found that the cause is related to the behavior of StoreKit2.Transaction.updates. Specifically, when listening for transaction updates, the system returns a large number of historical transactions — some dating back as far as one year. These callbacks are interpreted as "new" transactions, which in turn trigger repeated calls to Apple’s receipt verification servers, leading to an abnormal surge in traffic and putting pressure on our backend services. This behavior is ongoing and is something we've never encountered in our previous experience. It appears to be outside of expected behavior, and we suspect it may be due to some kind of abnormality or unintended usage scenario. We would appreciate guidance on the following: Is this a known behavior or issue with StoreKit2? Are there specific device states or conditions that could cause the system to emit historical transactions in bulk? Are there any recommended practices for mitigating or filtering such transaction floods? We have attached logs for reference. Any help identifying the root cause or suggestions for investigation would be greatly appreciated. 2025-07-24 12:39:58.594 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713445834000, "originalTransactionId" : "430001791317037", "purchaseDate" : 1713445834000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", } 2025-07-24 12:39:58.594 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "c4f79de2-a027-4b34-b777-6851f83f7e64", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713445849000, "originalTransactionId" : "430001791317270", "purchaseDate" : 1713445849000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001791317270", } 2025-07-24 12:39:58.594 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "02f305d7-0b2d-4d55-b427-192e61b99024", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713511999000, "originalTransactionId" : "430001792218708", "purchaseDate" : 1713511999000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001792218708", } 2025-07-24 12:39:58.598 +0400 [INFO] [MKPaymentService:23]: [XLPay] listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "5ca85907-1ab6-4160-828e-8ab6d3574d6f", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713512034000, "originalTransactionId" : "430001792219189", "purchaseDate" : 1713512034000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001792219189", } 2025-07-24 12:39:58.599 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "04869b50-b181-4b69-b4ff-025175e9cf14", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713512049000, "originalTransactionId" : "430001792219440", "purchaseDate" : 1713512049000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001792219440", }
1
1
177
Sep ’25
StoreKit Configuration Not Syncing to Xcode
Hello! I am attempting to add Subscriptions to an App that Is already published on the App Store. I cannot get Xcode to actually sync what is in my App Store Connect. When adding the Storekit configuration file, I go through the automatic linking process and select the proper bundleID. The configuration file says 'Synced @ [CurrentTime]' however there are no subscriptions listed in there. I have attempted deleting the file several times, creating a new subscription group. With no success. Do I need to publish the subscriptions without the features first? Upon attempting to write the supporting code that will enable these features within the app, I cannot get Xcode to identify that I have these subscriptions. I have also tried pushing these to TestFlight, still with no success. Thank you.
Replies
14
Boosts
5
Views
1.8k
Activity
3w
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.
Replies
5
Boosts
0
Views
484
Activity
Apr ’26
Failed to get productIdentifier from StoreKit
I am currently developing an auto-renewal subscription in-app purchase for my app. Currently, the subscription items have already been approved in appStoreConnect, and the .store file is synced with appStoreConnect, so the subscription items are displayed well and the test is also progressing well. However, when I build without using the .store file to perform sandbox testing, the subscription items do not appear and response.invalidProductIdentifiers appears. Is there anything I need to do additionally so that the subscription items can appear in response.products? ps. The bank account item in the contract is in 'processing' status, and the paid app contract status is 'waiting for user information'.
Replies
1
Boosts
0
Views
197
Activity
May ’25
in-app purchases
I implemented consumable in-app purchases in an iPhone app using ProductView(). When I tap the payment button in ProductView(), I am taken to the payment screen and once the payment is completed the next code seems to be executed, so there doesn't seem to be a problem, but if I tap the payment button in ProductView() again, the next code is executed without taking me to the payment screen. This means that a single payment can be made multiple times. Can someone help? ProductView(id: "geminiOneMatch") .productViewStyle(.compact) .padding() .onInAppPurchaseCompletion { product, result in if case .success(.success(_)) = result { // 課金が成功した場合の処理 gemini.addOneMatch(amount: 20) popUpVM.geminiOneMatchPopUp = false dataManageVM.generateRespons(locale: locale) } }
Replies
0
Boosts
0
Views
257
Activity
Jul ’25
App Store Localization is Rejected
Why is this rejection coming? I don't understand.I can't see anything in my mailbox.
Replies
6
Boosts
0
Views
2.1k
Activity
Apr ’26
Production review failed because IAP products cannot be loaded
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) } }
Replies
2
Boosts
0
Views
226
Activity
Jul ’25
AppDistributor.current never returning
I try to access the AppDistributor.current (using try await) and the property never seem to return nor throw. The code I'm using looks like this: do { print("accessing current") let current = try await AppDistributor.current print("current obtained") switch(current) { case .appStore: return "AppStore" default: return "Unknown" } } catch { return "Exception: \(error)" } But the log only shows the accessing current and never the current obtained. Trying to step in the property starts with some assembly, but at some point, the debugger just never returned. I join a full Swift file of a sample test I'm using: SwiftMarketplaceTests.swift
Replies
0
Boosts
0
Views
155
Activity
Jun ’25
integrating In-App Purchases on PWA.
I am trying to launch a PWA as an application on App Store, and got rejected cause of In-App Purchases. Application is about content generation and selling subscriptions for premium content. So thats why needed to implement In-App Purchases. I have created wrapper via PWABuilder. And it is looking great so far, now my question is about How can I implement In-App Purchases in the bundle which is already created. Any suggestions will be appreciated. More clarification:- I have that product running on Web right now, we have integrated Stripe for payment handling for subscriptions. If there is nothing we can do for In-App Purchases then we have to make purchases on website and then user will be able to experience the changes. We tried that, but then we got rejected for showing locked premium content on user's feed. Also can't show any CTA or Subscribe text or button.
Replies
0
Boosts
0
Views
156
Activity
Jul ’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!
Replies
5
Boosts
2
Views
1k
Activity
Oct ’25
"In-App Purchases are not allowed" Error Persists After All Troubleshooting Steps
Hello, I am consistently receiving the error message "In-app purchases are not allowed on this device" whenever I try to make an in-app purchase on my iOS device. Despite following all the recommended solutions I could find online, the issue remains unresolved. Here is a list of the steps I have already taken: Checked Screen Time Settings: I navigated to Settings > Screen Time > Content & Privacy Restrictions > iTunes & App Store Purchases. I have confirmed that "In-App Purchases" is set to "Allow." I have also tried toggling this setting off and on again. Signed Out & In of Apple ID: I signed out of my Apple ID via Settings > [Your Name] > Media & Purchases, restarted the device, and then signed back in. Restarted the Device: I have force-restarted my device multiple times. Updated iOS: I have ensured my device is running the latest version of iOS (checked via Settings > General > Software Update). Verified Payment Method: I have confirmed that my payment method on file is valid and up-to-date. Created a New Sandbox Account: I also created a new Sandbox Tester account in App Store Connect and tested with it, but the result was the same. Device Information: Device Model: iPhone 15, iPhone 13 iOS Version: iOS 17.5, iOS 18 Even after performing all of these steps, the problem persists. Has anyone else encountered such a stubborn issue, or does anyone have a different solution I could try? Thank you in advance for your help.
Replies
0
Boosts
0
Views
250
Activity
Jul ’25
BUG in implemented StoreKit External Link Account API in a Vue + Capacitor app
I would like to clarify that my app is a Reader APP and a hybrid application built with Vue.js and Capacitor. To comply with Apple’s guidelines, I am not using any third-party SDKs for account management or payments. Instead, I am attempting to use the official StoreKit External Link Account API as required. To achieve this, I created a custom native Capacitor plugin in Swift, which calls the StoreKit 2 classes (SKStoreExternalLinkAccountRequest and SKStoreExternalLinkAccountViewController) to present the required modal before redirecting users to manage their accounts externally. However, I am encountering a technical issue: When building the app in Xcode 16 (with iOS Deployment Target set to 16+), the Swift compiler cannot find the StoreKit 2 classes (SKStoreExternalLinkAccountRequest and SKStoreExternalLinkAccountViewController). I have attached a screenshot showing the error in Xcode. Could you please clarify if there are any additional requirements or steps needed to access these StoreKit 2 APIs in a hybrid (Capacitor/Vue) app? Is there any limitation for hybrid apps, or is there a specific configuration needed in Xcode or the project to make these APIs available? I am committed to fully complying with Apple’s guidelines and want to ensure the best and safest experience for my users. Any guidance or documentation you can provide would be greatly appreciated. my plugin: my app in xcode - build failed I would really appreciate it if someone could help me.
Replies
1
Boosts
0
Views
155
Activity
Jun ’25
react native iap not providing the subscription information
I am handling the buy subscription with this function const handleBuySubscription = async (productId) =&gt; { try { await requestSubscription({ sku: productId, }); setLoading(false); } catch (error) { setLoading(false); if (error instanceof PurchaseError) { errorLog({ message: [${error.code}]: ${error.message}, error }); } else { errorLog({ message: "handleBuySubscription", error }); } } }; but the requestSubscription({ sku: productId, }) does not return anything, and it is stuck at await
Replies
0
Boosts
0
Views
92
Activity
Aug ’25
Why Non-Consumable product has originalTransactionId?
I try to call Get Transaction Info from App Store Server API, and the transactionId is for a Non-consumable type product, but it is odd that there are so many different transactionId and they have a same originalTransactionId { "bundleId": "${bundleId}", "environment": "Production", "inAppOwnershipType": "PURCHASED", "originalPurchaseDate": 1691220528000, "originalTransactionId": "${originalTransactionId}", "productId": "${productId}", "purchaseDate": 1691220528000, "quantity": 1, "signedDate": 1692590989925, "storefront": "USA", "storefrontId": "143441", "transactionId": "${originalTransactionId}", "transactionReason": "PURCHASE", "type": "Non-Consumable" } the defination of Non-Consumable is can only purchase once for same apple account. But why there would have originalTransactionId?
Replies
4
Boosts
0
Views
1.2k
Activity
3w
Consumable in-app purchases
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?
Replies
2
Boosts
0
Views
246
Activity
Jul ’25
Purchase Confirmation Letter
Hi all, I've received emails from other apps after making a purchase, with content like: You have purchased {App Name} on {Date & Time} and acknowledged that if you download or use this in-App Purchase within fourteen days of buying it, you will no longer be eligible to cancel this purchase. Do anyone know under what circumstances Apple sends a Purchase Confirmation Letter to the user's email after they purchase our digital products via IAP? Is this something developers can control? Additionally, I've seen pop-up reminders before making a payment in apps, with content similar to the above message. Are these reminders provided by Apple, or can developers create their own guidance to help users avoid accidental purchases? Kindly, Vanto
Replies
1
Boosts
0
Views
99
Activity
Jun ’25
Proper way to set up Sandbox iOS for Purchase Testing
I cannot explain how frustrating this is. Not that I want to compare to Android, but in 3 years of QA Testing my app, Android works like a dream, while iOS fights with me EVERY SINGLE STEP OF THE WAY. Hopefully someone here can tell me what I am missing/doing wrong/which god I must appease to get this to work. I have 3 REAL iPhones of varying iOS versions and ages. But they are all proper actual iPhones. We use google accounts at this company, so my primary email is a gmail one. I have created MANY sandbox accounts inside App Store Connect. Currently I have 2, and 2 of my devices (both 14's one of which is a Pro) have my Primary account as the main account for the device. But they both also have a Sandbox account which is simply my main email with a +sandbox in it to make it a new unique email. Here is the problem, nothing works as expected ever. I can install my Staging and Production apps from TestFlight, then I can make a subscription purchase as a customer would and I SHOULD see that subscription in my Sandbox right? That's the point of a Sandbox and TestFlight is it not? But in ALL cases whenever I try to view my 'Sandbox Subscriptions' it tells me I don't have any. Now, sometimes, very occasionally, I get a specific error message inside my app when attempting to make a purchase, this one states something like 'You already have a subscription, please restore it instead...' which makes no sense. Since it clearly states that I have none. But this message has a 'Manage' button to manage my subscriptions, tapping it lads me to a windows which amazing DOES have a subscription in it. But attempting to 'Cancel' it does nothing, just refreshes the screen to be the same. Now I think that this subscription is actually attached to the primary account on the device and NOT the sandbox account. So when this happens I cannot subscribe, I cannot restore, and I cannot manually alter the subscription within iOS. So I am stuck at this point. What am I doing wrong, am I setting this all up in the wrong order? Do I need to install some kind of profile or security cert, do I need to give a pint of blood to Imhotep? What am I missing. I even once sat on the phone for 90 minutes with an Apple Support Rep who took me through it step by step, same result. Also I just noticed that inside 'App Store Connect' when you look at the list of 'Sandbox' accounts there is a column for 'Last Purchase' which is entirely blank, apparently after a year of use I have NEVER purchased on the Sandbox, which is another reason I think my subs are going to the main email, not the sandbox one. I tried using the sandbox email as the main account for the whole device, I can't recall the result but it was worse and didn't work at all. So that's not it. https://developer.apple.com/help/app-store-connect/test-in-app-purchases/create-a-sandbox-apple-account/ The instructions on this page are not detailed enough and were not helpful to me. All I really want to know is how to fully setup a real actual iPhone for TestFlight and Sandbox testing of a app. WHat order do I create accounts, validate emails, attach to devices, login with etc etc etc. Step by step, nothing no matter how mundane missed out. A true idiots guide to making this work for me. Testing this on Android always takes 5 mins. iPhone, I'm lucky if I am done in half a day. Please help and thanks for reading!
Replies
1
Boosts
0
Views
114
Activity
Jul ’25
Storekit, how to change and retrieve current user storefront
I've been struggling to work with the Storekit framework and specifically to find the current Storefront used by the user of the app. Context : My app needs to behave differently depending on the country of the user. For me relying on Locale.current.region?.identifier does not seem very reliable, the user can change it really easily. I'm trying to use the Storekit framework like so : if let storefront = await StoreKit.Storefront.current{ return storefront.countryCode } As per Apple's Storekit documentation : Use current to determine a customer's current storefront region and offer in-app products suitable for that region. You maintain your own list of product identifiers and the storefronts in which you make them available. But I just can't find out what I need to change in my current configuration to get another country. The code keeps returning my original storefront (which is France) I've tried login in with a sandbox user defined on another country. Changed all settings on my device to another country. Changed my Apple's account region as described here. Also tried to logout from everything. The only thing that works is setting a local .storekit file as described here and changing the default storefront. Is Xcode overriding the default storefront when building on debug or TestFlight? does anyone know how can I test different storefronts with sandbox users without the local storekit file ? Thank you in advance.
Replies
3
Boosts
2
Views
642
Activity
Oct ’25
Updating this code to comply with TN3138: Handling App Store receipt signing certificate changes
My Mac app fails to open for some users with the error: "ABC.app does not support the latest receipt validation requirements." I assume this is due to the update of the App Store receipt signing intermediate certificate with one that uses the SHA-256 algorithm. I cannot reproduce this myself and I have trouble figuring out how to address this issue. Below is the code that decrypts the receipt and verifies its signature. How does this code need to be updated to support the new signing certificate? Thanks a lot in advance! inline static void CheckBundleSignature(void) { NSURL *bundleURL = [[NSBundle mainBundle] bundleURL]; SecStaticCodeRef staticCode = NULL; OSStatus status = SecStaticCodeCreateWithPath((__bridge CFURLRef)bundleURL, kSecCSDefaultFlags, &amp;staticCode); if (status != errSecSuccess) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to validate bundle signature: Create a static code", nil]; } NSString *requirementText = @"anchor apple generic"; SecRequirementRef requirement = NULL; status = SecRequirementCreateWithString((__bridge CFStringRef)requirementText, kSecCSDefaultFlags, &amp;requirement); if (status != errSecSuccess) { if (staticCode) CFRelease(staticCode); [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to validate bundle signature: Create a requirement", nil]; } status = SecStaticCodeCheckValidity(staticCode, kSecCSDefaultFlags, requirement); if (status != errSecSuccess) { if (staticCode) CFRelease(staticCode); if (requirement) CFRelease(requirement); [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to validate bundle signature: Check the static code validity", nil]; } if (staticCode) CFRelease(staticCode); if (requirement) CFRelease(requirement); } static NSData *DecodeReceiptData(NSData *receiptData) { CMSDecoderRef decoder = NULL; SecPolicyRef policyRef = NULL; SecTrustRef trustRef = NULL; @try { OSStatus status = CMSDecoderCreate(&amp;decoder); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Create a decoder", nil]; } status = CMSDecoderUpdateMessage(decoder, receiptData.bytes, receiptData.length); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Update message", nil]; } status = CMSDecoderFinalizeMessage(decoder); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Finalize message", nil]; } NSData *ret = nil; CFDataRef dataRef = NULL; status = CMSDecoderCopyContent(decoder, &amp;dataRef); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to decode receipt data: Get decrypted content", nil]; } ret = [NSData dataWithData:(__bridge NSData *)dataRef]; CFRelease(dataRef); size_t numSigners; status = CMSDecoderGetNumSigners(decoder, &amp;numSigners); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: Get singer count", nil]; } if (numSigners == 0) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: No signer found", nil]; } policyRef = SecPolicyCreateBasicX509(); CMSSignerStatus signerStatus; OSStatus certVerifyResult; status = CMSDecoderCopySignerStatus(decoder, 0, policyRef, TRUE, &amp;signerStatus, &amp;trustRef, &amp;certVerifyResult); if (status) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: Get signer status", nil]; } if (signerStatus != kCMSSignerValid) { [NSException raise:@"MacAppStore Receipt Validation Error" format:@"Failed to check receipt signature: No valid signer", nil]; } return ret; } @catch (NSException *e) { @throw e; } @finally { if (policyRef) CFRelease(policyRef); if (trustRef) CFRelease(trustRef); if (decoder) CFRelease(decoder); } }
Replies
1
Boosts
0
Views
197
Activity
Jul ’25
Inconsistent behavior with transactionId and appAccountToken in iOS Sandbox purchases (StoreKit1 & StoreKit2)
Hi, I'm reaching out to report a recurring issue with in-app purchases on iOS that seems to be related to Apple’s transaction handling — not to third-party libraries. In my Flutter application, I use both StoreKit2 and StoreKit1 (for comparison) via different packages, including the official in_app_purchase package. However, in both cases, I’m experiencing unexpected reuse of transactionId and appTransactionId values, even when initiating fresh purchases with unique appAccountToken values. Problem Summary: Purchase Stream Returns Old Purchases When calling buyNonConsumable() with a new product, the purchase stream still returns data for a previously purchased product, despite clearing all Sandbox transactions and using a new applicationUserName for each attempt. Transaction IDs Reused Across Distinct Purchases Even when generating a new UUID for appAccountToken on each purchase, the returned appTransactionId and transactionId are reused — this breaks our server-side logic, which expects these fields to uniquely identify purchases and users. Example Logs: // First purchase { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928154716" } // Second purchase (different user context) { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928429780" } Even when using a different productId, the appTransactionId stays the same. When using StoreKit1, the productId updates properly, but the transactionId still matches the previous one. This behavior also affects App Store Server Notifications (V2): we have observed notifications tied to appAccountTokens from completely different user accounts (based on internal logs), sometimes delayed by days or weeks. I’ve prepared a reproducible example using the official Flutter in_app_purchase sample with minimal changes — you can find it here: Github gist The code is almost identical to the package example. I only added UUID generation for applicationUserName in _getToken(). In the actual app (not in this example), I retrieve the token from an API. Additional Observations from the Community: We’ve also found similar issues reported in other frameworks and languages. For instance, a developer using react-native-iap observed that App Store Server Notifications in TestFlight were tied to previously deleted users, even after signing up with a new user account and generating a new appAccountToken. Details here: User A deleted → User B signs up → receives upgrade event with User A’s token Notification uses appAccountToken from old account, not the new one This strengthens the suspicion that the issue may be related to how Apple associates transactions with Apple IDs in test environments. Questions: Is it expected for transactionId or appTransactionId to persist across purchases within the same Apple ID, even for different user contexts (e.g., separate logins in the same app)? Is there any official recommendation for avoiding this kind of data reuse in Sandbox or TestFlight environments? Should I expect appAccountToken in server notifications to always match the latest value provided during the purchase? Thank you in advance for your assistance. I would appreciate any clarification or advice regarding this issue, as it impacts production logic that relies on these identifiers being unique and consistent.
Replies
1
Boosts
1
Views
238
Activity
Jun ’25
StoreKit2.Transaction.updates Returning Large Amounts of Historical Transactions, Causing Verification Traffic Surge
Over the past two days, we've observed an unusual spike in requests from some iOS users to our server endpoint responsible for verifying App Store purchase receipts. After sampling and analyzing the data, we found that the cause is related to the behavior of StoreKit2.Transaction.updates. Specifically, when listening for transaction updates, the system returns a large number of historical transactions — some dating back as far as one year. These callbacks are interpreted as "new" transactions, which in turn trigger repeated calls to Apple’s receipt verification servers, leading to an abnormal surge in traffic and putting pressure on our backend services. This behavior is ongoing and is something we've never encountered in our previous experience. It appears to be outside of expected behavior, and we suspect it may be due to some kind of abnormality or unintended usage scenario. We would appreciate guidance on the following: Is this a known behavior or issue with StoreKit2? Are there specific device states or conditions that could cause the system to emit historical transactions in bulk? Are there any recommended practices for mitigating or filtering such transaction floods? We have attached logs for reference. Any help identifying the root cause or suggestions for investigation would be greatly appreciated. 2025-07-24 12:39:58.594 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713445834000, "originalTransactionId" : "430001791317037", "purchaseDate" : 1713445834000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", } 2025-07-24 12:39:58.594 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "c4f79de2-a027-4b34-b777-6851f83f7e64", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713445849000, "originalTransactionId" : "430001791317270", "purchaseDate" : 1713445849000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001791317270", } 2025-07-24 12:39:58.594 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "02f305d7-0b2d-4d55-b427-192e61b99024", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713511999000, "originalTransactionId" : "430001792218708", "purchaseDate" : 1713511999000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001792218708", } 2025-07-24 12:39:58.598 +0400 [INFO] [MKPaymentService:23]: [XLPay] listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "5ca85907-1ab6-4160-828e-8ab6d3574d6f", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713512034000, "originalTransactionId" : "430001792219189", "purchaseDate" : 1713512034000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001792219189", } 2025-07-24 12:39:58.599 +0400 listenForTransactions :{ "appTransactionId" : "704289572311513293", "deviceVerificationNonce" : "04869b50-b181-4b69-b4ff-025175e9cf14", "environment" : "Production", "inAppOwnershipType" : "PURCHASED", "originalPurchaseDate" : 1713512049000, "originalTransactionId" : "430001792219440", "purchaseDate" : 1713512049000, "quantity" : 1, "signedDate" : 1753346396278, "storefrontId" : "143481", "transactionId" : "430001792219440", }
Replies
1
Boosts
1
Views
177
Activity
Sep ’25