Hello,
I am currently implementing External Purchase Link and External Purchase Custom Link and am encountering an issue where both
ExternalPurchaseLink.canOpen and ExternalPurchaseCustomLink.isEligible always return false under all test conditions.
I would like to confirm whether my setup is missing any required steps or whether this behavior is expected.
Below are the details of my current environment and configuration:
🔧 1. Development Environment
Xcode: 16.3, 16.4, 26.0 beta 4
Devices:
iPhone running iOS 26.2 beta
iPhone running iOS 16.7.12
macOS 15.5 (real device testing)
Simulator iOS 18.0
Build Type: Local development build using a Developer Provisioning Profile
Sandbox account signed in during testing
🔑 2. Entitlements (Developer site & Xcode)
In Certificates → Identifiers → App ID, both capabilities are enabled:
StoreKit External Purchase
StoreKit External Purchase Link
The .entitlements file in Xcode includes:
com.apple.developer.storekit.external-purchase = YES
com.apple.developer.storekit.external-purchase-link = YES
The Provisioning Profile also contains both entitlements (confirmed via codesign -d --entitlements :-).
📄 3. Info.plist Configuration
Both keys are configured with correct region codes according to documentation:
SKExternalPurchase
SKExternalPurchaseCustomLinkRegions
🌍 4. Test Storefront
Device storefront verified as United States (US) or Portugal (PT) (US = target region for External Purchase Link, PT = EU region)
But despite all the above configuration,
both API calls consistently return false:
ExternalPurchaseLink.canOpen // false
ExternalPurchaseCustomLink.isEligible // false
So I cannot proceed to testing the remaining flow (token retrieval, link opening, etc.)
------ Questions ------
❓ Q1) Local Development Build Limitation
Is it expected behavior that Developer-signed local builds always return
canOpen = false / isEligible = false
for External Purchase Link & Custom Link?
Is there a technical or policy restriction that prevents eligibility in local dev builds?
❓ Q2) App Store Connect Configuration Requirement
Are there mandatory App Store Connect settings (such as external purchase URLs, support URL, disclosures, or country configuration) that must be enabled before eligibility becomes true?
Currently, no External Purchase Link or Custom Link menu is visible in my App Store Connect app settings.
Is this menu only available after certain approvals or under specific conditions?
❓ Q3) TestFlight Requirement
Do External Purchase Link and Custom Link only return eligibility = true on:
TestFlight builds, or
Distribution-signed builds?
Or should eligibility also work on developer builds?
Formal confirmation would be helpful.
❓ Q4) Developer Account Type Limitation
We are using an Individual Developer Account (not Organization).
Can Individual accounts fully request, test, and ship apps using:
External Purchase Link
External Purchase Custom Link
Or are there limitations on account type?
🙏 Request
We have completed all documented setup steps (Entitlements → Provisioning → Info.plist),
but eligibility remains false, blocking feature validation.
Please clarify which of the following is the cause:
Local development builds do not support eligibility
Missing App Store Connect configuration (not visible to us)
Account type restriction
Region rollout or entitlement approval requirement
Any additional setup not documented publicly
Thank you for your assistance.
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm working on a watchOS app that has an iOS counterpart. There will be a subscription required to unlock functionality and I would like the user to be able to make the purchase on either the iPhone or the watch and have both apps unlock.
The first link below says that StoreKit 2's Transaction.currentEntitlements will not work in this case like it does with extensions. The second link says it might work but doesn't in the sandbox.
What is the best way to make this work? Will it just work in the App Store? Should I use WCSession to send the purchase information from one platform to the other and store it in the keychain? Something else?
Via https://www.revenuecat.com/blog/engineering/ios-in-app-subscription-tutorial-with-storekit-2-and-swift/
"Transaction.currentEntitlements can be used in extensions the same way it was used in the previous steps. This works for extensions like Widgets and Intents. However, an iOS app with a companion watchOS app will not work even though Transaction.currentEntitlements can be executed in it. A companion watch app does not stay updated with the same transaction history as its iOS app because they are separate platforms."
Via https://developer.apple.com/forums/thread/739963
"In TestFlight I was able to confirm that the Watch app and IOS app share in-app purchases. It seems the problems confirming this with Storekit and Sandbox are limits of the testing environments."
Hi everyone,
I’m facing an issue where StoreKit is returning 0 products from the App Store, even though my auto-renewable subscriptions are approved in App Store Connect.
When calling queryProductDetails using Flutter’s in_app_purchase package (which uses StoreKit under the hood), StoreKit reports success but returns an empty list.
The logs show the following error:
IAPError(code: storekit_no_response, source: app_store, message: "StoreKit: Failed to get response from platform.")
InAppPurchase.isAvailable() returns true, but no product details are received.
Already verified:
• Subscriptions are approved in App Store Connect
• Product identifiers in the app match those in App Store Connect exactly
• In-App Purchase capability is enabled in Xcode
• Paid Applications Agreement, banking, and tax details are active and complete
• Using the latest version of the Flutter in_app_purchase package
StoreKit should normally return the list of available products in the production environment, but it consistently returns an empty array along with the “storekit_no_response” error.
Has anyone else encountered this issue or found any potential causes for StoreKit failing to return products in the production environment? Any insights would be greatly appreciated.
Thank you.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Connect
In-App Purchase
I'm using code similar to the following to conditionally show the SubscriptionStoreView and the .storeButton(.visible, for: .restorePurchases) modifier is used to allow the user to restore an existing subscription.
How can I listen for events that would allow me to close this view once the subscription is restored?
The .onInAppPurchaseCompletion closure does not handle this and it also appears that listening for results in Transaction.currentEntitlements also doesn't handle the fact that a subscription is restored.
Any guidance on how to determine if the subscription has been restored would be greatly appreciated.
Finally, how can this be tested effectively in both TestFlight and in Xcode with the simulator.
if subscriptionManager.subscription == .none {
SubscriptionStoreView(groupID: "1234567") {
SubscriptionMarketingView(transparency: false)
.containerBackground(for: .subscriptionStoreFullHeight) {
GradientBackground()
}
}
.backgroundStyle(.clear)
.storeButton(.visible, for: .restorePurchases)
.storeButton(.visible, for: .redeemCode)
.onInAppPurchaseCompletion { product, result in
Task {
await subscriptionManager.entitlements()
}
}
}
On this page:
https://developer.apple.com/documentation/storekit/testing-age-assurance-in-sandbox
It says:
Start with a Sandbox account.
What is a Sandbox account and how does one set one up?
Just it just simply mean an Apple account id used for testing, or something else?
According to Apple's documentation, SKOverlay is designed to recommend other applications to users. I'm seeking clarification on whether it also supports displaying update prompts for the host application itself.
Use case:
My app (for example, HelloDeveloper) is live at version 2.0, but some users are still on version 1.0. I want to display a soft update prompt that allows users to remain in the app.
Question:
Is it possible to use SKOverlay with my app's App Store ID to present an update option without requiring users to leave the app?
I'm trying to implement my first in-app purchase, and I've created the IAP in App Store Connect, and created a sandbox account.
When I open Settings > Developer in the iPhone Simulator, there is a "Sandbox Apple Account" option at the bottom.
If I click the blue "Sign In" link I'm asked for the email and password, so I enter the correct credentials for the sandbox account. The "Sign In" text goes grey for a few seconds, then it goes blue again. It never changes to show that I'm signed in. Should it? (I think so.)
Do I need to sign into the Simulator's Apple Account, too? (I don't think so.)
Anyway, aside from that, in my app the IAP is listed and I have a button to purchase it. When I click it I'm asked for the email and password to sign into the Apple Account. I enter the correct sandbox email and password (they are definitely correct) and I see this in the Xcode console:
Purchase did not return a transaction: Error Domain=ASDErrorDomain Code=530 "(null)" UserInfo={client-environment-type=Sandbox, NSUnderlyingError=0x600000d0c7b0 {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.}}}
Why is it talking about password reuse? AFAIK, I have only one sandbox account for this app (and none for any of my other apps), and this is the only one of my apps that has an IAP.
Any ideas on how to get an IAP working? Thanks!
If an app on the App Store still uses StoreKit 1 (a.k.a. the Original StoreKit) to handle In-App Purchases, would IAPs work for users who download such app on iOS 26.2?
Would the app allow the users to purchase an IAP via StoreKit 1 or would it be impossible to buy the IAP on iOS 26?
The iOS Documentation says that SKPaymentQueue (which is a part of StoreKit 1) is "Deprecated" and "No longer supported.", with the support being for iOS 3.0–18.0.
Does this mean that apps using StoreKit 1 won't be able to make IAP purchases when running on iOS 26?
Hi,
We have a app with some auto-renewing subscription in a group of subscriptions.
When a user upgrade from a subscription to another, the "user receive a refund of the prorated amount of their original subscription" (https://developer.apple.com/app-store/subscriptions/).
How is the prorated calculated ?
Example : subscription to 14,99$ / month. If subscriber upgrade after 10 days, is the refund calculated 10/30 of 14,99$ (so ~5$) ?
I use [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]. Works on my App which is in the store (compiled pre-iOS 26).
If I compile the same App now, same codebase with Xcode Version 26.0, restore does not work. Nothing happens. Tested on real device (iOS 26).
Documentation says its deprecated, but my deployment target is iOS 12.
Anyone has similar issues? Any recommendations?
Topic:
App & System Services
SubTopic:
StoreKit
Hello,
I’m experiencing an issue with StoreKit 2 when passing a new appAccountToken for each purchase request.
Case-ID: 15948169 (for DTS reference)
Description of the Problem
When initiating a purchase, I generate a new UUID to use as the appAccountToken:
let serverTransactionId = UUID()
let options: Set<Product.PurchaseOption> = [
.appAccountToken(serverTransactionId)
]
let result = try await product.purchase(options: options)
Expected Behavior:
Each new purchase should return the updated appAccountToken that I pass into the purchase options.
Actual Behavior:
The payload response after success always contains the same appAccountToken from the very first transaction. It ignores subsequent UUIDs I pass and keeps reusing the original one.
This causes issues because the same identifier is being reused across multiple transactions, making it difficult to map purchases to the correct user session.
Steps to Reproduce
Generate a fresh UUID using UUID().
Pass it as .appAccountToken when calling purchase().
Complete the transaction in the sandbox environment.
Inspect the payload response → The appAccountToken value is always the same as the first one used, not the newly provided one.
Additional Info
I do have a focused test project that reproduces this issue.
The issue appears specific to appAccountToken persistence across multiple transactions.
Has anyone else experienced this behavior with StoreKit 2? Is this expected (Apple caching the first token) or could this be a bug?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
In-App Purchase
App Store Receipts
We have encountered an issue when verifying transactions using the Get Transaction Info API.
We tested the behavior in both the sandbox and production environments and observed the following results.
When calling the production endpoint:
https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}
with a transactionId generated in the sandbox environment, the API returns HTTP 401 Unauthorized.
However, based on the documentation and common understanding, we expected HTTP 404 Not Found in this case.
Using the same JWT token, if we call the sandbox endpoint:
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId},
we receive HTTP 200 OK with the expected response body.
We have also confirmed that the same behavior occurs when using the Get Transaction History API — it works correctly in the sandbox environment but returns 401 in production.
Could you please confirm whether this behavior (receiving 401 instead of 404) is expected by design, or if it indicates a potential issue?
If this is not the intended behavior, we would appreciate any guidance or instructions to resolve it.
Thank you very much for your technical support.
「Get Transaction Info」APIを用いてトランザクションの検証を行ったところ、以下の問題が発生しました。
サンドボックス環境および本番環境の両方で検証を行い、次の結果を確認しています。
本番環境エンドポイント https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}
に対して サンドボックス環境で生成された transactionId を使用すると、HTTP 401 Unauthorized が返却されます。
(一般的には、この場合 404 Not Found が返る想定であると理解しています。)
同一のJWTトークン を用いて サンドボックス環境のエンドポイント
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId}
を呼び出した場合は、HTTP 200 OK が返り、期待通りのレスポンスボディを受け取ることができています。
また、同様の挙動が Get Transaction History を使用した場合にも発生することを確認しています。
サンドボックス環境では正常に動作しますが、本番環境では401が返却されます。
この挙動(401が返却されること)は仕様上想定されたものか、または何らかの問題によるものかご確認をお願いいたします。
もし想定外の挙動である場合は、解決に向けたご案内をいただけますと幸いです。
本件について、技術的なサポートをお願いいたします。
よろしくお願いいたします。
💬 Post Content
Hello everyone,
I’m currently testing In-App Purchases (auto-renewable subscriptions) for my iOS app,
and I’m experiencing an issue where the product information cannot be fetched from StoreKit.
❓ Questions
For sandbox testing, is it absolutely necessary to submit a new app version for review (with the in-app purchase included)? Or can I test subscriptions without submitting a new build to App Review?
Under what conditions does the error
IAPError(code: storekit_no_response,
source: app_store,
message: StoreKit: Failed to get response from platform.)
🧪 What I’ve Tried
• Confirmed that the bundle identifier matches the App Store Connect record
• Verified that In-App Purchase capability is enabled in Xcode
• Ensured the sandbox tester account is logged in (Settings → App Store)
• Removed the StoreKit configuration file to use the sandbox environment
• Tried both real device and simulator (same error)
• Accepted the Paid Apps Agreement in App Store Connect
• Products are created but currently not yet submitted for review
🔍 Environment
• App type: iOS app built with Flutter (using in_app_purchase plugin)
• Build type: Archive build installed via Xcode (Run on device) and testFlight
• StoreKit Configuration File: Currently removed (for sandbox test);
• Status: Ready to submit in App Store Connect
Topic:
App & System Services
SubTopic:
StoreKit
With the imminent suspension of SHA-1 on App Store receipts, we desperately need an objective C code sample demonstrating how to calculate the same SDH-256 hash on device to compared with the hash from the App Store receipt.
The forced migration to SHA256 for app store receipts this month mean we have to rewrite our on device receipt validation code. However there is no documentation or objC sample code on how to validate the SHA256 hash from MAS receipts. Thje documentation at
https://developer.apple.com/documentation/technotes/tn3138-handling-app-store-receipt-signing-certificate-changes/ does not give any detail on how to validate SHA256. All my 100+ hours of experimentation and trial and error attempting to create a matching SHA256 has on device have failed.
We desperately need some ObjC Sample code to validate the SHA256 hash on device. Our existing SHA1 code is still working but we expect SHA1 hashes to disappear from MAS Receipts any day now.
Tnanks for any advice !
I have two sandbox users in App Store Connect, as I'm trying to test in-app purchases and Family Sharing. They're set up fine; I can make purchases in the app.
The issue is that the refund request sheet in my app sometimes shows properly and lets me request a refund, but I'd say >80% of the time the sheet just shows "Cannot Connect" with a "Retry" button. Hitting that button doesn't ever result in the sheet showing the refund page.
The only fix for this is to delete the app from the device, and restart the device.
This has to be a joke, right? I need to be able to test this IAP, and the sandbox environment is useless most of the time. Why?
Anyone experiencing this sort of issue?
iOS Storekit2 Appstore production environment, some user feedback in app purchase faliure, What our log records is StoreKitError.unknown,please How to solve problem, thanks
Dear Apple Technical Support Team,
We have encountered a potential issue related to transaction handling while using StoreKit v2, and would greatly appreciate your assistance in confirming the behavior or providing any relevant guidance.
Issue Description:
When calling Transaction.unfinished and listening to Transaction.updates on the client side, we noticed that some transactions—despite having already been processed and successfully completed with finish()—are being returned again upon the next app launch, which results in duplicate receipt uploads.
Current Handling Flow:
1. Upon app launch:
• Iterate over Transaction.unfinished to retrieve unfinished transactions;
• Simultaneously listen for transaction changes via Transaction.updates (e.g., renewals, refunds);
2. For each verified transaction, we immediately call await transaction.finish();
3. We then construct a transaction model, store it locally, and report it to our backend for receipt verification;
4. After the server successfully verifies the receipt, the client deletes the corresponding local record;
5. On every app launch, the client checks for any locally stored receipts that haven’t been uploaded, and re-uploads them if necessary.
Key Code Snippets:
private static func verifyReceipt(receiptResult: VerificationResult) -> Transaction? {
switch receiptResult {
case .unverified(_, _):
return nil
case .verified(let signedType):
return signedType
}
}
public static func handleUnfinishedTransactions(payConfig: YCStoreKitPayConfig, complete: ((YCStoreKitReceiptModel?) -> Void)?) {
Task.detached {
for await unfinishedResult in Transaction.unfinished {
let transaction = YCStoreKitV2Manager.verifyReceipt(receiptResult: unfinishedResult)
if let transaction {
await transaction.finish()
if transaction.revocationDate == nil {
let receipt = YCStoreKitV2Manager.createStoreKitReceiptModel(
transation: transaction,
jwsString: unfinishedResult.jwsRepresentation,
payConfig: payConfig,
isRenew: false
)
complete?(receipt)
}
}
}
}
}
private func observeTransactionUpdates() -> Task<Void, Never> {
return Task {
for await updateResult in Transaction.updates {
let transaction = YCStoreKitV2Manager.verifyReceipt(receiptResult: updateResult)
if let transaction {
await transaction.finish()
if transaction.revocationDate == nil {
let receipt = YCStoreKitV2Manager.createStoreKitReceiptModel(
transation: transaction,
jwsString: updateResult.jwsRepresentation,
payConfig: self.payConfig,
isRenew: false
)
self.callProgressChanged(.receiptPrepared, receiptModel: receipt, errorType: .none, error: nil)
}
}
}
}
}
Our Questions:
1. Is it possible for Transaction.unfinished or Transaction.updates to return transactions that have already been finished?
Specifically, if a transaction was successfully finished in a previous app launch, could it still be returned again during the next launch?
2. Are there any flaws in our current handling process?
Our current sequence is: finish() → construct model → local save → report to server → delete after verification. Could this order lead to timing issues where StoreKit considers a transaction unfinished?
3. If we need your assistance in investigating specific user transaction records or logs, what key information should we provide?
We greatly appreciate your support and look forward to your response to help us further optimize our transaction processing logic.
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?
Hi. If the app is in landscape only and when the SKStoreProductViewController is presented, the safeArea changes to what looks like a portrait mode safe area. When the SKStoreProductViewController is dismissed, the safeArea does NOT revert back to the original values.
Is there a way to force the safeArea to "reset"? I've submitted some bug tickets through Apple Feedback but I haven't received any response about it.
The below code will pop up the SKStoreProductViewController and if you have a UIView that is constrained to the safe area, then you can visibly notice that the safe area is changed and doesn't go back.
I have tested this on iPhone 14 Pro, iPhone 15, and iPhone 16 Pro and in the Simulators. The incorrect behavior happens on those and probably more.
Thanks.
#import "ViewController.h"
#import &lt;StoreKit/StoreKit.h&gt;
@interface ViewController ()
@property (nonatomic, strong) SKStoreProductViewController *productViewController;
@end
@implementation ViewController
- (IBAction)buttonTapped:(id)sender {
self.productViewController = [[SKStoreProductViewController alloc] init];
NSDictionary *parameters = @{
@"id" : @"6443575749"
};
[self.productViewController loadProductWithParameters:parameters completionBlock:^(BOOL result, NSError * _Nullable error) {
[self presentViewController:self.productViewController animated:YES completion:^{
// presented
// The panel that is constraint to the safe area visibly shows that the safe area is no longer correct.
}];
}];
}
@end
Hi there! Whenever I try to add a new Offer Code for my app's subscription, I get an unknown error. When I get to the last step of the "Create Offer for Codes" flow, I get an error that error reads "An error has occurred. Try again later." I have been getting this same error for over a week now, so any help figuring out how to add new offer codes would be greatly appreciated!