User Initiated a Single Consumable Purchase but Was Charged Twice
A user initiated a single in-app purchase for a consumable item, but they were charged twice. Both transactions have the same purchase token.
Additional details: After the user successfully completed the in-app purchase, the completeTransactions callback was triggered again. This was called at app launch using SwiftyStoreKit.completeTransactions to finish any pending transactions.
Could this be causing the duplicate charge? Any insights would be appreciated.
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
In my understanding, with the latest court ruling in the US, iOS apps may decide to support web-based payments for subscriptions and in-app purchases. However, are we allowed to exclusively support web-based payments and not implement StoreKit at all?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Connect
In-App Purchase
We are currently integrating In-App Purchases for our app and have configured App Store Server Notifications (v2) in the Sandbox environment.
During testing, we observed the following issue:
When a transaction is cancelled, declined, or pending (e.g., Ask to Buy flows or authorization pending),
No App Store Server Notification is sent to our webhook endpoint.
We only receive webhook events where the status is "purchased".
This becomes a critical problem for us because our backend must accurately track transaction states including failed and pending purchases, especially for wallet top-up use cases.
Additionally, we tried mocking failed transactions (via Xcode local environment and turning off In-App Purchases from Developer Settings) to simulate a technical failure scenario.
Even in these cases, no webhook notification was received when the purchase failed server-side.
Is it expected behavior in Sandbox that only successful transactions ("purchased") trigger webhooks?
Are failed or pending transactions suppressed in Sandbox intentionally?
Will webhook behavior be different in Production (i.e., will we receive webhook notifications for failures there)?
Is there any extra configuration or entitlement needed to fully test failure scenarios via webhooks in Sandbox?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit
In-App Purchase
App Store Server Notifications
StoreKit ask to buy should have more data in pending state. When user try to purchase ask to buy, we should get at least transactionID, product itself, and time that user start the request. So we can keep track of the whole transaction flow
jwsRepresentation should always available for every state, actually even failing state. And should attach state inside of it. Instead of only available after verified purchase. So we can use transactionID and everything relate to transaction for both waiting for purchase and clearing up the cancel or invalid purchase
Currently we only have jwsRepresentation after complete purchase, which is very limited its usage
Hi,
I have developed an app which has two in-app purchase subscriptions. During the test, the app can successfully get the status of the subscriptions. After it's released, I downloaded it from app store and subscribed it with my apple account. I found that in most cases, the app can identify that I have subscribed it and I can use its all functions. But yesterday, when I launched it again, it showed the warning that I haven't subscribed it. I checked my subscription in my account and the subscription status hasn't been changed, that is, I have subscribed it. And after one hour, I launched it again. This time the app identified that I have subscribed it. Why? The following is the code about listening to the subscription status. Is there any wrong about it?
HomeView()
.onAppear(){
Task {
await getSubscriptionStatus()
}
}
func getSubscriptionStatus() async {
var storeProducts = [Product]()
do {
let productIds = ["6740017137","6740017138"]
storeProducts = try await Product.products(for: productIds)
} catch {
print("Failed product request: \(error)")
}
guard let subscription1 = storeProducts.first?.subscription else {
// Not a subscription
return
}
do {
let statuses = try await subscription1.status
for status in statuses {
let info = try checkVerified(status.renewalInfo)
switch status.state {
case .subscribed:
if info.willAutoRenew {
purchaseStatus1 = true
debugPrint("getSubscriptionStatus user subscription is active.")
} else {
purchaseStatus1 = false
debugPrint("getSubscriptionStatus user subscription is expiring.")
}
case .inBillingRetryPeriod:
debugPrint("getSubscriptionStatus user subscription is in billing retry period.")
purchaseStatus1 = false
case .inGracePeriod:
debugPrint("getSubscriptionStatus user subscription is in grace period.")
purchaseStatus1 = false
case .expired:
debugPrint("getSubscriptionStatus user subscription is expired.")
purchaseStatus1 = false
case .revoked:
debugPrint("getSubscriptionStatus user subscription was revoked.")
purchaseStatus1 = false
default:
fatalError("getSubscriptionStatus WARNING STATE NOT CONSIDERED.")
}
}
} catch {
// do nothing
}
guard let subscription2 = storeProducts.last?.subscription else {
// Not a subscription
return
}
do {
let statuses = try await subscription2.status
for status in statuses {
let info = try checkVerified(status.renewalInfo)
switch status.state {
case .subscribed:
if info.willAutoRenew {
purchaseStatus2 = true
debugPrint("getSubscriptionStatus user subscription is active.")
} else {
purchaseStatus2 = false
debugPrint("getSubscriptionStatus user subscription is expiring.")
}
case .inBillingRetryPeriod:
debugPrint("getSubscriptionStatus user subscription is in billing retry period.")
purchaseStatus2 = false
case .inGracePeriod:
debugPrint("getSubscriptionStatus user subscription is in grace period.")
purchaseStatus2 = false
case .expired:
debugPrint("getSubscriptionStatus user subscription is expired.")
purchaseStatus2 = false
case .revoked:
debugPrint("getSubscriptionStatus user subscription was revoked.")
purchaseStatus2 = false
default:
fatalError("getSubscriptionStatus WARNING STATE NOT CONSIDERED.")
}
}
} catch {
// do nothing
}
if purchaseStatus1 == true || purchaseStatus2 == true {
purchaseStatus = true
} else if purchaseStatus1 == false && purchaseStatus2 == false {
purchaseStatus = false
}
return
}
Topic:
App & System Services
SubTopic:
StoreKit
Our macOS app has one in-app purchase (IAP) implemented using StoreKit 1. It works for us and beta testers but App Review get SKErrorDomain Error Code 0 / ASDErrorDomain Code 500 / AMSErrorDomain 305 on first attempt to make the in-app purchase.
However, the purchase succeeds at second attempt. We've looked through our entire IAP related code and App Store Connect setup but can’t find the reason. It's a standard implementation:
LegacyPaymentQueueObserver for SKPaymentQueue observation.
AppDelegate for initiation of payment queue observation on app launch
LegacyStoreKitPurchasableProduct for initiating a purchase and listening for the result
LegacyStoreKitProductsRequester for how we load the product before user can make in-app purchase. It happens this way:
PreviewResultsViewModelcalls loadProducts()on an instance of StoreKitPurchaseManager, which asks an instance of LegacyStoreKitProductsRequesterto requestProducts(forIdentifiers:)
Any guidance to resolve this would be appreciated.
How can I get the region region currently used in the macOS App Store? Preferably via Swift libraries, but any command / function will suffice.
The following StoreKit property seems to always return the region for the Apple Account associated with my macOS user.
await Storefront.current?.countryCode
See the Apple docs.
My macOS Apple Account region is US; in the App Store, when I sign into a different Apple Account whose region is GB (UK), Storefront.current?.countryCode continues to return US, not GB (or UK).
I correctly see prices in pounds instead of in dollars, British spelling instead of American spelling, apps listed in my purchased tab for the UK (not the US) Apple Account, and, in the Account Settings dialog, the UK Apple Account email address, billing address & Country/Region set to United Kingdom.
I didn't get any relevant results from the following command lines:
defaults find GB
defaults find UK
defaults find uk-apple-id@example.com
defaults find uk-apple-id
The following didn't change after I signed into the UK Apple Account in the App Store:
$ defaults read com.apple.AppStoreComponents
{
ASCLocaleID = "en-US@calendar=gregorian";
}
Maybe Storefront.current?.countryCode only specifies the country code for the Storefront that will be used for in-app purchases, instead of for purchasing new apps from the App Store; maybe the former is tied to the Apple Account for the macOS user, instead of to the Apple Account for the App Store. If that's the case, what other mechanism can I use to obtain the country code for the App Store storefront?
I have been struggling to test the IAP response but it is returning empty. I am now in the very beginning of one app, and I don't want to submit the contacts and banking and tax stuff that early. are these necessary for even testing IAP results locally? I think it does not make sense if I have to.
Greetings!
I was reviewing the documentation for the Apple Store Server Notification v1 APIs and saw the deprecation notice. We are currently unable to migrate to v2 in the near future. With that in mind, I want to know if there were any plans to shut off v1, and if so, was there any timeline we could expect? Thank you in advance.
Reference: https://developer.apple.com/documentation/appstoreservernotifications/app-store-server-notifications-version-1
Can you implement storekit2 for in app purchases for a flutter project?
Topic:
App & System Services
SubTopic:
StoreKit
Hi,
We use the (now deprecated) server-side receipt verification API (*1) with the app we are maintaining and there are some points I would like confirm on how its response changes based on whether the purchase being processed was a subscription that used an offer code or not.
I am particularly concerned about the following:
Are there any properties of the response that are added or missing?
Is there any property indicating that an offer code was used?
If there is, which field is that and what values does it take?
Are there any special steps or options required when processing the receipt which used an offer code on the server side?
*1 https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
We have received reports from users that their in-app purchases suddenly appear as unpurchased.
We would like to know the cause of this issue.
Our implementation does not use a server; purchases are determined solely on the client side.
These reports often occur after updating the app version,
but we have been unable to reproduce the issue in our development environment.
Topic:
App & System Services
SubTopic:
StoreKit
Dear Apple Developer Support Team,
I hope you are doing well.
We are reaching out to request clarification and guidance regarding the In-App Purchase (IAP) requirements for our upcoming iOS application.
Our app offers paid access to digital video content. Each video has a dynamic price determined by our backend based on multiple factors (such as duration, category, and promotions). Additionally, users are allowed to select and purchase multiple videos at the same time, which results in a combined total price that varies per transaction.
Challenges we face with IAP on iOS
Dynamic pricing:
Apple requires IAP products to have static pricing defined in App Store Connect.
Our video prices change frequently and cannot be represented by fixed IAP product SKUs.
Multiple-item purchases:
iOS does not support a single purchase that includes multiple different IAP products.
Processing many separate IAP transactions in sequence results in a poor user experience and is likely to cause failures.
Product creation limitations:
Since our catalog contains many videos with frequently changing prices, it is not feasible to create individual IAP products per video.
Given these restrictions, we are unclear how to remain compliant while still providing a functional purchase flow for our users.
Question
In this scenario, where:
video prices are dynamic,
users may purchase multiple videos together, and
IAP does not support multi-item purchases or dynamic pricing,
are we allowed to use Stripe or another external payment provider to handle these purchases?
If not, we would appreciate Apple’s guidance on what the recommended and compliant approach would be for apps that must price digital content dynamically and allow bulk purchasing.
We want to ensure full compliance with App Store Review Guidelines and would like confirmation on the correct implementation strategy before proceeding.
Thank you for your time, and we look forward to your clarification.
Kind regards,
Muhammad Adnan
Koderlabs
Some users cannot repurchase a subscription SKU after it has expired.
Flow:
User previously subscribed.
User canceled and the subscription fully expired.
After weeks, user reinstalls the app and taps the same SKU.
StoreKit does not create a new purchase transaction.
Instead, StoreKit always returns the old expired transaction in updatedTransactions.
Therefore, the user is permanently unable to purchase the SKU again.
We have already tried:
Adding payment observer at app launch
Calling finishTransaction for all transactions
Clearing queue at startup
SKReceiptRefreshRequest
Server-side verifyReceipt
Ensuring subscription is truly expired (not in grace/retry)
Not calling restoreCompletedTransactions
None of these resolved the issue. StoreKit still only sends the old transaction and never generates a new one.
Expected behavior:
A new purchase transaction should be created when user taps the expired subscription SKU.
Actual behavior:
StoreKit repeatedly pushes the old expired transaction, blocking new purchases.
We can provide:
Some users cannot repurchase a subscription SKU after it has expired.
Flow:
User previously subscribed.
User canceled and the subscription fully expired.
After weeks, user reinstalls the app and taps the same SKU.
StoreKit does not create a new purchase transaction.
Instead, StoreKit always returns the old expired transaction in updatedTransactions.
Therefore, the user is permanently unable to purchase the SKU again.
We have already tried:
Adding payment observer at app launch
Calling finishTransaction for all transactions
Clearing queue at startup
SKReceiptRefreshRequest
Server-side verifyReceipt
Ensuring subscription is truly expired (not in grace/retry)
Not calling restoreCompletedTransactions
None of these resolved the issue. StoreKit still only sends the old transaction and never generates a new one.
Expected behavior:
A new purchase transaction should be created when user taps the expired subscription SKU.
Actual behavior:
StoreKit repeatedly pushes the old expired transaction, blocking new purchases.
We can provide:
Affected user’s base64 receipt
verifyReceipt full response
Transaction logs (transactionIdentifier, original_transaction_id, productIdentifier, state)
Please help investigate why StoreKit is not allowing a new subscription purchase.
Affected user’s base64 receipt
verifyReceipt full response
Transaction logs (transactionIdentifier, original_transaction_id, productIdentifier, state)
Please help investigate why StoreKit is not allowing a new subscription purchase.
Topic:
App & System Services
SubTopic:
StoreKit
What is the expected behavior for App Receipts and ASSN v2 notifications when a subscription is set to "Remove from Sale"?
I tried to test this in Sandbox, but the "Remove from Sale" setting in App Store Connect doesn't seem to affect the Sandbox environment.
For existing subscribers, what happens in the receipt? Does auto_renew_status change to 0 and is expiration_intent populated immediately?
Also, which notificationType is sent via ASSN v2?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
App Store Server Notifications
App Store Receipts
FB19377002
I am looking to improve and review my subscription purchase handling logic, for the best user experience.
Considering that StoreKit2 caches local raw transactions (in case user is offline), is it really necessary to persist "unlocked status" in UserDefaults or SwiftData Model or AppStorage? Are there significant delays when reading Transaction.currentEntitlements from locally stored cache, versus reading it from UserDefaults; or, as in the latest SKDemo example, even reading it from stored in SwiftData ?
https://developer.apple.com/forums/thread/706450
I only have subscriptions ( I don't have noncosumable or consubale products). Do I still need to persist subscription status?
Hello,
I am consistently receiving the error message "In-app purchases are not allowed on this device" whenever I try to make an in-app purchase on my iOS device. Despite following all the recommended solutions I could find online, the issue remains unresolved.
Here is a list of the steps I have already taken:
Checked Screen Time Settings:
I navigated to Settings > Screen Time > Content & Privacy Restrictions > iTunes & App Store Purchases.
I have confirmed that "In-App Purchases" is set to "Allow." I have also tried toggling this setting off and on again.
Signed Out & In of Apple ID:
I signed out of my Apple ID via Settings > [Your Name] > Media & Purchases, restarted the device, and then signed back in.
Restarted the Device:
I have force-restarted my device multiple times.
Updated iOS:
I have ensured my device is running the latest version of iOS (checked via Settings > General > Software Update).
Verified Payment Method:
I have confirmed that my payment method on file is valid and up-to-date.
Created a New Sandbox Account:
I also created a new Sandbox Tester account in App Store Connect and tested with it, but the result was the same.
Device Information:
Device Model: iPhone 15, iPhone 13
iOS Version: iOS 17.5, iOS 18
Even after performing all of these steps, the problem persists. Has anyone else encountered such a stubborn issue, or does anyone have a different solution I could try?
Thank you in advance for your help.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit Test
StoreKit
In-App Purchase
Is there an API Endpoint that I can call to check if user still have valid subscription?
I want to be sure that his subscription renewal was succesful (ie: I dont want to give him another month/year/.. if his latest renewal wasnt successful)
Would GET https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId} be the correct API endpoint to call?
But I wonder, after subscription auto-renews, do we still use the same transactionId to check whether his subs is still valid?
Topic:
App & System Services
SubTopic:
StoreKit
I'm encountering an issue with the App Store Server API where the appAccountToken is not preserved when users migrate their Apple ID email addresses. I've submitted
Feedback Assistant ticket FB18709241 but wanted to check if anyone else has experienced this and get community input on best practices.
The Issue
When a user migrates their Apple ID from one email to another (e.g., from olduser@example.com to newuser@icloud.com), the App Store creates a new subscription
transaction with a different originalTransactionId, but the appAccountToken is not carried forward from the original transaction.
What I'm Seeing
note: these values are fake
When querying /inApps/v1/subscriptions/{originalTransactionId} with the either post-migration transaction ID or the pre-migration transaction ID, the API returns both transactions:
Pre-migration transaction (status: 2 - inactive):
originalTransactionId: "12345678910111"
Contains: "appAccountToken": "abc123-def456-ghi789"
Post-migration transaction (status: 1 - active):
originalTransactionId: "67891011121314"
Missing: appAccountToken entirely
The Problem
The appAccountToken is our only way to link App Store subscriptions to user accounts. Without it on the new transaction:
Users lose access to premium features despite having valid subscriptions
Server-side renewal notifications can't be matched to user accounts
Manual support intervention is required for each affected user
Questions for the Community
Has anyone else encountered this issue with Apple ID migrations?
What's the recommended approach for handling this scenario?
Is there an alternative mechanism to maintain the subscription-to-user linkage across migrations?
Questions for Apple Engineers
Is this the expected behavior, or should the appAccountToken be preserved?
Are there any planned improvements to handle this migration scenario?
What's the best practice for developers to handle this case?
Interestingly, both the old and new transaction IDs return the same JSON response from the App Store Server API, suggesting Apple maintains internal linkage between
these transactions, but the appAccountToken isn't carried forward to the active transaction.
Any insights or similar experiences would be greatly appreciated!
Thank you!!
Feedback Assistant: FB18709241
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit
App Store Server Notifications
App Store Server API
Hello,
I have a question regarding the App Store Server API's getTransactionInfo endpoint.
Previously, the official documentation for getTransactionInfo mentioned that:
“This endpoint doesn’t support an app transaction. To get information about an app transaction, decode the signed app transaction received from the device.”
However, as of June 2025, I can no longer find this sentence in the current documentation (link). Now, the docs state that all in-app purchase transaction IDs are supported (consumable, non-consumable, auto-renewable subscriptions, etc.).
But in practice, when I call getTransactionInfo with an AppTransactionId (extracted from a signed App Transaction JWS), I receive this error: apiErrorCode: 4000048
“Invalid request. App transactions aren’t supported by this endpoint.”
Is this endpoint supposed to support AppTransactionId now, or is the restriction still in place (but not mentioned in the docs)?
Is there any official statement about when this restriction was added/removed?
Can you clarify if only in-app purchase transaction IDs (and not AppTransactionIds) are supported for this endpoint, or has the policy changed recently?
Any clarification or historical context would be greatly appreciated.
Additionally, I would like to know about the behavior of an App Transaction in the event of a refund.
If a user receives a refund for the app itself (not an in-app purchase), how can changes to the AppTransaction be detected?
Does the App Store Server Notification v2 provide notifications for app-level refunds, or are such events only visible by decoding the latest App Transaction JWS on the device?
Is there any way to receive app-level refund information server-side, or must we always rely on the device to provide the updated signed app transaction?
Any clarification on this refund flow and notification coverage would also be appreciated.
Thank you!