I’m testing an auto-renewable subscription on TestFlight. Now the user can't re-purchase the same product – Apple just restores the old (expired) one, and no payment sheet appears.
How can I let the same TestFlight user re-subscribe to an expired product?
Do I have to create a new productId for every test cycle?
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
We have had a small number of users of our mac app complaining that the app suddenly can't detect their subscription or previous purchase history. These users are not new, and have been using the app successfully for some time.
In the app we do this using the following (very standard) code at app startup:
let result: VerificationResult<AppTransaction> = try await AppTransaction.shared
For those users experiencing the failure, the result is coming back as unverified.
So far we've been unable to find the cause or a solution, but it seems to have become worse with the release of macOS 15.4.
We've tried resetting, rebooting and reinstalling the app.
It's worth adding the (probably obvious) that it's impossible to test or fault-find with this, because we can't replicate the issue in a development environment.
Any suggestions gratefully received.
Background
We sell a suite of iPadOS/macOS apps that share a single auto-renewable subscription using this architecture.
Per “Offering a Subscription Across Multiple Apps” we require users to sign in before purchasing so we can propagate the entitlement and avoid duplicate subscriptions across apps.
To enforce that sign-in step we plan to turn off Streamlined Purchasing in App Store Connect.
Question
We also want to distribute subscription offer codes (for promotion, retention, appeasing dissatisfied customers, etc.).
After Streamlined Purchasing is turned off, will customers still be able to redeem offer codes outside the app (App Store “Redeem Code” UI or redemption URL)?
If outside-app redemption remains possible, it bypasses our sign-in gate and could let the same customer buy the suite twice (once via each app).
Is there an approved method to limit offer-code redemption to the in-app flow only, or otherwise prevent such duplicate subscriptions?
If no such limitation exists, what best-practice workaround does Apple recommend for multi-app suites that must turn off Streamlined Purchasing yet still wish to use offer codes without duplication risk?
Environment
StoreKit 2; server-side receipt validation & cross-app entitlement propagation.
Apps support the in-app presentCodeRedemptionSheet flow.
We expect to use both one-time-use and custom offer codes.
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
Hi apple team,
I'm using Apple Root Certificates from https://www.apple.com/certificateauthority/ for communicating with App Store Server Library for receipt validation API.
Apple Computer, Inc Root certificate from the website is Not Valid After: Monday, 10 February 2025 at 01:18:14 Central European Standard Time.
When we can expect update of this certificate.
Thank you
I has sandbox account with Japanese local. When i build app directly to check, price is displayed in Japanese Currency. But when I install app from the Test Flight, price is always displayed in USD Currency.
the issue is appear in iOS 18.5
How can i fix this issue ?
It seems that beta 3 broke StoreKit Testing when running against an iOS 26 simulator device. Specifically, when validating product IDs, the debug console displays messages like the following:
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
[492a4cfa_SK1] Could not parse product: missingValue(for: [StoreKit.ProductResponse.Key.price], expected: StoreKit.BackingValue)
In addition, the SKProductsResponse (I am using the original StoreKit API), lists all requested product IDs in invalidProductIdentifiers. The products array is empty.
StoreKit Testing behaves as expected when Xcode 26 beta 3 is run against an iOS 18.4 simulator device.
I’m testing auto-renewable subscription purchases in the sandbox environment.
When I buy a subscription package using a sandbox test user, I don’t receive any App Store Server Notifications from the sandbox.
However, when I use the “Request Test Notification” option in App Store Connect, the notification is received successfully.
My sandbox server notification URL is configured correctly and publicly accessible.
I also call finishTransaction() after purchase, and the receipt is verified successfully with the sandbox verification endpoint.
To further debug, I used the getNotificationHistory API to fetch notifications for yesterday and today (Nov 3–4, 2025).
Apple’s API response does not include any notifications or transaction IDs for my today’s purchases (Nov 4, 2025) — even though I can confirm from logs that those transactions were completed and verified successfully.
It looks like sandbox purchase notifications are not being sent or logged, even though test notifications work fine.
Could someone from Apple please confirm if there’s currently an issue with sandbox server notifications for auto-renewable subscriptions?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store Connect
In-App Purchase
In a recent update to developer news, Apple posted more details about compliance tools for the new laws coming into effect in Texas on Jan 1.
https://developer.apple.com/news/?id=2ezb6jhj
It is not explicitly stated in the news update or documentation but when the new RESCIND_CONSENT notification is sent to the developer, what happens on the child account devices? Does the app just disappear. Does the developer need to take any action in the App itself?
https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
Thanks,
Eric
Topic:
App & System Services
SubTopic:
StoreKit
We are experiencing an Unknown Error (error code 0) when testing purchases in the Apple Sandbox environment.
The error does not occur every time, but it fails with an Unknown Error in approximately 8 out of 20 attempts.
Due to this issue, our app is failing to pass Apple’s review.
Issue Details
This issue only occurs in the Apple Sandbox environment.
After attempting a purchase, the Apple payment API returns SKPaymentTransaction Unknown Error.
Returned error code: SKErrorUnknown (error.code 0).
This problem is preventing us from conducting proper payment tests and getting approval from Apple.
Would it be possible to receive support or any solutions for this issue?
The documentation mentions the following:
Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store.
This way, you can use one server environment to handle both Sandbox and Production environments. It is necessary to pass App Review.
However, I'm not manually hitting these URLs - I'm using Apple's libraries.
Specifically, the environment is used in SignedDataVerifier and AppStoreServerAPIClient.
(I can't link to these because, for some reason, the domain apple.github.io is not allowed. The documentation for these is only found there. You can find it quickly by searching these terms and the domain.)
Here is how SignedDataVerifier is being used:
const verifier = new SignedDataVerifier(
appleRootCertificates,
APPLE_ENABLE_ONLINE_CHECKS,
APPLE_ENVIRONMENT,
APPLE_BUNDLE_ID,
APPLE_APP_ID
)
const verifiedNotification: ResponseBodyV2DecodedPayload = await verifier.verifyAndDecodeNotification(signedPayload)
if (!verifiedNotification)
{
// Failure
return
}
Here is how AppStoreServerAPIClient is being used:
const appStoreServerAPIClient = new AppStoreServerAPIClient(
SIGNING_KEY,
APPLE_IAP_KEY_ID,
APPLE_IAP_ISSUER_ID,
APPLE_BUNDLE_ID,
APPLE_ENVIRONMENT
)
const statusResponse: StatusResponse = await appStoreServerAPIClient.getAllSubscriptionStatuses(originalTransactionId, [Status.ACTIVE])
In the source code for SignedDataVerifier.verifyAndDecodeNotification, I can see that it throws a VerificationException(VerificationStatus.INVALID_ENVIRONMENT) error .
So for SignedDataVerifier is it as simple as wrapping my code in a try/catch and checking that the error's status code is 21007? I'm unsure about this because if you scroll to the bottom of the linked source code file, you can see the enumeration VerificationStatus, but it's unclear if this member has a value of 21007.
The source code for AppStoreServerAPIClient only says that it throws an APIException if a response could not be processed, so I'm not too sure about how to handle this one.
Our application is currently under review, and we are still facing issues because we receive a 401 Unauthorized response from the App Store Connect API when using the production environment.
Our app integrates with Chargebee for subscription management, and in production, Chargebee is unable to authenticate with the App Store Server API. This results in a 401 Unauthorized error, preventing the user’s subscription from being synced correctly into our system. Interestingly, the same configuration works in the sandbox environment, but fails in production.
We’ve tried authenticating using JWTs generated from multiple keys (including App Store Connect API / Team Keys with both Admin and App Manager access, and also In-App Purchase keys), all with the same result — sandbox access works, production does not.
Here is our example code for testing with JWT token:
const jwt = require('jsonwebtoken');
const fs = require('fs');
const https = require('https');
const config = {
keyId: '<key_id>',
issuerId: 'issuer_id',
bundleId: 'bundle_id',
privateKey: fs.readFileSync('path_to_key')
};
const { keyId, issuerId, bundleId, privateKey } = config;
const now = Math.floor(Date.now() / 1000);
const jwtToken = jwt.sign(
{
iss: issuerId,
iat: now,
exp: now + 60 * 10, // 10 minutes is fine for test
aud: 'appstoreconnect-v1',
bid: bundleId
},
privateKey,
{
algorithm: 'ES256',
header: {
alg: 'ES256',
kid: keyId,
typ: 'JWT'
}
}
);
console.log('Generated JWT:\n', jwtToken);
// prod
const originalTransactionId = '<prod_transaction_id>';
const hostname = 'api.storekit.itunes.apple.com';
// sandbox
// const originalTransactionId = '<sandbox_transaction_id>';
// const hostname = 'api.storekit-sandbox.itunes.apple.com'
const options = {
hostname,
port: 443,
path: `/inApps/v1/history/${originalTransactionId}`,
method: 'GET',
headers: {
Authorization: `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
},
};
const callAppStoreConnectApi = async () => {
const req = https.request(options, (res) => {
console.log(`\nStatus Code: ${res.statusCode}`);
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Response Body:\n', data || '[Empty]');
});
});
req.on('error', (e) => {
console.error('Request Error:', e);
});
req.end();
};
callAppStoreConnectApi();
With this code, we were able to authenticate successfully in the sandbox environment, but not in production.
I read in this discussion: https://developer.apple.com/forums/thread/711801 that the issue was resolved once the app was published to the App Store, but I haven’t found any official documentation confirming this.
Does anyone know what the issue could be?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit
App Store Connect
App Store Connect API
Problem Description:
1、I have two sandbox accounts from different countries. Account A is from Mainland China (CHN), and Account B is from the United States (USA). When I switch the sandbox account from Account A (CHN) to Account B (USA) in the system settings and restart the app, the value of SKPaymentQueue.defaultQueue.storefront.countryCode always returns "CHN" instead of the expected "USA".
2、This issue only occurs on iOS 18.2 and above.
3、On the same device running iOS 17.5.1, the behavior is correct. However, after upgrading the system to iOS 18.3.2, the error occurs.
4、In the sandbox environment, although SKStorefront.countryCode returns the wrong value, the currency type for Apple's in-app purchase is correct when initiating the payment.
5、The issue only exists in the sandbox environment and does not occur in the App Store-downloaded package.
Demo Code:
- (IBAction)clickButton:(id)sender
{
NSString *appStoreCountryCode3 = SKPaymentQueue.defaultQueue.storefront.countryCode;
NSLog(@"%@",appStoreCountryCode3);
}
Demo Testing Steps and Results:
1、Sandbox Account A (China) will print "CHN".
2、Go to Settings - Developer - (at the bottom) SANDBOX APPLE ACCOUNT, and switch to another sandbox account B (USA).
3、Restart the Demo App.
4、Print results:
iOS 17.5.1: "USA" ✅ → Upgrade the system of the same device to iOS 18.3.2 → "CHN" ❌
iOS 18.2.1: "CHN" ❌
iOS 18.3.1: "CHN" ❌
iOS 18.3.2: "CHN" ❌
Possible Clues:
Starting with iOS 18.2, Apple changed the entry point for setting up sandbox accounts, which introduced this bug. It seems that when users switch sandbox accounts on iOS 18.2, Apple engineers forgot to notify the SKStorefront class to update the countryCode value.
Before iOS 18.2: Settings - App Store - Sandbox Account
iOS 18.2 and later: Settings - Developer - (at the bottom) Sandbox Account
Although it doesn't affect the App Store package, it does impact our development and testing process. We hope this issue can be fixed in future versions. Thank you!
We are in the process of implementing promotional offers for auto-renewable subscriptions in our app using StoreKit 2.
For testing, we use a sandbox user alongside a new user on our platform. I can successfully purchase an Introductory Offer through the app. Once the user is eligible for a Promotional Offer (based on a previous purchase), we retrieve the Promotional Offer identifier and signature from our backend and display the offer.
After initiating the purchase and having the user enter their Sandbox password, the transaction is added to the Payment Queue. However, it fails with the following error:
Purchase failed error: invalidOfferSignature
Additionally, the error returned is:
"Purchase did not return a transaction: Error Domain=ASDServerErrorDomain Code=3903 "Unable to Purchase" UserInfo={NSLocalizedFailureReason=Unable to Purchase, client-environment-type=Sandbox, AMSServerErrorCode=3903, storefront-country-code=IND}"
We are using StoreKit 2 APIs for this process.
Has anyone encountered this issue when working with StoreKit 2, or found a solution to resolve it?
I did an in app purchase in my development app and now I cannot get rid of it. It is a "monthly" subscription that seems to renew every 1 day.
I can see the subscription when I go to settings then tap on Subscriptions.
Then I tap the item and choose "Cancel Subscription", revealing a new modal sheet saying "Confirm Cancellation".
When I "Confirm", I get the popup:
"Your request is temporarily unable to be processed, please try again later".
However, this is anything BUT temporary, has gone on for a couple weeks now.
As such, I am unable to test subscriptions in my development app.
I've tried logging out, restarting, different devices, etc.
The phone is logged in under my primary user account, and I may not have been logged into sandbox email when I did the purchase.
Can someone forcibly remove it for me?
when we test in app purchase feature in TestFlight, we realize that TestFlight using our real iCloud account in AppStore, but because the app installed from TestFlight, they know it is still "Testing Phase" so they purchase set to FREE.
Now im asking about how to test in app purchase for CANCEL SUBSCRIPTION?
I can't found in my real iCloud account, either for my sandbox account inside AppStore settings.
Please any response will helpful. Thankyou.
We got access into Advanced Commerce API and trying out the server APIs.
I was trying out the Migrate a Subscription to Advanced Commerce API but the API was just simply returning not found to me with a generic error code 4040000 (this is undocumented in the API doc).
Here is the request body
{
"descriptors": {
"description": "User migrated from old plan to Essential",
"displayName": "Essential Plan"
},
"items": [
{
"sku": "com.company.essential",
"description": "A new subscription description after migration",
"displayName": "Essential"
}
],
"requestInfo": {
"requestReferenceId": "11aa3174-9aeb-41a6-996d-fc655a793c06"
},
"storefront": "HKG",
"targetProductId": "com.company.subscription.base",
"taxCode": "C003-00-1"
}
Headers
Authorization: Bearer <REQUEST_TOKEN>
And the response
{
"errorCode": 4040000,
"errorMessage": "Not found."
}
Am I doing something wrong or there will be additional configuration needed?
A purchase can result in success with verificationResult .unverified. Is there a list of reasons for which the transaction might be unverified and how should i handle it in my app? From my understanding, a successful unverified transaction means the user has already paid for the purchase. So, do i just ignore the unverified transaction or do i provide content to the user anyways?
We have in-app purchases live and working fine for standard subscriptions.
We also have promotional offers active for existing users (to give existing users a discount as a thank you).
Yet, regardless of the user type (existing vs new... we have tested with all types), we get the "Your account is not eligible for this offer" error message when clicking the discounted offer.
What is the logic for determining eligibility?
I'm trying to debug as it's not clear to me why this message would show up.
We are using React Native IAP.
In general, how does the eligibility check work? What conditions are being evaluated and compared? And what could break those conditions?
I appreciate your help!
DDD
We have some users who have upgraded to iOS 26 beta3. Currently, we observe that when these users make in-app purchases, our code calls [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; method, and we clearly receive the successful removal callback in the delegate method - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray<SKPaymentTransaction *> *)transactions. However, when users click on products with the same productId again, the method - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions still returns information about previously removed transactions, preventing users from making further in-app purchases.