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

In iOS 26 beta3 version, the finishTransaction method is unable to remove transactions from the SKPaymentQueue, causing transactions to remain in the queue even after being processed.
We have some users who have upgraded to iOS 26 beta3. Currently, we observe that when these users make in-app purchases, our code calls [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; method, and we clearly receive the successful removal callback in the delegate method - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray<SKPaymentTransaction *> *)transactions. However, when users click on products with the same productId again, the method - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions still returns information about previously removed transactions, preventing users from making further in-app purchases.
4
0
314
Jul ’25
Inconsistent notification coming from AppStore Servers
I encountered a scenario involving a subscription and need to determine if it's a problem or an expected outcome. Here are the details: My service received a notification from Apple of type DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED. The status field received on the payload was equal to 1 - Active. (2024-12-19T15:34:53.801) My service again received a DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED. But the status field received was 2 - Expired. (2024-12-19T23:34:57.527) My service received an EXPIRED with subtype VOLUNTARY notification. (2024-12-19T23:35:01.669) Is the event 2 an inconsistent event? Since we are receiving a notification that means the auto renew was disabled when the subscription was already expired.
0
0
328
Jan ’25
Sandbox Url Not Receiving App Store Server Notifications
I have an Expo React Native application and I am using react-native-iap library. I have setup the URL for sandbox and production url to receive notifications when a something happens regarding subscriptions. I am not able to receive those notifications when I simulate the purchase on the Simulator using Xcode. I then have used the node library app-store-server-library to mock a test notification and I am receiving the test notification when I call requestTestNotification method. below is the react-native code I am using: import {useEffect, useState} from "react"; import { initConnection, Sku, Subscription, useIAP, SubscriptionIOS, requestSubscription, PurchaseError, clearTransactionIOS } from "react-native-iap"; import styles from "@/screens/IAP/IAPStyles"; import CustomText from "@/components/CustomText"; import Heading from "@/components/Heading"; import subscriptionsProducts from "@/utilities/products"; function IAPScreen() { const [isPurchasing, setIsPurchasing] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false); const { subscriptions, currentPurchase, finishTransaction, getSubscriptions } = useIAP(); function RenderProduct({ item } : { item: Subscription }) { const iosSubscription: SubscriptionIOS = item as SubscriptionIOS; return( <View style={styles.productCard}> <CustomText customStyles={styles.productTitle} text={iosSubscription.title} size={"medium"} /> <CustomText customStyles={styles.productDescription} text={iosSubscription.description} size={"small"} /> <CustomText customStyles={styles.productPrice} text={iosSubscription.localizedPrice} size={"small"} /> <Button title="Purchase" disabled={isPurchasing} onPress={ () => HandlePurchase(iosSubscription.productId) } /> </View> ); } async function HandlePurchase(sku: Sku) { try { setIsPurchasing(true); await requestSubscription({ sku, andDangerouslyFinishTransactionAutomaticallyIOS: false }); } catch (error: any) { Alert.alert("Error", "Failed to purchase: " + error.message); } finally { setIsPurchasing(false); } } useEffect(() => { setLoading(true); console.log(`[${new Date().toISOString()}] Initializing IAP connection...`); const setupIAP = async () => { try { const result = await initConnection(); console.log(`[${new Date().toISOString()}] IAP connection initialized:`, result); await clearTransactionIOS(); await getSubscriptions({ skus: subscriptionsProducts }); } catch (error: any) { Alert.alert("Error", "Failed to load products: " + error.message); } finally { setLoading(false); } }; setupIAP() .finally(() => setLoading(false)); }, []); useEffect(() => { const checkCurrentPurchase = async () => { try { if(currentPurchase?.productId) { console.log("Current purchase: ", currentPurchase); console.log("Transaction Id: ", currentPurchase.transactionId); await finishTransaction({ purchase: currentPurchase, isConsumable: false, }); } } catch (error) { if(error instanceof PurchaseError) { console.log("Purchase error: ", error); } else { Alert.alert("Error", "Failed to finish transaction: " + error); } } } if(currentPurchase) { console.log("Finishing current purchase."); checkCurrentPurchase() .catch(error => Alert.alert("Error", "Failed to finish transaction: " + error.message)); } }, [currentPurchase, finishTransaction]); return( <View style={styles.mainContainer}> <Heading text={"Packages & Subscriptions"} type={"h2"} customStyles={styles.header} /> { loading ? <ActivityIndicator size="large" /> : subscriptions.length > 0 ? ( <> <FlatList data={subscriptions} renderItem={RenderProduct} keyExtractor={(item) => item.productId} /> </> ) : ( <CustomText customStyles={styles.productDescription} text={"No available products."} size={"small"} /> ) } </View> ); } export default IAPScreen; I am using a store kit file where I just edited the scheme of application to use that store kit file. I would be really thankful If you can help me in this matter.
1
0
149
May ’25
Best current approach to detecting legacy paid app download (without relying on deprecated APIs)?
I’m trying to determine the most appropriate modern method for detecting whether a user originally downloaded a paid app (prior to transitioning the app to freemium/IAP-based access). Historically, this was done by checking for a valid App Store receipt and using SKReceiptRefreshRequest to ensure a fresh one was available. However, SKReceiptRefreshRequest and many related aspects of StoreKit receipt handling are now deprecated in iOS 17+. The current Apple documentation on receipt validation still refers to SKReceiptRefreshRequest, which makes things unclear. With so many deprecations and the push toward StoreKit 2, what’s the recommended path to: Check for a valid App Store receipt Confirm that the app was originally purchased (as a paid app, not via IAP) Persist this info to exempt the user from paywalling the app in the future I don’t need to validate purchases of IAPs — just to detect a legacy paid app download. Any guidance on best practice for this use case, preferably using non-deprecated APIs (StoreKit 2 or otherwise), would be appreciated.
0
0
53
Jun ’25
Official document for CONSUMPTION_REQUEST - What kind of data we are receiving?
This documentation describes what kind of data we should be sending to Apple server, once we are receiving CONSUMPTION_REQUEST https://developer.apple.com/documentation/appstoreserverapi/consumptionrequest But, it doesn't describe what kind of data we are receiving, when we are receiving CONSUMPTION_REQUEST? May I know, is such a document available? Thank you.
0
0
528
Dec ’24
Store Kit Set up
I'm working on my first subscription for an app I've built, and can't seem to figure out where I'm going wrong with this one whether it's something in Xcode or App Store Connect. Any idea where to start? Somethings to check. TIA.
0
0
277
Dec ’24
Reporting your App Store Server Notifications issue
To receive server notifications from the App Store, follow the instructions in Enabling App Store Server Notifications. If your server doesn’t receive any notifications, check your server logs for any incoming web request issues, and confirm that your server supports the Transport Layer Security (TLS) 1.2 protocol or later. If you implement version 2 of App Store Server Notifications, call the Get Notification History endpoint. If there is an issue sending a notification, the endpoint returns the error the App Store received from your server. If your issue persists, submit a Feedback Assistant report with the following information: The bundleId or appAppleId of your app The date and time your issue occurred The raw HTTP body of your notification The affected transactionId(s) if applicable The version of App Store Server Notifications (i.e., Version 1 or Version 2) The environment (i.e., Production or Sandbox) To submit the report, perform these steps: Log into Feedback Assistant. Click on the Compose icon to create a new report. Select the Developer Tools & Resources topic. In the sheet that appears: Enter a title for your report. Select “App Store Server Notifications” from the “Which area are you seeing an issue with?” pop-up menu. Select “Incorrect/Unexpected Behavior” from the “What type of feedback are you reporting?” pop-up menu. Enter a description of your issue. Add the information gathered above to the sheet. Submit your report. After filing your report, please respond in your existing Developer Forums post with the Feedback Assistant ID. Use your Feedback Assistant ID to check for updates or resolutions. For more information, see Understanding feedback status.
0
0
601
Feb ’25
How to make StoreKit config & App Store Server notifications work together?
Hi, Using StoreKit 2 with App Store Server notifications like so: User selects a purchase App calls Product.purchase() If successful, App Store notifies our backend with the transaction details, importantly with a UUID for the transaction ID. This works fine, but when I try to test contingent pricing via the handy StoreKit config Transaction Manager in Xcode by creating a PurchaseIntent if I then complete the purchase in the app the Transaction ID is sequential, (0 for the first, 1 for the second etc), which doesn't work for us as the backend might already have that ID stored so the purchase never completes. If I disable the config file it works fine, but then I can't use the Transaction Manager debug tool. Is there a way to override the ID of a custom transaction that's created via the StoreKit configuration? Thanks
0
0
59
Jun ’25
Could not find the main bundle or the Info.plist is missing a CFBundleIdentifier
I had a standalone python application (created with pyinstaller) which was working perfectly alone. This macOS application was created in VS. I later decided to improve the application by implementing some Swift features (Subscription Manager). This required me to write a brief Swift file (Subscription Management) in XCode which the Python file called on. Python Standalone Application Calling Swift : # Function to check if the user has a valid subscription def check_subscription(): subscription_manager_path = "/Users/isseyyohannes/Library/Developer/Xcode/DerivedData/SubscriptionManager2-ezwjnnjruizvamaesqighyoxljmy/Build/Products/Debug/SubscriptionManager2" # Adjust path try: result = subprocess.run([subscription_manager_path], capture_output=True, text=True, check=True) return "VALID_SUBSCRIPTION" in result.stdout # Return True if valid, False otherwise except subprocess.CalledProcessError as e: print(f"Error checking subscription: {e}") return False # Return False if there's an issue However, when I try to run xcrun altool --validate-app ... I get the following error message. The error reads as follows Running altool at path '/Applications/Xcode.app/Contents/SharedFrameworks/ContentDeliveryServices.framework/Frameworks/AppStoreService.framework/Support/altool'... 2025-02-16 11:02:21.089 *** Error: Validation failed for '/Users/isseyyohannes/Desktop/ALGORA Performance.app'. 2025-02-16 11:02:21.089 *** Error: Could not find the main bundle or the Info.plist is missing a CFBundleIdentifier in ‘/Users/isseyyohannes/Desktop/ALGORA Performance.app’. Unable to validate your application. (-21017) { NSLocalizedDescription = "Could not find the main bundle or the Info.plist is missing a CFBundleIdentifier in \U2018/Users/isseyyohannes/Desktop/ALGORA Performance.app\U2019."; NSLocalizedFailureReason = "Unable to validate your application."; I located the Info.plist file which had everything correct (my Bundle ID, etc.) for the python file. I am successfully able to notarize the application, just having issues submitting it. **It is worth noting I currently have a python executable calling on a file written in swift code and created in xcode. I created the python application with the following command on VS. pyinstaller --onefile --noconsole \ --icon="/Users/isseyyohannes/Downloads/E0BWowfbDkzEiEAckjsHAsYMzpdjjttT.icns" \ --codesign-identity "Developer ID Application: Issey Yohannes (GL5BCCW69X)" \ --add-binary="/Users/isseyyohannes/Library/Developer/Xcode/DerivedData/SubscriptionManager2-ezwjnnjruizvamaesqighyoxljmy/Build/Products/Debug/SubscriptionManager2:." \ --osx-bundle-identifier com.algora1 \ /Users/isseyyohannes/Desktop/ALGORA\ Performance.py Note : All steps on Apple Developer site are done (ex. Creating identifier, secret password etc)
1
0
349
Feb ’25
Can users create their own subscription plans?
I'm considering developing an app where users can create their own subscription plans by freely setting their prices, similar to YouTube's membership feature. I understand that in-app purchases must be used to unlock features within the app. With that in mind, I searched for APIs to enable this functionality but couldn't find relevant information. When I contacted Apple directly, they mentioned that they couldn't provide specific answers unless the app is under review. If anyone has knowledge about the following points, I would greatly appreciate your response: Is it possible to implement a feature similar to YouTube's membership using in-app purchase APIs? If it's not feasible with in-app purchases, is it allowed to use external payment services like Stripe?
0
0
315
Dec ’24
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
97
May ’25
not getting stable release versions of some apps from the ios app store
I have been receiving beta software from the iPad App Store, despite not being enrolled in a beta program. Additionally, I do not have TestFlight or the Feedback app installed on my device. There are no certificates or profiles displayed either. I am using the App Store app that comes preinstalled on my device (note that I am not located in Europe). My iPad has been experiencing significant bugs, including numerous screen glitches and periods of sluggishness. Furthermore, numerous applications have crashed frequently. I was able to confirm that I was receiving beta software because the crash reports include beta identifier numbers. According to Apple documentation regarding analytic reports, a beta identifier will only be visible for beta applications. anyone know what could be going on or how to fix this?
0
0
51
Apr ’25
Issues with Integration of Promotional Offers in React Native app
Hi All, We are trying to integrate Promotional Offer in our app, We have a React Native app and are using react-native-iap for handling our in app purchases, as per the documentation we are generating signature in our BE and passing the proper details to the function as well, but for subscription request which have offer applied we are getting the apple pop up properly as well with offer details but when trying to subscribe it gives us SKErrroDomain: 12, for subscription without applying offer the subscription goes through but when we apply the offer we get the above error. Our app is currently in Development Stages and has not been sent for review sam for our subscription plans as well. Please let me know what could be the probable cause for this and help us resolve the issue. This is the code snippet of ours for the front end : export const buySubscription = async (subscriptionData: any) => { try { if (subscriptionData.offer_id) { const response = await getSubscriptionSignature( subscriptionData.productId, subscriptionData.offer_id, ); const offerData = response?.data; const offer = { identifier: offerData?.offer_id, keyIdentifier: offerData?.key_id, nonce: offerData?.nonce, signature: offerData?.signature, timestamp: Number(offerData?.timestamp), }; await requestSubscription({ sku: subscriptionData.productId, withOffer: offer, }); } else { await requestSubscription({ sku: subscriptionData.productId }); } } catch (err) { logger.error('Subscription error: ' + JSON.stringify(err)); throw err; } }; and 
from my python Backend which generates the signature:

def generate_signature(self, product_id: str, offer_id: str) -> dict: """ Generate signature for Apple StoreKit promotional offers. Args: product_id: The product identifier from App Store Connect offer_id: The promotional offer identifier Returns: dict: Contains signature and required metadata Reference: https://developer.apple.com/documentation/storekit/in-app_purchase/original_api_for_in-app_purchase/subscriptions_and_offers/implementing_promotional_offers_in_your_app """ try: # Generate UUID without dashes and use as nonce nonce = str(uuid.uuid4()) timestamp = get_current_time_ms() # milliseconds # Create the payload string in exact order required by Apple payload_components = [ self.bundle_id, # App Bundle ID self.key_id, # Key ID from App Store Connect product_id, # Product identifier offer_id, # Promotional offer identifier nonce, # UUID without dashes str(timestamp) # Current timestamp in milliseconds ] payload_str = "\u2063".join(payload_components) # Use Unicode separator logger.debug(f"Signing payload: {payload_str}") # Create SHA256 hash of the payload digest = hashes.Hash(hashes.SHA256()) digest.update(payload_str.encode('utf-8')) payload_hash = digest.finalize() # Sign the hash using ES256 (ECDSA with SHA-256) signature = self.private_key.sign( data=payload_hash, signature_algorithm=ec.ECDSA(hashes.SHA256()) ) # Encode signature in base64 signature_b64 = base64.b64encode(signature).decode('utf-8') logger.info(f"Generated signature for product {product_id} and offer {offer_id}") return { "key_id": self.key_id, # Changed to match Apple's naming "nonce": nonce, # UUID without dashes "timestamp": timestamp, # As integer "signature": signature_b64, # Base64 encoded signature "product_id": product_id, # Changed to match Apple's naming "offer_id": offer_id # Changed to match Apple's naming } except Exception as e: logger.error(f"Failed to generate signature: {str(e)}") raise HTTPException( status_code=500, detail=f"Failed to generate signature: {str(e)}" )
0
0
69
Apr ’25
Potential Issue Identified in Apple Documentation
While reviewing the Apple Documentation, I came across a potential issue in one of the examples that I believe is worth addressing. The example appears to compare strings instead of integers, which could lead to unexpected behavior in production environments. Specifically, in the line where originalMajorVersion (a string) is compared with newBusinessModelMajorVersion (also a string) using <: if originalMajorVersion < newBusinessModelMajorVersion This comparison performs a lexicographical check rather than evaluating the numerical values of the strings. As a result, strings like "10" would incorrectly be considered less than "2", which is not the desired behaviour when comparing version numbers. I have reported this via the Feedback assistant (FB16432337) but at the time of posting this there has been no reply at all (23 days) Supporting business model changes by using the app transaction do { // Get the appTransaction. let shared = try await AppTransaction.shared if case .verified(let appTransaction) = shared { // Hard-code the major version number in which the app's business model changed. let newBusinessModelMajorVersion = "2" // Get the major version number of the version the customer originally purchased. let versionComponents = appTransaction.originalAppVersion.split(separator: ".") let originalMajorVersion = versionComponents[0] if originalMajorVersion < newBusinessModelMajorVersion { // This customer purchased the app before the business model changed. // Deliver content that they're entitled to based on their app purchase. } else { // This customer purchased the app after the business model changed. } } } catch { // Handle errors. }
3
0
325
Feb ’25
ASDServerErrorDomain Code=3512
有人遇到这个问题吗,在支付的时候提示未知错误,具体的错误信息如下: 交易失败,outTradeNo:2025022631999900326, productId:com.f6car.p0001, error:Err-or -Domain=SKErrorDomain Code=0 "发生未知错误" UserInfo={NSLocalizedDescription=发生未知错误, NSUnderlyingError=0x302f50120 {Error Domain=ASDServerErrorDomain Code=3512 "无效的应用程序外部版本。" UserInfo={NSLocalizedFailureReason=无效的应用程序外部版本。}}} 寻求解决方案,感谢.
0
0
245
Feb ’25