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

Issue in storekit purchase and sandbox IAP testing
I have using the internal testflight testers to test the app and IAP everyting is working once i created the Beta link for external testers who all are not able to get the IAP and similarly who all are I added as internal testers after beta link was created they are alos not able to purchase It was showing in the app as "Unable to process your request" and i am getting the error as followsPurchase did not return a transaction: Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={NSUnderlyingError=0x28291ebb0 {Error Domain=AMSErrorDomain Code=305 "Purchase Failed" UserInfo={NSLocalizedDescription=Purchase Failed, AMSStatusCode=200, AMSServerPayload={ "cancel-purchase-batch" = 1; customerMessage = "Unable to process your request."; dialog = { defaultButton = ok; explanation = "Please try again later."; 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 <…>
0
0
59
Aug ’25
Unexpected ONE_TIME_CHARGE Refund Callback After Successful Purchase
We are using consumable in-app purchases. Starting from May 27th, we began receiving refund callbacks with the notificationType set to ONE_TIME_CHARGE immediately after users successfully completed a payment. { "notificationType": "ONE_TIME_CHARGE", "signedPayload": "..." } During this period, we did not make any changes to our App release or server-side purchase handling logic. Could this issue result in actual refunds being processed? What steps should we take to resolve this issue? We also noticed in your changelog that a new notification type ONE_TIME_CHARGE has been introduced. Can we safely ignore callbacks with the ONE_TIME_CHARGE notification type without affecting refund processing or user experience?
0
0
180
May ’25
How to test each status of Get All Subscription Statuses of App Store Server API
I am currently using the App Store Server API Get All Subscription Statuses in the app I am in charge of. Please let me confirm the following regarding Get All Subscription Statuses. ■Prerequisites The language used is Objective-c, and I am using both XCode 15 and 16. I also have an App Store Connect account. ■Questions Is it possible to set and test each status of the App Store Server API Get All Subscription Statuses with TestFlight?
0
0
68
Mar ’25
Failed to get productIdentifier from StoreKit
I am currently developing an auto-renewal subscription in-app purchase for my app. Currently, the subscription items have already been approved in appStoreConnect, and the .store file is synced with appStoreConnect, so the subscription items are displayed well and the test is also progressing well. However, when I build without using the .store file to perform sandbox testing, the subscription items do not appear and response.invalidProductIdentifiers appears. Is there anything I need to do additionally so that the subscription items can appear in response.products? ps. The bank account item in the contract is in 'processing' status, and the paid app contract status is 'waiting for user information'.
1
0
188
May ’25
Proper way to set up Sandbox iOS for Purchase Testing
I cannot explain how frustrating this is. Not that I want to compare to Android, but in 3 years of QA Testing my app, Android works like a dream, while iOS fights with me EVERY SINGLE STEP OF THE WAY. Hopefully someone here can tell me what I am missing/doing wrong/which god I must appease to get this to work. I have 3 REAL iPhones of varying iOS versions and ages. But they are all proper actual iPhones. We use google accounts at this company, so my primary email is a gmail one. I have created MANY sandbox accounts inside App Store Connect. Currently I have 2, and 2 of my devices (both 14's one of which is a Pro) have my Primary account as the main account for the device. But they both also have a Sandbox account which is simply my main email with a +sandbox in it to make it a new unique email. Here is the problem, nothing works as expected ever. I can install my Staging and Production apps from TestFlight, then I can make a subscription purchase as a customer would and I SHOULD see that subscription in my Sandbox right? That's the point of a Sandbox and TestFlight is it not? But in ALL cases whenever I try to view my 'Sandbox Subscriptions' it tells me I don't have any. Now, sometimes, very occasionally, I get a specific error message inside my app when attempting to make a purchase, this one states something like 'You already have a subscription, please restore it instead...' which makes no sense. Since it clearly states that I have none. But this message has a 'Manage' button to manage my subscriptions, tapping it lads me to a windows which amazing DOES have a subscription in it. But attempting to 'Cancel' it does nothing, just refreshes the screen to be the same. Now I think that this subscription is actually attached to the primary account on the device and NOT the sandbox account. So when this happens I cannot subscribe, I cannot restore, and I cannot manually alter the subscription within iOS. So I am stuck at this point. What am I doing wrong, am I setting this all up in the wrong order? Do I need to install some kind of profile or security cert, do I need to give a pint of blood to Imhotep? What am I missing. I even once sat on the phone for 90 minutes with an Apple Support Rep who took me through it step by step, same result. Also I just noticed that inside 'App Store Connect' when you look at the list of 'Sandbox' accounts there is a column for 'Last Purchase' which is entirely blank, apparently after a year of use I have NEVER purchased on the Sandbox, which is another reason I think my subs are going to the main email, not the sandbox one. I tried using the sandbox email as the main account for the whole device, I can't recall the result but it was worse and didn't work at all. So that's not it. https://developer.apple.com/help/app-store-connect/test-in-app-purchases/create-a-sandbox-apple-account/ The instructions on this page are not detailed enough and were not helpful to me. All I really want to know is how to fully setup a real actual iPhone for TestFlight and Sandbox testing of a app. WHat order do I create accounts, validate emails, attach to devices, login with etc etc etc. Step by step, nothing no matter how mundane missed out. A true idiots guide to making this work for me. Testing this on Android always takes 5 mins. iPhone, I'm lucky if I am done in half a day. Please help and thanks for reading!
1
0
99
Jul ’25
InApp purchases ok and ko at the same time (?!)
I'm working on an app the has implemented inapp purchases. They have been working so far, and they keep working currently. But just a couple of days ago, a specific user sent us a support ticket stating that when he purchases and item the bank charges it for the purchase, but within the app, the purchase fails and he doesn't receive the item. He sent us screenshots showing: The iOS native modal when a purchases has been finished correctly ("You're all set - Your purchase was successful "). Right after that modal, the app shows an internal modal showing "The purchase failed, please, try again later". Checking the app logs, that failure modal was triggered by "The operation couldn’t be completed. (StoreKit.StoreKitError error 1.)". Reading docs about this error leads me to think about device or user restrictions (parental controls, usage limits, etc...). It seems that in theses cases the bank charge could be issued but refunded later once Apple ultimately declines the purchase. However the user says that he doesn't have any kind of restriction. The only related thing is a "this device is also restricted by a profile" message, but everyone seems to have this message. What could it be causing this issue? In what scenariowould the app show a native OK modal but a storekit error 1? I'm pretty sure the app is well configured because I keep receiving purchases of all kind, from different users with any problem.
0
0
341
Mar ’25
Dev Defined IAP Transaction Metadata
I have an app that works by being able to map IAP transactions to a predefined user ID. This means when I consume events from Apple's App Store Server Notifications endpoints I have to do a reverse lookup in order to assign permissions within my app. Workflow: User purchases subscription within the app via IAP. The app persists the subscriptionID from the Apple IAP library in my cloud database (Firestore). Cloud function receives the event from App Store Server Notifications endpoint and looks up the user ID containing the persisted transactionID (with retries to avoid race condition). Question: This workflow works but it seems an improvement would be to allow dev's to append metadata, like the user ID, to the transaction submitted to IAP that we can access within the signedTransactionInfo of the event from the App Store Server Notifications endpoint in order to facilitate a direct lookup of the user document needing it's permissions updated. This would greatly simplify workflows that use non-Apple systems as a source of truth for app permissions. Does this actually exist already? If not, is there a feature request platform?
0
0
57
Mar ’25
App Clip Closes Before SKOverlay Can Show “Open” Button When Live Activity Is Involved
I have an App Clip that uses SKOverlay.AppClipConfiguration to install the full app. Before I added a Live Activity call (Activity.request), the user could see “Install,” then “Open.” Now, once “Get” is tapped, the Clip immediately closes—no “Open” button appears. If I remove the Live Activity code, it works again. I’ve confirmed that parent/child entitlements match, and tested via TestFlight. Is there a known issue or recommended workaround for combining SKOverlay + Live Activities in an App Clip so it doesn’t dismiss prematurely? Any insights are appreciated! Note live activity is for App Clip only.
1
0
325
Mar ’25
Receiving 404 Error for APNs Server Notifications When Validating signedPayload
Hi everyone, I'm experiencing an issue with APNs server notifications where I receive a 404 error when trying to validate the signedPayload from Apple's notification. Below is a sanitized version of my code: class ServerNotificationAppleController extends Controller { // URL for StoreKit keys (Sandbox environment) private $storeKitKeysUrl = 'https://api.storekit-sandbox.itunes.apple.com/inApps/v1/keys'; public function handleNotification(Request $request) { \Log::info($request); $signedPayload = $request->input('signedPayload'); if (!$signedPayload) { return response()->json(['error' => 'signedPayload not provided'], 400); } // Step 1: Create your JWT token (token creation logic can be in a separate service) $jwtToken = $this->generateAppleJWT(); // Step 2: Send a request to the StoreKit keys endpoint $response = Http::withHeaders([ 'Authorization' => 'Bearer ' . $jwtToken, ])->get($this->storeKitKeysUrl); Log::info('Apple Keys Status:', ['status' => $response->status()]); Log::info('Apple Keys Body:', ['body' => $response->body()]); if ($response->status() !== 200) { return response()->json(['error' => "Apple public keys couldn't be retrieved"], 401); } $keysData = $response->json(); // Step 3: Validate the signedPayload $validatedPayload = $this->validateSignedPayload($signedPayload, $keysData); if (!$validatedPayload) { return response()->json(['error' => 'Invalid signedPayload'], 400); } // Process the validated data as needed Log::info("Apple Purchase Data:", (array)$validatedPayload); return response()->json(['message' => 'Notification processed successfully'], 200); } private function generateAppleJWT() { // API key details (replace placeholders with actual values) $keyId = config('services.apple.key_id'); // e.g., <YOUR_KEY_ID> $issuerId = config('services.apple.issuer_id'); // e.g., <YOUR_ISSUER_ID> $privateKey = file_get_contents(storage_path(config('services.apple.private_key'))); // Set current UTC time and expiration time (20 minutes later) $nowUtc = Carbon::now('UTC'); $expirationUtc = $nowUtc->copy()->addMinutes(20); // Create the payload with UTC timestamps $payload = [ 'iss' => $issuerId, 'iat' => $nowUtc->timestamp, 'exp' => $expirationUtc->timestamp, 'aud' => 'appstoreconnect-v1', 'bid' => 'com.example.app', // Replace with your Bundle ID if necessary ]; // Generate the JWT token return JWT::encode($payload, $privateKey, 'ES256', $keyId); } private function validateSignedPayload($signedPayload, $keysData) { try { $jwkKeys = JWK::parseKeySet($keysData); return JWT::decode($signedPayload, $jwkKeys, ['RS256']); } catch (\Exception $e) { Log::error("Apple Purchase Validation Error: " . $e->getMessage()); return null; } } } I’m particularly puzzled by the fact that I receive a 404 error when trying to retrieve the public keys from the StoreKit keys endpoint. Has anyone encountered this issue or can provide insight into what might be causing the error? Any help or suggestions would be greatly appreciated. Thanks!
2
0
436
Mar ’25
storekit2_products_error
Cannot retrieve products for iap or subscription on simulator iPhone 16 and real device iPhone XR also. lutter: IAPError(code: storekit2_products_error, source: app_store, message: The operation couldn’t be completed. (NSURLErrorDomain error -1009.), details: The operation couldn’t be completed. (NSURLErrorDomain error -1009.)) flutter: Error fetching IAP products: IAPError(code: storekit2_products_error, source: app_store, message: The operation couldn’t be completed. (NSURLErrorDomain error -1009.), details: The operation couldn’t be completed. (NSURLErrorDomain error -1009.))
1
0
144
Jun ’25
Problem with siubscriptions in Sandbox
The phone is set up with the developer program to cancel subscriptions from the app we developed. However, after the OS update on the phone, the subscriptions no longer appear in the developer program, although the subscription does exist in the app itself. We are attaching the log. 🔖 8/9/2025, 10:59:44 AM ["expires_date_pst": 2025-09-08 21:58:36 America/Los_Angeles, "original_purchase_date_ms": 1753167687000, "original_purchase_date_pst": 2025-07-22 00:01:27 America/Los_Angeles, "purchase_date_ms": 1757307516000, "purchase_date_pst": 2025-09-07 21:58:36 America/Los_Angeles, "product_id": com.topwall.premium_trial.monthly.trial, "in_app_ownership_type": PURCHASED, "web_order_line_item_id": 2000000111040333, "purchase_date": 2025-09-08 04:58:36 Etc/GMT, "is_trial_period": false, "original_purchase_date": 2025-07-22 07:01:27 Etc/GMT, "expires_date_ms": 1757393916000, "expires_date": 2025-09-09 04:58:36 Etc/GMT, "transaction_id": 2000001002316107, "is_in_intro_offer_period": false, "subscription_group_identifier": 21733009, "original_transaction_id": 2000000966725103, "quantity": 1] 🔹🔹🔹🔹🔹🔹🔹🔹🔹🔹 🟢 8/9/2025, 10:59:44 AM StoreKit isActive: true до 2025-09-09 07:58:36 Why do you think the subscription created in the app doesn’t show up in the sandbox?
0
0
59
Sep ’25
App store payment screen not showing
Hello I am trying to add payments on my app. I have added all products I need in subscriptions. I also tried by using storekit but from my understanding testing with storekit is nothing to rely on. My app works on Android (with the same product IDs same unction call) iOS is not why is that?
1
0
54
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
371
Sep ’25
Internal testing. Receipt not always contain last consumable purchase.
After game restart first purchase is contained in receipt but the next ones is the same as first one so new purchases is not added. I afraid players can be charged for purchase but on my server I will not receive new purchases instead receipt with old one so they can do not receive in game currency. Will in production I receive a receipts with new consumable every time player purchase it? I use Unity3d In-app purchasing 5.0.1.
4
0
216
Sep ’25
Subscription IAP - SubscriptionStoreView results and errors - more info needed. FB19376771
FB19376771 Transactions monitoring. If I only have subscriptions, do I really need to "bother" with any sort of monitorTransactions() or just rely on subscription status (subscribed, revoked, cancelled ...) ? This is in line with Apple SKDemo and recommendation: // Only handle consumables and non consumables here. Check the subscription status each time // before unlocking a premium subscription feature. switch transaction.productType { ref: [https://developer.apple.com/documentation/storekit/implementing-a-store-in-your-app-using-the-storekit-api) The "Only handle consumables and non consumables here" recommendation by Apple in ref to the process transaction code above is nuanced and confusing if we know what was with other external experts recommendation saying when using only SK2 Views : "This is where most developers trip up in trying to get an experience that App Review is happy" ... continuing : "Be careful: that Purchase View code alone isn’t enough, because one of the possible completion status is .pending: the purchase is in the process of happening but hasn’t completed yet, so you still need to watch the transaction queue manually to be absolutely sure of handling the process completely." Does this holds true for the new SubscriptionStoreView ? We are not sure with quite obscure Apple documentation what SubscriptionStoreView handles, other than purchase (and now subscribe) function, and we do not know what diverse type of error handling messages it can return. Moreover, Apple documents: "Only handle consumables and non consumables here" ? @Apple can you please share more insights on Purchase button on SubscriptionStoreView e.g A) does it close ( finish). the purchase transaction ? B) What error results can it return ? C) What .onInAppPurchaseCompletion can handle as result ?
0
0
77
Aug ’25
StoreKit2 caches local raw transactions and retrieval
FB19377002 I am looking to improve and review my subscription purchase handling logic, for the best user experience. Considering that StoreKit2 caches local raw transactions (in case user is offline), is it really necessary to persist "unlocked status" in UserDefaults or SwiftData Model or AppStorage? Are there significant delays when reading Transaction.currentEntitlements from locally stored cache, versus reading it from UserDefaults; or, as in the latest SKDemo example, even reading it from stored in SwiftData ? https://developer.apple.com/forums/thread/706450 I only have subscriptions ( I don't have noncosumable or consubale products). Do I still need to persist subscription status?
0
0
83
Aug ’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
132
Aug ’25