I'm adding my first in-app purchase to an app, and I'm finding the process incredibly frustrating.
Aside from the Apple Developer Documentation not being clear enough, and kind of glossing over the technical steps required (and their sample code being woefully inadequate), App Store Connect and the testing sandbox simply don't work as they say they do.
For example, in my app I've purchased the IAP and now I want to request a refund. I select the purchase, I choose a refund reason, and this page says, "To set up a test for approved refunds, select any refund reason on the refund request sheet, and submit the sheet. The App Store automatically approves the refund request in the testing environment."
Well, when I re-launch the app the purchase is still there. I can't request a refund again because it says this is a duplicate refund request, so it knows that the purchase has had a request, and it's supposed to have automatically refunded it, but it clearly hasn't.
So, I try clearing the purchase history via the Settings app > Developer > Sandbox Apple Account. Same thing. Purchase remains.
Try clearing purchase history in App Store Connect. Same thing.
How on Earth does anyone get an in-app purchase to work when the entire testing environment is so badly executed?
How do I get past this? The IAP is the last part of the app that needs to be implemented, and I've lost a week on this already.
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
Hi 👋! I want to switch the business model in my app from premium to freemium and do it gracefully for existing users. Essentially, I wish to provide newly-paywalled content for free to existing paid users (people who bought the original app).
It seems clear that I should be using appTransaction's originalAppVersion property to check against purchases made in a previous version of the app, per the documentation. However, there seems to be broad confusion over whether originalAppVersion returns the version number or the build number and how to test for it. Examples of confusion can be found here, here and here.
This lack of clarity seems especially dangerous due to the difficulty in testing these values. In the sandbox originalAppVersion returns 1.0 by default, so whether you design for version number or build number, you'll always return a positive as long as your value is more than 1. There is a real risk to unknowingly either never identify previous premium users or accidentally identify everyone as premium (essentially giving away your app for free).
For example, my app's current version number is 1.4.0 and build number is 18, so 1.4.0 (18). As this is a major change, for this new update I might as well go for version number 2.0.0, and let's say I release the app with build number 5, so 2.0.0 (5). If I expect originalAppVersion to return the version number, I would match it against 2, because anything before 2.0.0 needs to be marked as premium. However, if I expect the build number, I should check against 19 and respectively bump up my build number: 5 -> 19.
In the standard version/build "v.v.v (b)" format, does originalAppVersion return app version or app build?
If it indeed does return build, and not version, I guess I'll start all of my future build numbers from 100 just in case: 2.0.0 (100). The only way I imagine I can test this is to print the value on the visual interface in a live version of the app, and ask a random user 🤷♂️.
Topic:
App & System Services
SubTopic:
StoreKit
I am developing and testing Apple Promotional Offers and encountered issues during implementation.
Purchase Failure
When using the following API, the promotional purchase sheet is displayed, but after attempting payment, the transaction fails.
API:
public static func promotionalOffer(
offerID: String,
keyID: String,
nonce: UUID,
signature: Data,
timestamp: Int
) -> Product.PurchaseOption
Observed behavior:
Alert shows:
Unable to Purchase [Environment: Xcode]
Not Eligible for Offer
Console logs:
AMSErrorDomain Code=305
ASDErrorDomain Code=3903
Discount Not Applied
When calling this API, there are no errors, but the promotional price is not applied, and the user still sees the original price.
API:
public static func promotionalOffer(
_ offerID: String,
compactJWS: String
) -> [Product.PurchaseOption]
Questions
Does Promotional Offers support testing with .storekit files in Xcode?
Can Promotional Offers be tested with sandbox accounts using real offers configured in App Store Connect?
How should I troubleshoot and resolve the above issues?
• Could it be related to the subscription history of the sandbox account?
• Do I need to use TestFlight instead of Xcode to test certain scenarios?
• Could this be caused by signature parameters or StoreKit configuration?
The development subscription management screen for managing your sandbox account subscriptions is today showing products that belong to other apps.
Is there a known issue right now?
Created a FB with screenshots
FB FB20199893
Topic:
App & System Services
SubTopic:
StoreKit
hello,
I have an app with In-App purchase set up, it works fine in TestFlight on an iOS device but the download from the App Store gives me a transaction failed message.
I tried to replicate in the simulator but from what I've read is that you can't do in-app purchase testing in the simulator. Perhaps I'm wrong here but I've tried multiple sandbox accounts, etc.. and still get an unusual "...password can't be reused..." error (pasted below) in the debug description.
I'm curious if anyone else has dealt with this scenario and if so how did you go about researching/identifying the issue and/or resolving it.
Partial error message:
<SKPaymentQueue: 0x60000000e370>: Payment completed with error: Error Domain=ASDErrorDomain Code=530 "(null)" UserInfo={NSUnderlyingError=0x600000c74750 {Error Domain=AMSErrorDomain Code=100 "Authentication Failed The authentication failed." UserInfo={NSMultipleUnderlyingErrorsKey=(
"Error Domain=AMSErrorDomain Code=2 "Password reuse not available for account The account state does not support password reuse." UserInfo={NSDebugDescription=Password reuse not available for account The account state does not support password reuse., AMSDescription=Password reuse not available for account, AMSFailureReason=The account state does not support password reuse.}"
I have an iOS app with a main target and an app extension. I want the app extension to write data to the SwiftData in App Groups, while the main target reads the data. However, currently, the app extension only has permission to read data, not to write it.Is the issue due to my incorrect configuration or an Apple-imposed restriction?
Hi!
I'm trying to implement a two week free trial for my existing paid ipad app. Following the guidance from the wwdc2022/10007, I'm using AppTransaction.shared and checking the result. I'm getting a verified result, but the appTransaction.originalPurchaseDate is always the same date - 2013-08-01 07:00:00 +0000 / 397033200, even the particular sandbox account user never had a purchase.
This makes testing the logical branch of "has this user never purchased this app before" if the app store is always telling us that it's been purchased. (I've been using new sandbox account, so there should be no history)
Here's some code that includes hacking around always getting that original purchase date. We're in the final stretches, and wanting to test things that will be closer to actual store behavior (and I'm thinking that always returning a purchased date for an unpurchased app wouldn't be happening)
Am I just holding things wrong? Sandbox bug/limitatiin I just have to live with?
thanks!
++md
class MJAppStore: NSObject {
@objc static let shared = MJAppStore()
@objc func verifyAppStoreStatus(_ completion: @escaping (MJAppStoreStatus, Error?) -> Void) {
Task {
do {
let status = try await doVerificationThing()
completion(status, nil)
} catch {
completion(.error, error)
}
}
}
func doVerificationThing() async throws -> MJAppStoreStatus {
do {
let result = try await AppTransaction.shared
print("TRIAL: survived AppTransaction.shared")
switch result {
case .unverified(_, _):
print("TRIAL: app transaction UNVERIFIED")
return .free
case .verified(_):
let appTransaction = try result.payloadValue
// hack around the app store sandbox accounts saying we're purchased even though
// we're not really. 2013-08-01 07:00:00 +0000
print("TRIAL: app transaction VERIFIED \(appTransaction.originalPurchaseDate.timeIntervalSinceReferenceDate) -> \(appTransaction.originalPurchaseDate)")
if appTransaction.originalPurchaseDate.timeIntervalSinceReferenceDate == 397033200 {
return .free
} else {
return .purchased
}
}
} catch {
...
works perfectly on android but doesn't work at all on IOS and i have used the same bundle id and product ids on both stores.
The error that i get on IOS is : "IAP initialization failed: NoProductsAvailable - No Product returned from store"
Here are the things that i've done:
Created an App ID on the apple developer portal with the correct capabilities
I have enabled the correct capabilities on the xcode project Unity Framework is embed and signed, Storekit (do not embed)
In singin and capabilities in-app purchases is there
I am using testflight to submit the app with a distribution certificate that appears to be valid
I've checked the the bundle identifier and it's the same everywhere (unity project, xcode project, App ID)
All of the products are cleared for sale and are in the status "ready to submit"
I always uninstall the old app version before testing the new one
My banking updates are still processing does this effect TestFlight IAP
Paid Apps Agreement is in Pending User Info state does this effect also
I still haven't filled out the tax forms, so I'm wondering if I need to complete them before my app's in-app purchases (IAPs) work in TestFlight.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
Apple Unity Plug-Ins
Hi,
I've been unable to successfully test in the sandbox environment for a StoreKit 2 subscription group and can't seem to find the missing piece.
I am calling the following line of code:
let products = try await Product.products(for: [subscriptionID])
Expected behavior: The product is returned in the products array.
Actual result: The array is empty
I have done the following:
Successfully tested our logic using a storekit configuration file locally in Xcode.
Created the Subscription group in App Store Connect. The subscription product is currently "Waiting for Review", but it is our first so will not be approved without being attached to a distribution build review.
Created a Sandbox user account in App Store Connect -> Users -> Sandbox
Signed into the sandbox user account in Settings -> Developer -> Sandbox Apple Account
Signed the Paid Apps Agreement for our organization
A few debugging notes:
I deleted all apps before installing from Xcode
I've tried both locally and in TestFlight builds
Restarted my device
Verified productID matches the productID in App Store Connect
I'm not sure if I'm missing something, but any help would be appreciated.
Thanks
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Connect
In-App Purchase
I've just released my first on app store. I have monthly renewing subscription on my app, there is no pay wall for few days.
When I look at my app store page after release it shows me app price upfront instead of users download the app. I know I have subscription, but I don't want it to be upfront before install
I know other store that have the same model and users get to download their app. What am I missing?
We have implementend Storekit 2 in our app, for one time purchases and subscriptions, so it is iOS15 and higher only.
Everything works fine, but now we want to add App Store promotions for our IAP's. That doesn't work because the App requires the app to implement SKPaymentTransactionObserver
How to Promote Your In-App Purchases
Make sure your app supports a delegate method in SKPaymentTransactionObserver. You can choose to customize which promoted in-app purchases a user sees on a specific device by implementing SKProductStorePromotionController.
The problem is that this observer is part of the original Storekit API and not of the new one.
What can we do to make this work with the new Storekit 2 API?
Hello,
I am encountering an issue where I receive an App Store Server Notification V2 upon the purchase of a consumable item.
I have configured the App Store Server Notification V2 endpoint to handle notifications related to subscription products.
However, I did not expect to receive notifications for consumable purchases.
The notification includes the following signedPayload decoded into the ResponseBodyV2DecodedPayload object with the following values:
notificationUUID: 3cd6410b-0c89-4247-aba5-20710e79895e
notificationType: null
subtype: null
The transaction information decoded from the ResponseBodyV2DecodedPayload object is as follows:
transactionId: 2000000633622618
webOrderLineItemId: null
productId: heart_2
To debug, I called the Get Notification History API of the App Store Server API, and the mentioned notification for the consumable product purchase is not included in the history. Only notifications related to subscription product purchases are retrieved.
According to the notification type documentation, there is no explanation for cases where both notificationType and subtype are null, nor is there any mention of receiving notifications for consumable purchases. Therefore, I am uncertain how to interpret and handle this notification.
Could you please provide an explanation or guidance on this issue?
Thank you.
References:
https://developer.apple.com/forums/thread/737592
https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
In-App Purchase
App Store Server Notifications
App Store Server API
My app has a couple of consumable IAP items. I have tested this extensively and it works in all test scenarios including loads of beta testers using testflight. However, Apple's production reviewer reports that loading of the products hangs in their setup.
This is very frustrating as I have no means of recreating the problem.
My first product was tested ok an all my IAP items are approved for release. However, I did not explicitly assign them to my build. I read somewhere that you need to do that but could not find in App Store Connect after my first product was approved.
Below is the relevant code section. What am I missing?
class DonationManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
@Published var products: [SKProduct] = [] // This is observed by a view. But apparently that view never gets populated in Apple's production review setup
@Published var isPurchasing: Bool = false
@Published var purchaseMessage: String? = nil
let productIDs: Set<String> = ["Donation_5", "Donation_10", "Donation_25", "Donation_50"]
override init() {
super.init()
SKPaymentQueue.default().add(self)
fetchProducts()
}
deinit {
SKPaymentQueue.default().remove(self)
}
func fetchProducts() {
print("Attempting to fetch products with IDs: \(productIDs)")
let request = SKProductsRequest(productIdentifiers: productIDs)
request.delegate = self
request.start()
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
DispatchQueue.main.async {
self.products = response.products.sorted { $0.price.compare($1.price) == .orderedAscending }
print("Successfully fetched \(self.products.count) products.")
if !response.invalidProductIdentifiers.isEmpty {
print("Invalid Product Identifiers: \(response.invalidProductIdentifiers)")
self.purchaseMessage = NSLocalizedString("Some products could not be loaded. Please check App Store Connect.", comment: "")
} else if self.products.isEmpty {
print("No products were fetched. This could indicate a problem with App Store Connect configuration or network.")
self.purchaseMessage = NSLocalizedString("No products available. Please try again later.", comment: "")
}
}
}
...and the view showing the items:
@StateObject private var donationManager = DonationManager()
var body: some View {
VStack(spacing: 24) {
Spacer()
// Donation options -------------------
if donationManager.products.isEmpty {
ProgressView(NSLocalizedString("Loading donation options...", comment: ""))
.foregroundColor(DARK_BROWN)
.italic()
.font(.title3)
.padding(.top, 16)
} else {
ForEach(donationManager.products, id: \.self) { product in
Button(action: {
donationManager.buy(product: product)
}) {
HStack {
Image(systemName: "cup.and.saucer.fill")
.foregroundColor(.pink)
Text("\(product.localizedTitle) \(product.priceLocale.currencySymbol ?? "$")\(product.price)")
}
.buttonStyle()
}
.disabled(donationManager.isPurchasing)
}
}
I'm encountering a crash on app launch. The crash is observed in iOS version 17.6 but not in iOS version 18.5. The only new notable thing I added to this app version was migrate to store kit 2.
Below is the error message from Xcode:
Referenced from: <DCC68597-D1F6-32AA-8635-FB975BD853FE> /private/var/containers/Bundle/Application/6FB3DDE4-6AD5-4778-AD8A-896F99E744E8/callbreak.app/callbreak
Expected in: <A0C8B407-0ABF-3C28-A54C-FE8B1D3FA7AC> /usr/lib/swift/libswift_Concurrency.dylib
Symbol not found: _$sScIsE4next9isolation7ElementQzSgScA_pSgYi_tYa7FailureQzYKFTu
Referenced from: <DCC68597-D1F6-32AA-8635-FB975BD853FE> /private/var/containers/Bundle/Application/6FB3DDE4-6AD5-4778-AD8A-896F99E744E8/callbreak.app/callbreak
Expected in: <A0C8B407-0ABF-3C28-A54C-FE8B1D3FA7AC> /usr/lib/swift/libswift_Concurrency.dylib
dyld config: DYLD_LIBRARY_PATH=/usr/lib/system/introspection DYLD_INSERT_LIBRARIES=/usr/lib/libLogRedirect.dylib:/usr/lib/libBacktraceRecording.dylib:/usr/lib/libMainThreadChecker.dylib:/usr/lib/libRPAC.dylib:/System/Library/PrivateFrameworks/GPUToolsCapture.framework/GPUToolsCapture:/usr/lib/libViewDebuggerSupport.dylib```
and Stack Trace:
```* thread #1, stop reason = signal SIGABRT
* frame #0: 0x00000001c73716f8 dyld`__abort_with_payload + 8
frame #1: 0x00000001c737ce34 dyld`abort_with_payload_wrapper_internal + 104
frame #2: 0x00000001c737ce68 dyld`abort_with_payload + 16
frame #3: 0x00000001c7309dd4 dyld`dyld4::halt(char const*, dyld4::StructuredError const*) + 304
frame #4: 0x00000001c73176a8 dyld`dyld4::prepare(...) + 4088
frame #5: 0x00000001c733bef4 dyld`start + 1748```
Note: My app is a Godot App and uses objc static libraries. I am using swift with bridging headers for interoperability. This issue wasn't observed until my last version in which the migration to storekit2 was the only notable change.
Hey folks!
In one of our apps we're using the FamilyControls framework to manage the screentime for the child. The app requires the Guardian to activate a subscription on his/her device to enable the functionality for the Child on the child device.
We're currently using StoreKit 2 to get notified when there is an active subscription. The issue we're seeing (and our users) is that an activated subscription isn't instantly propagated to the Family members, in this case the device belonging to the child.
Is this a known bug, and are the possible any workarounds? Currently we have to ask our users to "Restore Purchase" or wait minutes/hours for the app to active, which isn't a viable solution. We want it to "just work" :)
Sincerely,
César Pinto Castillo
Ambi Studio
We are running into exceptions when trying to parse Purchase Date and Original Purchase Date from the base64 encoded receipt.
Expected RFC 3339 format of ASN.1 Field Value is: yyyy-MM-ddTHH:mm:ssZ but we end up getting back 2025-04-22T19.49.03Z.
Started to happen on 3rd Dec 2024.
Hi, I'm wondering if an in-app purchase is already made, but my app has not yet call finishTransaction(_:). Would that transaction be settled to me? Or do I only receive payment after the transaction is marked as 'finish' via storekit?
I implemented consumable in-app purchases in an iPhone app using StoreKit's ProductView().
When I tap the payment button in ProductView(), I am taken to the payment screen and once the payment is completed, the desired code appears to be executed, so there doesn't seem to be a problem, but when I tap the payment button in ProductView() again, the desired code is executed without being taken to the payment screen.
So one payment can be used any number of times.
I thought I wrote it exactly according to the reference, but
will it be okay in a production environment?
Is there any code that is necessary?
Hello. I launched my new mobile app Drop Pin Location to promote your business or brand on the go, on January 12, 2023. How can i market and campaign to get more daily users?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store Server Notifications
Marketing
I have auto-renewable subscriptions, and in Xcode everything works fine. It shows a list of subscriptions where I can make a test purchase.
But when I send it for review, the review team, as well as TestFlight, simply do not have subscriptions. If the problem was in the code, it would not work in the sandbox as I think.
But I think that I configured everything correctly in the subscription settings. The only thing: it shows there for the subscription in appstoreconnect that it is preparing for review, but nothing can be done about it, because it will be solved with the first release of the application.
But I do not know where else to look and what to do. The problem is probably not in the code, but I also redirected the subscription config in appstoreconnect a bunch of times.
I asked help on review team, no way. Tried to google and chat GPT, no ideas where to find a solution.