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

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
271
Mar ’25
Calling AppTransaction in a SpriteKit app
Hello 👋! I'm trying to switch the business model in my app from premium to freemium, gracefully so that existing users aren't bothered. I'd like to provide newly-paywalled content for original paid users. For this to work, I need to use originalAppVersion in AppTransaction, and support iOS 16 at minimum. I've asked about originalAppVersion earlier here. I've upped to iOS 16 already, but I don't exactly know how to call AppTransaction.shared to get originalAppVersion. The issue lies in the fact that my app is based on SpriteKit, so the operative areas I have available to call AppTransaction are ostensibly limited to AppDelegate and GameViewController. I'm using the code from Supporting business model changes by using the app transaction, but if I place it in either GameViewController or AppDelegate, for example in application(_:didFinishLaunchingWithOptions:) or viewDidLoad(), I get an error concerning async/await. Now, I understand the gist of it: these are not asynchronous methods. So I'm trying to understand how to do it perhaps outside of these methods. How do I call AppTransaction.shared (and fetch originalAppVersion) in a SpriteKit based app?
0
0
143
Jul ’25
Verification failed with status INVALID_CHAIN_LENGTH
我正在通过集成app-store-server-library-java来实现 iap服务端校验。我参照了官网提供的Verification Usage 的代码,运行的时候异常信息如下: at com.apple.itunes.storekit.verification.ChainVerifier.verifyChainWithoutCaching(ChainVerifier.java:98) at com.apple.itunes.storekit.verification.ChainVerifier.verifyChain(ChainVerifier.java:71) at com.apple.itunes.storekit.verification.SignedDataVerifier.decodeSignedObject(SignedDataVerifier.java:186) at com.apple.itunes.storekit.verification.SignedDataVerifier.verifyAndDecodeTransaction(SignedDataVerifier.java:72) 我的代码如下: import com.apple.itunes.storekit.model.ResponseBodyV2DecodedPayload; import com.apple.itunes.storekit.verification.SignedDataVerifier; import com.apple.itunes.storekit.verification.VerificationException; import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.DecodedJWT; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Base64; import java.util.Set; public class ExampleVerification { public static void main(String[] args) throws FileNotFoundException { String bundleId = "com.example"; Environment environment = Environment.SANDBOX; Set<InputStream> rootCAs = Set.of( new FileInputStream("AppleRootCA-G3.cer"), new FileInputStream("AppleRootCA-G2.cer") ); Long appAppleId = null; // appAppleId must be provided for the Production environment SignedDataVerifier signedPayloadVerifier = new SignedDataVerifier(rootCAs, bundleId, appAppleId, environment, true); String appTransactionJWS = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkFwcGxlX1hjb2RlX0tleSIsIng1YyI6WyJNSUlCeXpDQ0FYR2dBd0lCQWdJQkFUQUtCZ2dxaGtqT1BRUURBakJJTVNJd0lBWURWUVFERXhsVGRHOXlaVXRwZENCVVpYTjBhVzVuSUdsdUlGaGpiMlJsTVNJd0lBWURWUVFLRXhsVGRHOXlaVXRwZENCVVpYTjBhVzVuSUdsdUlGaGpiMlJsTUI0WERUSTFNRFl3TXpFeE1UQXdNRm9YRFRJMk1EWXdNekV4TVRBd01Gb3dTREVpTUNBR0ExVUVBeE1aVTNSdmNtVkxhWFFnVkdWemRHbHVaeUJwYmlCWVkyOWtaVEVpTUNBR0ExVUVDaE1aVTNSdmNtVkxhWFFnVkdWemRHbHVaeUJwYmlCWVkyOWtaVEJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCTnZZZ3o1MW1CbEMweE5McW9rMUJCcithRWJEb1ZEeVkyaVRsejZsK1JjYVR4QStVY2ptMjBESTNncFFlM280a2doRGxSbGowdEo1enBGUHgyQWR2VCtqVERCS01CSUdBMVVkRXdFQlwvd1FJTUFZQkFmOENBUUF3SkFZRFZSMFJCQjB3RzRFWlUzUnZjbVZMYVhRZ1ZHVnpkR2x1WnlCcGJpQllZMjlrWlRBT0JnTlZIUThCQWY4RUJBTUNCNEF3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU40bUJWTHBoZkpjYjdweHF2b09XcjkyK1czYU5LRG9pazV5Vk9BT0NEVmxBaUFYWVF0czJubWZGMStGYzlSODJHXC96QWhaVU00aDNTXC9VdFE4Q1lPS2p3ZlE9PSJdfQ.eyJhcHBsaWNhdGlvblZlcnNpb24iOiIxIiwib3JpZ2luYWxQdXJjaGFzZURhdGUiOjAsImJ1bmRsZUlkIjoiYnJpZ2h0LnVuaWhhbmQuY24iLCJhcHBUcmFuc2FjdGlvbklkIjoiMCIsImRldmljZVZlcmlmaWNhdGlvbiI6IlRYdGRvMWZtNDhQVDdXUUh5cHU4K2l3TW55YmNoTTNNeG5XUnhOR1JqSFhQQnVqMXdUaldcL05zN3JtUmJlQTd3IiwicmVjZWlwdFR5cGUiOiJYY29kZSIsIm9yaWdpbmFsQXBwbGljYXRpb25WZXJzaW9uIjoiMSIsInJlcXVlc3REYXRlIjoxNzYxMDM1OTMzNTE3LCJvcmlnaW5hbFBsYXRmb3JtIjoiaU9TIiwicmVjZWlwdENyZWF0aW9uRGF0ZSI6MTc2MTAzNTkzMzUxNywiZGV2aWNlVmVyaWZpY2F0aW9uTm9uY2UiOiI1ZDhmNzM5Mi01N2YwLTQyM2YtOTMzNy1hZDQ0YTk5MDM4Y2EifQ.2ZO5xsx-yywP4IyaDz4KQ3mq181ZGwlX2uANSm-kHq50KIdMMUDveMsCrcZmHdzLH2rpfPsXKaIMdM25Hdcuuw"; DecodedJWT unverifiedJWT = JWT.decode(appTransactionJWS); String header = unverifiedJWT.getHeader(); System.out.println(new String(Base64.getDecoder().decode(header))); try { signedPayloadVerifier.verifyAndDecodeTransaction(appTransactionJWS); } catch (VerificationException e) { e.printStackTrace(); } } } 查看了ChainVerifier.java 源代码,发现 private static final int EXPECTED_CHAIN_LENGTH = 3; // <--- 关键常量 // ... PublicKey verifyChainWithoutCaching(String[] certificates, boolean performRevocationChecking, Date effectiveDate) throws VerificationException { // ... 解析证书代码 ... if (parsedCertificates.size() != EXPECTED_CHAIN_LENGTH) { throw new VerificationException(VerificationStatus.INVALID_CHAIN_LENGTH); // <--- 抛出异常点 } // ... 后续验证代码 ... } appTransactionJWS是来自客户端的沙盒环境。 我发现沙盒环境的jws总是包含一个证书,而后端验证又必须要求三个证书,请问这个问题如何解决。
0
0
76
Oct ’25
AppStore response times for the store test environment to make purchases is very long.
I enter the payment wall, there it takes more or less 3 to 4 minutes to show the plans, when I select the monthly plan the loader is shown and from there the pop up to make the purchase in sandbox does not appear, I have waited until a maximum of 50 minutes and it is not shown, I go back and close the app I do the same steps and I am still there, without showing the pop up. Doing this same process in xcode, everything happens immediately without any interruption.
0
1
129
May ’25
SKAdNetwork 3.0 postbacks are not being delivered.
Hi To test SkAdNetwork, I installed the profile from the documentation (https://developer.apple.com/documentation/storekit/testing-ad-attributions-with-a-downloaded-profile) on my ios device. and turned off and on Allow Apps to Request to Track to change the idfa value. In this state, I installed the app (bundle id: id436731843) using SKAdNetwork with the following values at April 8th AM 01 (UTC) SkAdnetwork version: 3.0 fidelity fidelity=0, nonce=b3346a51-f7b5-42a2-a508-775a62317c83, timestamp=1744076349671, signature=... fidelity=1, nonce=b3346a51-f7b5-42a2-a508-775a62317c83, timestamp=1744076349672, signature=... itunesitem=436731843 network=87u5trcl3r.skadnetwork sourceapp=1220307907 I was hoping to get a postback after , but after a day, the postback is still not delivered. Any idea why the postback is not delivered? Thank you
0
0
41
Apr ’25
app signatures do not appear in sandbox
I've been trying to make my app available on the App Store for a month now, but I can't because the signatures I created don't appear in the sandbox app. I did all the configuration in the store and in the app. I tested the same code in another app with signatures and it was loaded, but the signature for that specific app doesn't appear. I've tried contacting Apple support, but they can't help me. It almost seems like it's on purpose. I'm treated like crap and they don't even give me an explanation about what's happening. Can anyone help me?
0
1
179
Mar ’25
Cannot get public keys for jwks verification
I am using the public url https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/jwsPublicKeys to get the jwks keys to verify the signed payload for store kit payments. I am checking Apple server notifications. const APPLE_JWKS_URL = "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/jwsPublicKeys" // Apple JWK set (cached by jose) const appleJWKS = createRemoteJWKSet(new URL(APPLE_JWKS_URL)); const jwks = await appleJWKS(); logger.debug("Apple JWKS Keys: %O", jwks); // Log the keys if (!signedPayload) { // return res.status(400).json({ error: "Missing signedPayload" }); } // Step 1: Verify JWS (signature + payload) using Apple's JWKS const { payload, protectedHeader } = await jwtVerify( signedPayload, appleJWKS, { algorithms: ["ES256"], // Apple uses ES256 for signing } );
0
1
353
May ’25
Is there an API provided by the App Store Server API for canceling subscriptions?
Is there an App Store Server API available that allows cancellation of specific subscriptions by specifying transaction_id or similar identifiers? Background of these questions: We occasionally suspend user accounts due to violations of our service terms and conditions. In such cases, we would like to forcibly cancel their subscriptions if possible. However, we could not find relevant information in the documentation, which is why we are reaching out with these questions. Please let us know.
0
0
91
Jul ’25
Unexpected Change in Apple Refund Handling CONSUMPTION_REQUEST - Impact on Subscription App with AI Backend
We offer a 3-day free trial, and our paywall clearly states that users will be charged after the trial ends. However, some users request refunds after the charge - even after fully using our app for days or even weeks. In some cases, refunds are approved despite the users having consumed our AI processing services for up to a month. Since our app relies on backend AI processing, each user session incurs a real cost. To prevent losses, we utilize RevenueCat’s CONSUMPTION_REQUEST system and have set our refundPreference to: "2. You prefer that Apple declines the refund". Until recently, Apple typically respected this preference, and 90% of refund requests were declined as intended. However, starting about a week ago, we observed a sudden reversal: Apple is now approving around 90% of refund requests, despite our refund preference. As a result, we are operating at a loss and have had to halt both our marketing campaigns and our 3-day free trial. We’re trying to understand whether this shift is due to a change in Apple’s refund policy, or if we need to handle CONSUMPTION_REQUEST differently on our end. Has anyone else experienced similar changes? Any insights would be greatly appreciated.
0
1
360
May ’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
122
May ’25
Questions regarding refundPreference sending consumption data
Apple notification description: notificationType: The type that describes the In-App Purchase or external purchase event for which the App Store sends the version 2 notification. App Store Server Notifications 2.0+ string notificationType Possible Values CONSUMPTION_REQUEST A notification type that indicates that the customer initiated a refund request for a consumable In-App Purchase or auto-renewable subscription, and the App Store is requesting that you provide consumption data. For more information, see Send Consumption Information. When the developer receives the refund request notification and sends the refundPreference, they say: GRANT_PRORATED You prefer that the App Store grants a prorated refund. Discussion Use these values in the refundPreference field of a ConsumptionRequest. The following constraints apply to the GRANT_PRORATED option: If the product is a consumable or non-consumable In-App Purchase or a non-renewing subscription, you may include a consumptionPercentage value in the ConsumptionRequest. Question: Requesting a refund for a non-consumable type does not send a notification to the developer. However, when a developer sends a refund preference, they can still send a GRANT_PRORATED request for a non-consumable type.
0
0
39
Jan ’26
Auto Renew Subscription Model
Hi, I want to offer an auto-renewable subscription (e.g., $1/month) that grants users (10 document analyses per month), with the count resetting at the start of each billing cycle. -Unused analyses will not roll over to the next month- Additionally, any analyses generated while the subscription is active will remain accessible to the user permanently, even if they cancel the subscription. The paywall, app description, and metadata will clearly state that the subscription grants (10 document analyses per month with no rollover) We want this to be implemented as an auto-renewable subscription model, not as a consumable service or a token/credit system (which we want to avoid). Is this model acceptable under Apple’s guidelines, or would it be considered a token/credit system? Any insights or alternative suggestions would be appreciated.. Thanks
0
0
320
Mar ’25
Request Guidance on IAP Requirements for Dynamically Priced Digital Video Content
Dear Apple Developer Support Team, I hope you are doing well. We are reaching out to request clarification and guidance regarding the In-App Purchase (IAP) requirements for our upcoming iOS application. Our app offers paid access to digital video content. Each video has a dynamic price determined by our backend based on multiple factors (such as duration, category, and promotions). Additionally, users are allowed to select and purchase multiple videos at the same time, which results in a combined total price that varies per transaction. Challenges we face with IAP on iOS Dynamic pricing: Apple requires IAP products to have static pricing defined in App Store Connect. Our video prices change frequently and cannot be represented by fixed IAP product SKUs. Multiple-item purchases: iOS does not support a single purchase that includes multiple different IAP products. Processing many separate IAP transactions in sequence results in a poor user experience and is likely to cause failures. Product creation limitations: Since our catalog contains many videos with frequently changing prices, it is not feasible to create individual IAP products per video. Given these restrictions, we are unclear how to remain compliant while still providing a functional purchase flow for our users. Question In this scenario, where: video prices are dynamic, users may purchase multiple videos together, and IAP does not support multi-item purchases or dynamic pricing, are we allowed to use Stripe or another external payment provider to handle these purchases? If not, we would appreciate Apple’s guidance on what the recommended and compliant approach would be for apps that must price digital content dynamically and allow bulk purchasing. We want to ensure full compliance with App Store Review Guidelines and would like confirmation on the correct implementation strategy before proceeding. Thank you for your time, and we look forward to your clarification. Kind regards, Muhammad Adnan Koderlabs
0
0
94
Dec ’25
Conversion tracking with the SKAN
Hello all, We developed an iOS app which we started advertising now. In our iOS app we already implemented the updatePostbackConversionValue(_:completionHandler:) to send in-app events in increasing numbers (first open -> 1, lead ->2, conversion -> 3). From our understanding this should be enough for alle ad networks (Apple Ads, Google Ads, Meta Ads, Microsoft Ads and Reddit Ads) to receive those numbers - at least they receive the app installs from the SKAN already. Is this correct or do we miss something here in the integration? We currently really struggle to assure that everything is working and we do not see any conversions coming in - even though two weeks of advertising have passed already. I look forward for any feedback or discussion and I am also happy to share more details if needed. Best regards, Manuel
0
0
65
Jun ’25
Subscribe button does nothing in App Review, but In
Hello, My app "MyCourses" (bundle id: com.ahmedbaqer.mycourses) was rejected under Guideline 2.1 because "No action followed when we tapped the button to subscribe to a course" on iPadOS 26.1. When I run the same code (version 1.0.0 (11)) from Xcode on a real device using a StoreKit configuration file (In App Purchase.storekit), the purchase flow works correctly: When I tap the "Subscribe via Apple" button, the App Store purchase sheet appears. The purchase completes successfully and unlocks the course. I use the in_app_purchase Flutter plugin and queryProductDetails to load products. However, when I install the build via TestFlight (and in App Review), tapping the same "Subscribe via Apple" button does nothing – which matches the behavior described by App Review. From my logs it looks like queryProductDetails is returning an empty productDetails list in that environment. For In‑App Purchases: I created 7 non‑consumable products in App Store Connect. Their Product IDs exactly match the IDs in my In App Purchase.storekit file (used only for local Xcode testing). All IAPs are now in "Waiting for Review" status and are linked to the iOS app version 1.0.0 (11) in the “In‑App Purchases and Subscriptions” section. At the time of the original review, some IAPs were in "Developer Action Needed / Rejected" state, so I suspect queryProductDetails may have returned no products and the reviewer saw no action after tapping the button. My questions: When IAP products are in "Waiting for Review" and linked to the app version, should queryProductDetails return them during App Review / TestFlight, or do they need to be fully approved first? Is there any additional configuration required so that the subscribe button reliably shows the App Store purchase sheet for reviewers (for example, any specific StoreKit / sandbox settings)? Are there recommended best practices to show a clearer error state when queryProductDetails returns no products, so that App Review understands this is a configuration / IAP-status issue rather than a UI bug? Any guidance from Apple engineers or other developers who faced a similar situation would be greatly appreciated. Thanks in advance.
0
0
221
Nov ’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
103
Apr ’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
57
Apr ’25