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

sandbox account isn't logging in on purchase window
I don't know if I am posting this in the right place. I am using xcode's phone simulator and I have setup my sandbox account on appstoreconnect under users and access/sandbox/test accounts then in my app on the simulator when I tap the subscribe button to purchase my product the a window pops up for in app purchases and I get a login prompt for my sandbox credentials, but no matter how many times I enter them after tapping ok all I get is a blank login prompt. also not this a brand new sandbox account and I've only changed the password 3 times, that seems to be important because its inconsistent with some of the errors I am getting on the error log here is error log. Purchase did not return a transaction: Error Domain=ASDErrorDomain Code=530 "(null)" UserInfo={NSUnderlyingError=0x600000d09080 {Error Domain=AMSErrorDomain Code=100 "Authentication Failed The authentication failed." UserInfo={NSMultipleUnderlyingErrorsKey=( "Error Domain=AMSErrorDomain Code=2 "Password reuse not available for account The account state does not support password reuse." UserInfo={NSDebugDescription=Password reuse not available for account The account state does not support password reuse., AMSDescription=Password reuse not available for account, AMSFailureReason=The account state does not support password reuse.}", "Error Domain=AMSErrorDomain Code=0 "Authentication Failed Encountered an unrecognized authentication failure." UserInfo={NSDebugDescription=Authentication Failed Encountered an unrecognized authentication failure., AMSDescription=Authentication Failed, AMSFailureReason=Encountered an unrecognized authentication failure.}" ), AMSDescription=Authentication Failed, NSDebugDescription=Authentication Failed The authentication failed., AMSFailureReason=The authentication failed.}}, client-environment-type=Sandbox}
0
0
120
May ’25
cant test in app purchases without it defaulting to family sharing request
hi, when i test an in app purchase with my sandbox acount i get: "ask permission. a request to buy ** will be sent to your parent or gardian (environment: sandbox)" When i made the sandbox account, i didnt see any option to set my age. What am i missing here, I dont seem to be able to fully test in app purchases since my sandbox accounts always defualt to this family sharing thing? Thanks
0
0
405
Aug ’25
Python App. Sandbox testing IAP Auto Renewal Subscription
I have created a Python app and built it with pyinstaller and codesigned everything. Now I want to Sandbox test it. In my appstore connect account i have created a subscriptions id. I read that if I am logged out from the AppStore and have codesigned my .app file with a Developer Certificate i should be able to run the app on my local mac and when i click on the "Buy" button it should connect to my app store connect setup. I have implemented StoreKit in my app and use a storekit_bridge to combine the .swift code with my python app. However when i run the app. I get this: "25-07-24 21:01:12,557 - FEC - WARNING - StoreKit: fetchProducts returned empty result 2025-07-24 21:01:12,557 - FEC - INFO - StoreKit fetch_products returned: {"products": []} 2025-07-24 21:01:12,557 - FEC - ERROR - StoreKit: Failed to parse product info: No products returned from JSON" And no login screen appears where I should be able to enter my Sandbox email adress and password. Anyone here who has experience with a Python app combined with In App Purchases? Hope someone can help me out with this.
0
0
152
Jul ’25
RequestReviewAction never triggering rating dialog
Hello, We are having an issue with the RequestReview API and were hoping to get some help. We know that there is no guarantee that the in-app review modal will show and we know that there are 3 circumstances in which it will definitely not appear: if the user has turned off in-app review/ratings in their settings if the user has submitted a review for that app on that device within the last 365 days if the user has been asked for a review >3 times in the last 365 days When testing our implementation, every single one of our testers did not receive the rating modal despite the fact that we had all our testers turn on the app rating setting and that we have never asked for reviews from our app before. So that seems suspicious. While it is possible that something is up with our code (and I have provided some snippets below) we are also concerned that apple maybe is suppressing it for another reason. We really want to go live with our app review code but unfortunately we are not able to get confidence that it will ever appear for the user. Can you please help us understand why this isn't working. The code: We are using the SwiftUI approach to requesting review. Here are some relevant code snippets Important to note, we have a modal that appears when the user is in our list of active, targeted users. If they tap yes on this modal, it should show the in app rate the app system modal. If they tap no, we present them with an airship survey so that they can give feedback. Here is the code for the Yes button action: @Environment(\.requestReview) private var requestReview private var yesButton: some View { Button( action: { dismiss() requestReview() }, label: { Text(Lingua.General.appRateFirstButton) .regularParagraph() .frame(width: 180, height: 35) } ) .customButtonStyle( foregroundColor: .black, backgroundColor: Color(.powderBlue), radius: 36 ) } and this is the logic we use to determine whether we want to show them the modal in the first place. Obviously, a lot of this code leads to deeper areas in our logic and code but to give an idea... private func showAppRateModalIfNeeded() { if preferencesManager.appRateReviewShown == nil, accountManager.userAccount?.permissions.rateTheApp == true { let appReviewModalVC = UIHostingController(rootView: AppReviewModal()) appReviewModalVC.view.backgroundColor = .init(white: 0, alpha: 0.6) appReviewModalVC.modalPresentationStyle = .overFullScreen appReviewModalVC.modalTransitionStyle = .crossDissolve parentVC?.navigationController?.present(appReviewModalVC, animated: true) preferencesManager.appRateReviewShown = true } } When testing in debug, we do find that the modal appears and works as expected. However, on release builds nobody is able to trigger it. Why? Are we doing something wrong here or is Apple just suppressing it. We are thinking about implementing the button taking the user directly into the app store review but we'd prefer to do the lower-friction dialog in-app if we can get it work so the user doesn't get sent out of the app.
2
0
158
Dec ’25
Sandbox apple in app purchases not working
Received error that does not have a corresponding StoreKit Error: Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase More details: Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase" UserInfo={AMSFailureReason=Server canceled the purchase, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?guid=00008110-000A4DC10E51401E, AMSDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later.\n\n[Environment: Sandbox]"; initialCheckboxValue = 1; isFree = 1; "m-allowed" = 0; message = "Unable to process your request."; okButtonString = OK; }; failureType = ""; "m-allowed" = 0; metrics = { actionUrl = "sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy"; asnState = 0; dialogId = "MZCommerce.SystemError"; eventType = dialog; message = "Unable to process your re"; mtEventTime = "2025-07-28 12:34:22 Etc/GMT"; mtTopic = "xp_its_main"; options = ( OK ); }; pings = ( ); }, NSDebugDescription=Purchase Failed Server canceled the purchase} Received error that does not have a corresponding StoreKit Error: Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={client-environment-type=Sandbox, storefront-country-code=IND, NSUnderlyingError=0x1276116e0 {Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase" UserInfo={AMSFailureReason=Server canceled the purchase, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?guid=00008110-000A4DC10E51401E, AMSDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later.\n\n[Environment: Sandbox]"; initialCheckboxValue = 1; isFree = 1; "m-allowed" = 0; message = "Unable to process your request."; okButtonString = OK; }; failureType = ""; "m-allowed" = 0; metrics = { actionUrl = "sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy"; asnState = 0; dialogId = "MZCommerce.SystemError"; eventType = dialog; message = "Unable to process your re"; mtEventTime = "2025-07-28 12:34:22 Etc/GMT"; mtTopic = "xp_its_main"; options = ( OK ); }; pings = ( ); }, NSDebugDescription=Purchase Failed Server canceled the purchase}}} Purchase did not return a transaction: Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={client-environment-type=Sandbox, storefront-country-code=IND, NSUnderlyingError=0x1276116e0 {Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase" UserInfo={AMSFailureReason=Server canceled the purchase, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?guid=00008110-000A4DC10E51401E, AMSDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later.\n\n[Environment: Sandbox]"; initialCheckboxValue = 1; isFree = 1; "m-allowed" = 0; message = "Unable to process your request."; okButtonString = OK; }; failureType = ""; "m-allowed" = 0; metrics = { actionUrl = "sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy"; asnState = 0; dialogId = "MZCommerce.SystemError"; eventType = dialog; message = "Unable to process your re"; mtEventTime = "2025-07-28 12:34:22 Etc/GMT"; mtTopic = "xp_its_main"; options = ( OK ); }; pings = ( ); }, NSDebugDescription=Purchase Failed Server canceled the purchase}}}
3
0
232
Jul ’25
"StoreKit Testing in Xcode" certificate is not trusted on iOS 26
Hello. I have setup a StoreKit testing in the app that was and still is perfectly working on iOS 18. Unfortunately when run on iOS 26 the following error gets printed in the console after calling Transaction.currentEntitlement(for:) method: Failed to verify certificate chain due to client recoverable failure: Error Domain=NSOSStatusErrorDomain Code=-67843 "“StoreKit Testing in Xcode” certificate is not trusted" UserInfo={NSLocalizedDescription=“StoreKit Testing in Xcode” certificate is not trusted, NSUnderlyingError=0x109de7e10 {Error Domain=NSOSStatusErrorDomain Code=-67843 "Certificate 0 “StoreKit Testing in Xcode” has errors: Root is not trusted;" UserInfo={NSLocalizedDescription=Certificate 0 “StoreKit Testing in Xcode” has errors: Root is not trusted;}}} I'm not seeting any StoreKit Testing certificates in phone's certificate trust settings. This test was performed on iOS 26.0 (23A341) with app built in Xcode 16.4. FB20339145
1
0
178
Nov ’25
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
109
Jul ’25
Same same originalTransactionId, productId, but the transactionIds are different.
We are seeing an issue with a potential duplicate charge issue regarding our app. We have received two transactions with the same originalTransactionId but different transactionIds: Order A: TransactionId: [Redacted_TID_1] OriginalTransactionId: [Redacted_OID_A] ProductId: weekly_subscription PurchaseDate: [Redacted_Timestamp_1] SignedDate: [Redacted_Timestamp_2] Price: [Redacted_Price] CAD Order B: TransactionId: [Redacted_TID_2] OriginalTransactionId: [Redacted_OID_A] ProductId: weekly_subscription PurchaseDate: [Redacted_Timestamp_1] SignedDate: [Redacted_Timestamp_3] Price: [Redacted_Price] CAD Both transactions share the same originalTransactionId, productId, and expiresDate, but the transactionIds are different. We wanna confirm: Were customers charged once or twice for this subscription renewal? If only one charge was made, how should we programmatically determine the “valid” transaction among multiple records with the same originalTransactionId to avoid confusion in our server-side receipt validation? We appreciate any clarification so we can handle this correctly in our backend.
1
0
272
Sep ’25
Help testing sandbox purchases in Xcode
I am attempting to test non-consumable sandbox purchases, however I am unable to select the "Approve Transaction" option after initiating a purchase. Leaving the transaction in a permanent pending state. I can view the transaction in the transaction manager by navigating to Debug -> Storekit -> Manage Transactions.. but the only available options to select are "Refund Transaction" or "Delete Transaction". What am I missing about Xcode or my environment that is preventing me from testing this purchase flow? Some more information about my set up. I have a .storekit file synced to AppStore Connect with my product Id. There's a StoreKitTestCertificate.cert saved in the project. I've tried running the build on my phone as Debug, Release and ReleaseForRunning all with the same behavior after making a purchase. My phone is logged into a sandbox test user apple account in the developer iOS settings
1
0
214
Sep ’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
147
Aug ’25
Guidance on Migrating Active Subscriptions from Apple Server Notifications v1 to v2
I’m reaching out regarding our existing in-app subscription implementation that currently uses App Store Server Notifications version 1 (v1). Our live application has a significant number of active recurring subscriptions that are being managed through the v1 webhook integration. We have now developed a revamped version of our application, which uses the same Apple Developer Account and App Store Connect setup, but in this new app version, we’ve implemented App Store Server Notifications version 2 (v2). Before moving forward with the migration, I would like to clarify the following points to ensure a smooth transition and avoid any disruptions to ongoing subscriptions: Backward Compatibility: Will existing active subscriptions (originally created and managed via v1 notifications) continue to work seamlessly once we switch to v2, or do we need to maintain both v1 and v2 endpoints during the transition? Notification Delivery: If both webhook versions are configured simultaneously, will Apple send notifications to both endpoints, or only the one currently configured in App Store Connect? Migration Strategy: What is Apple’s recommended best practice for migrating from v1 to v2 in a scenario where the live app still has active subscriptions tied to the v1 webhook? Potential Risks or Considerations: Are there any known limitations, delays, or issues that we should prepare for during this migration (for example, differences in payload structure or event types between v1 and v2 that could affect subscription lifecycle management)? I would greatly appreciate your guidance or documentation links that outline the correct migration steps and recommended approach for ensuring continuity of service for all existing subscribers.
0
0
146
Oct ’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
198
Dec ’25
Cannot repurchase subscription SKU — StoreKit keeps returning old expired transaction
Some users cannot repurchase a subscription SKU after it has expired. Flow: User previously subscribed. User canceled and the subscription fully expired. After weeks, user reinstalls the app and taps the same SKU. StoreKit does not create a new purchase transaction. Instead, StoreKit always returns the old expired transaction in updatedTransactions. Therefore, the user is permanently unable to purchase the SKU again. We have already tried: Adding payment observer at app launch Calling finishTransaction for all transactions Clearing queue at startup SKReceiptRefreshRequest Server-side verifyReceipt Ensuring subscription is truly expired (not in grace/retry) Not calling restoreCompletedTransactions None of these resolved the issue. StoreKit still only sends the old transaction and never generates a new one. Expected behavior: A new purchase transaction should be created when user taps the expired subscription SKU. Actual behavior: StoreKit repeatedly pushes the old expired transaction, blocking new purchases. We can provide: Some users cannot repurchase a subscription SKU after it has expired. Flow: User previously subscribed. User canceled and the subscription fully expired. After weeks, user reinstalls the app and taps the same SKU. StoreKit does not create a new purchase transaction. Instead, StoreKit always returns the old expired transaction in updatedTransactions. Therefore, the user is permanently unable to purchase the SKU again. We have already tried: Adding payment observer at app launch Calling finishTransaction for all transactions Clearing queue at startup SKReceiptRefreshRequest Server-side verifyReceipt Ensuring subscription is truly expired (not in grace/retry) Not calling restoreCompletedTransactions None of these resolved the issue. StoreKit still only sends the old transaction and never generates a new one. Expected behavior: A new purchase transaction should be created when user taps the expired subscription SKU. Actual behavior: StoreKit repeatedly pushes the old expired transaction, blocking new purchases. We can provide: Affected user’s base64 receipt verifyReceipt full response Transaction logs (transactionIdentifier, original_transaction_id, productIdentifier, state) Please help investigate why StoreKit is not allowing a new subscription purchase. Affected user’s base64 receipt verifyReceipt full response Transaction logs (transactionIdentifier, original_transaction_id, productIdentifier, state) Please help investigate why StoreKit is not allowing a new subscription purchase.
1
0
173
Jan ’26
IAP Product Info Error & Sandbox Webhook/Test API Failures
Hello, I'm suddenly encountering errors with In-App Purchases (IAP) and my sandbox webhooks, which were working correctly just a few days ago. Specifically: Product Information Error: I'm receiving the following error when trying to fetch product information: { "type": "product_info", "result": "error", "error": "An unknown error occurred" } This API call was functioning normally until today. Sandbox Webhook Failure: I have configured a sandbox webhook, but my server is not receiving any notifications from the App Store. Test API 401: When I attempt to call the test API (https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/test), I consistently receive a 401 (Unauthorized) error. Could you please provide some guidance on how to troubleshoot these issues? Thank you.
1
0
112
Nov ’25
Auto-renewable subscriptions not loading in React Native app after App Store approval when testing via Xcode
Hello, I’m facing an issue with auto-renewable subscriptions in my React Native iOS app using react-native-iap. Before App Store approval, everything worked perfectly — I could test in-app purchases and subscriptions locally on my iPhone through Xcode using a development build and sandbox tester. After the app was approved and released on the App Store, I needed to make some updates. Now, when I build and run the same project again through Xcode (Debug, development-signed) on my iPhone, the subscriptions no longer load. The same product IDs are approved and live in App Store Connect. The live version on the App Store works fine (subscriptions load and process successfully). But on the local Xcode build, getSubscriptions() returns invalid product identifiers or an empty list. No changes were made to the bundle ID or product IDs The build is signed with an Apple Development certificate and uses the same team and bundle identifier as the published app. “In-App Purchase” capability is enabled. I’ve also tried deleting the app, rebooting the device, and re-logging into a sandbox tester account, but the issue persists. It looks like the sandbox environment no longer works for the app after its first App Store release. Has anyone experienced this issue where the same approved app’s IAPs work in production but fail to load in sandbox/dev builds through Xcode after release? Any guidance on re-enabling sandbox testing for updates would be greatly appreciated. Environment: Xcode 26.0.1 React Native with react-native-iap Auto-renewable subscriptions (3 SKUs) Thank you!
0
0
113
Oct ’25
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
107
Sep ’25
in-app-purchase
I have implemented in-app-purchase in one of my iOS application. I generated the consumable products and these are approved by Apple Showing with Green check. I used StoreKit-2 and locally testing with store sync configuration file. The products I have created on developer accounts automatically synced by Store Configuration file and showing no the screen and I can purchase the product with Xcode environment in development. When uploaded app to test flight, I am not getting products with sandbox email id. Cases I have tries: -Remove my original Apple ID from device and just testing with Apple Sandbox email id -> Not getting products. -Remove configuration file while uploading to test flight -> not fetching products. -Checked the archive build and its in Release mode. Note: I am as a (Admin) team member, not account holder. Issue: Not fetching products on Test flight with Sandbox & in live app. Is there something I have to track here: Agreements for Free Apps Agreement with EFFECTIVE DATE showing 3 Jul 2025 - 20 Sep 2025 & STATUS is Active Agreements for Paid Apps Agreement with EFFECTIVE DATE showing nothing & STATUS is New
0
0
375
Sep ’25
Transaction.currentEntitlements failing every time
I am shown as being subscribed to our service in the Subscriptions list in settings yet when going to the Storekit2 page in my app it shows me as NOT being subscribed and is unresponsive. I select Restore Subscription, that grays briefly, asks for a password, then returns to blue and nothing else happens. Bouncing back and forth between monthly and yearly likewise gives no response. The Transaction.currentEntitlements seems to be empty so it thinks the user is not subscribed. I have unsubscribed, and resubscribed via the Settings page to no avail.
2
0
94
Apr ’25
sandbox account isn't logging in on purchase window
I don't know if I am posting this in the right place. I am using xcode's phone simulator and I have setup my sandbox account on appstoreconnect under users and access/sandbox/test accounts then in my app on the simulator when I tap the subscribe button to purchase my product the a window pops up for in app purchases and I get a login prompt for my sandbox credentials, but no matter how many times I enter them after tapping ok all I get is a blank login prompt. also not this a brand new sandbox account and I've only changed the password 3 times, that seems to be important because its inconsistent with some of the errors I am getting on the error log here is error log. Purchase did not return a transaction: Error Domain=ASDErrorDomain Code=530 "(null)" UserInfo={NSUnderlyingError=0x600000d09080 {Error Domain=AMSErrorDomain Code=100 "Authentication Failed The authentication failed." UserInfo={NSMultipleUnderlyingErrorsKey=( "Error Domain=AMSErrorDomain Code=2 "Password reuse not available for account The account state does not support password reuse." UserInfo={NSDebugDescription=Password reuse not available for account The account state does not support password reuse., AMSDescription=Password reuse not available for account, AMSFailureReason=The account state does not support password reuse.}", "Error Domain=AMSErrorDomain Code=0 "Authentication Failed Encountered an unrecognized authentication failure." UserInfo={NSDebugDescription=Authentication Failed Encountered an unrecognized authentication failure., AMSDescription=Authentication Failed, AMSFailureReason=Encountered an unrecognized authentication failure.}" ), AMSDescription=Authentication Failed, NSDebugDescription=Authentication Failed The authentication failed., AMSFailureReason=The authentication failed.}}, client-environment-type=Sandbox}
Replies
0
Boosts
0
Views
120
Activity
May ’25
cant test in app purchases without it defaulting to family sharing request
hi, when i test an in app purchase with my sandbox acount i get: "ask permission. a request to buy ** will be sent to your parent or gardian (environment: sandbox)" When i made the sandbox account, i didnt see any option to set my age. What am i missing here, I dont seem to be able to fully test in app purchases since my sandbox accounts always defualt to this family sharing thing? Thanks
Replies
0
Boosts
0
Views
405
Activity
Aug ’25
Python App. Sandbox testing IAP Auto Renewal Subscription
I have created a Python app and built it with pyinstaller and codesigned everything. Now I want to Sandbox test it. In my appstore connect account i have created a subscriptions id. I read that if I am logged out from the AppStore and have codesigned my .app file with a Developer Certificate i should be able to run the app on my local mac and when i click on the "Buy" button it should connect to my app store connect setup. I have implemented StoreKit in my app and use a storekit_bridge to combine the .swift code with my python app. However when i run the app. I get this: "25-07-24 21:01:12,557 - FEC - WARNING - StoreKit: fetchProducts returned empty result 2025-07-24 21:01:12,557 - FEC - INFO - StoreKit fetch_products returned: {"products": []} 2025-07-24 21:01:12,557 - FEC - ERROR - StoreKit: Failed to parse product info: No products returned from JSON" And no login screen appears where I should be able to enter my Sandbox email adress and password. Anyone here who has experience with a Python app combined with In App Purchases? Hope someone can help me out with this.
Replies
0
Boosts
0
Views
152
Activity
Jul ’25
RequestReviewAction never triggering rating dialog
Hello, We are having an issue with the RequestReview API and were hoping to get some help. We know that there is no guarantee that the in-app review modal will show and we know that there are 3 circumstances in which it will definitely not appear: if the user has turned off in-app review/ratings in their settings if the user has submitted a review for that app on that device within the last 365 days if the user has been asked for a review >3 times in the last 365 days When testing our implementation, every single one of our testers did not receive the rating modal despite the fact that we had all our testers turn on the app rating setting and that we have never asked for reviews from our app before. So that seems suspicious. While it is possible that something is up with our code (and I have provided some snippets below) we are also concerned that apple maybe is suppressing it for another reason. We really want to go live with our app review code but unfortunately we are not able to get confidence that it will ever appear for the user. Can you please help us understand why this isn't working. The code: We are using the SwiftUI approach to requesting review. Here are some relevant code snippets Important to note, we have a modal that appears when the user is in our list of active, targeted users. If they tap yes on this modal, it should show the in app rate the app system modal. If they tap no, we present them with an airship survey so that they can give feedback. Here is the code for the Yes button action: @Environment(\.requestReview) private var requestReview private var yesButton: some View { Button( action: { dismiss() requestReview() }, label: { Text(Lingua.General.appRateFirstButton) .regularParagraph() .frame(width: 180, height: 35) } ) .customButtonStyle( foregroundColor: .black, backgroundColor: Color(.powderBlue), radius: 36 ) } and this is the logic we use to determine whether we want to show them the modal in the first place. Obviously, a lot of this code leads to deeper areas in our logic and code but to give an idea... private func showAppRateModalIfNeeded() { if preferencesManager.appRateReviewShown == nil, accountManager.userAccount?.permissions.rateTheApp == true { let appReviewModalVC = UIHostingController(rootView: AppReviewModal()) appReviewModalVC.view.backgroundColor = .init(white: 0, alpha: 0.6) appReviewModalVC.modalPresentationStyle = .overFullScreen appReviewModalVC.modalTransitionStyle = .crossDissolve parentVC?.navigationController?.present(appReviewModalVC, animated: true) preferencesManager.appRateReviewShown = true } } When testing in debug, we do find that the modal appears and works as expected. However, on release builds nobody is able to trigger it. Why? Are we doing something wrong here or is Apple just suppressing it. We are thinking about implementing the button taking the user directly into the app store review but we'd prefer to do the lower-friction dialog in-app if we can get it work so the user doesn't get sent out of the app.
Replies
2
Boosts
0
Views
158
Activity
Dec ’25
Sandbox apple in app purchases not working
Received error that does not have a corresponding StoreKit Error: Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase More details: Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase" UserInfo={AMSFailureReason=Server canceled the purchase, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?guid=00008110-000A4DC10E51401E, AMSDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later.\n\n[Environment: Sandbox]"; initialCheckboxValue = 1; isFree = 1; "m-allowed" = 0; message = "Unable to process your request."; okButtonString = OK; }; failureType = ""; "m-allowed" = 0; metrics = { actionUrl = "sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy"; asnState = 0; dialogId = "MZCommerce.SystemError"; eventType = dialog; message = "Unable to process your re"; mtEventTime = "2025-07-28 12:34:22 Etc/GMT"; mtTopic = "xp_its_main"; options = ( OK ); }; pings = ( ); }, NSDebugDescription=Purchase Failed Server canceled the purchase} Received error that does not have a corresponding StoreKit Error: Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={client-environment-type=Sandbox, storefront-country-code=IND, NSUnderlyingError=0x1276116e0 {Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase" UserInfo={AMSFailureReason=Server canceled the purchase, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?guid=00008110-000A4DC10E51401E, AMSDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later.\n\n[Environment: Sandbox]"; initialCheckboxValue = 1; isFree = 1; "m-allowed" = 0; message = "Unable to process your request."; okButtonString = OK; }; failureType = ""; "m-allowed" = 0; metrics = { actionUrl = "sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy"; asnState = 0; dialogId = "MZCommerce.SystemError"; eventType = dialog; message = "Unable to process your re"; mtEventTime = "2025-07-28 12:34:22 Etc/GMT"; mtTopic = "xp_its_main"; options = ( OK ); }; pings = ( ); }, NSDebugDescription=Purchase Failed Server canceled the purchase}}} Purchase did not return a transaction: Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={client-environment-type=Sandbox, storefront-country-code=IND, NSUnderlyingError=0x1276116e0 {Error Domain=AMSErrorDomain Code=305 "Purchase Failed Server canceled the purchase" UserInfo={AMSFailureReason=Server canceled the purchase, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?guid=00008110-000A4DC10E51401E, AMSDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later.\n\n[Environment: Sandbox]"; initialCheckboxValue = 1; isFree = 1; "m-allowed" = 0; message = "Unable to process your request."; okButtonString = OK; }; failureType = ""; "m-allowed" = 0; metrics = { actionUrl = "sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy"; asnState = 0; dialogId = "MZCommerce.SystemError"; eventType = dialog; message = "Unable to process your re"; mtEventTime = "2025-07-28 12:34:22 Etc/GMT"; mtTopic = "xp_its_main"; options = ( OK ); }; pings = ( ); }, NSDebugDescription=Purchase Failed Server canceled the purchase}}}
Replies
3
Boosts
0
Views
232
Activity
Jul ’25
"StoreKit Testing in Xcode" certificate is not trusted on iOS 26
Hello. I have setup a StoreKit testing in the app that was and still is perfectly working on iOS 18. Unfortunately when run on iOS 26 the following error gets printed in the console after calling Transaction.currentEntitlement(for:) method: Failed to verify certificate chain due to client recoverable failure: Error Domain=NSOSStatusErrorDomain Code=-67843 "“StoreKit Testing in Xcode” certificate is not trusted" UserInfo={NSLocalizedDescription=“StoreKit Testing in Xcode” certificate is not trusted, NSUnderlyingError=0x109de7e10 {Error Domain=NSOSStatusErrorDomain Code=-67843 "Certificate 0 “StoreKit Testing in Xcode” has errors: Root is not trusted;" UserInfo={NSLocalizedDescription=Certificate 0 “StoreKit Testing in Xcode” has errors: Root is not trusted;}}} I'm not seeting any StoreKit Testing certificates in phone's certificate trust settings. This test was performed on iOS 26.0 (23A341) with app built in Xcode 16.4. FB20339145
Replies
1
Boosts
0
Views
178
Activity
Nov ’25
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
109
Activity
Jul ’25
Same same originalTransactionId, productId, but the transactionIds are different.
We are seeing an issue with a potential duplicate charge issue regarding our app. We have received two transactions with the same originalTransactionId but different transactionIds: Order A: TransactionId: [Redacted_TID_1] OriginalTransactionId: [Redacted_OID_A] ProductId: weekly_subscription PurchaseDate: [Redacted_Timestamp_1] SignedDate: [Redacted_Timestamp_2] Price: [Redacted_Price] CAD Order B: TransactionId: [Redacted_TID_2] OriginalTransactionId: [Redacted_OID_A] ProductId: weekly_subscription PurchaseDate: [Redacted_Timestamp_1] SignedDate: [Redacted_Timestamp_3] Price: [Redacted_Price] CAD Both transactions share the same originalTransactionId, productId, and expiresDate, but the transactionIds are different. We wanna confirm: Were customers charged once or twice for this subscription renewal? If only one charge was made, how should we programmatically determine the “valid” transaction among multiple records with the same originalTransactionId to avoid confusion in our server-side receipt validation? We appreciate any clarification so we can handle this correctly in our backend.
Replies
1
Boosts
0
Views
272
Activity
Sep ’25
isEligibleForIntroOffer(for:) aways return ture
Product.SubscriptionInfo.isEligibleForIntroOffer(for: "21340582") In the production environment, I have already used the intro offer for this group, but this method still returns true.
Replies
1
Boosts
0
Views
81
Activity
May ’25
Help testing sandbox purchases in Xcode
I am attempting to test non-consumable sandbox purchases, however I am unable to select the "Approve Transaction" option after initiating a purchase. Leaving the transaction in a permanent pending state. I can view the transaction in the transaction manager by navigating to Debug -> Storekit -> Manage Transactions.. but the only available options to select are "Refund Transaction" or "Delete Transaction". What am I missing about Xcode or my environment that is preventing me from testing this purchase flow? Some more information about my set up. I have a .storekit file synced to AppStore Connect with my product Id. There's a StoreKitTestCertificate.cert saved in the project. I've tried running the build on my phone as Debug, Release and ReleaseForRunning all with the same behavior after making a purchase. My phone is logged into a sandbox test user apple account in the developer iOS settings
Replies
1
Boosts
0
Views
214
Activity
Sep ’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
147
Activity
Aug ’25
Guidance on Migrating Active Subscriptions from Apple Server Notifications v1 to v2
I’m reaching out regarding our existing in-app subscription implementation that currently uses App Store Server Notifications version 1 (v1). Our live application has a significant number of active recurring subscriptions that are being managed through the v1 webhook integration. We have now developed a revamped version of our application, which uses the same Apple Developer Account and App Store Connect setup, but in this new app version, we’ve implemented App Store Server Notifications version 2 (v2). Before moving forward with the migration, I would like to clarify the following points to ensure a smooth transition and avoid any disruptions to ongoing subscriptions: Backward Compatibility: Will existing active subscriptions (originally created and managed via v1 notifications) continue to work seamlessly once we switch to v2, or do we need to maintain both v1 and v2 endpoints during the transition? Notification Delivery: If both webhook versions are configured simultaneously, will Apple send notifications to both endpoints, or only the one currently configured in App Store Connect? Migration Strategy: What is Apple’s recommended best practice for migrating from v1 to v2 in a scenario where the live app still has active subscriptions tied to the v1 webhook? Potential Risks or Considerations: Are there any known limitations, delays, or issues that we should prepare for during this migration (for example, differences in payload structure or event types between v1 and v2 that could affect subscription lifecycle management)? I would greatly appreciate your guidance or documentation links that outline the correct migration steps and recommended approach for ensuring continuity of service for all existing subscribers.
Replies
0
Boosts
0
Views
146
Activity
Oct ’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
198
Activity
Dec ’25
Cannot repurchase subscription SKU — StoreKit keeps returning old expired transaction
Some users cannot repurchase a subscription SKU after it has expired. Flow: User previously subscribed. User canceled and the subscription fully expired. After weeks, user reinstalls the app and taps the same SKU. StoreKit does not create a new purchase transaction. Instead, StoreKit always returns the old expired transaction in updatedTransactions. Therefore, the user is permanently unable to purchase the SKU again. We have already tried: Adding payment observer at app launch Calling finishTransaction for all transactions Clearing queue at startup SKReceiptRefreshRequest Server-side verifyReceipt Ensuring subscription is truly expired (not in grace/retry) Not calling restoreCompletedTransactions None of these resolved the issue. StoreKit still only sends the old transaction and never generates a new one. Expected behavior: A new purchase transaction should be created when user taps the expired subscription SKU. Actual behavior: StoreKit repeatedly pushes the old expired transaction, blocking new purchases. We can provide: Some users cannot repurchase a subscription SKU after it has expired. Flow: User previously subscribed. User canceled and the subscription fully expired. After weeks, user reinstalls the app and taps the same SKU. StoreKit does not create a new purchase transaction. Instead, StoreKit always returns the old expired transaction in updatedTransactions. Therefore, the user is permanently unable to purchase the SKU again. We have already tried: Adding payment observer at app launch Calling finishTransaction for all transactions Clearing queue at startup SKReceiptRefreshRequest Server-side verifyReceipt Ensuring subscription is truly expired (not in grace/retry) Not calling restoreCompletedTransactions None of these resolved the issue. StoreKit still only sends the old transaction and never generates a new one. Expected behavior: A new purchase transaction should be created when user taps the expired subscription SKU. Actual behavior: StoreKit repeatedly pushes the old expired transaction, blocking new purchases. We can provide: Affected user’s base64 receipt verifyReceipt full response Transaction logs (transactionIdentifier, original_transaction_id, productIdentifier, state) Please help investigate why StoreKit is not allowing a new subscription purchase. Affected user’s base64 receipt verifyReceipt full response Transaction logs (transactionIdentifier, original_transaction_id, productIdentifier, state) Please help investigate why StoreKit is not allowing a new subscription purchase.
Replies
1
Boosts
0
Views
173
Activity
Jan ’26
IAP Product Info Error & Sandbox Webhook/Test API Failures
Hello, I'm suddenly encountering errors with In-App Purchases (IAP) and my sandbox webhooks, which were working correctly just a few days ago. Specifically: Product Information Error: I'm receiving the following error when trying to fetch product information: { "type": "product_info", "result": "error", "error": "An unknown error occurred" } This API call was functioning normally until today. Sandbox Webhook Failure: I have configured a sandbox webhook, but my server is not receiving any notifications from the App Store. Test API 401: When I attempt to call the test API (https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/test), I consistently receive a 401 (Unauthorized) error. Could you please provide some guidance on how to troubleshoot these issues? Thank you.
Replies
1
Boosts
0
Views
112
Activity
Nov ’25
Clarification Needed: Using action=write-review outside of the app
Hello, is it allowed to include the action=write-review URL parameter in customer support emails to direct users to the App Store review page? Example: https://apps.apple.com/app/id[APP_ID]?action=write-review I want to make it easy for customers to leave feedback after positive support interactions, but only if it's compliant.
Replies
0
Boosts
0
Views
135
Activity
Oct ’25
Auto-renewable subscriptions not loading in React Native app after App Store approval when testing via Xcode
Hello, I’m facing an issue with auto-renewable subscriptions in my React Native iOS app using react-native-iap. Before App Store approval, everything worked perfectly — I could test in-app purchases and subscriptions locally on my iPhone through Xcode using a development build and sandbox tester. After the app was approved and released on the App Store, I needed to make some updates. Now, when I build and run the same project again through Xcode (Debug, development-signed) on my iPhone, the subscriptions no longer load. The same product IDs are approved and live in App Store Connect. The live version on the App Store works fine (subscriptions load and process successfully). But on the local Xcode build, getSubscriptions() returns invalid product identifiers or an empty list. No changes were made to the bundle ID or product IDs The build is signed with an Apple Development certificate and uses the same team and bundle identifier as the published app. “In-App Purchase” capability is enabled. I’ve also tried deleting the app, rebooting the device, and re-logging into a sandbox tester account, but the issue persists. It looks like the sandbox environment no longer works for the app after its first App Store release. Has anyone experienced this issue where the same approved app’s IAPs work in production but fail to load in sandbox/dev builds through Xcode after release? Any guidance on re-enabling sandbox testing for updates would be greatly appreciated. Environment: Xcode 26.0.1 React Native with react-native-iap Auto-renewable subscriptions (3 SKUs) Thank you!
Replies
0
Boosts
0
Views
113
Activity
Oct ’25
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
107
Activity
Sep ’25
in-app-purchase
I have implemented in-app-purchase in one of my iOS application. I generated the consumable products and these are approved by Apple Showing with Green check. I used StoreKit-2 and locally testing with store sync configuration file. The products I have created on developer accounts automatically synced by Store Configuration file and showing no the screen and I can purchase the product with Xcode environment in development. When uploaded app to test flight, I am not getting products with sandbox email id. Cases I have tries: -Remove my original Apple ID from device and just testing with Apple Sandbox email id -> Not getting products. -Remove configuration file while uploading to test flight -> not fetching products. -Checked the archive build and its in Release mode. Note: I am as a (Admin) team member, not account holder. Issue: Not fetching products on Test flight with Sandbox & in live app. Is there something I have to track here: Agreements for Free Apps Agreement with EFFECTIVE DATE showing 3 Jul 2025 - 20 Sep 2025 & STATUS is Active Agreements for Paid Apps Agreement with EFFECTIVE DATE showing nothing & STATUS is New
Replies
0
Boosts
0
Views
375
Activity
Sep ’25
Transaction.currentEntitlements failing every time
I am shown as being subscribed to our service in the Subscriptions list in settings yet when going to the Storekit2 page in my app it shows me as NOT being subscribed and is unresponsive. I select Restore Subscription, that grays briefly, asks for a password, then returns to blue and nothing else happens. Bouncing back and forth between monthly and yearly likewise gives no response. The Transaction.currentEntitlements seems to be empty so it thinks the user is not subscribed. I have unsubscribed, and resubscribed via the Settings page to no avail.
Replies
2
Boosts
0
Views
94
Activity
Apr ’25