There is an issue with StoreKit. The line let products = try await StoreKit.Product.products(for: ids) doesn't always work correctly. Sometimes it returns an array with the correct data, and sometimes it returns an empty array. I'm passing the correct ids to products(for:).
This problem only occurs when using the sandbox environment (in TestFlight builds). In the App Store version, everything works fine.
StoreKit configuration is none. All IAPs are approved.
Everything was working fine before this. The problem was discovered on May 1, 2025.
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 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.
Hello,
Is anyone else seeing Purchase.PurchaseResult.UserCancelled, despite a successful transaction?
I had a user notify me today that he:
Attempted a purchase
Entered payment credentials
Was asked to opt in to email subscription notifications
Opted In
Was shown my app's "User Canceled Purchase" UI
Attempted to repurchase
Was alerted that he was "Already Subscribed"
I have adjusted my code to check Transaction.currentEntitlements on receiving a .userCancelled result, to avoid this in the future. Is this logically sound?
Here is my code - please let me know if you see any issues:
func purchase(product: Product, userId: String) async throws -> StoreKit.Transaction {
let purchaseUUID = UUID()
let options: Set<Product.PurchaseOption> = [.appAccountToken(purchaseUUID)]
let result = try await product.purchase(options: options)
switch result {
case .success(let verification):
guard case .verified(let tx) = verification else {
throw PurchaseError.verificationFailed // Show Error UI
}
return try await processVerified(tx)
case .userCancelled:
for await result in Transaction.currentEntitlements {
if case .verified(let tx) = result, tx.productID == product.id, tx.revocationDate == nil {
return try await processVerified(tx)
}
}
throw PurchaseError.cancelled // Show User Cancelled UI
case .pending:
throw PurchaseError.pending // Show Pending UI
@unknown default:
throw PurchaseError.unknown // Show Error UI
}
}
@MainActor
func processVerified(_ transaction: StoreKit.Transaction) async throws -> StoreKit.Transaction {
let id = String(transaction.id)
if await transactionCache.contains(id) {
await transaction.finish()
return transaction // Show Success UI
}
let (ok, error) = await notifyServer(transaction)
guard ok else {
throw error ?? PurchaseError.serverFailure(nil) // Show Error UI
}
await transaction.finish()
await transactionCache.insert(id)
return transaction // Show Success UI
}
The only place the "User Cancelled Purchase" UI is displayed on my app is after the one instance of "throw PurchaseError.cancelled" above.
This happened in Production, but I have also seen userCancelled happen unexpectedly in Sandbox.
Thank you for your time and help.
Hi! I’m new in programming apps for Apple Store and I’m creating my first app. I already send my for review but I get an answer of problems with the subs flow. If there’s anyone who can help me fix this problem and implement my subscriptions in my app and test it out I would be thankful, I want the flow work like in the image!
We were facing the same error yesterday and tried many different solutions, but none of them worked. However, today the issue resolved itself and everything is working properly now.
The issue we were facing was:
#1. not getting any product in didReceive method of storekit.
#2. it was working sometimes (i.e. if we try 3 times then 1 time we were getting the product
The sample code provided in https://developer.apple.com/wwdc21/10114 doesn't appear to call finish() on unverified transactions, and I haven't been able to find any documentation regarding what to do with unfinished transactions. However, Apple has always emphasized the importance of finishing transactions, and since a transaction object is provided even with the unverified state, I'd love some guidance!
The majority of our sandbox calls to verifyReceipt end in an ETIMEDOUT error. This is making it very difficult to verify our purchase flow for our pending release. We have not yet migrated to StoreKit 2 and still rely on this API endpoint.
The Apple API status page reports no issues.
Is anyone else encountering this?
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}
Topic:
App & System Services
SubTopic:
StoreKit
Im building a small iphone app with StoreKit and currently testing it in testflight right on my mac, not on iphone. StoreKit part almost exactly copied from SKDemo from one of the Apple's WWDC. For some users and for myself Transaction.currentEntitlements always returns .unverified results. I double-checked Apple Connect settings, i checked my internet connection and everything is fine. Is there some pitfalls for testflight on mac? How can I find out what is causing this problem?
I'm using TestFlight to test an app with payment/subscription functionality. I created sandbox accounts in AppStore Connect accordingly to be able to test the subscriptions. I'm logged in with the sandbox account.
When I try to subscribe in the App the wrong account (this is my actual real AppleID) is used for the subscription although it is recognized that this is just a sandbox subscription.
I tried:
logging off/on into the sandbox account
creating a totally new sandbox account
trying to trigger the payment with no logged in sandbox account
The result is always: in the payment popup it is stated that the purchase account will be my original AppleID and not a sandbox account.
How can I switch the accounts? Is this a bug at Apple's side somehow?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
TestFlight
In-App Purchase
StoreKit
I've recently published an app, and while developing it, I could always get consistent entitlements from Transaction.currentEntitlements. But now I see some inconsistent behaviour for a subscribed device in the AppStore version. It looks like sometimes the entitlements do not emit value for the subscriptions.
It usually happens on the first couple tries when the device goes offline, or on the first couple tries when the device goes online. But it also happens randomly at other times as well.
Can there be a problem with Transaction.currentEntitlements when the connectivity was just changed?
Of course my implementation may also be broken. I will give you the details of my implementation below.
I have a SubscriptionManager that is observable (irrelevant parts of the entity is omitted):
final class SubscriptionManager: NSObject, ObservableObject {
private let productIds = ["yearly", "monthly"]
private(set) var purchasedProductIDs = Set<String>()
var hasUnlockedPro: Bool {
return !self.purchasedProductIDs.isEmpty
}
@MainActor
func updatePurchasedProducts() async {
var purchasedProductIDs = Set<String>()
for await result in Transaction.currentEntitlements {
guard case .verified(let transaction) = result else {
continue
}
if transaction.revocationDate == nil {
purchasedProductIDs.insert(transaction.productID)
} else {
purchasedProductIDs.remove(transaction.productID)
}
}
// only update if changed to avoid unnecessary published triggers
if purchasedProductIDs != self.purchasedProductIDs {
self.purchasedProductIDs = purchasedProductIDs
}
}
}
And I call the updatePurchasedProducts() when the app first launches in AppDelegate, before returning true on didFinishLaunchingWithOptions as:
Task(priority: .high) {
await DependencyContainer.shared.subscriptionManager.updatePurchasedProducts()
}
You may be wondering maybe the request is not finished yet and I fail to refresh my UI, but it is not the case. Because later on, every time I do something related to a subscribed content, I check the hasUnlockedPro computed property of the subscription manager, which still returns false, meaning the purchasedProductIDs is empty.
You may also be curious about the dependency container approach, but I ensured by testing multiple times that there is only one instance of the SubscriptionManager at all times in the app.
Which makes me think maybe there is something wrong with Transaction.currentEntitlements
I would appreciate any help regarding this problem, or would like to know if anyone else experienced similar problems.
We are experiencing an issue with StoreKit in the sandbox environment. When performing an SKProductsRequest, some or all of the product identifiers we provide are returning in invalidProductIdentifiers of SKProductsResponse, even though all of them are valid and approved.
This issue started occurring today and seems intermittent—occasionally, a previously failing product identifier works after one or two retries without any changes on our end.
We have verified that:
All product identifiers are correctly configured and approved in App Store Connect.
There are no recent changes to our product configuration.
The same identifiers have worked previously without issue.
Could you please assist us in identifying the cause of this behavior or let us know if there is an ongoing issue affecting StoreKit sandbox services?
Thank you for your support.
We’ve recently encountered an increased rate of purchase errors StoreKit.InvalidRequestError error 1 (https://developer.apple.com/documentation/storekit/invalidrequesterror) specifically on iOS 18.4.
We suspect this might be related to the new Advanced Commerce API introduced by Apple, although we haven’t made any changes related to this API in our app.
Have you experienced similar issues since the release of iOS 18.4 or when integrating the Advanced Commerce API? Any insights or suggestions would be greatly appreciated.
Thanks!
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
In-App Purchase
Advanced Commerce API
I'm developing storekitV2, my app is providing the way to refund some product, and I use method below.
func beginRefundRequest(in scene: UIWindowScene) async throws -> Transaction.RefundRequestStatus
however when i call the method, the modal view presented but the view shows error with message 'cannot connect'. when I select retry button, something done with indicator and get same result.
how can I solve this problem?
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.
Product.SubscriptionInfo.isEligibleForIntroOffer(for: "21340582")
In the production environment, I have already used the intro offer for this group, but this method still returns true
Product.SubscriptionInfo.isEligibleForIntroOffer(for: "21340582")
In the production environment, I have already used the intro offer for this group, but this method still returns true.
Hello,
I am encountering an issue where I receive an App Store Server Notification V2 upon the purchase of a consumable item.
I have configured the App Store Server Notification V2 endpoint to handle notifications related to subscription products.
However, I did not expect to receive notifications for consumable purchases.
The notification includes the following signedPayload decoded into the ResponseBodyV2DecodedPayload object with the following values:
notificationUUID: 3cd6410b-0c89-4247-aba5-20710e79895e
notificationType: null
subtype: null
The transaction information decoded from the ResponseBodyV2DecodedPayload object is as follows:
transactionId: 2000000633622618
webOrderLineItemId: null
productId: heart_2
To debug, I called the Get Notification History API of the App Store Server API, and the mentioned notification for the consumable product purchase is not included in the history. Only notifications related to subscription product purchases are retrieved.
According to the notification type documentation, there is no explanation for cases where both notificationType and subtype are null, nor is there any mention of receiving notifications for consumable purchases. Therefore, I am uncertain how to interpret and handle this notification.
Could you please provide an explanation or guidance on this issue?
Thank you.
References:
https://developer.apple.com/forums/thread/737592
https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
In-App Purchase
App Store Server Notifications
App Store Server API
Hello,
Our server isn't receiving Apple App Store Server Notification for in-app purchases.
It works for Sandbox Server, even I sent a test notification which I received on the Production Server URL, but the real in-app payment notification isn't coming to the server.
I checked this already: https://developer.apple.com/documentation/appstoreservernotifications/enabling-app-store-server-notifications and everything here has been reviewed.
Transport Layer Security (TLS) version and others things mentioned have been checked, test notification was received. But the main in-app purchase notification for live transaction isn't coming. What's the issue precisely?
Topic:
App & System Services
SubTopic:
StoreKit
I try to call Get Transaction Info from App Store Server API, and the transactionId is for a Non-consumable type product, but it is odd that there are so many different transactionId and they have a same originalTransactionId
{
"bundleId": "${bundleId}",
"environment": "Production",
"inAppOwnershipType": "PURCHASED",
"originalPurchaseDate": 1691220528000,
"originalTransactionId": "${originalTransactionId}",
"productId": "${productId}",
"purchaseDate": 1691220528000,
"quantity": 1,
"signedDate": 1692590989925,
"storefront": "USA",
"storefrontId": "143441",
"transactionId": "${originalTransactionId}",
"transactionReason": "PURCHASE",
"type": "Non-Consumable"
}
the defination of Non-Consumable is can only purchase once for same apple account. But why there would have originalTransactionId?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
In-App Purchase
App Store Receipts
App Store Server API