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

Can StoreKit products be observed with ObservableObject? Can I get notified when a users subscription has lapsed without polling Transaction.currentEntitlements?
I have an auto-renewable subscription. I have two methods helping me keep track of when they are expired @MainActor public func isPurchased(product: Product) async -> Bool { guard let state = await product.currentEntitlement else { return false } switch state { case .unverified(_, _): return false case .verified(let transaction): await transaction.finish() return isTransactionRelevant(transaction) } } private func isTransactionRelevant(_ transaction: Transaction) -> Bool { if let revocationDate = transaction.revocationDate { logger.error("Transaction verification failed: Transaction was revoked on \(revocationDate)") return false } if let expirationDate = transaction.expirationDate, expirationDate < Date() { logger.error("Transaction verification failed: Transaction expired on \(expirationDate)") return false } if transaction.isUpgraded { logger.error("Transaction verification failed: Transaction was upgraded") return false } logger.info("Transaction verification succeeded") return true } I also have this that I can call to get the latest state of purchases @MainActor public func updateStoreKitSubscriptionStatus() async { var currentProductsPurchased: [Product] = [] for await result in Transaction.currentEntitlements { if case .verified(let transaction) = result { if isTransactionRelevant(transaction) { if let product = products.first( where: { $0.id == transaction.productID }) { currentProductsPurchased.append(product) } } await transaction.finish() } } self.purchasedProducts = currentProductsPurchased } Right now when a subscription expires the user needs to manually do some action that triggers updateStoreKitSubscriptionStatus() as it appears that expirations do not come through in Transaction.updates. I am surprised there does not seem to be a better way. Does StoreKit not notify you somewhere that an auto-renewable subscription has expired? Can you observe it in an ObservableObject? Or do I need to just frequently poll Transaction.currentEntitlements even if I dont expect frequent updates?
0
0
117
Jul ’25
Sandbox refund testing "Cannot connect" error
I'm running into a refund issue when testing in-app purchases in sandbox mode. I'm able to successfully purchase items but roughly 1 out of 3 times, when the refund window pops up, it says it cannot connect even though our webhook servers can verify the transaction id sent from apple. My internet connection is good and I've varied the wait period from purchase to refund from 15mins to 2 hours. Any help would be greatly appreciated.
1
0
164
Sep ’25
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
195
May ’25
Switching App Store Server Notifications from V1 → V2 — what happens to existing subscriptions?
Hello — quick question about App Store Server Notifications migration. We have a live app using Production V1 notifications for recurring in-app subscriptions. We plan to switch the Production webhook to V2. After the switch: Will notifications for existing subscriptions be delivered in V1 format, V2 format, or will it depend (e.g., queued V1 retries vs new V2 deliveries)? If V1 retries are queued, how long should we expect overlap/retries to continue? Any recommended cutover best practices (support both formats, revert process, etc.)? Happy to share additional details. Thanks.
1
0
207
Dec ’25
[StoreKit1] IAP Works in TestFlight but Fails During App Review (2.1 Rejection)
Hello Apple Developer Team, We're experiencing consistent IAP approval rejections under Guideline 2.1, despite successful TestFlight verification. Here's our detailed situation: Environment StoreKit 1 implementation Tested on iOS 18.5 or 18.6 devices Sandbox environment works perfectly Verification Steps Taken ✅ Confirmed all Product IDs match App Store Connect exactly ✅ Validated 10+ successful TestFlight transactions (attached screenshot samples) ✅ Verified banking/tax agreements are active Objective-C Code (StoreKit1 Implementation) - (void)buyProductId:(NSString *)pid AndSetGameOrderID:(NSString *)orderID{ if([SKPaymentQueue canMakePayments]){ if (!hasAddObserver) { [[SKPaymentQueue defaultQueue] addTransactionObserver:_neo]; hasAddObserver = YES; } self.neoOrderID = orderID; [[NSUserDefaults standardUserDefaults] setValue:orderID forKey:Pay_OrderId_Key]; self.productID = pid; NSArray * product = [[NSArray alloc]initWithObjects:self.productID, nil]; NSSet * nsset = [NSSet setWithArray:product]; SKProductsRequest * request = [[SKProductsRequest alloc]initWithProductIdentifiers:nsset]; request.delegate = self; [request start]; }else{ NSString * Err = @"Pembelian tidak diizinkan. Silakan aktifkan perizinan di pengaturan"; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); return; } } - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSArray * product = response.products; if ([product count] == 0) { [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; NSString * Err = [NSString stringWithFormat:@"Err = 01, Item tidak ditemukan %@",self.productID]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); return; } SKProduct * p = nil; for (SKProduct * pro in product) { if ([pro.productIdentifier isEqualToString:self.productID]){ p = pro; }else{ [request cancel]; [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; NSString * Err = [NSString stringWithFormat:@"Err = 02, %@",self.productID]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); return; } } SKMutablePayment * mPayment = [SKMutablePayment paymentWithProduct:p]; mPayment.applicationUsername = [NSString stringWithFormat:@"%@",self.neoOrderID]; if(!hasAddObserver){ [[SKPaymentQueue defaultQueue] addTransactionObserver:_neo]; hasAddObserver = YES; } [[SKPaymentQueue defaultQueue] addPayment:mPayment]; } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{ [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; NSString * Err = [NSString stringWithFormat:@"Err = 0%ld %@", (long)error.code, self.productID]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); } - (void)requestDidFinish:(SKRequest *)request{ } - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{ for(SKPaymentTransaction *tran in transaction){ if (SKPaymentTransactionStatePurchased == tran.transactionState){ [self completeTransaction:tran]; }else if(SKPaymentTransactionStateFailed == tran.transactionState){ [self failedTransaction:tran]; } } } - (void)failedTransaction: (SKPaymentTransaction *)transaction { NSString * detail = [NSString stringWithFormat:@"%ld",(long)transaction.error.code]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [detail UTF8String]); [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void)completeTransaction:(SKPaymentTransaction *)transaction{ NSMutableDictionary * mdic = [NSMutableDictionary dictionary]; NSString * productIdentifier = transaction.payment.productIdentifier; NSData * _recep = nil; NSString * _receipt = @""; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { _recep = transaction.transactionReceipt; _receipt = [[NSString alloc]initWithData:_recep encoding:NSUTF8StringEncoding]; } else { _recep = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; _receipt = [_recep base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; } NSString * gameOrderid = [transaction payment].applicationUsername; if (gameOrderid == nil) { gameOrderid = [[NSUserDefaults standardUserDefaults] objectForKey:Pay_OrderId_Key]; } if(_receipt != nil && gameOrderid != nil){ mdic[@"orderid"] = gameOrderid; mdic[@"productid"] = productIdentifier; mdic[@"receipt"] = _receipt; }else{ [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; return; } NSData * data = [NSJSONSerialization dataWithJSONObject:mdic options:kNilOptions error:nil]; NSString * jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; if (hasAddObserver) { [[SKPaymentQueue defaultQueue] removeTransactionObserver:_neo]; hasAddObserver = NO; } // UnitySendMessage("GameManager", "IAPPurchaseSuecess", [jsonString UTF8String]); [self verifyReceipt:_recep completion:^(BOOL success, NSDictionary *response) { if (success) { NSLog(@"verify success"); // [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; [self verifySuecessDelTransactions]; } }]; } - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { for(SKPaymentTransaction *tran in queue.transactions){ if (SKPaymentTransactionStatePurchased == tran.transactionState){ [self completeTransaction:tran]; } } } - (void)verifySuecessDelTransactions{ SKPaymentQueue *paymentQueue = [SKPaymentQueue defaultQueue]; NSArray<SKPaymentTransaction *> *transactions = paymentQueue.transactions; if (transactions.count == 0) { return; } for (SKPaymentTransaction *transaction in transactions) { if (transaction.transactionState == SKPaymentTransactionStatePurchased || transaction.transactionState == SKPaymentTransactionStateRestored) { [paymentQueue finishTransaction:transaction]; } } }
1
0
165
Aug ’25
StoreKit2 caches local raw transactions and retrieval
FB19377002 I am looking to improve and review my subscription purchase handling logic, for the best user experience. Considering that StoreKit2 caches local raw transactions (in case user is offline), is it really necessary to persist "unlocked status" in UserDefaults or SwiftData Model or AppStorage? Are there significant delays when reading Transaction.currentEntitlements from locally stored cache, versus reading it from UserDefaults; or, as in the latest SKDemo example, even reading it from stored in SwiftData ? https://developer.apple.com/forums/thread/706450 I only have subscriptions ( I don't have noncosumable or consubale products). Do I still need to persist subscription status?
0
0
99
Aug ’25
Not able to renew membership
You can still renew your membership within the next 8 days and your apps will remain available on the App Store during this time. Open the Apple Developer app on your iPhone, iPad, or Mac. Sign in to your account, tap/click Renew, and follow the prompts. I'm getting this message but renew button is not visible in Developer App or on website. How to to renew?
3
0
838
Oct ’25
IAP receipt validation fails with status code: 21002
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.
2
0
266
Aug ’25
Help: IAP Works in Xcode but Not in TestFlight – Cannot Link IAP to Version for Review
Hi everyone, I've been going back and forth with Apple’s review team for over 10 days now, and I'm still unable to get my first In-App Purchase (IAP) working correctly. Here's what’s happening: ✅ The IAP works perfectly when I build and run directly from Xcode. ❌ However, when I test the app via TestFlight, tapping the purchase buttons does nothing—the IAP sheet doesn't appear. Key issue (I think): I believe the IAP hasn't been submitted properly for review. On App Store Connect, I cannot select the IAP under the “In-App Purchases” section of the version submission page. It's grayed out or not listed at all. As a result, Apple keeps rejecting my binary due to the IAP not being included in the review. What I’ve already done: Created the IAP (non-consumable) Set pricing and cleared all errors Checked Bundle ID, Product ID, and entitlements Added In-App Purchase capability to the app target Uploaded the binary via Xcode Waited multiple times for status updates My questions: What’s the correct process to link the IAP to a specific app version if it doesn't show up in the version page? Could this be an issue with App Store Connect metadata or approval timing, or am I missing something in Xcode/build settings? Is there any way to force re-sync the IAP so it appears when submitting the build? Has anyone resolved a similar issue recently? This process has been incredibly frustrating, and the feedback from the review team so far has been very vague. I would really appreciate any detailed insight or steps to ensure the IAP is submitted correctly and works on TestFlight. Thank you in advance!
1
0
133
Jun ’25
IAP working in StoreKitTest on XCODE but not in TestFlight – shows mapped error "Product not available"
Hey everyone, I'm currently preparing an older iOS app for App Store release that includes a non-consumable In‑App Purchase using StoreKit 2. Everything works perfectly in the StoreKitTest environment inside Xcode – the product loads, the purchase flow runs, the transaction verifies. However, when I run the same app through TestFlight, I always get the error: ❌ Product not available - mapped to Here’s what I’ve already checked: ✅ The product ID is correct and matches what’s in App Store Connect (case-sensitive). ✅ The IAP is created in App Store Connect and includes: Title Product ID Price Tier Screenshot for review ✅ The App Store "Paid Applications" agreement is active. ✅ The app is using the correct bundle ID. ✅ I'm using Product.products(for: [productID]) from StoreKit 2. ✅ I’ve implemented fallback and retry logic (e.g. reload after delay). ✅ All IAP logic is wrapped in @MainActor and async-safe. As the App got Rejected on Review, the IAP is also now in the Rejected Status. Now the IAP shows status: 🟠 "Developer Action Required" And App Review rejected the IAP with the message: "Your first In‑App Purchase must be submitted together with a new app version." But if I add the App to the Test again and therefore the IAP, then the app will get Rejected again for App Completeness, IAP does not work... What am I doing wrong here? :) Thanks a lot in advance Cheers, Niklas
1
0
133
Aug ’25
ExternalPurchaseCustomLink.token(for:) returns nil when isEligible is true
Environment iOS 18.1+ StoreKit External Purchase Link Entitlement (EU) App distributed via App Store in France Problem Summary I'm implementing external purchase links for EU users using ExternalPurchaseCustomLink. While the implementation works correctly in my TestFlight testing, some production users experience token(for:) returning nil. Implementation Following Apple's documentation, my flow is: Check eligibility using ExternalPurchaseCustomLink.isEligible If eligible, call ExternalPurchaseCustomLink.token(for: "ACQUISITION") Store the token for use in the external purchase flow // Simplified implementation guard #available(iOS 18.1, *) else { return } let isEligible = await ExternalPurchaseCustomLink.isEligible guard isEligible else { return } // This returns nil for some users despite isEligible being true let token = try await ExternalPurchaseCustomLink.token(for: "ACQUISITION") Configuration Entitlement: com.apple.developer.storekit.external-purchase-link is present Info.plist: SKExternalPurchaseCustomLinkRegions set to ["fr"] App is only available in France via App Store Observed Behavior For affected users: ExternalPurchaseCustomLink.isEligible returns true token(for:) returns nil (not throwing an error) The token generation was never previously called for these users Questions Under what conditions does token(for:) return nil when isEligible is true? Is there additional validation I should perform before calling token(for:)? Are there known issues with token generation on specific iOS versions? Any guidance on debugging this issue would be appreciated.
1
0
125
Jan ’26
Can't fetch products
Use the following method to fetch: let appProducts = try await Product.products(for: productIdentifiers) The following checks have been carried out ✅ Must-check points App ID capabilities Subscription product status (ready to submit) Why The result is an empty array?
0
0
71
Sep ’25
(verifyreceipt) I cannot verify from the server whether the user's iap payment is successful or not
I have three questions about verify receipt I use this api (https://buy.itunes.apple.com/verifyReceipt)to verify receipt is success or not. But since last month, this interface has started to return an error(21002). I see this document (https://developer.apple.com/documentation/appstorereceipts/verifyreceipt) say its Deprecated. My question is, is the error suddenly returned recently because the interface has been deprecated or for some other reason? (I haven't modified my code about this recently) 2. I can not understand this document: (https://developer.apple.com/documentation/appstorereceipts/validating_receipts_on_the_device) Does this mean that in the new version, as long as the app returns a payment success (purchaseDetails.status == PurchaseStatus.purchased), the payment is guaranteed to be successful, and my server does not need to request payment result verification from Apple's server? 3. I try to use this (https://github.com/apple/app-store-server-library-java) to get TransactionInfo, but I dont konw to get Transaction status to know is success or not. my java server code : AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment); TransactionInfoResponse response = client.getTransactionInfo(transactionId); (bug i can note get transaction status, how do i konw this Transaction is success or not)
0
0
82
Aug ’25
SubscriptionStoreView(groupID:) bug in TestFlight
Hello, I seem to have a strange bug when testing 2 of my Apps that calls SubscriptionStoreView(groupID: storekitmanager.groupID), where storekitmanager is using @Observable, with groupID from the Subscription Group in App Store Connect. They have the following: Yearly, Biannually, and Monthly. Their productIDs and groupIDs have been configured in each app with the correct IDs. In my main Apple ID, when the SubscriptionStoreView is presented, selecting Monthly, tap Subscribe, and nothing happens. Selecting either Yearly or Biannually, tap Subscribe and the Confirmation Dialog that triggers faceID/touchID will appear correctly. This happens in both Apps for TestFlight. I have a Manage Subscriptions button that uses: manageSubscriptionsSheet(isPresented:subscriptionGroupID:) I can change the subscription to Monthly in that manage subscriptions. However, if I switch to a Sandbox Apple Account, the "bug" described above does not happen. The Sandbox account when selecting Monthly and tap Subscribe will trigger the Confirmation Dialog (in both Apps). Not sure if my main account is "stuck" in some loop where it is trying to purchase Monthly in TestFlight but it is not completed. Has anyone ever encountered such a bug?
1
0
197
Jan ’26
When subscribing to the change notification in the App Store, it was found that the type of notification received by the server did not match the expected problem.
Description of the Issue We are encountering issues with App Store Server Notifications, where the notification type received does not align with our expectations for subscription changes. Specifically, we receive DID_RENEW notifications in scenarios involving downgrades or intra-tier upgrades, which disrupts our backend subscription management logic and may lead to incorrect user subscription states and billing errors. Below are two reproducible scenarios with associated transaction data. Scenario 1: Downgrade from Premium Yearly (with 7-Day Trial) to Standard Yearly Steps to Reproduce: A user purchases the Premium Yearly subscription with a 7-day free trial (initial transaction ID: 700002116066502). On the day before the trial ends, the user downgrades to the Standard Yearly subscription via the App Store’s subscription management interface. Expected Behavior: We expect to receive a DID_CHANGE_RENEWAL_STATUS or a similar downgrade notification (e.g., DID_CHANGE_RENEWAL_PREF) to indicate the subscription plan has been downgraded, enabling immediate updates to the user’s subscription status. Actual Behavior: Upon downgrade, our server receives a DID_RENEW notification instead of a downgrade-specific notification. This causes our system to incorrectly interpret the action as a renewal of the Premium subscription rather than a downgrade to Standard, with no subsequent downgrade confirmation received. Relevant Transaction Data: New Transaction ID: 700002122810952 Notification Type: DID_RENEW Notification UUID: 26de9b0e-5d61-4893-b679-0bd18ae3d208 Original Transaction ID (Trial): 700002116066502 Scenario 2: Intra-Tier Upgrade from Standard Monthly to Standard Yearly Steps to Reproduce: A user purchases the Standard Monthly subscription. On the day before the monthly subscription expires, the user switches to the Standard Yearly subscription via the App Store’s subscription management interface. Expected Behavior: The original Monthly subscription expires normally, with a DID_EXPIRE notification received. Upon expiration, we expect a DID_RENEW or SUBSCRIBED notification for the new Yearly subscription to confirm its activation, allowing seamless transition of the user’s subscription plan. Actual Behavior: Immediately after the upgrade, our server receives a DID_RENEW notification for the new Yearly plan. No further notifications are received, including no DID_EXPIRE for the original Monthly subscription. This leaves our system unable to confirm the expiration of the Monthly plan, potentially resulting in duplicate active subscriptions in our records. Relevant Transaction Data: Transaction ID: 70002902074491 Notification Type: DID_RENEW Notification UUID: 3d35bc2f-e769-4549-bf50-d84d41b10342 Impact These notification discrepancies disrupt our subscription lifecycle management, potentially causing incorrect user entitlements, over-billing, and a degraded user experience. Requested Assistance Please confirm the correct notification types for these scenarios as per Apple’s documentation (e.g., App Store Server Notifications V2). Provide guidance on handling these edge cases, including any recent changes to notification logic. If this is a backend issue, please investigate the provided transaction IDs and Notification UUIDs. Thank you for your prompt assistance. We look forward to your response to help resolve this issue for our users. 问题描述: 我们在处理 App Store 订阅变更通知时发现,服务器接收到的通知类型与预期不符,具体表现为降级或同级升级场景下错误接收到 DID_RENEW 通知。这影响了我们后端订阅管理的逻辑,可能导致用户订阅状态和计费出现偏差。以下是两个可重现的场景及相关交易数据。 场景 1:高级年费(含 7 天试用)降级至标准年费 重现步骤: 用户购买高级年费订阅并开启 7 天免费试用(初始交易 ID:700002116066502)。 在试用期结束前一天,用户通过 App Store 的订阅管理界面降级至标准年费订阅。 预期行为: 我们预期会收到 DID_CHANGE_RENEWAL_STATUS 或类似的降级通知(如 DID_CHANGE_RENEWAL_PREF),以表明订阅计划已降级,从而允许我们立即更新用户订阅状态。 实际行为: 降级后,服务器立即收到 DID_RENEW 通知,而非降级相关通知。这导致系统误认为高级订阅仍在续订,而非降级为标准订阅,且后续未收到任何降级确认通知。 相关交易数据: 新交易 ID:700002122810952 通知类型:DID_RENEW 通知 UUID:26de9b0e-5d61-4893-b679-0bd18ae3d208 原始交易 ID(试用):700002116066502 场景 2:标准月费订阅升级为标准年费订阅 重现步骤: 用户购买标准月费订阅。 在月费订阅到期前一天,用户通过 App Store 的订阅管理界面切换至标准年费订阅。 预期行为: 原月费订阅正常到期,预期收到 DID_EXPIRE 通知。 月费到期后,预期收到新年费订阅的 DID_RENEW 或 SUBSCRIBED 通知,以确认年费订阅开始,方便我们无缝切换用户订阅计划。 实际行为: 升级后,服务器立即收到新年费订阅的 DID_RENEW 通知。 后续未收到任何通知,包括原月费订阅的 DID_EXPIRE 通知。这导致我们无法确认月费订阅的到期状态,可能在记录中出现重复的活跃订阅。 相关交易数据: 交易 ID:70002902074491 通知类型:DID_RENEW 通知 UUID:3d35bc2f-e769-4549-bf50-d84d41b10342 影响: 这些通知异常干扰了我们的订阅生命周期管理,可能导致用户权益错误、潜在的重复计费以及用户体验下降。 请求协助: 请确认根据 Apple 文档(例如 App Store Server Notifications V2)在上述场景中应返回的正确通知类型。 请提供处理此类边缘场景的指导,包括通知逻辑的任何最新变更。 如果这是后端问题,请调查提供的交易 ID 和通知 UUID。 感谢您的及时协助,期待您的回复以帮助我们为用户解决问题。
1
0
113
Sep ’25
Auto renewable subsction, auto renews to different product id
I need help understanding how the In-App purchase subscription works within the same subscription group and how the Order plays a role at the time of auto-renewal. In my case, I have 5 in-app products in the same group as given below with their Order, Order Product ---------------------- 1 Gold Annual 2 Gold Monthly 3 Silver Annual Old, 3 Silver Annual New 4 Silver Monthly One user purchased Silver annual New, at the time of expiry, after a year, it auto-renewed to Silver annual Old. My understanding is that if a user has purchased Silver annual New, at the time of renewal, it should renew with the same product, not with different product id. Is this behaviour expected? Is there any official document which can explain this behaviour? Note: There was no Order given before, recently we have changed the Order to manage Upgrade and Downgrade for In-App subscriptions.
0
0
76
Nov ’25
# Critical Bug: Apple servers not generating com.apple.developer.storekit entitlement for App ID com.driftnotes.app
Problem Summary Apple's provisioning servers are not generating the com.apple.developer.storekit entitlement for App ID com.driftnotes.app (Team ID: 43Y6AG5NPY), making it impossible to build iOS apps for physical devices despite all configurations being correct. Environment macOS: 15.3.1 (24D70) Xcode: 16.1 (xcode-select version 2409) Flutter: 3.35.2 • channel stable Account: Individual Developer (Kazakhstan) Bundle ID: com.driftnotes.app Team ID: 43Y6AG5NPY Error Message Error (Xcode): Provisioning profile "iOS Team Provisioning Profile: com.driftnotes.app" doesn't include the com.apple.developer.storekit entitlement. /Users/vyacheslavkuzin/Desktop/FlutterProjects/DriftNotesDart/ios/Runner.xcodeproj Steps to Reproduce Configure App ID with In-App Purchase capability (✅ verified in Developer Portal) Add In-App Purchase capability in Xcode project (✅ done) Configure entitlements file with StoreKit keys (✅ done) Enable automatic signing in Xcode (✅ done) Run: flutter build ios --release Build completes successfully ("Xcode build done. 13,8s") but fails at signing stage Expected vs Actual Result Expected: Provisioning profile should include com.apple.developer.storekit entitlement Actual: Profile is created WITHOUT the entitlement, despite all configurations being correct Configuration Details Developer Portal App ID com.driftnotes.app has In-App Purchase capability enabled ✅ All agreements are active in App Store Connect ✅ Xcode Project In-App Purchase capability added via Signing & Capabilities ✅ Automatically manage signing: Enabled ✅ Team: 43Y6AG5NPY (Vyacheslav Kuzin) ✅ Entitlements File (ios/Runner/Runner.entitlements) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.developer.in-app-payments</key> <array> <string>merchant.com.driftnotes.app</string> </array> <key>com.apple.developer.storekit</key> <true/> </dict> </plist> Build Settings CODE_SIGN_ENTITLEMENTS: Runner/Runner.entitlements ✅ PRODUCT_BUNDLE_IDENTIFIER: com.driftnotes.app ✅ DEVELOPMENT_TEAM: 43Y6AG5NPY ✅ Troubleshooting Attempted Multiple Attempts Profile Recreation: Manual and automatic profiles recreated dozens of times Cache Cleanup: Complete removal of: ~/Library/Developer/Xcode/DerivedData/* ~/Library/MobileDevice/Provisioning\ Profiles/* Flutter clean & pod cache clean Signing Methods: Tested both manual and automatic signing management Wait Periods: 48+ hours for server propagation Complete Profile Deletion: Removed ALL profiles from Developer account per Apple Support Apple Support Workaround Following Senior Advisor recommendation: ✅ Deleted all provisioning profiles from account ✅ Confirmed IAP capability in project ✅ Created StoreKit Configuration File for testing ✅ Verified automatic signing management ✅ Multiple "Try Again" attempts in Xcode Result: Problem persists Apple Support Reference Case #102680105923 - Senior Advisor Simone confirmed after internal team consultation that this requires engineering team attention and directed to Developer Forums. Technical Analysis What Works Flutter build completes successfully Pod install executes without issues (25,9s) Xcode build finishes successfully (13,8s) All dependencies resolve correctly What Fails Provisioning profile generation: Server creates profile but omits StoreKit entitlement All profile types affected: Both manual and automatic profiles Consistent across configurations: Debug, Release, Profile all fail identically Root Cause This appears to be a server-side bug where Apple's provisioning systems are not properly correlating the App ID's In-App Purchase capability with the StoreKit entitlement generation for this specific App ID (com.driftnotes.app). The issue is NOT in client-side configuration - all settings match Apple's official documentation exactly. The problem occurs during the server-side provisioning profile generation process. Request for Engineering Team This issue requires attention from Apple's provisioning infrastructure team to resolve the server-side entitlement generation bug for App ID com.driftnotes.app. Impact Critical: Complete inability to build iOS app for physical devices Business: Blocking app deployment and updates Developer Experience: Extensive time spent on troubleshooting correctly configured setup All configurations have been verified multiple times and match Apple's official documentation. The issue has been escalated through Apple Support (Case #102680105923) and requires engineering team intervention.
0
0
80
Sep ’25
Apple ID Password Is Required During In-App Purchase
Hello, I would like to ask about an Apple ID authentication behavior during in-app purchases. Our app uses a StoreKit 1 (SKPaymentQueue-based) implementation, and there are no differences in the in-app purchase logic between the TestFlight build and the App Store production build. However, we have observed that some users are prompted to enter their Apple ID password during in-app purchases. The observed behavior is as follows: On the first in-app purchase, the system prompts for the Apple ID password After the password is entered once, subsequent purchases proceed normally using Face ID (double side-button press) Even after deleting and reinstalling the app, or switching between TestFlight and App Store builds, the password prompt does not reappear if authentication has already occurred This behavior can occur even when the Apple ID already has an active auto-renewable subscription The only confirmed change on our side is: The app is now built with Xcode 26 instead of Xcode 18 Based on this, we are currently considering the following possible causes: A change in purchase authentication behavior due to the Xcode version update Expiration of the Apple ID purchase authentication session after a long period without purchases In addition to these points, we would like to ask: Are there any other common conditions or security policies in iOS or the App Store that may cause the system to require Apple ID password input during in-app purchases? Is this behavior considered expected under certain circumstances? We would appreciate your clarification on whether this is expected system behavior or if there are any implementation aspects we should further review. Thank you for your support.
0
0
89
Jan ’26
Can StoreKit products be observed with ObservableObject? Can I get notified when a users subscription has lapsed without polling Transaction.currentEntitlements?
I have an auto-renewable subscription. I have two methods helping me keep track of when they are expired @MainActor public func isPurchased(product: Product) async -> Bool { guard let state = await product.currentEntitlement else { return false } switch state { case .unverified(_, _): return false case .verified(let transaction): await transaction.finish() return isTransactionRelevant(transaction) } } private func isTransactionRelevant(_ transaction: Transaction) -> Bool { if let revocationDate = transaction.revocationDate { logger.error("Transaction verification failed: Transaction was revoked on \(revocationDate)") return false } if let expirationDate = transaction.expirationDate, expirationDate < Date() { logger.error("Transaction verification failed: Transaction expired on \(expirationDate)") return false } if transaction.isUpgraded { logger.error("Transaction verification failed: Transaction was upgraded") return false } logger.info("Transaction verification succeeded") return true } I also have this that I can call to get the latest state of purchases @MainActor public func updateStoreKitSubscriptionStatus() async { var currentProductsPurchased: [Product] = [] for await result in Transaction.currentEntitlements { if case .verified(let transaction) = result { if isTransactionRelevant(transaction) { if let product = products.first( where: { $0.id == transaction.productID }) { currentProductsPurchased.append(product) } } await transaction.finish() } } self.purchasedProducts = currentProductsPurchased } Right now when a subscription expires the user needs to manually do some action that triggers updateStoreKitSubscriptionStatus() as it appears that expirations do not come through in Transaction.updates. I am surprised there does not seem to be a better way. Does StoreKit not notify you somewhere that an auto-renewable subscription has expired? Can you observe it in an ObservableObject? Or do I need to just frequently poll Transaction.currentEntitlements even if I dont expect frequent updates?
Replies
0
Boosts
0
Views
117
Activity
Jul ’25
Sandbox refund testing "Cannot connect" error
I'm running into a refund issue when testing in-app purchases in sandbox mode. I'm able to successfully purchase items but roughly 1 out of 3 times, when the refund window pops up, it says it cannot connect even though our webhook servers can verify the transaction id sent from apple. My internet connection is good and I've varied the wait period from purchase to refund from 15mins to 2 hours. Any help would be greatly appreciated.
Replies
1
Boosts
0
Views
164
Activity
Sep ’25
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
195
Activity
May ’25
Switching App Store Server Notifications from V1 → V2 — what happens to existing subscriptions?
Hello — quick question about App Store Server Notifications migration. We have a live app using Production V1 notifications for recurring in-app subscriptions. We plan to switch the Production webhook to V2. After the switch: Will notifications for existing subscriptions be delivered in V1 format, V2 format, or will it depend (e.g., queued V1 retries vs new V2 deliveries)? If V1 retries are queued, how long should we expect overlap/retries to continue? Any recommended cutover best practices (support both formats, revert process, etc.)? Happy to share additional details. Thanks.
Replies
1
Boosts
0
Views
207
Activity
Dec ’25
[StoreKit1] IAP Works in TestFlight but Fails During App Review (2.1 Rejection)
Hello Apple Developer Team, We're experiencing consistent IAP approval rejections under Guideline 2.1, despite successful TestFlight verification. Here's our detailed situation: Environment StoreKit 1 implementation Tested on iOS 18.5 or 18.6 devices Sandbox environment works perfectly Verification Steps Taken ✅ Confirmed all Product IDs match App Store Connect exactly ✅ Validated 10+ successful TestFlight transactions (attached screenshot samples) ✅ Verified banking/tax agreements are active Objective-C Code (StoreKit1 Implementation) - (void)buyProductId:(NSString *)pid AndSetGameOrderID:(NSString *)orderID{ if([SKPaymentQueue canMakePayments]){ if (!hasAddObserver) { [[SKPaymentQueue defaultQueue] addTransactionObserver:_neo]; hasAddObserver = YES; } self.neoOrderID = orderID; [[NSUserDefaults standardUserDefaults] setValue:orderID forKey:Pay_OrderId_Key]; self.productID = pid; NSArray * product = [[NSArray alloc]initWithObjects:self.productID, nil]; NSSet * nsset = [NSSet setWithArray:product]; SKProductsRequest * request = [[SKProductsRequest alloc]initWithProductIdentifiers:nsset]; request.delegate = self; [request start]; }else{ NSString * Err = @"Pembelian tidak diizinkan. Silakan aktifkan perizinan di pengaturan"; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); return; } } - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSArray * product = response.products; if ([product count] == 0) { [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; NSString * Err = [NSString stringWithFormat:@"Err = 01, Item tidak ditemukan %@",self.productID]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); return; } SKProduct * p = nil; for (SKProduct * pro in product) { if ([pro.productIdentifier isEqualToString:self.productID]){ p = pro; }else{ [request cancel]; [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; NSString * Err = [NSString stringWithFormat:@"Err = 02, %@",self.productID]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); return; } } SKMutablePayment * mPayment = [SKMutablePayment paymentWithProduct:p]; mPayment.applicationUsername = [NSString stringWithFormat:@"%@",self.neoOrderID]; if(!hasAddObserver){ [[SKPaymentQueue defaultQueue] addTransactionObserver:_neo]; hasAddObserver = YES; } [[SKPaymentQueue defaultQueue] addPayment:mPayment]; } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{ [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; NSString * Err = [NSString stringWithFormat:@"Err = 0%ld %@", (long)error.code, self.productID]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [Err UTF8String]); } - (void)requestDidFinish:(SKRequest *)request{ } - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{ for(SKPaymentTransaction *tran in transaction){ if (SKPaymentTransactionStatePurchased == tran.transactionState){ [self completeTransaction:tran]; }else if(SKPaymentTransactionStateFailed == tran.transactionState){ [self failedTransaction:tran]; } } } - (void)failedTransaction: (SKPaymentTransaction *)transaction { NSString * detail = [NSString stringWithFormat:@"%ld",(long)transaction.error.code]; // UnitySendMessage("GameManager", "IAPPurchaseFailed", [detail UTF8String]); [[SKPaymentQueue defaultQueue]removeTransactionObserver:_neo]; hasAddObserver = NO; [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void)completeTransaction:(SKPaymentTransaction *)transaction{ NSMutableDictionary * mdic = [NSMutableDictionary dictionary]; NSString * productIdentifier = transaction.payment.productIdentifier; NSData * _recep = nil; NSString * _receipt = @""; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { _recep = transaction.transactionReceipt; _receipt = [[NSString alloc]initWithData:_recep encoding:NSUTF8StringEncoding]; } else { _recep = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; _receipt = [_recep base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; } NSString * gameOrderid = [transaction payment].applicationUsername; if (gameOrderid == nil) { gameOrderid = [[NSUserDefaults standardUserDefaults] objectForKey:Pay_OrderId_Key]; } if(_receipt != nil && gameOrderid != nil){ mdic[@"orderid"] = gameOrderid; mdic[@"productid"] = productIdentifier; mdic[@"receipt"] = _receipt; }else{ [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; return; } NSData * data = [NSJSONSerialization dataWithJSONObject:mdic options:kNilOptions error:nil]; NSString * jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; if (hasAddObserver) { [[SKPaymentQueue defaultQueue] removeTransactionObserver:_neo]; hasAddObserver = NO; } // UnitySendMessage("GameManager", "IAPPurchaseSuecess", [jsonString UTF8String]); [self verifyReceipt:_recep completion:^(BOOL success, NSDictionary *response) { if (success) { NSLog(@"verify success"); // [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; [self verifySuecessDelTransactions]; } }]; } - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { for(SKPaymentTransaction *tran in queue.transactions){ if (SKPaymentTransactionStatePurchased == tran.transactionState){ [self completeTransaction:tran]; } } } - (void)verifySuecessDelTransactions{ SKPaymentQueue *paymentQueue = [SKPaymentQueue defaultQueue]; NSArray<SKPaymentTransaction *> *transactions = paymentQueue.transactions; if (transactions.count == 0) { return; } for (SKPaymentTransaction *transaction in transactions) { if (transaction.transactionState == SKPaymentTransactionStatePurchased || transaction.transactionState == SKPaymentTransactionStateRestored) { [paymentQueue finishTransaction:transaction]; } } }
Replies
1
Boosts
0
Views
165
Activity
Aug ’25
SKOverlay App Store Connect Attribution Missing?
I am trying use SKOverlay to promote an app, and I currently have the campaignToken field populated. However, even when installs happen from the SKOverlay, nothing shows up under that campaign token in App Store Connect. Is there something that I'm missing? I don't have the providerToken set.
Replies
0
Boosts
0
Views
120
Activity
May ’25
StoreKit2 caches local raw transactions and retrieval
FB19377002 I am looking to improve and review my subscription purchase handling logic, for the best user experience. Considering that StoreKit2 caches local raw transactions (in case user is offline), is it really necessary to persist "unlocked status" in UserDefaults or SwiftData Model or AppStorage? Are there significant delays when reading Transaction.currentEntitlements from locally stored cache, versus reading it from UserDefaults; or, as in the latest SKDemo example, even reading it from stored in SwiftData ? https://developer.apple.com/forums/thread/706450 I only have subscriptions ( I don't have noncosumable or consubale products). Do I still need to persist subscription status?
Replies
0
Boosts
0
Views
99
Activity
Aug ’25
Not able to renew membership
You can still renew your membership within the next 8 days and your apps will remain available on the App Store during this time. Open the Apple Developer app on your iPhone, iPad, or Mac. Sign in to your account, tap/click Renew, and follow the prompts. I'm getting this message but renew button is not visible in Developer App or on website. How to to renew?
Replies
3
Boosts
0
Views
838
Activity
Oct ’25
IAP receipt validation fails with status code: 21002
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.
Replies
2
Boosts
0
Views
266
Activity
Aug ’25
Help: IAP Works in Xcode but Not in TestFlight – Cannot Link IAP to Version for Review
Hi everyone, I've been going back and forth with Apple’s review team for over 10 days now, and I'm still unable to get my first In-App Purchase (IAP) working correctly. Here's what’s happening: ✅ The IAP works perfectly when I build and run directly from Xcode. ❌ However, when I test the app via TestFlight, tapping the purchase buttons does nothing—the IAP sheet doesn't appear. Key issue (I think): I believe the IAP hasn't been submitted properly for review. On App Store Connect, I cannot select the IAP under the “In-App Purchases” section of the version submission page. It's grayed out or not listed at all. As a result, Apple keeps rejecting my binary due to the IAP not being included in the review. What I’ve already done: Created the IAP (non-consumable) Set pricing and cleared all errors Checked Bundle ID, Product ID, and entitlements Added In-App Purchase capability to the app target Uploaded the binary via Xcode Waited multiple times for status updates My questions: What’s the correct process to link the IAP to a specific app version if it doesn't show up in the version page? Could this be an issue with App Store Connect metadata or approval timing, or am I missing something in Xcode/build settings? Is there any way to force re-sync the IAP so it appears when submitting the build? Has anyone resolved a similar issue recently? This process has been incredibly frustrating, and the feedback from the review team so far has been very vague. I would really appreciate any detailed insight or steps to ensure the IAP is submitted correctly and works on TestFlight. Thank you in advance!
Replies
1
Boosts
0
Views
133
Activity
Jun ’25
IAP working in StoreKitTest on XCODE but not in TestFlight – shows mapped error "Product not available"
Hey everyone, I'm currently preparing an older iOS app for App Store release that includes a non-consumable In‑App Purchase using StoreKit 2. Everything works perfectly in the StoreKitTest environment inside Xcode – the product loads, the purchase flow runs, the transaction verifies. However, when I run the same app through TestFlight, I always get the error: ❌ Product not available - mapped to Here’s what I’ve already checked: ✅ The product ID is correct and matches what’s in App Store Connect (case-sensitive). ✅ The IAP is created in App Store Connect and includes: Title Product ID Price Tier Screenshot for review ✅ The App Store "Paid Applications" agreement is active. ✅ The app is using the correct bundle ID. ✅ I'm using Product.products(for: [productID]) from StoreKit 2. ✅ I’ve implemented fallback and retry logic (e.g. reload after delay). ✅ All IAP logic is wrapped in @MainActor and async-safe. As the App got Rejected on Review, the IAP is also now in the Rejected Status. Now the IAP shows status: 🟠 "Developer Action Required" And App Review rejected the IAP with the message: "Your first In‑App Purchase must be submitted together with a new app version." But if I add the App to the Test again and therefore the IAP, then the app will get Rejected again for App Completeness, IAP does not work... What am I doing wrong here? :) Thanks a lot in advance Cheers, Niklas
Replies
1
Boosts
0
Views
133
Activity
Aug ’25
ExternalPurchaseCustomLink.token(for:) returns nil when isEligible is true
Environment iOS 18.1+ StoreKit External Purchase Link Entitlement (EU) App distributed via App Store in France Problem Summary I'm implementing external purchase links for EU users using ExternalPurchaseCustomLink. While the implementation works correctly in my TestFlight testing, some production users experience token(for:) returning nil. Implementation Following Apple's documentation, my flow is: Check eligibility using ExternalPurchaseCustomLink.isEligible If eligible, call ExternalPurchaseCustomLink.token(for: "ACQUISITION") Store the token for use in the external purchase flow // Simplified implementation guard #available(iOS 18.1, *) else { return } let isEligible = await ExternalPurchaseCustomLink.isEligible guard isEligible else { return } // This returns nil for some users despite isEligible being true let token = try await ExternalPurchaseCustomLink.token(for: "ACQUISITION") Configuration Entitlement: com.apple.developer.storekit.external-purchase-link is present Info.plist: SKExternalPurchaseCustomLinkRegions set to ["fr"] App is only available in France via App Store Observed Behavior For affected users: ExternalPurchaseCustomLink.isEligible returns true token(for:) returns nil (not throwing an error) The token generation was never previously called for these users Questions Under what conditions does token(for:) return nil when isEligible is true? Is there additional validation I should perform before calling token(for:)? Are there known issues with token generation on specific iOS versions? Any guidance on debugging this issue would be appreciated.
Replies
1
Boosts
0
Views
125
Activity
Jan ’26
Testing price change for the auto-renewing subscriptions
We are considering a price change for the auto-renewing subscriptions we currently offer in a Production environment and have made system modifications to our servers. We would like to implement a price change for purchases made through our SANDBOX Apple account in order to test if our system is capable of handling the price change.
Replies
1
Boosts
0
Views
75
Activity
Aug ’25
Can't fetch products
Use the following method to fetch: let appProducts = try await Product.products(for: productIdentifiers) The following checks have been carried out ✅ Must-check points App ID capabilities Subscription product status (ready to submit) Why The result is an empty array?
Replies
0
Boosts
0
Views
71
Activity
Sep ’25
(verifyreceipt) I cannot verify from the server whether the user's iap payment is successful or not
I have three questions about verify receipt I use this api (https://buy.itunes.apple.com/verifyReceipt)to verify receipt is success or not. But since last month, this interface has started to return an error(21002). I see this document (https://developer.apple.com/documentation/appstorereceipts/verifyreceipt) say its Deprecated. My question is, is the error suddenly returned recently because the interface has been deprecated or for some other reason? (I haven't modified my code about this recently) 2. I can not understand this document: (https://developer.apple.com/documentation/appstorereceipts/validating_receipts_on_the_device) Does this mean that in the new version, as long as the app returns a payment success (purchaseDetails.status == PurchaseStatus.purchased), the payment is guaranteed to be successful, and my server does not need to request payment result verification from Apple's server? 3. I try to use this (https://github.com/apple/app-store-server-library-java) to get TransactionInfo, but I dont konw to get Transaction status to know is success or not. my java server code : AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment); TransactionInfoResponse response = client.getTransactionInfo(transactionId); (bug i can note get transaction status, how do i konw this Transaction is success or not)
Replies
0
Boosts
0
Views
82
Activity
Aug ’25
SubscriptionStoreView(groupID:) bug in TestFlight
Hello, I seem to have a strange bug when testing 2 of my Apps that calls SubscriptionStoreView(groupID: storekitmanager.groupID), where storekitmanager is using @Observable, with groupID from the Subscription Group in App Store Connect. They have the following: Yearly, Biannually, and Monthly. Their productIDs and groupIDs have been configured in each app with the correct IDs. In my main Apple ID, when the SubscriptionStoreView is presented, selecting Monthly, tap Subscribe, and nothing happens. Selecting either Yearly or Biannually, tap Subscribe and the Confirmation Dialog that triggers faceID/touchID will appear correctly. This happens in both Apps for TestFlight. I have a Manage Subscriptions button that uses: manageSubscriptionsSheet(isPresented:subscriptionGroupID:) I can change the subscription to Monthly in that manage subscriptions. However, if I switch to a Sandbox Apple Account, the "bug" described above does not happen. The Sandbox account when selecting Monthly and tap Subscribe will trigger the Confirmation Dialog (in both Apps). Not sure if my main account is "stuck" in some loop where it is trying to purchase Monthly in TestFlight but it is not completed. Has anyone ever encountered such a bug?
Replies
1
Boosts
0
Views
197
Activity
Jan ’26
When subscribing to the change notification in the App Store, it was found that the type of notification received by the server did not match the expected problem.
Description of the Issue We are encountering issues with App Store Server Notifications, where the notification type received does not align with our expectations for subscription changes. Specifically, we receive DID_RENEW notifications in scenarios involving downgrades or intra-tier upgrades, which disrupts our backend subscription management logic and may lead to incorrect user subscription states and billing errors. Below are two reproducible scenarios with associated transaction data. Scenario 1: Downgrade from Premium Yearly (with 7-Day Trial) to Standard Yearly Steps to Reproduce: A user purchases the Premium Yearly subscription with a 7-day free trial (initial transaction ID: 700002116066502). On the day before the trial ends, the user downgrades to the Standard Yearly subscription via the App Store’s subscription management interface. Expected Behavior: We expect to receive a DID_CHANGE_RENEWAL_STATUS or a similar downgrade notification (e.g., DID_CHANGE_RENEWAL_PREF) to indicate the subscription plan has been downgraded, enabling immediate updates to the user’s subscription status. Actual Behavior: Upon downgrade, our server receives a DID_RENEW notification instead of a downgrade-specific notification. This causes our system to incorrectly interpret the action as a renewal of the Premium subscription rather than a downgrade to Standard, with no subsequent downgrade confirmation received. Relevant Transaction Data: New Transaction ID: 700002122810952 Notification Type: DID_RENEW Notification UUID: 26de9b0e-5d61-4893-b679-0bd18ae3d208 Original Transaction ID (Trial): 700002116066502 Scenario 2: Intra-Tier Upgrade from Standard Monthly to Standard Yearly Steps to Reproduce: A user purchases the Standard Monthly subscription. On the day before the monthly subscription expires, the user switches to the Standard Yearly subscription via the App Store’s subscription management interface. Expected Behavior: The original Monthly subscription expires normally, with a DID_EXPIRE notification received. Upon expiration, we expect a DID_RENEW or SUBSCRIBED notification for the new Yearly subscription to confirm its activation, allowing seamless transition of the user’s subscription plan. Actual Behavior: Immediately after the upgrade, our server receives a DID_RENEW notification for the new Yearly plan. No further notifications are received, including no DID_EXPIRE for the original Monthly subscription. This leaves our system unable to confirm the expiration of the Monthly plan, potentially resulting in duplicate active subscriptions in our records. Relevant Transaction Data: Transaction ID: 70002902074491 Notification Type: DID_RENEW Notification UUID: 3d35bc2f-e769-4549-bf50-d84d41b10342 Impact These notification discrepancies disrupt our subscription lifecycle management, potentially causing incorrect user entitlements, over-billing, and a degraded user experience. Requested Assistance Please confirm the correct notification types for these scenarios as per Apple’s documentation (e.g., App Store Server Notifications V2). Provide guidance on handling these edge cases, including any recent changes to notification logic. If this is a backend issue, please investigate the provided transaction IDs and Notification UUIDs. Thank you for your prompt assistance. We look forward to your response to help resolve this issue for our users. 问题描述: 我们在处理 App Store 订阅变更通知时发现,服务器接收到的通知类型与预期不符,具体表现为降级或同级升级场景下错误接收到 DID_RENEW 通知。这影响了我们后端订阅管理的逻辑,可能导致用户订阅状态和计费出现偏差。以下是两个可重现的场景及相关交易数据。 场景 1:高级年费(含 7 天试用)降级至标准年费 重现步骤: 用户购买高级年费订阅并开启 7 天免费试用(初始交易 ID:700002116066502)。 在试用期结束前一天,用户通过 App Store 的订阅管理界面降级至标准年费订阅。 预期行为: 我们预期会收到 DID_CHANGE_RENEWAL_STATUS 或类似的降级通知(如 DID_CHANGE_RENEWAL_PREF),以表明订阅计划已降级,从而允许我们立即更新用户订阅状态。 实际行为: 降级后,服务器立即收到 DID_RENEW 通知,而非降级相关通知。这导致系统误认为高级订阅仍在续订,而非降级为标准订阅,且后续未收到任何降级确认通知。 相关交易数据: 新交易 ID:700002122810952 通知类型:DID_RENEW 通知 UUID:26de9b0e-5d61-4893-b679-0bd18ae3d208 原始交易 ID(试用):700002116066502 场景 2:标准月费订阅升级为标准年费订阅 重现步骤: 用户购买标准月费订阅。 在月费订阅到期前一天,用户通过 App Store 的订阅管理界面切换至标准年费订阅。 预期行为: 原月费订阅正常到期,预期收到 DID_EXPIRE 通知。 月费到期后,预期收到新年费订阅的 DID_RENEW 或 SUBSCRIBED 通知,以确认年费订阅开始,方便我们无缝切换用户订阅计划。 实际行为: 升级后,服务器立即收到新年费订阅的 DID_RENEW 通知。 后续未收到任何通知,包括原月费订阅的 DID_EXPIRE 通知。这导致我们无法确认月费订阅的到期状态,可能在记录中出现重复的活跃订阅。 相关交易数据: 交易 ID:70002902074491 通知类型:DID_RENEW 通知 UUID:3d35bc2f-e769-4549-bf50-d84d41b10342 影响: 这些通知异常干扰了我们的订阅生命周期管理,可能导致用户权益错误、潜在的重复计费以及用户体验下降。 请求协助: 请确认根据 Apple 文档(例如 App Store Server Notifications V2)在上述场景中应返回的正确通知类型。 请提供处理此类边缘场景的指导,包括通知逻辑的任何最新变更。 如果这是后端问题,请调查提供的交易 ID 和通知 UUID。 感谢您的及时协助,期待您的回复以帮助我们为用户解决问题。
Replies
1
Boosts
0
Views
113
Activity
Sep ’25
Auto renewable subsction, auto renews to different product id
I need help understanding how the In-App purchase subscription works within the same subscription group and how the Order plays a role at the time of auto-renewal. In my case, I have 5 in-app products in the same group as given below with their Order, Order Product ---------------------- 1 Gold Annual 2 Gold Monthly 3 Silver Annual Old, 3 Silver Annual New 4 Silver Monthly One user purchased Silver annual New, at the time of expiry, after a year, it auto-renewed to Silver annual Old. My understanding is that if a user has purchased Silver annual New, at the time of renewal, it should renew with the same product, not with different product id. Is this behaviour expected? Is there any official document which can explain this behaviour? Note: There was no Order given before, recently we have changed the Order to manage Upgrade and Downgrade for In-App subscriptions.
Replies
0
Boosts
0
Views
76
Activity
Nov ’25
# Critical Bug: Apple servers not generating com.apple.developer.storekit entitlement for App ID com.driftnotes.app
Problem Summary Apple's provisioning servers are not generating the com.apple.developer.storekit entitlement for App ID com.driftnotes.app (Team ID: 43Y6AG5NPY), making it impossible to build iOS apps for physical devices despite all configurations being correct. Environment macOS: 15.3.1 (24D70) Xcode: 16.1 (xcode-select version 2409) Flutter: 3.35.2 • channel stable Account: Individual Developer (Kazakhstan) Bundle ID: com.driftnotes.app Team ID: 43Y6AG5NPY Error Message Error (Xcode): Provisioning profile "iOS Team Provisioning Profile: com.driftnotes.app" doesn't include the com.apple.developer.storekit entitlement. /Users/vyacheslavkuzin/Desktop/FlutterProjects/DriftNotesDart/ios/Runner.xcodeproj Steps to Reproduce Configure App ID with In-App Purchase capability (✅ verified in Developer Portal) Add In-App Purchase capability in Xcode project (✅ done) Configure entitlements file with StoreKit keys (✅ done) Enable automatic signing in Xcode (✅ done) Run: flutter build ios --release Build completes successfully ("Xcode build done. 13,8s") but fails at signing stage Expected vs Actual Result Expected: Provisioning profile should include com.apple.developer.storekit entitlement Actual: Profile is created WITHOUT the entitlement, despite all configurations being correct Configuration Details Developer Portal App ID com.driftnotes.app has In-App Purchase capability enabled ✅ All agreements are active in App Store Connect ✅ Xcode Project In-App Purchase capability added via Signing & Capabilities ✅ Automatically manage signing: Enabled ✅ Team: 43Y6AG5NPY (Vyacheslav Kuzin) ✅ Entitlements File (ios/Runner/Runner.entitlements) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.developer.in-app-payments</key> <array> <string>merchant.com.driftnotes.app</string> </array> <key>com.apple.developer.storekit</key> <true/> </dict> </plist> Build Settings CODE_SIGN_ENTITLEMENTS: Runner/Runner.entitlements ✅ PRODUCT_BUNDLE_IDENTIFIER: com.driftnotes.app ✅ DEVELOPMENT_TEAM: 43Y6AG5NPY ✅ Troubleshooting Attempted Multiple Attempts Profile Recreation: Manual and automatic profiles recreated dozens of times Cache Cleanup: Complete removal of: ~/Library/Developer/Xcode/DerivedData/* ~/Library/MobileDevice/Provisioning\ Profiles/* Flutter clean & pod cache clean Signing Methods: Tested both manual and automatic signing management Wait Periods: 48+ hours for server propagation Complete Profile Deletion: Removed ALL profiles from Developer account per Apple Support Apple Support Workaround Following Senior Advisor recommendation: ✅ Deleted all provisioning profiles from account ✅ Confirmed IAP capability in project ✅ Created StoreKit Configuration File for testing ✅ Verified automatic signing management ✅ Multiple "Try Again" attempts in Xcode Result: Problem persists Apple Support Reference Case #102680105923 - Senior Advisor Simone confirmed after internal team consultation that this requires engineering team attention and directed to Developer Forums. Technical Analysis What Works Flutter build completes successfully Pod install executes without issues (25,9s) Xcode build finishes successfully (13,8s) All dependencies resolve correctly What Fails Provisioning profile generation: Server creates profile but omits StoreKit entitlement All profile types affected: Both manual and automatic profiles Consistent across configurations: Debug, Release, Profile all fail identically Root Cause This appears to be a server-side bug where Apple's provisioning systems are not properly correlating the App ID's In-App Purchase capability with the StoreKit entitlement generation for this specific App ID (com.driftnotes.app). The issue is NOT in client-side configuration - all settings match Apple's official documentation exactly. The problem occurs during the server-side provisioning profile generation process. Request for Engineering Team This issue requires attention from Apple's provisioning infrastructure team to resolve the server-side entitlement generation bug for App ID com.driftnotes.app. Impact Critical: Complete inability to build iOS app for physical devices Business: Blocking app deployment and updates Developer Experience: Extensive time spent on troubleshooting correctly configured setup All configurations have been verified multiple times and match Apple's official documentation. The issue has been escalated through Apple Support (Case #102680105923) and requires engineering team intervention.
Replies
0
Boosts
0
Views
80
Activity
Sep ’25
Apple ID Password Is Required During In-App Purchase
Hello, I would like to ask about an Apple ID authentication behavior during in-app purchases. Our app uses a StoreKit 1 (SKPaymentQueue-based) implementation, and there are no differences in the in-app purchase logic between the TestFlight build and the App Store production build. However, we have observed that some users are prompted to enter their Apple ID password during in-app purchases. The observed behavior is as follows: On the first in-app purchase, the system prompts for the Apple ID password After the password is entered once, subsequent purchases proceed normally using Face ID (double side-button press) Even after deleting and reinstalling the app, or switching between TestFlight and App Store builds, the password prompt does not reappear if authentication has already occurred This behavior can occur even when the Apple ID already has an active auto-renewable subscription The only confirmed change on our side is: The app is now built with Xcode 26 instead of Xcode 18 Based on this, we are currently considering the following possible causes: A change in purchase authentication behavior due to the Xcode version update Expiration of the Apple ID purchase authentication session after a long period without purchases In addition to these points, we would like to ask: Are there any other common conditions or security policies in iOS or the App Store that may cause the system to require Apple ID password input during in-app purchases? Is this behavior considered expected under certain circumstances? We would appreciate your clarification on whether this is expected system behavior or if there are any implementation aspects we should further review. Thank you for your support.
Replies
0
Boosts
0
Views
89
Activity
Jan ’26