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

HELP WITH SUBSCRIPTIONS
Hey everyone, I really need help. My app versions keep getting approved for distribution and my subscriptions and business agreements are all approved. Yet, when the paywall in my app appears, and someone clicks the subscribe button to pay, the IAP isn't appearing. It just loads forever. When I tested in Xcode it just kept saying products not found. Id's are the same, bundle id is the same, ive done everything. Can someone help pls.
3
0
226
Jan ’26
Unable to Authenticate with App Store Server API in Production (401 Error)
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://aninterestingwebsite.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?
3
2
402
Oct ’25
How to change promotional image of in-app purchase
My apps stuck on review stage. Reason of it is Guideline 2.3.2 - Performance - Accurate Metadata We noticed that your promotional image to be displayed on the App Store does not sufficiently represent the associated promoted in-app purchase. Specifically, we found the following issue with your promotional image: – Your promotional image is the same as your app’s icon. The problem is i can't change this image. My subscription is still in 'in review' stage and I don't have option like delete image or change image. I replied to the review explaining that I cannot change it as long as the subscription is under review, but I haven’t received any meaningful reply, except that I need to change promotion image to be eligible for further review(which i cant do because i haven't option to change this image). Has anyone had such a problem before?
4
2
2k
Oct ’25
Inconsistent behavior with transactionId and appAccountToken in iOS Sandbox purchases (StoreKit1 & StoreKit2)
Hi, I'm reaching out to report a recurring issue with in-app purchases on iOS that seems to be related to Apple’s transaction handling — not to third-party libraries. In my Flutter application, I use both StoreKit2 and StoreKit1 (for comparison) via different packages, including the official in_app_purchase package. However, in both cases, I’m experiencing unexpected reuse of transactionId and appTransactionId values, even when initiating fresh purchases with unique appAccountToken values. Problem Summary: Purchase Stream Returns Old Purchases When calling buyNonConsumable() with a new product, the purchase stream still returns data for a previously purchased product, despite clearing all Sandbox transactions and using a new applicationUserName for each attempt. Transaction IDs Reused Across Distinct Purchases Even when generating a new UUID for appAccountToken on each purchase, the returned appTransactionId and transactionId are reused — this breaks our server-side logic, which expects these fields to uniquely identify purchases and users. Example Logs: // First purchase { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928154716" } // Second purchase (different user context) { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928429780" } Even when using a different productId, the appTransactionId stays the same. When using StoreKit1, the productId updates properly, but the transactionId still matches the previous one. This behavior also affects App Store Server Notifications (V2): we have observed notifications tied to appAccountTokens from completely different user accounts (based on internal logs), sometimes delayed by days or weeks. I’ve prepared a reproducible example using the official Flutter in_app_purchase sample with minimal changes — you can find it here: Github gist The code is almost identical to the package example. I only added UUID generation for applicationUserName in _getToken(). In the actual app (not in this example), I retrieve the token from an API. Additional Observations from the Community: We’ve also found similar issues reported in other frameworks and languages. For instance, a developer using react-native-iap observed that App Store Server Notifications in TestFlight were tied to previously deleted users, even after signing up with a new user account and generating a new appAccountToken. Details here: User A deleted → User B signs up → receives upgrade event with User A’s token Notification uses appAccountToken from old account, not the new one This strengthens the suspicion that the issue may be related to how Apple associates transactions with Apple IDs in test environments. Questions: Is it expected for transactionId or appTransactionId to persist across purchases within the same Apple ID, even for different user contexts (e.g., separate logins in the same app)? Is there any official recommendation for avoiding this kind of data reuse in Sandbox or TestFlight environments? Should I expect appAccountToken in server notifications to always match the latest value provided during the purchase? Thank you in advance for your assistance. I would appreciate any clarification or advice regarding this issue, as it impacts production logic that relies on these identifiers being unique and consistent.
1
1
226
Jun ’25
Double-counted consumable in StoreKit unfinished and updates workflow
In Getting started with In-App Purchase using StoreKit views and the corresponding sample project, Store simultaneously enumerates Transaction.unfinished and Transaction.updates. Since, "if your app has unfinished transactions, the updates listener receives them once, immediately after the app launches," it appears that Transaction.unfinished would also receive the same unfinished transactions causing handle(updatedTransaction:) to be called for twice for each transaction, causing consumables to be double-counted. Is this a bug in the sample? Is there more information on concurrent execution of unfinished and updates?
1
1
226
Mar ’26
Custom Capacitor 6 plugin with SPM: "plugin is not implemented on ios" despite being compiled
Hi everyone, I'm building an iOS app using Capacitor 6 with Swift Package Manager (SPM). I have a custom native plugin (AppleIAPPlugin) for StoreKit 2 In-App Purchases that lives in the App target (not as an SPM package). Despite compiling successfully, the JavaScript bridge throws: "AppleIAP" plugin is not implemented on ios Setup AppleIAPPlugin.swift: swift import Foundation import Capacitor import StoreKit @objc(AppleIAPPlugin) public class AppleIAPPlugin: CAPPlugin, CAPBridgedPlugin { public let identifier = "AppleIAPPlugin" public let jsName = "AppleIAP" public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "getProducts", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "purchase", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "restorePurchases", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getCurrentEntitlements", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "openManageSubscriptions", returnType: CAPPluginReturnPromise), ] @objc func getProducts(_ call: CAPPluginCall) { /* StoreKit 2 implementation */ } @objc func purchase(_ call: CAPPluginCall) { /* ... */ } // etc. } AppleIAPPlugin.m: objc #import <Foundation/Foundation.h> #import <Capacitor/Capacitor.h> CAP_PLUGIN(AppleIAPPlugin, "AppleIAP", CAP_PLUGIN_METHOD(getProducts, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(purchase, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(restorePurchases, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getCurrentEntitlements, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(openManageSubscriptions, CAPPluginReturnPromise); ) MyBridgeViewController.swift (custom bridge to register the plugin): swift import UIKit import Capacitor class MyBridgeViewController: CAPBridgeViewController { override open func capacitorDidLoad() { bridge?.registerPluginType(AppleIAPPlugin.self) } } Main.storyboard points to MyBridgeViewController (module: App) instead of CAPBridgeViewController. TypeScript side: typescript import { registerPlugin } from "@capacitor/core"; export const AppleIAP = registerPlugin("AppleIAP"); What I've verified Both .swift and .m files are in the Xcode project's Compile Sources build phase nm on the compiled binary confirms OBJC_CLASS_$_AppleIAPPlugin symbol exists The build succeeds with zero errors Other SPM-based Capacitor plugins (Share, Media, NativeAudio) work fine — they have pluginMethods and jsName symbols in the binary; my custom plugin does NOT A bridging header (App-Bridging-Header.h) is configured with #import <Capacitor/Capacitor.h> What I've tried (all failed) .m file with CAP_PLUGIN macro only (no CAPBridgedPlugin in Swift) Added CAPBridgedPlugin protocol conformance to Swift class Created MyBridgeViewController subclass with registerPluginType() in capacitorDidLoad() Removed/added override public func load() method Added #import <Foundation/Foundation.h> to .m file Various bridging header configurations Multiple clean builds and derived data wipes Environment Xcode 16 Capacitor 6 (via SPM, binary xcframework) iOS 17+ deployment target Physical device testing (not simulator) Question How should a custom plugin in the App target be registered with Capacitor 6 when using SPM? The SPM-based plugins from node_modules get auto-discovered, but my custom plugin in the App target does not. Is there a step I'm missing to make registerPluginType() work, or should I structure my custom plugin as a local SPM package instead? Any guidance would be greatly appreciated.
1
0
56
1w
AppStoreServerNotificationV2 EXPIRED event after removing from sale
Our app is supposed to be removed from sale on May 31st. Subscriptions our app is offering will also be removed on May 1st one month before our app removal. I would like to know if AppStoreServerNotificationV2 EXPIRED event will be sent to a specified endpoint after the removal of these subscriptions. I think each subscription will be canceled automatically from May 1st to May 31st and it will send EXPIRED event to our server, but is it true? Thank you in advance.
1
0
155
Feb ’26
In-App Purchases not loading in production / TestFlight — Previously missing Paid Apps Agreement — App rejected under Guideline 3.1.2
Hello, My app was rejected on iPad (iPad Air 11-inch M3, iPadOS 26.2.1) with two related issues: Guideline 2.1 – Performance – App Completeness “The app exhibited one or more bugs that would negatively impact users. Bug description: the premium subscription cannot be loaded properly.” Guideline 3.1.2 – Business – Payments – Subscriptions “The submission did not include all the required information for apps offering auto-renewable subscriptions.” I am using StoreKit 2 with SubscriptionStoreView to present the auto-renewable subscription. During development: Subscriptions load correctly in the simulator (sandbox). On real devices, I test without a local StoreKit configuration file to fetch products from App Store Connect. The subscription UI (title, duration, price) displays correctly when products are returned. At the time of review, the Paid Apps Agreement was not active. I suspect this may have caused the subscription products to fail loading on the review device. Since then: Paid Apps Agreement is now Active. SubscriptionStoreView should automatically show required metadata. Because the subscription failed to load on iPad during review, the required information (title, price, duration) was not visible, which likely triggered the 3.1.2 rejection. Additionally, in TestFlight I sometimes see inconsistent behavior where the app appears but cannot be installed (“App Not Available”). Also, my app was rejected, but the subscription is still waiting for review. I would really appreciate guidance on the following: Am I potentially missing any required configuration that could prevent products from loading in production? Is there any propagation delay after activating the Paid Apps Agreement that could affect product availability? If I am overlooking something in configuration or testing, please let me know what I should specifically verify before resubmitting. Thank you very much for your help.
1
1
235
3w
Unresolved pending purchases for consumables
In our app we are running into a few issues with pending purchases staying on receipt indefinitely. These are consumable purchases where we received the purchase succeeded from apple but then something went wrong on our servers to validate and confirm the purchase. At this point the purchase stays on the apple receipt indefinitely or until we confirm it. The problem is there are lots of scenarios where we can't confirm purchases anymore (like a game world expired/banned player/etc). So there's a few things I'd like to know to see how this could be handle correctly. 1- Was the user already charged, and if yes would they ever be refunded if the purchase is not confirmed (some sort of expiry)? 2- Is there a way to cancel this sort of pending transaction directly from the app or backend? 3- If one of these users asked for a refund from apple would this clear the purchase from the receipt? Any information would be greatI couldn't find a lot of info on this topic.
0
1
53
Jun ’25
Unable to sign in to Sandbox Apple Account on Simulator
I am unable to sign in to a Sandbox Apple Account, where this issue occurs only via Simulator. Under Settings > Developer, I tap "Sign In" under Sandbox Apple Account. I enter my account credentials, and after bringing me back to the Developer page, the Sign In button briefly appears as disabled, before being re-enabled, without signing in to the account. (The account credentials are also recognized as correct, as I will receive an alert popup if incorrect.) See screenshots below: After signing in, Sign In button appears disabled... ... then is re-enabled without actually signing in to the account. I have now tried setting up multiple sandbox accounts via App Store Connect with various permutations (no confirmation of Apple Account email, confirming Apple Account email, logging in to iCloud and accepting terms of service), running different device simulators, running simulators on different Mac computers... none of which yield a different result. By contrast, I can sign in to the Sandbox Apple Account without issue on a physical device. The problem occurs only via Simulator.
2
1
277
2w
Apple Media Services T&C pop-up
I had published an App, and my app has App Clip supported. The issue I faced is that I had received complaints where the user keep seeing the pop up "Apple Media Services Terms and Conditions Have Changed" when user clicked on the "Open" Button in the App Clip. What we had tried so far: Let user switch the Apple Id's region to our supported region. Let user try to log out and log in to Apple Id within the supported region.
1
0
229
Feb ’26
When is the unverified branch of AppTransaction.shared entered?
Hi all, I am adding the following StoreKit 2 code to my app, and I don't see anything in Apple's documentation that explains the unverified case. When is that case exercised? Is it when someone has tampered with the app receipt? Or is it for more mundane things like poor network connectivity? // Apple's docstring on `shared` states: // If your app fails to get an AppTransaction by accessing the shared property, see refresh(). // Source: https://aninterestingwebsite.com/documentation/storekit/apptransaction/shared var appTransaction: VerificationResult<AppTransaction>? do { appTransaction = try await AppTransaction.shared } catch { appTransaction = try? await AppTransaction.refresh() } guard let appTransaction = appTransaction else { AppLogger.error("Couldn't get the app store transaction") return false } switch appTransaction { case .unverified(appTransaction, verificationError): // For what reasons should I expect this branch to be entered in production? return await inspectAppTransaction(appTransaction, verifiedByApple: false) case .verified(let appTransaction): return await inspectAppTransaction(appTransaction, verifiedByApple: true) } Thank you, Lou
12
1
852
Apr ’25
Can you buy an IAP via StoreKit 1 on iOS 26.2?
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?
1
1
199
Dec ’25
storefront.countryCode is fixed to USA in iOS 26 beta
Whether using Storefront.current?.countryCode or SKPaymentQueue.default().storefront?.countryCode, both are returning "USA" only. (It used to return the correct country code before the update.) In the sandbox environment, the country code is returned correctly, but in the TestFlight environment, it always returns "USA". There's no mention of this behavior in the beta release notes, so I'm posting it here for visibility.
1
1
178
Jul ’25
Mismatch between App Store Server API `expiresDate` (July 23) and iOS UI “Expires on” date (July 22) for 1-month subscription
Hi everyone, I’m seeing a consistent one-day discrepancy between the expiresDate returned by the App Store Server API and the “Expires on” date shown in the iOS Settings / App Store subscription list. I’d like to confirm whether this behavior is expected or if I’m misunderstanding the way Apple rounds dates. Reproduction steps Step Action Result 1 Purchase a 1-month auto-renewable subscription on 23 June 2025 14:00 JST (UTC+9) Transaction succeeds 2 Immediately fetch the transaction with GET /inApps/v1/subscriptions/{transactionId} Response contains "expiresDate": "2025-07-23T05:00:00Z" (= 23 July 2025 14:00 JST) 3 On the same device open Settings › Apple ID › Subscriptions (or App Store › Account › Subscriptions) UI shows Expires on: 22 July 2025 The same happens for every monthly renewal and on multiple devices. Region is Japan, device time zone Asia/Tokyo. What I understand so far (and my hypothesis) Apple’s docs say a monthly subscription renews “on the same calendar date” of the next month, so renewal in this example is 23 July. If the renewal is scheduled for 23 July at 14:00 JST, the subscription is fully usable until the end of 22 July in calendar terms, because the new billing period starts the moment the 23rd begins in Apple’s canonical time zone. Therefore, it might be intentional for the UI to display 22 July—i.e., “you can keep using it through the 22nd; on the 23rd it renews.” This hypothesis makes sense internally, yet it still looks confusing to end users who read “Expires on 22 July” and assume access ends at 00:00 on the 22nd, a whole day earlier than in reality. Questions Is showing the day before the renewal date the official/expected behavior? If so, could Apple clarify that the “Expires on” label represents the last full calendar day rather than the exact expiry timestamp? Which value should we surface in-app when telling users “Your subscription is valid until …”? The server’s expiresDate (precise to the second, converted to user time zone), or A UI-style date that’s one day earlier, matching Settings / App Store? Does Apple have a public document describing this rounding/visual convention? Have other developers encountered user confusion about the apparent 1-day “shortening” and, if so, how did you word your in-app messaging? Any insight from Apple engineers or fellow developers would be greatly appreciated. Thank you!
0
1
286
Jun ’25
Transaction.currentEntitlements sometimes does not emit a result until device is reboot
I have the typical StoreKit 2 manager class, where I check currentEntitlements for subscription. I have filed a feedback (FB22349195), I hope someone can take a look at it. func updateCustomerProductStatus() async { var activeSubscription: String? = nil // BUG: In some cases the currentEntitlements does not emit a transaction until the device is reboot for await result in Transaction.currentEntitlements { print("Found transaction: \(result)") // This print does not appear until a restart! do { let transaction = try checkVerified(result) // Skip revoked transactions if transaction.revocationDate != nil { print("Skipping revoked transaction for \(transaction.productID)") continue } // Skip expired subscriptions if let expirationDate = transaction.expirationDate, expirationDate < Date() { print("Skipping expired subscription for \(transaction.productID)") continue } // Check product type switch transaction.productType { case .autoRenewable: activeSubscription = transaction.productID default: break } } catch { print("Unable to verify transaction: \(error)") } } // Update state once after processing all entitlements self.activeSubscription = activeSubscription print("updateCustomerProductStatus() activeSubscription: \(activeSubscription ?? "nil")") } There is some unexpected behavior where the currentEntitlements does not emit a result until the iPhone device is reboot. This bug appeared in iOS 26.4 (and in the betas).
0
1
77
1w
Unknown Error when Trying to Add New Offer Codes
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!
3
1
638
Dec ’25
Don't receive ONE_TIME_CHARGE Notification
the email say this notification type will be available to you in production beginning May 27, 2025, and you can test it in sandbox now. but i didn't receice this notification yet
Replies
1
Boosts
2
Views
170
Activity
May ’25
85% of Subscriptions are in Billing Retry State
One of our apps has 85% stuck in Billing Retry -- We are so confused. All the users are from the US, and have a one-week free trial. We had 1,000 subscriptions expire from this issue. So any help would be so appreciated.
Replies
2
Boosts
2
Views
222
Activity
Jan ’26
HELP WITH SUBSCRIPTIONS
Hey everyone, I really need help. My app versions keep getting approved for distribution and my subscriptions and business agreements are all approved. Yet, when the paywall in my app appears, and someone clicks the subscribe button to pay, the IAP isn't appearing. It just loads forever. When I tested in Xcode it just kept saying products not found. Id's are the same, bundle id is the same, ive done everything. Can someone help pls.
Replies
3
Boosts
0
Views
226
Activity
Jan ’26
Unable to Authenticate with App Store Server API in Production (401 Error)
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://aninterestingwebsite.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?
Replies
3
Boosts
2
Views
402
Activity
Oct ’25
How to change promotional image of in-app purchase
My apps stuck on review stage. Reason of it is Guideline 2.3.2 - Performance - Accurate Metadata We noticed that your promotional image to be displayed on the App Store does not sufficiently represent the associated promoted in-app purchase. Specifically, we found the following issue with your promotional image: – Your promotional image is the same as your app’s icon. The problem is i can't change this image. My subscription is still in 'in review' stage and I don't have option like delete image or change image. I replied to the review explaining that I cannot change it as long as the subscription is under review, but I haven’t received any meaningful reply, except that I need to change promotion image to be eligible for further review(which i cant do because i haven't option to change this image). Has anyone had such a problem before?
Replies
4
Boosts
2
Views
2k
Activity
Oct ’25
Inconsistent behavior with transactionId and appAccountToken in iOS Sandbox purchases (StoreKit1 & StoreKit2)
Hi, I'm reaching out to report a recurring issue with in-app purchases on iOS that seems to be related to Apple’s transaction handling — not to third-party libraries. In my Flutter application, I use both StoreKit2 and StoreKit1 (for comparison) via different packages, including the official in_app_purchase package. However, in both cases, I’m experiencing unexpected reuse of transactionId and appTransactionId values, even when initiating fresh purchases with unique appAccountToken values. Problem Summary: Purchase Stream Returns Old Purchases When calling buyNonConsumable() with a new product, the purchase stream still returns data for a previously purchased product, despite clearing all Sandbox transactions and using a new applicationUserName for each attempt. Transaction IDs Reused Across Distinct Purchases Even when generating a new UUID for appAccountToken on each purchase, the returned appTransactionId and transactionId are reused — this breaks our server-side logic, which expects these fields to uniquely identify purchases and users. Example Logs: // First purchase { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928154716" } // Second purchase (different user context) { "appAccountToken": "2d5a0880-f68e-44a7-a414-f51204e63904", "appTransactionId": "704464472748013865", "transactionId": "2000000928429780" } Even when using a different productId, the appTransactionId stays the same. When using StoreKit1, the productId updates properly, but the transactionId still matches the previous one. This behavior also affects App Store Server Notifications (V2): we have observed notifications tied to appAccountTokens from completely different user accounts (based on internal logs), sometimes delayed by days or weeks. I’ve prepared a reproducible example using the official Flutter in_app_purchase sample with minimal changes — you can find it here: Github gist The code is almost identical to the package example. I only added UUID generation for applicationUserName in _getToken(). In the actual app (not in this example), I retrieve the token from an API. Additional Observations from the Community: We’ve also found similar issues reported in other frameworks and languages. For instance, a developer using react-native-iap observed that App Store Server Notifications in TestFlight were tied to previously deleted users, even after signing up with a new user account and generating a new appAccountToken. Details here: User A deleted → User B signs up → receives upgrade event with User A’s token Notification uses appAccountToken from old account, not the new one This strengthens the suspicion that the issue may be related to how Apple associates transactions with Apple IDs in test environments. Questions: Is it expected for transactionId or appTransactionId to persist across purchases within the same Apple ID, even for different user contexts (e.g., separate logins in the same app)? Is there any official recommendation for avoiding this kind of data reuse in Sandbox or TestFlight environments? Should I expect appAccountToken in server notifications to always match the latest value provided during the purchase? Thank you in advance for your assistance. I would appreciate any clarification or advice regarding this issue, as it impacts production logic that relies on these identifiers being unique and consistent.
Replies
1
Boosts
1
Views
226
Activity
Jun ’25
Double-counted consumable in StoreKit unfinished and updates workflow
In Getting started with In-App Purchase using StoreKit views and the corresponding sample project, Store simultaneously enumerates Transaction.unfinished and Transaction.updates. Since, "if your app has unfinished transactions, the updates listener receives them once, immediately after the app launches," it appears that Transaction.unfinished would also receive the same unfinished transactions causing handle(updatedTransaction:) to be called for twice for each transaction, causing consumables to be double-counted. Is this a bug in the sample? Is there more information on concurrent execution of unfinished and updates?
Replies
1
Boosts
1
Views
226
Activity
Mar ’26
Custom Capacitor 6 plugin with SPM: "plugin is not implemented on ios" despite being compiled
Hi everyone, I'm building an iOS app using Capacitor 6 with Swift Package Manager (SPM). I have a custom native plugin (AppleIAPPlugin) for StoreKit 2 In-App Purchases that lives in the App target (not as an SPM package). Despite compiling successfully, the JavaScript bridge throws: "AppleIAP" plugin is not implemented on ios Setup AppleIAPPlugin.swift: swift import Foundation import Capacitor import StoreKit @objc(AppleIAPPlugin) public class AppleIAPPlugin: CAPPlugin, CAPBridgedPlugin { public let identifier = "AppleIAPPlugin" public let jsName = "AppleIAP" public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "getProducts", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "purchase", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "restorePurchases", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getCurrentEntitlements", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "openManageSubscriptions", returnType: CAPPluginReturnPromise), ] @objc func getProducts(_ call: CAPPluginCall) { /* StoreKit 2 implementation */ } @objc func purchase(_ call: CAPPluginCall) { /* ... */ } // etc. } AppleIAPPlugin.m: objc #import <Foundation/Foundation.h> #import <Capacitor/Capacitor.h> CAP_PLUGIN(AppleIAPPlugin, "AppleIAP", CAP_PLUGIN_METHOD(getProducts, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(purchase, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(restorePurchases, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getCurrentEntitlements, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(openManageSubscriptions, CAPPluginReturnPromise); ) MyBridgeViewController.swift (custom bridge to register the plugin): swift import UIKit import Capacitor class MyBridgeViewController: CAPBridgeViewController { override open func capacitorDidLoad() { bridge?.registerPluginType(AppleIAPPlugin.self) } } Main.storyboard points to MyBridgeViewController (module: App) instead of CAPBridgeViewController. TypeScript side: typescript import { registerPlugin } from "@capacitor/core"; export const AppleIAP = registerPlugin("AppleIAP"); What I've verified Both .swift and .m files are in the Xcode project's Compile Sources build phase nm on the compiled binary confirms OBJC_CLASS_$_AppleIAPPlugin symbol exists The build succeeds with zero errors Other SPM-based Capacitor plugins (Share, Media, NativeAudio) work fine — they have pluginMethods and jsName symbols in the binary; my custom plugin does NOT A bridging header (App-Bridging-Header.h) is configured with #import <Capacitor/Capacitor.h> What I've tried (all failed) .m file with CAP_PLUGIN macro only (no CAPBridgedPlugin in Swift) Added CAPBridgedPlugin protocol conformance to Swift class Created MyBridgeViewController subclass with registerPluginType() in capacitorDidLoad() Removed/added override public func load() method Added #import <Foundation/Foundation.h> to .m file Various bridging header configurations Multiple clean builds and derived data wipes Environment Xcode 16 Capacitor 6 (via SPM, binary xcframework) iOS 17+ deployment target Physical device testing (not simulator) Question How should a custom plugin in the App target be registered with Capacitor 6 when using SPM? The SPM-based plugins from node_modules get auto-discovered, but my custom plugin in the App target does not. Is there a step I'm missing to make registerPluginType() work, or should I structure my custom plugin as a local SPM package instead? Any guidance would be greatly appreciated.
Replies
1
Boosts
0
Views
56
Activity
1w
AppStoreServerNotificationV2 EXPIRED event after removing from sale
Our app is supposed to be removed from sale on May 31st. Subscriptions our app is offering will also be removed on May 1st one month before our app removal. I would like to know if AppStoreServerNotificationV2 EXPIRED event will be sent to a specified endpoint after the removal of these subscriptions. I think each subscription will be canceled automatically from May 1st to May 31st and it will send EXPIRED event to our server, but is it true? Thank you in advance.
Replies
1
Boosts
0
Views
155
Activity
Feb ’26
iOS 26.4 beta3,内购支付,票据被延迟
在iOS 26.4 beta3版本中,当前支付完成,获取的票据是上次支付的票据,在拉起一笔支付成,获取的又是上次支付的票据
Replies
1
Boosts
1
Views
264
Activity
Mar ’26
In-App Purchases not loading in production / TestFlight — Previously missing Paid Apps Agreement — App rejected under Guideline 3.1.2
Hello, My app was rejected on iPad (iPad Air 11-inch M3, iPadOS 26.2.1) with two related issues: Guideline 2.1 – Performance – App Completeness “The app exhibited one or more bugs that would negatively impact users. Bug description: the premium subscription cannot be loaded properly.” Guideline 3.1.2 – Business – Payments – Subscriptions “The submission did not include all the required information for apps offering auto-renewable subscriptions.” I am using StoreKit 2 with SubscriptionStoreView to present the auto-renewable subscription. During development: Subscriptions load correctly in the simulator (sandbox). On real devices, I test without a local StoreKit configuration file to fetch products from App Store Connect. The subscription UI (title, duration, price) displays correctly when products are returned. At the time of review, the Paid Apps Agreement was not active. I suspect this may have caused the subscription products to fail loading on the review device. Since then: Paid Apps Agreement is now Active. SubscriptionStoreView should automatically show required metadata. Because the subscription failed to load on iPad during review, the required information (title, price, duration) was not visible, which likely triggered the 3.1.2 rejection. Additionally, in TestFlight I sometimes see inconsistent behavior where the app appears but cannot be installed (“App Not Available”). Also, my app was rejected, but the subscription is still waiting for review. I would really appreciate guidance on the following: Am I potentially missing any required configuration that could prevent products from loading in production? Is there any propagation delay after activating the Paid Apps Agreement that could affect product availability? If I am overlooking something in configuration or testing, please let me know what I should specifically verify before resubmitting. Thank you very much for your help.
Replies
1
Boosts
1
Views
235
Activity
3w
Unresolved pending purchases for consumables
In our app we are running into a few issues with pending purchases staying on receipt indefinitely. These are consumable purchases where we received the purchase succeeded from apple but then something went wrong on our servers to validate and confirm the purchase. At this point the purchase stays on the apple receipt indefinitely or until we confirm it. The problem is there are lots of scenarios where we can't confirm purchases anymore (like a game world expired/banned player/etc). So there's a few things I'd like to know to see how this could be handle correctly. 1- Was the user already charged, and if yes would they ever be refunded if the purchase is not confirmed (some sort of expiry)? 2- Is there a way to cancel this sort of pending transaction directly from the app or backend? 3- If one of these users asked for a refund from apple would this clear the purchase from the receipt? Any information would be greatI couldn't find a lot of info on this topic.
Replies
0
Boosts
1
Views
53
Activity
Jun ’25
Unable to sign in to Sandbox Apple Account on Simulator
I am unable to sign in to a Sandbox Apple Account, where this issue occurs only via Simulator. Under Settings > Developer, I tap "Sign In" under Sandbox Apple Account. I enter my account credentials, and after bringing me back to the Developer page, the Sign In button briefly appears as disabled, before being re-enabled, without signing in to the account. (The account credentials are also recognized as correct, as I will receive an alert popup if incorrect.) See screenshots below: After signing in, Sign In button appears disabled... ... then is re-enabled without actually signing in to the account. I have now tried setting up multiple sandbox accounts via App Store Connect with various permutations (no confirmation of Apple Account email, confirming Apple Account email, logging in to iCloud and accepting terms of service), running different device simulators, running simulators on different Mac computers... none of which yield a different result. By contrast, I can sign in to the Sandbox Apple Account without issue on a physical device. The problem occurs only via Simulator.
Replies
2
Boosts
1
Views
277
Activity
2w
Apple Media Services T&C pop-up
I had published an App, and my app has App Clip supported. The issue I faced is that I had received complaints where the user keep seeing the pop up "Apple Media Services Terms and Conditions Have Changed" when user clicked on the "Open" Button in the App Clip. What we had tried so far: Let user switch the Apple Id's region to our supported region. Let user try to log out and log in to Apple Id within the supported region.
Replies
1
Boosts
0
Views
229
Activity
Feb ’26
When is the unverified branch of AppTransaction.shared entered?
Hi all, I am adding the following StoreKit 2 code to my app, and I don't see anything in Apple's documentation that explains the unverified case. When is that case exercised? Is it when someone has tampered with the app receipt? Or is it for more mundane things like poor network connectivity? // Apple's docstring on `shared` states: // If your app fails to get an AppTransaction by accessing the shared property, see refresh(). // Source: https://aninterestingwebsite.com/documentation/storekit/apptransaction/shared var appTransaction: VerificationResult<AppTransaction>? do { appTransaction = try await AppTransaction.shared } catch { appTransaction = try? await AppTransaction.refresh() } guard let appTransaction = appTransaction else { AppLogger.error("Couldn't get the app store transaction") return false } switch appTransaction { case .unverified(appTransaction, verificationError): // For what reasons should I expect this branch to be entered in production? return await inspectAppTransaction(appTransaction, verifiedByApple: false) case .verified(let appTransaction): return await inspectAppTransaction(appTransaction, verifiedByApple: true) } Thank you, Lou
Replies
12
Boosts
1
Views
852
Activity
Apr ’25
Can you buy an IAP via StoreKit 1 on iOS 26.2?
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?
Replies
1
Boosts
1
Views
199
Activity
Dec ’25
storefront.countryCode is fixed to USA in iOS 26 beta
Whether using Storefront.current?.countryCode or SKPaymentQueue.default().storefront?.countryCode, both are returning "USA" only. (It used to return the correct country code before the update.) In the sandbox environment, the country code is returned correctly, but in the TestFlight environment, it always returns "USA". There's no mention of this behavior in the beta release notes, so I'm posting it here for visibility.
Replies
1
Boosts
1
Views
178
Activity
Jul ’25
Mismatch between App Store Server API `expiresDate` (July 23) and iOS UI “Expires on” date (July 22) for 1-month subscription
Hi everyone, I’m seeing a consistent one-day discrepancy between the expiresDate returned by the App Store Server API and the “Expires on” date shown in the iOS Settings / App Store subscription list. I’d like to confirm whether this behavior is expected or if I’m misunderstanding the way Apple rounds dates. Reproduction steps Step Action Result 1 Purchase a 1-month auto-renewable subscription on 23 June 2025 14:00 JST (UTC+9) Transaction succeeds 2 Immediately fetch the transaction with GET /inApps/v1/subscriptions/{transactionId} Response contains "expiresDate": "2025-07-23T05:00:00Z" (= 23 July 2025 14:00 JST) 3 On the same device open Settings › Apple ID › Subscriptions (or App Store › Account › Subscriptions) UI shows Expires on: 22 July 2025 The same happens for every monthly renewal and on multiple devices. Region is Japan, device time zone Asia/Tokyo. What I understand so far (and my hypothesis) Apple’s docs say a monthly subscription renews “on the same calendar date” of the next month, so renewal in this example is 23 July. If the renewal is scheduled for 23 July at 14:00 JST, the subscription is fully usable until the end of 22 July in calendar terms, because the new billing period starts the moment the 23rd begins in Apple’s canonical time zone. Therefore, it might be intentional for the UI to display 22 July—i.e., “you can keep using it through the 22nd; on the 23rd it renews.” This hypothesis makes sense internally, yet it still looks confusing to end users who read “Expires on 22 July” and assume access ends at 00:00 on the 22nd, a whole day earlier than in reality. Questions Is showing the day before the renewal date the official/expected behavior? If so, could Apple clarify that the “Expires on” label represents the last full calendar day rather than the exact expiry timestamp? Which value should we surface in-app when telling users “Your subscription is valid until …”? The server’s expiresDate (precise to the second, converted to user time zone), or A UI-style date that’s one day earlier, matching Settings / App Store? Does Apple have a public document describing this rounding/visual convention? Have other developers encountered user confusion about the apparent 1-day “shortening” and, if so, how did you word your in-app messaging? Any insight from Apple engineers or fellow developers would be greatly appreciated. Thank you!
Replies
0
Boosts
1
Views
286
Activity
Jun ’25
Transaction.currentEntitlements sometimes does not emit a result until device is reboot
I have the typical StoreKit 2 manager class, where I check currentEntitlements for subscription. I have filed a feedback (FB22349195), I hope someone can take a look at it. func updateCustomerProductStatus() async { var activeSubscription: String? = nil // BUG: In some cases the currentEntitlements does not emit a transaction until the device is reboot for await result in Transaction.currentEntitlements { print("Found transaction: \(result)") // This print does not appear until a restart! do { let transaction = try checkVerified(result) // Skip revoked transactions if transaction.revocationDate != nil { print("Skipping revoked transaction for \(transaction.productID)") continue } // Skip expired subscriptions if let expirationDate = transaction.expirationDate, expirationDate < Date() { print("Skipping expired subscription for \(transaction.productID)") continue } // Check product type switch transaction.productType { case .autoRenewable: activeSubscription = transaction.productID default: break } } catch { print("Unable to verify transaction: \(error)") } } // Update state once after processing all entitlements self.activeSubscription = activeSubscription print("updateCustomerProductStatus() activeSubscription: \(activeSubscription ?? "nil")") } There is some unexpected behavior where the currentEntitlements does not emit a result until the iPhone device is reboot. This bug appeared in iOS 26.4 (and in the betas).
Replies
0
Boosts
1
Views
77
Activity
1w
Unknown Error when Trying to Add New Offer Codes
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!
Replies
3
Boosts
1
Views
638
Activity
Dec ’25