StoreKit

RSS for tag

Support in-app purchases and interactions with the App Store using StoreKit.

StoreKit Documentation

Posts under StoreKit subtopic

Post

Replies

Boosts

Views

Activity

Can StoreKit products be observed with ObservableObject? Can I get notified when a users subscription has lapsed without polling Transaction.currentEntitlements?
I have an auto-renewable subscription. I have two methods helping me keep track of when they are expired @MainActor public func isPurchased(product: Product) async -> Bool { guard let state = await product.currentEntitlement else { return false } switch state { case .unverified(_, _): return false case .verified(let transaction): await transaction.finish() return isTransactionRelevant(transaction) } } private func isTransactionRelevant(_ transaction: Transaction) -> Bool { if let revocationDate = transaction.revocationDate { logger.error("Transaction verification failed: Transaction was revoked on \(revocationDate)") return false } if let expirationDate = transaction.expirationDate, expirationDate < Date() { logger.error("Transaction verification failed: Transaction expired on \(expirationDate)") return false } if transaction.isUpgraded { logger.error("Transaction verification failed: Transaction was upgraded") return false } logger.info("Transaction verification succeeded") return true } I also have this that I can call to get the latest state of purchases @MainActor public func updateStoreKitSubscriptionStatus() async { var currentProductsPurchased: [Product] = [] for await result in Transaction.currentEntitlements { if case .verified(let transaction) = result { if isTransactionRelevant(transaction) { if let product = products.first( where: { $0.id == transaction.productID }) { currentProductsPurchased.append(product) } } await transaction.finish() } } self.purchasedProducts = currentProductsPurchased } Right now when a subscription expires the user needs to manually do some action that triggers updateStoreKitSubscriptionStatus() as it appears that expirations do not come through in Transaction.updates. I am surprised there does not seem to be a better way. Does StoreKit not notify you somewhere that an auto-renewable subscription has expired? Can you observe it in an ObservableObject? Or do I need to just frequently poll Transaction.currentEntitlements even if I dont expect frequent updates?
0
0
94
Jul ’25
Operation of Server Notifications V2 when Apple account is withdrawal
Please allow me to confirm the Server Notifications V2 specification. I am aware that if withdrawal an Apple account that has a subscription, the subscription will eventually be cancelled. Regarding Server Notifications V2 notifications with a notificationType of EXPIRED, am I correct in thinking that they will be sent when the subscription expires even if the Apple account is withdrawal?
0
0
307
Feb ’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
370
Sep ’25
StoreKit returns restored for SKUs marked Consumable (no purchase sheet); Flutter in_app_purchase + SK2
What platform are you targeting? And what version? iOS, testing in Sandbox on a physical device. What version of Xcode are you using? [Xcode __] What version of the OS are you testing on? iOS 18 on iPhone 15 pro. What specific API are you using? StoreKit 2 via Flutter’s in_app_purchase plugin (Dart), which uses in_app_purchase_storekit under the hood. What are the exact steps you took? In App Store Connect, I created several Consumable IAPs (status “Ready to Submit”). Example product IDs: USD3.99TenMinuteCoffeePlan (Consumable) USD24.99OneHourDinnerPlan (Consumable) USD14.99InviteAFriendAsGenie (Consumable) Signed in as a Sandbox tester on device (Settings → App Store → Sandbox Account). App queries products with InAppPurchase.instance.queryProductDetails(ids) — products load successfully. Call buyConsumable(purchaseParam: PurchaseParam(productDetails: ...)). Listen to purchaseStream and log PurchaseDetails. If something failed, what are the symptoms? The purchase sheet often does not appear. The purchase stream reports PurchaseStatus.restored, immediately, for SKUs that are marked Consumable. Example log lines (from Dart): Products loaded: 6 Product: id=USD3.99TenMinuteCoffeePlan, price=3.99 Product: id=USD24.99OneHourDinnerPlan, price=24.99 Product: id=USD14.99InviteAFriendAsGenie, price=14.99 Purchase update: productID=USD3.99TenMinuteCoffeePlan, status=PurchaseStatus.restored, pendingComplete=false, purchaseID=2000000991974131 Purchase update: productID=USD24.99OneHourDinnerPlan, status=PurchaseStatus.restored, pendingComplete=false, purchaseID=2000000992079251 Purchase update: productID=USD14.99InviteAFriendAsGenie, status=PurchaseStatus.restored, pendingComplete=false, purchaseID=2000000999910991 Purchase update: productID=USD29.99InviteAFriendAsGenie, status=PurchaseStatus.restored, pendingComplete=false, purchaseID=2000001003571920 If nothing failed, what results did you see? And what were you expecting? Actual: restored events (no sheet) for items configured as Consumable. Expected: For Consumables, a purchase sheet followed by purchased status. Consumables shouldn’t “restore”. What else have you tried? Verified every SKU shows Type = Consumable and Ready to Submit in App Store Connect; “Cleared for Sale” enabled; pricing/localization filled. Created new product IDs (to avoid any prior non-consumable history). Verified I’m not calling restorePurchases. In the listener, I only grant benefits on PurchaseStatus.purchased (not on restored). Observed that queryProductDetails succeeds; some IDs that aren’t fully configured return “not found,” as expected. Minimal code (core bits): final _iap = InAppPurchase.instance; Future<void> init() async { final resp = await _iap.queryProductDetails({ 'USD3.99TenMinuteCoffeePlan', 'USD24.99OneHourDinnerPlan', 'USD14.99InviteAFriendAsGenie', 'USD29.99InviteAFriendAsGenie', }); _products = resp.productDetails; _sub = _iap.purchaseStream.listen(_onUpdates); } Future<void> buy(ProductDetails p) async { final param = PurchaseParam(productDetails: p); await _iap.buyConsumable(purchaseParam: param); // iOS SK2 path } void _onUpdates(List<PurchaseDetails> list) async { for (final pd in list) { print('status=${pd.status}, id=${pd.productID}, pending=${pd.pendingCompletePurchase}, purchaseID=${pd.purchaseID}'); switch (pd.status) { case PurchaseStatus.purchased: // deliver & (if pendingCompletePurchase) completePurchase break; case PurchaseStatus.restored: // for consumables, I do not deliver here break; default: break; } } } Questions for the community/Apple: Under what conditions would StoreKit 2 return restored for a SKU that’s set to Consumable? Is there any server-side caching of old product type or ownership tied to a product ID that could cause this in Sandbox? Is “Ready to Submit” sufficient for Sandbox testing of IAPs, or must the SKUs be attached to a submitted build before StoreKit treats them as consumable? If a product ID was ever created/purchased as Non-Consumable historically, does creating a new ASC entry with the same string ID as Consumable still cause restored for that tester? Besides creating brand-new product IDs and/or resetting the Sandbox tester’s purchase history, is there any other recommended way to clear this state? Happy to provide a device sysdiagnose or a stripped test project if that helps. Thanks!
0
0
171
Sep ’25
Crash on UIApplicationInvalidInterfaceOrientation when [SKStoreProductViewController shouldAutorotate] is returning YES
I get crash reports which I can't reproduce when trying to present an SKStoreProductViewController : Fatal Exception: UIApplicationInvalidInterfaceOrientation Supported orientations has no common orientation with the application, and [SKStoreProductViewController shouldAutorotate] is returning YES No matter what app Deployment info orientation I try I can't get my SKStoreProductViewController shouldAutorotate property to return YES. It is always false. Does anyone knows why or how to get an SKStoreProductViewController to return shouldAutorotate YES?
0
0
42
Apr ’25
"Bug description: We were unable to tap on 'Upgrade to Pro' button."
Hey guys, I am developing a habit tracker app right now. I know it's basic but it's my first app and I wanted an easy start but this is more of a struggle than I thought. So I am trying to publish my app fully on the App Store however everytime I do there is some kind of bug with it. But when I test it, it works perfect for me. Anyways, in this case the reviewer said when they go to the pro page of my app to purchase a pro subscription and try to press upgrade to pro nothing pops up. It pops up normally for me on my iPhone but not other devices. I wanted to make sure if this was normal or if there is something I am supposed to fix. Any help is appreciated. Thank you.
1
0
78
Mar ’25
StoreKit payment return error
this error occurred during the StoreKit1 payment process. Could you please tell me the reason? SKErrorDomain Code=0 "发生未知错误\" UserInfo= {NSLocalizedDescription=发生未知错误,NSUnderlyingError=0x17652ee50 {Error Domain=ASDErrorDomain Code=500"(null)\"Userlnfo={NSUnderlyingError=0x17652d530 {Error Domain=AMSErrorDomain Code=203 "(null)" Userlnfo= {NSUnderlyingError=0x17652c3c0 {Error Domain=AMSErrorDomain Code=203"(null)\"UserInfo=0x1663e5940(not displayed)}}}
1
0
98
Jun ’25
react native iap not providing the subscription information
I am handling the buy subscription with this function 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, }) does not return anything, and it is stuck at await
0
0
79
Aug ’25
StoreKit Subscription Fails to Load During App Review, Works in Sandbox/TestFlight
Hi everyone, I’m facing a recurring issue with my macOS app being rejected during App Store review, and I’d really appreciate any guidance. The subscription flow in my app is implemented using StoreKit, and everything works perfectly in our development environment using a StoreKit configuration file. It also behaves as expected in Sandbox testing and TestFlight — I even had few beta testers confirm that the subscription information is displayed correctly and the purchase flow completes without issues. All required subscription details are configured in App Store Connect: • Subscription duration and the description of the services offered • Price and price per unit where applicable • Paid apps agreement and related forms are correctly filled However, when the app is submitted for review, the subscription screen fails to display the expected information. From what I can tell, the product information fails to load from the App Store in the review environment — even though everything is working fine on our side. We’ve already submitted a video to Apple showing the subscription UI working in the Sandbox environment, but the app continues to be rejected under guideline 3.1.2 due to missing subscription info in the binary. Is anyone else experiencing similar behavior during review? Could there be a caching issue or delay in StoreKit syncing for newly configured products? Any help or suggestions are very welcome. Thanks in advance!
0
0
100
Apr ’25
How to validate Streamlined Purchasing with storekit 2
users download app with Streamlined Purchasing ,but the logic of checking subscription doesn't work. there the code: func checkSubscriptionStatus() async { for await entitlement in Transaction.currentEntitlements { guard case .verified(let transaction) = entitlement else { continue } if transaction.productID == monthlyProductID || transaction.productID == yearlyProductID { if transaction.revocationDate == nil && !transaction.isUpgraded { let activeSubscribed = transaction.expirationDate ?? .distantFuture > .now if activeSubscribed { hasActiveSubscription = activeSubscribed // other operation } } } } }
0
0
262
Mar ’25
Adding In-App Purchase to app + review required?
I'm trying to understand the IAP development process. I created my first Product on App Store Connect and am trying to build my app to use it. However it keeps failing with "Invalid product ID.". From what I've read, this is because the product has not yet gone through review. But what I don't understand is, of course it hasn't gone through review yet, because trying to use it in any capacity fails, even though I'm using a real physical device and using a Sandbox User. Is this the correct workflow? It seems very backwards that I have to submit the product for review, even before I know how it's going to be used. I'm still building the screen for the product page, and haven't even started touching any backend APIs, yet it's asking for screenshots. Am I misunderstanding something here?
0
0
76
Apr ’25
ExternalPurchase.presentNoticeSheet returns only cancelled in App Store version
Hello, Our app has been released in Korea with an external payment feature. The function that must display the required modal for external payments is as follows: let result = try await ExternalPurchase.presentNoticeSheet() guard result != .cancelled else { return } However, while testing in the development environment, the API returns a result of continuedWithExternalPurchaseToken(token: String), but in the version distributed through the App Store, it returns cancelled. We would like to know how we can receive the continuedWithExternalPurchaseToken result in the App Store version. Thank you for your guidance. If anyone here has experienced a similar issue or found a solution, I would greatly appreciate your insights as well.
1
0
52
Sep ’25
App Store Server API JWT Authentication Issue
Issue Description I am experiencing persistent 401 Unauthorized errors when attempting to access the App Store Server API using JWT authentication. Despite following Apple's documentation and regenerating keys, I am unable to successfully authenticate. Implementation Details I'm implementing JWT authentication for the App Store Server API to retrieve transaction information from the following endpoint: https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionID} My JWT generation code (in PHP/Laravel) follows Apple's documentation: php$kid = '6W6H649LJ4'; $header = [ "alg" => "ES256", "kid" => $kid, "typ" => "JWT" ]; $iss = 'b8d99de7-b43b-4cbb-aada-546ec784e249'; // App Store Connect API Key Issuer ID $bid = 'com.gitiho.learnCourse'; // Bundle ID $payload = [ "iss" => $iss, "iat" => time(), "exp" => time() + 3600, "aud" => "appstoreconnect-v1", "bid" => $bid ]; $pathFileAuthKeyP8 = "AuthKey_6W6H649LJ4.p8"; $contentFileAuthKey = \File::get(base_path($pathFileAuthKeyP8)); $alg = "ES256"; $jwt = \Firebase\JWT\JWT::encode($payload, $contentFileAuthKey, $alg, null, $header); Steps Taken to Troubleshoot Verified that the Issuer ID is correct and in UUID format Confirmed that the Key ID matches the private key filename Regenerated the key with proper App Store Server API permissions Ensured the private key file is properly formatted with correct headers and footers Verified that the JWT is being properly encoded using the ES256 algorithm Confirmed the bundle ID is correct for our application Checked that the API endpoint URL is correct Additional Information This implementation previously worked correctly We started experiencing 401 errors recently without changing our implementation We are using the Firebase JWT library for PHP to encode the JWT Request Could you please help identify what might be causing these authentication failures? Is there any recent change in the authentication requirements or endpoint URLs that might be affecting our integration? Thanks for support me.
0
0
101
Apr ’25
'Invalid value for purchase intake' error
Hello, I recently saw this error from StoreKit in the Console - 'Invalid value for purchase intake' - while debugging a SKPayment subscription issue (where a valid receipt should be verified and restored, but isn't for one user). I haven't been able to find any documentation about this message and wondered if it was related at all. There were two other logs from StoreKit right before saying: 'Found 3 products in receipt with ID' 'Processing ad attribution purchase intake' Does anyone know what 'invalid value for purchase intake' is referencing? We don't have the AdAttributionKit implemented. It sounds like it might be related to that instead? Thank you
0
0
94
Jul ’25
StoreKit 2: jwsRepresentation Validation, Rate-Limit Relief, and Send Consumption Info Effectiveness
Hi everyone, We operate an online game where all in-app assets are stored server-side and require a logged-in account (no device binding). I’d like guidance on four areas: Do we really need deviceVerification / deviceVerificationNonce? – Because every purchase is tied to an account and we enforce a global transactionId UNIQUE constraint, replay or cross-account reuse appears infeasible. Under these conditions, is omitting device verification acceptable, or are there situations where Apple still recommends it? Permanent rate-limit increase for the App Store Server API – During anniversary events we saw bursts of ~18 000 requests per hour, breaching the current hourly cap on the App Store Server API (verifyTransaction, getNotificationHistory, etc.). Is there a formal process to request a long-term rate-limit expansion (or an alternative tier) from Apple? When is an App Store Server API call required for a StoreKit 2 jwsRepresentation? Docs say “call the API if you’re unsure,” but there’s no clear cut-off. Because we fully validate the JWS signature plus the entire certificate chain (including CRL/OCSP checks) on our server, local cryptographic validation seems sufficient for consumables. For subscriptions we still plan to hit the API to fetch the latest status. Does this separation match Apple’s best practice? If Apple does recommend hitting the API for consumables as well, we’d like a concrete rule of thumb—e.g. “if the item price is USD 50 or higher, always use the API.” Is establishing such thresholds consistent with Apple’s intent? Refund-risk reduction from Send Consumption Info – Adapty reports a 40–60 % refund-rate drop for subscriptions when using Send Consumption Info (blog reference). Can we expect similar reduction for consumable IAP in social/online games? Any real-world results would be helpful. Thanks in advance for any guidance!
0
0
179
Apr ’25
SubscriptionStoreView not showing free trial offer in release build
I'm using the SwiftUI view SubscriptionStoreView (https://developer.apple.com/documentation/storekit/subscriptionstoreview/) with a subscription group that has 2 subscriptions. I set up a free trial offer in App Store Connect (https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-introductory-offers-for-auto-renewable-subscriptions/). The storekit file in Xcode is synced with the App Store. In debug build, this works and appears correctly, showing the free trial offer: But in release build, the free trial offer is not shown: The code is very simple: SubscriptionStoreView(productIDs: [ "[PRODUCT ID FOR ANNUAL SUBSCRIPTION]", "[PRODUCT ID FOR BIMONTHLY SUBSCRIPTION]" ]) Does anyone have a solution? Thank you. (Xcode 16.3, macOS 15.5, iOS 18.5)
0
0
121
May ’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
75
Apr ’25
Show Price Increase Consent
I'm currently still on StoreKit 1, and am testing the paymentQueueShouldShowPriceConsent delegate function. In my local .storekit file, I have a renewable subscription set up with a promotional offer. My test flow is as follows: User subscribes to renewable subscription Let subscription auto-renew once or twice User subscribes to renewable subscription with promotional offer with significant price reduction Promotional offer lapses and price increases to normal Expect paymentQueueShouldShowPriceConsent delegate function to trigger However, #5 never does get invoked, despite re-trying the subscription and promotional offers in various configurations. Manually triggering the Request Price Increase Consent option in the Xcode StoreKit transactions list does invoke the delegate function, but letting the promotional offer lapse does not. My storefront is set to Korea, and my simulator region is set to Korea as well. According to the documentation here and here, consent is required for all price increases in Korea. Is there some way I could check if things are working as intended?
0
0
80
Apr ’25