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

Cannot Cancel Sandbox Subscription
I did an in app purchase in my development app and now I cannot get rid of it. It is a "monthly" subscription that seems to renew every 1 day. I can see the subscription when I go to settings then tap on Subscriptions. Then I tap the item and choose "Cancel Subscription", revealing a new modal sheet saying "Confirm Cancellation". When I "Confirm", I get the popup: "Your request is temporarily unable to be processed, please try again later". However, this is anything BUT temporary, has gone on for a couple weeks now. As such, I am unable to test subscriptions in my development app. I've tried logging out, restarting, different devices, etc. The phone is logged in under my primary user account, and I may not have been logged into sandbox email when I did the purchase. Can someone forcibly remove it for me?
2
2
710
Nov ’25
SKErrorDomain Code 2 Problem
We are facing a serious issues with in app purchases in our app. We offer 3 IAP: auto-renewable subscription 1W, auto-renewable subscription 1Y, non-consumable one-time purchase (LifeTime access) In our case 90-95% of transactions fail and we mostly get SKError code=2 . Sometime purchase fails several times for the same user so it’s very hard to believe that user intentionally cancels transaction for the same product 4 or even 5 times in a row. It happens regardless iOS version, device model, our app version. We've checked multiple threads with the same issue but coudn't find any solution. We do not offer any promotions, product identifiers are valid... Some users are able to make a purchases without any issues.
1
0
262
Jul ’25
Urgent - React Native IAP Issue
While using react-native-iap and being successfully connected with initConnection() I'm not receiving information on subscriptions with requestSubscription(). Attaching the code here, if anyone could assist asap please would be really grateful thanks! Been at it all day and just can't figure. const handleBuySubscription = async (productId) => { try { await requestSubscription({ sku: productId, }); setLoading(false); } catch (error) { setLoading(false); if (error instanceof PurchaseError) { errorLog({ message: [${error.code}]: ${error.message}, error }); } else { errorLog({ message: "handleBuySubscription", error }); } } }; but the requestSubscription({ sku: productId, })
0
0
117
Aug ’25
Why Non-Consumable product has originalTransactionId?
I try to call Get Transaction Info from App Store Server API, and the transactionId is for a Non-consumable type product, but it is odd that there are so many different transactionId and they have a same originalTransactionId { "bundleId": "${bundleId}", "environment": "Production", "inAppOwnershipType": "PURCHASED", "originalPurchaseDate": 1691220528000, "originalTransactionId": "${originalTransactionId}", "productId": "${productId}", "purchaseDate": 1691220528000, "quantity": 1, "signedDate": 1692590989925, "storefront": "USA", "storefrontId": "143441", "transactionId": "${originalTransactionId}", "transactionReason": "PURCHASE", "type": "Non-Consumable" } the defination of Non-Consumable is can only purchase once for same apple account. But why there would have originalTransactionId?
4
0
1.2k
1w
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
154
Jul ’25
App Store Server Notifications Update
Hello Apple Support Team, We're a developer team that has created an app with subscription-based features, and we've been using App Store Server Notifications to receive updates about user subscription status changes. I'm reaching out to inquire about potential modifications to the App Store Server Notifications approach that might have improved notification delivery times for my app. So on our appstore app, when a user purchases a subscription, the apple server notifications reach our server and send us the complete detail of that user’s purchase for eg he upgraded or downgraded etc. And then based on the data we receive from app store server notifications, we save it in our database, along with updating the users subscription table in the database. Previously, we experienced delays in receiving the real time notifications from apple on our server, sometimes taking a few minutes, while other times they would arrive immediately. And because of this issue, the users faced delay in seeing their subscription updates, as our db was updated only after the app store server notification reached our server. However, recently, we've noticed a significant improvement, and notifications are now being delivered still in real-time, but without any noticeable delays. I'm wondering if Apple has made any changes to the App Store Server Notifications system that might have resolved the delay issue. Could you please confirm if any modifications were made in 2025, specifically from January onwards, that might have improved notification delivery times? Additionally, I'd like to know if these changes apply to both sandbox testing and production environments. If possible, could you please provide more information about the changes or direct me to a resource that might explain the updates? I'd appreciate your assistance in confirming this information, and I'm looking forward to hearing back from you.
0
0
149
May ’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
117
Jul ’25
Storekit, how to change and retrieve current user storefront
I've been struggling to work with the Storekit framework and specifically to find the current Storefront used by the user of the app. Context : My app needs to behave differently depending on the country of the user. For me relying on Locale.current.region?.identifier does not seem very reliable, the user can change it really easily. I'm trying to use the Storekit framework like so : if let storefront = await StoreKit.Storefront.current{ return storefront.countryCode } As per Apple's Storekit documentation : Use current to determine a customer's current storefront region and offer in-app products suitable for that region. You maintain your own list of product identifiers and the storefronts in which you make them available. But I just can't find out what I need to change in my current configuration to get another country. The code keeps returning my original storefront (which is France) I've tried login in with a sandbox user defined on another country. Changed all settings on my device to another country. Changed my Apple's account region as described here. Also tried to logout from everything. The only thing that works is setting a local .storekit file as described here and changing the default storefront. Is Xcode overriding the default storefront when building on debug or TestFlight? does anyone know how can I test different storefronts with sandbox users without the local storekit file ? Thank you in advance.
3
2
630
Oct ’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
AppDistributor.current never returning
I try to access the AppDistributor.current (using try await) and the property never seem to return nor throw. The code I'm using looks like this: do { print("accessing current") let current = try await AppDistributor.current print("current obtained") switch(current) { case .appStore: return "AppStore" default: return "Unknown" } } catch { return "Exception: \(error)" } But the log only shows the accessing current and never the current obtained. Trying to step in the property starts with some assembly, but at some point, the debugger just never returned. I join a full Swift file of a sample test I'm using: SwiftMarketplaceTests.swift
0
0
154
Jun ’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
Purchange is complete but show pending in account.
Hello I have buy new developepr membership and i have get email for purchase confirmation but when ihave go to developer account i have seen this. Purchase your membership. To continue your enrollment, complete your purchase now. Your purchase may take up to 48 hours to process. Please help me why it will show this if i have already buy the membership and i itune i have check and see the member is active and valid for 1 year. Please help me to solve this issue.
3
0
2.2k
3w
Purchase Confirmation Letter
Hi all, I've received emails from other apps after making a purchase, with content like: You have purchased {App Name} on {Date & Time} and acknowledged that if you download or use this in-App Purchase within fourteen days of buying it, you will no longer be eligible to cancel this purchase. Do anyone know under what circumstances Apple sends a Purchase Confirmation Letter to the user's email after they purchase our digital products via IAP? Is this something developers can control? Additionally, I've seen pop-up reminders before making a payment in apps, with content similar to the above message. Are these reminders provided by Apple, or can developers create their own guidance to help users avoid accidental purchases? Kindly, Vanto
1
0
96
Jun ’25
SKAN / AdAttributionKit Development Postback Not Triggering
We’re testing SKAN postbacks via AdAttributionKit but aren’t receiving any requests on our server even after generating development impressions and triggering a postback. Setup: Domain: https://linkrunner-skan.com Configured in Info.plist as: <key>NSAdvertisingAttributionReportEndpoint</key> <string>https://linkrunner-skan.com</string> <key>AttributionCopyEndpoint</key> <string>https://linkrunner-skan.com</string> Apple automatically appends the .well-known paths: /.well-known/private-click-measurement/report-attribution/ /.well-known/skadnetwork/report-attribution/ ATS diagnostics for the domain: PASS for all tests (TLS 1.0–1.3, PFS disabled, arbitrary loads allowed, etc.) Both .well-known paths are publicly accessible and return 200 OK Testing Flow: Enabled Developer → AdAttributionKit Developer Mode on iOS (15+) Followed Apple’s official guide: Testing AdAttributionKit with Developer Mode Generated test impression using: createAdAttributionKitDevelopmentImpression implemented in SKANManager.swift Called Postback.updateConversionValue with lockPostback: true Created Development Postback from Developer Settings Waited 30+ minutes while intercepting server requests (proxy + backend logs) What We’ve Tried So Far: Confirmed ATS compliance with nscurl --ats-diagnostics (all PASS) Verified .well-known paths are accessible publicly without redirects Tested endpoints manually with a POST request – server responds 200 OK Confirmed Info.plist entries exactly match Apple’s required keys Double-checked iOS device is running iOS 15+ with Developer Mode enabled Repeated test flow multiple times with fresh impressions and postbacks Waited up to 1 hour for postback (in case of delays) Issue: No POST requests are being received from Apple to either .well-known endpoint, even though the setup appears correct and ATS tests pass. References Used: Configuring an Advertised App Generating JWS Impressions Question: Has anyone faced a similar issue where AdAttributionKit Development Postbacks are not firing despite correct Info.plist setup, ATS compliance, and reachable .well-known endpoints? Any insight into possible missing configuration steps or testing nuances would be greatly appreciated.
0
1
124
Aug ’25
StoreKit 2 Fails to Load Subscription Products
We are experiencing a critical issue where StoreKit 2 is returning empty products when using Product.products(for:), specifically on devices running iOS 18.4.

 This issue does not occur on iOS 18.3 or earlier.

 Steps:

 Created a subscription product (e.g. "upm1") in App Store Connect
 Confirmed the product is active, localised, and part of a valid subscription group
 Call the following Swift code using StoreKit 2:
 Task { do { let products = try await Product.products(for: ["upm1"]) print(products) } catch { print("Error: (error)") } } 4. Result: products is an empty list.

 This regression is blocking subscription testing on iOS 18.4. 

 Kindly someone please advise on a potential fix or workaround.
2
2
302
Sep ’25
Problem with Family sharing on live build.
Hello, Our app supports family sharing skus, we successfully ran tests on family sharing features in sandbox, we noticed the feature does not work on live builds. The family member (child account) do see the subscription as "Shared with you". We attempted to restore multiple time and there is nothing to restore.
0
0
94
Aug ’25
App Store Receipt Availability After Pre-order Install and Future Deprecation Concerns
Hi Apple Developer Team, I'm looking to confirm some technical details regarding the pre-order flow and App Store receipt handling. Specifically, I have the following questions: Q1: After a user installs an app via pre-order and launches it for the first time, will a valid App Store receipt be available immediately via [[NSBundle mainBundle] appStoreReceiptURL]? Are there any known cases where the receipt might be missing or invalid, requiring a manual refresh (e.g., via SKReceiptRefreshRequest)? Q2: Is the pre-order flow currently supported in the sandbox environment? Specifically, is it possible to simulate pre-ordering an app and installing it in a sandbox or TestFlight environment, in order to test receipt generation and related logic? https://developer.apple.com/documentation/appstorereceipts/responsebody/receipt Q3: The receipt field in the App Store receipt structure is marked as deprecated. Is it still acceptable to use this field for validating receipts? Has Apple announced any timeline or system version in which this field will be fully removed or unsupported?
0
0
99
Jun ’25
IAPs after rejection
Hi everyone, After my app was initially rejected, all my IAPs now show the status “Developer Action Needed.” There’s nothing I need to change on them — they were just returned because it was the first submission and the app itself was rejected. Do I need to do anything with the IAPs before I resubmit, or will they still be attached and reviewed automatically with the next binary? I’ve seen conflicting answers, so I’d like to confirm the correct process. Thanks, Cris
1
0
116
Aug ’25
Cannot Cancel Sandbox Subscription
I did an in app purchase in my development app and now I cannot get rid of it. It is a "monthly" subscription that seems to renew every 1 day. I can see the subscription when I go to settings then tap on Subscriptions. Then I tap the item and choose "Cancel Subscription", revealing a new modal sheet saying "Confirm Cancellation". When I "Confirm", I get the popup: "Your request is temporarily unable to be processed, please try again later". However, this is anything BUT temporary, has gone on for a couple weeks now. As such, I am unable to test subscriptions in my development app. I've tried logging out, restarting, different devices, etc. The phone is logged in under my primary user account, and I may not have been logged into sandbox email when I did the purchase. Can someone forcibly remove it for me?
Replies
2
Boosts
2
Views
710
Activity
Nov ’25
SKErrorDomain Code 2 Problem
We are facing a serious issues with in app purchases in our app. We offer 3 IAP: auto-renewable subscription 1W, auto-renewable subscription 1Y, non-consumable one-time purchase (LifeTime access) In our case 90-95% of transactions fail and we mostly get SKError code=2 . Sometime purchase fails several times for the same user so it’s very hard to believe that user intentionally cancels transaction for the same product 4 or even 5 times in a row. It happens regardless iOS version, device model, our app version. We've checked multiple threads with the same issue but coudn't find any solution. We do not offer any promotions, product identifiers are valid... Some users are able to make a purchases without any issues.
Replies
1
Boosts
0
Views
262
Activity
Jul ’25
Don't receive ONE_TIME_CHARGE Notification
the email say this notification type will be available to you in production beginning May 27, 2025, and you can test it in sandbox now. but i didn't receice this notification yet
Replies
1
Boosts
2
Views
178
Activity
May ’25
Urgent - React Native IAP Issue
While using react-native-iap and being successfully connected with initConnection() I'm not receiving information on subscriptions with requestSubscription(). Attaching the code here, if anyone could assist asap please would be really grateful thanks! Been at it all day and just can't figure. const handleBuySubscription = async (productId) =&gt; { try { await requestSubscription({ sku: productId, }); setLoading(false); } catch (error) { setLoading(false); if (error instanceof PurchaseError) { errorLog({ message: [${error.code}]: ${error.message}, error }); } else { errorLog({ message: "handleBuySubscription", error }); } } }; but the requestSubscription({ sku: productId, })
Replies
0
Boosts
0
Views
117
Activity
Aug ’25
Why Non-Consumable product has originalTransactionId?
I try to call Get Transaction Info from App Store Server API, and the transactionId is for a Non-consumable type product, but it is odd that there are so many different transactionId and they have a same originalTransactionId { "bundleId": "${bundleId}", "environment": "Production", "inAppOwnershipType": "PURCHASED", "originalPurchaseDate": 1691220528000, "originalTransactionId": "${originalTransactionId}", "productId": "${productId}", "purchaseDate": 1691220528000, "quantity": 1, "signedDate": 1692590989925, "storefront": "USA", "storefrontId": "143441", "transactionId": "${originalTransactionId}", "transactionReason": "PURCHASE", "type": "Non-Consumable" } the defination of Non-Consumable is can only purchase once for same apple account. But why there would have originalTransactionId?
Replies
4
Boosts
0
Views
1.2k
Activity
1w
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
154
Activity
Jul ’25
App Store Server Notifications Update
Hello Apple Support Team, We're a developer team that has created an app with subscription-based features, and we've been using App Store Server Notifications to receive updates about user subscription status changes. I'm reaching out to inquire about potential modifications to the App Store Server Notifications approach that might have improved notification delivery times for my app. So on our appstore app, when a user purchases a subscription, the apple server notifications reach our server and send us the complete detail of that user’s purchase for eg he upgraded or downgraded etc. And then based on the data we receive from app store server notifications, we save it in our database, along with updating the users subscription table in the database. Previously, we experienced delays in receiving the real time notifications from apple on our server, sometimes taking a few minutes, while other times they would arrive immediately. And because of this issue, the users faced delay in seeing their subscription updates, as our db was updated only after the app store server notification reached our server. However, recently, we've noticed a significant improvement, and notifications are now being delivered still in real-time, but without any noticeable delays. I'm wondering if Apple has made any changes to the App Store Server Notifications system that might have resolved the delay issue. Could you please confirm if any modifications were made in 2025, specifically from January onwards, that might have improved notification delivery times? Additionally, I'd like to know if these changes apply to both sandbox testing and production environments. If possible, could you please provide more information about the changes or direct me to a resource that might explain the updates? I'd appreciate your assistance in confirming this information, and I'm looking forward to hearing back from you.
Replies
0
Boosts
0
Views
149
Activity
May ’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
117
Activity
Jul ’25
Storekit, how to change and retrieve current user storefront
I've been struggling to work with the Storekit framework and specifically to find the current Storefront used by the user of the app. Context : My app needs to behave differently depending on the country of the user. For me relying on Locale.current.region?.identifier does not seem very reliable, the user can change it really easily. I'm trying to use the Storekit framework like so : if let storefront = await StoreKit.Storefront.current{ return storefront.countryCode } As per Apple's Storekit documentation : Use current to determine a customer's current storefront region and offer in-app products suitable for that region. You maintain your own list of product identifiers and the storefronts in which you make them available. But I just can't find out what I need to change in my current configuration to get another country. The code keeps returning my original storefront (which is France) I've tried login in with a sandbox user defined on another country. Changed all settings on my device to another country. Changed my Apple's account region as described here. Also tried to logout from everything. The only thing that works is setting a local .storekit file as described here and changing the default storefront. Is Xcode overriding the default storefront when building on debug or TestFlight? does anyone know how can I test different storefronts with sandbox users without the local storekit file ? Thank you in advance.
Replies
3
Boosts
2
Views
630
Activity
Oct ’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
AppDistributor.current never returning
I try to access the AppDistributor.current (using try await) and the property never seem to return nor throw. The code I'm using looks like this: do { print("accessing current") let current = try await AppDistributor.current print("current obtained") switch(current) { case .appStore: return "AppStore" default: return "Unknown" } } catch { return "Exception: \(error)" } But the log only shows the accessing current and never the current obtained. Trying to step in the property starts with some assembly, but at some point, the debugger just never returned. I join a full Swift file of a sample test I'm using: SwiftMarketplaceTests.swift
Replies
0
Boosts
0
Views
154
Activity
Jun ’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
Purchange is complete but show pending in account.
Hello I have buy new developepr membership and i have get email for purchase confirmation but when ihave go to developer account i have seen this. Purchase your membership. To continue your enrollment, complete your purchase now. Your purchase may take up to 48 hours to process. Please help me why it will show this if i have already buy the membership and i itune i have check and see the member is active and valid for 1 year. Please help me to solve this issue.
Replies
3
Boosts
0
Views
2.2k
Activity
3w
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
Purchase Confirmation Letter
Hi all, I've received emails from other apps after making a purchase, with content like: You have purchased {App Name} on {Date & Time} and acknowledged that if you download or use this in-App Purchase within fourteen days of buying it, you will no longer be eligible to cancel this purchase. Do anyone know under what circumstances Apple sends a Purchase Confirmation Letter to the user's email after they purchase our digital products via IAP? Is this something developers can control? Additionally, I've seen pop-up reminders before making a payment in apps, with content similar to the above message. Are these reminders provided by Apple, or can developers create their own guidance to help users avoid accidental purchases? Kindly, Vanto
Replies
1
Boosts
0
Views
96
Activity
Jun ’25
SKAN / AdAttributionKit Development Postback Not Triggering
We’re testing SKAN postbacks via AdAttributionKit but aren’t receiving any requests on our server even after generating development impressions and triggering a postback. Setup: Domain: https://linkrunner-skan.com Configured in Info.plist as: <key>NSAdvertisingAttributionReportEndpoint</key> <string>https://linkrunner-skan.com</string> <key>AttributionCopyEndpoint</key> <string>https://linkrunner-skan.com</string> Apple automatically appends the .well-known paths: /.well-known/private-click-measurement/report-attribution/ /.well-known/skadnetwork/report-attribution/ ATS diagnostics for the domain: PASS for all tests (TLS 1.0–1.3, PFS disabled, arbitrary loads allowed, etc.) Both .well-known paths are publicly accessible and return 200 OK Testing Flow: Enabled Developer → AdAttributionKit Developer Mode on iOS (15+) Followed Apple’s official guide: Testing AdAttributionKit with Developer Mode Generated test impression using: createAdAttributionKitDevelopmentImpression implemented in SKANManager.swift Called Postback.updateConversionValue with lockPostback: true Created Development Postback from Developer Settings Waited 30+ minutes while intercepting server requests (proxy + backend logs) What We’ve Tried So Far: Confirmed ATS compliance with nscurl --ats-diagnostics (all PASS) Verified .well-known paths are accessible publicly without redirects Tested endpoints manually with a POST request – server responds 200 OK Confirmed Info.plist entries exactly match Apple’s required keys Double-checked iOS device is running iOS 15+ with Developer Mode enabled Repeated test flow multiple times with fresh impressions and postbacks Waited up to 1 hour for postback (in case of delays) Issue: No POST requests are being received from Apple to either .well-known endpoint, even though the setup appears correct and ATS tests pass. References Used: Configuring an Advertised App Generating JWS Impressions Question: Has anyone faced a similar issue where AdAttributionKit Development Postbacks are not firing despite correct Info.plist setup, ATS compliance, and reachable .well-known endpoints? Any insight into possible missing configuration steps or testing nuances would be greatly appreciated.
Replies
0
Boosts
1
Views
124
Activity
Aug ’25
StoreKit 2 Fails to Load Subscription Products
We are experiencing a critical issue where StoreKit 2 is returning empty products when using Product.products(for:), specifically on devices running iOS 18.4.

 This issue does not occur on iOS 18.3 or earlier.

 Steps:

 Created a subscription product (e.g. "upm1") in App Store Connect
 Confirmed the product is active, localised, and part of a valid subscription group
 Call the following Swift code using StoreKit 2:
 Task { do { let products = try await Product.products(for: ["upm1"]) print(products) } catch { print("Error: (error)") } } 4. Result: products is an empty list.

 This regression is blocking subscription testing on iOS 18.4. 

 Kindly someone please advise on a potential fix or workaround.
Replies
2
Boosts
2
Views
302
Activity
Sep ’25
Problem with Family sharing on live build.
Hello, Our app supports family sharing skus, we successfully ran tests on family sharing features in sandbox, we noticed the feature does not work on live builds. The family member (child account) do see the subscription as "Shared with you". We attempted to restore multiple time and there is nothing to restore.
Replies
0
Boosts
0
Views
94
Activity
Aug ’25
App Store Receipt Availability After Pre-order Install and Future Deprecation Concerns
Hi Apple Developer Team, I'm looking to confirm some technical details regarding the pre-order flow and App Store receipt handling. Specifically, I have the following questions: Q1: After a user installs an app via pre-order and launches it for the first time, will a valid App Store receipt be available immediately via [[NSBundle mainBundle] appStoreReceiptURL]? Are there any known cases where the receipt might be missing or invalid, requiring a manual refresh (e.g., via SKReceiptRefreshRequest)? Q2: Is the pre-order flow currently supported in the sandbox environment? Specifically, is it possible to simulate pre-ordering an app and installing it in a sandbox or TestFlight environment, in order to test receipt generation and related logic? https://developer.apple.com/documentation/appstorereceipts/responsebody/receipt Q3: The receipt field in the App Store receipt structure is marked as deprecated. Is it still acceptable to use this field for validating receipts? Has Apple announced any timeline or system version in which this field will be fully removed or unsupported?
Replies
0
Boosts
0
Views
99
Activity
Jun ’25
IAPs after rejection
Hi everyone, After my app was initially rejected, all my IAPs now show the status “Developer Action Needed.” There’s nothing I need to change on them — they were just returned because it was the first submission and the app itself was rejected. Do I need to do anything with the IAPs before I resubmit, or will they still be attached and reviewed automatically with the next binary? I’ve seen conflicting answers, so I’d like to confirm the correct process. Thanks, Cris
Replies
1
Boosts
0
Views
116
Activity
Aug ’25