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?
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 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
Dear Apple Support Team,
We are currently implementing auto-renewable subscriptions in our iOS app and are testing the integration using the sandbox environment.
On the iOS app side, the in-app purchase flow completes successfully and displays a "Purchase Successful" message. However, we are not receiving any server notification callbacks on our configured App Store Server Notifications (Sandbox) webhook URL.
For your reference, the webhook URL we have set in App Store Connect (Sandbox) is:
https://9c0f-182-79-123-254.ngrok-free.app/ios/webhook
Despite successfully completing a subscription purchase in the sandbox, there is no evidence that the webhook is being triggered.
We would appreciate your guidance in resolving this issue or confirming if there are any additional configurations or steps required on our end.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit Test
StoreKit
In-App Purchase
App Store Receipts
Hello! I am trying to get my app set up to support external payments. The snag I am hitting at the moment is it seems that relevant pages are not accessible?
There is this old EU doc
https://developer.apple.com/support/apps-using-alternative-payment-providers-in-the-eu/
But the more updated US doc titled "Distributing apps in the U.S. that provide an external purchase link - Support" is not available where it should be https://developer.apple.com/support/storekit-external-entitlement-us/
In addition the link for requesting the entitlement seems to be broken
https://developer.apple.com/contact/request/storekit-external-entitlement-us/
Any idea how one can access these? Perhaps this is just a temporary error?
Description:
We are developing an iOS app that offers only one auto-renewable subscription.
Recently, a user who had not subscribed for over a year purchased the subscription again through our app. However, when they checked Settings > Subscriptions, the new subscription did not appear. Instead, only their past subscription history was visible, and there was no option to cancel the new subscription. We have verified this issue with a screenshot provided by the user.
Additionally, we checked our app’s logs and confirmed that StoreKit.product.purchase successfully returned .success, indicating that the purchase was processed correctly.
Code used for purchase:
let result = try await product?
.purchase(options: [
.appAccountToken(uuid)
])
switch result {
case .success(let verificationResult):
switch verificationResult {
case .verified(let transaction):
addPurchaseLog("verificationResult.verified.")
case .unverified(_, let verificationError):
addPurchaseLog("verificationResult.unverified.")
case .userCancelled, .pending, .none:
addPurchaseLog("verificationResult.userCancelled or .pending or .none")
@unknown default:
addPurchaseLog("verificationResult.unknown.")
}
Despite the successful purchase, the new subscription does not appear under the user's active subscriptions. Could this be a bug in StoreKit? If there are any known issues or workarounds, we would appreciate any guidance.
Environment:
Xcode: 16.2
iOS version: 16.0+
Hello,
We have been approved for the Advanced commerce API and we are trying to implement dynamically created subscriptions via the SubscriptionCreateRequest.
We followed the Sending Advanced Commerce API requests from your app (https://developer.apple.com/documentation/storekit/sending-advanced-commerce-api-requests-from-your-app) documentation but we are not able to make it work correctly.
We created a generic subscription in the Appstore connect, product ID: com.example.subscription
Then in the app we load the subscription:
try await Product.products(for: ["com.example.subscription"])
We do the JWS serialization on our backend and then we wrap the jwt and convert it to Data in the app as this:
let request = """
{
"signatureInfo": {
"token": "\(result.signedPayload)"
}
}
"""
let advancedCommerceRequestData = Data(request.utf8)
Lastly, we apply the purchase options on the generic product as this:
try await product.purchase(
options: [
Product.PurchaseOption.custom(
key: "advancedCommerceData",
value: advancedCommerceRequestData
)
]
)
It doesn't show any error, but on the payment sheet it shows the data from the generic subscription and not the data that was in the SubscriptionCreateRequest.
Here is an example of the generated jwt:
eyJraWQiOiI4V0tNQjhLWTI0IiwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiI0MDZkYmEyOS04ZjIyLTQ3ZDUtYWI1Mi1kY2M2NTQ5OTE1Y2MiLCJiaWQiOiJjby5oZXJvaGVyby5IZXJvaGVybyIsImlhdCI6MTc0NjQzNTcxNCwiYXVkIjoiYWR2YW5jZWQtY29tbWVyY2UtYXBpIiwibm9uY2UiOiJhMzY2MGIwMS1kMDcyLTRlZDYtYmYyMS01MWU1Y2U5MDRmYTUiLCJyZXF1ZXN0IjoiZXlKdmNHVnlZWFJwYjI0aU9pSkRVa1ZCVkVWZlUxVkNVME5TU1ZCVVNVOU9JaXdpY21WeGRXVnpkRWx1Wm04aU9uc2ljbVZ4ZFdWemRGSmxabVZ5Wlc1alpVbGtJam9pTVdSaVlqZG1ZbVl0WWpFNE55MDBZMlJoTFRrNE16WXRNalUzTTJZeU1UaGpOekZpSW4wc0luTjBiM0psWm5KdmJuUWlPaUpEV2tVaUxDSjJaWEp6YVc5dUlqb2lNU0lzSW1OMWNuSmxibU41SWpvaVExcExJaXdpZEdGNFEyOWtaU0k2SWxNd01qRXRNRGd0TVNJc0ltUmxjMk55YVhCMGIzSnpJanA3SW1ScGMzQnNZWGxPWVcxbElqb2lVM1ZpYzJOeWFYQjBhVzl1SUZCbGRISWc0b0tzSURVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWxOMVluTmpjbWx3ZEdsdmJpQlFaWFJ5SU9LQ3JDQTFJbjBzSW5CbGNtbHZaQ0k2SWxBeFRTSXNJbWwwWlcxeklqcGJleUprYVhOd2JHRjVUbUZ0WlNJNklsTjFZbk5qY21sd2RHbHZiaUJRWlhSeUlPS0NyQ0ExSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpUZFdKelkzSnBjSFJwYjI0Z1VHVjBjaURpZ3F3Z05TSXNJbkJ5YVdObElqb3hOVEF3TUN3aWMydDFJam9pY1dkeGIzUnNlSEY1WVdGaFlsOTRiV3RvWlhWdGFHWjJhbXhtWDBWVlVqQTFJbjFkZlE9PSJ9.kJ0f_q2A11Mn9OBmvX6SRmtW5P--volFTVcq_Gohs3N51ECfZqS3WHOxOZc7aojq_qiUHGFp_evmHP51f3LzSw
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
In-App Purchase
Advanced Commerce API
Hi Team,
I’ve successfully implemented the subscription flow for my app. However, I’m currently facing challenges related to testing the cancellation behavior for auto-renewable subscriptions.
Specifically:
I’m unable to locate the correct payload structure for the following test endpoint:
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/test
I’m also unclear on how to simulate or complete the full lifecycle of a subscription (including cancellation) using Apple’s sandbox environment or APIs.
Could you please guide me on how to:
Retrieve or construct the proper payload for the test notification API?
Simulate a cancellation flow end-to-end in the sandbox for auto-renewable subscriptions?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit Test
StoreKit
App Store Server Notifications
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
}
);
We’ve recently observed an escalating number of complaints from AlipayHK users regarding duplicate charges when completing transactions via Apple Pay. While no similar issues have been reported by users of other credit card providers integrated with Apple Pay, the problem appears isolated to AlipayHK transactions.
Key Details:
Multiple users confirm being charged twice for single transactions.
Complaints are increasing in frequency, indicating a potential systemic issue.
No overlapping reports from non-AlipayHK payment methods at this time.
To safeguard customer trust and ensure seamless payment experiences, we kindly request Apple’s support in:
Investigating whether the root cause stems from Apple Pay’s transaction handling.
Collaborating with AlipayHK (if necessary) to resolve the issue promptly.
Providing guidance on interim measures to prevent further duplicate charges.
Could Apple confirm if this is a known issue and share a timeline for resolution? We’re eager to assist in any way possible to mitigate impact on users.
Thank you for your urgent attention to this matter.
Topic:
App & System Services
SubTopic:
StoreKit
Hey folks!
In one of our apps we're using the FamilyControls framework to manage the screentime for the child. The app requires the Guardian to activate a subscription on his/her device to enable the functionality for the Child on the child device.
We're currently using StoreKit 2 to get notified when there is an active subscription. The issue we're seeing (and our users) is that an activated subscription isn't instantly propagated to the Family members, in this case the device belonging to the child.
Is this a known bug, and are the possible any workarounds? Currently we have to ask our users to "Restore Purchase" or wait minutes/hours for the app to active, which isn't a viable solution. We want it to "just work" :)
Sincerely,
César Pinto Castillo
Ambi Studio