StoreKit

RSS for tag

Support in-app purchases and interactions with the App Store using StoreKit.

StoreKit Documentation

Posts under StoreKit tag

340 Posts
Sort by:
Post not yet marked as solved
0 Replies
285 Views
My company did an app for a customer 10 years ago. The app has in-app purchases (not subscriptions). It is an Objective-C code base. Over the years we've done minor updates to this app in order to keep it in compliance, but no major overhaul. The app has been pretty reliable in the past, but recently my customer is getting a flood of complaints from users who say their in-app purchases disappear and can't be restored. It's happening too often to just be a case of bad internet connection or similar issue. The only thing that seems to be consistent is that it's never US-based users. Whenever I test this app using sandbox accounts, I cannot reproduce the problems that are being reported. I'm always able to make purchases and restore them after reinstalling the app, or restore them to a different device. The content associated with the purchases always downloads correctly, even if I intentionally interrupt and restore internet access. What can I do to figure this out? Should an IAP based app from 2014 be expected to work correctly today? Are there new legal issues with non-US app stores that we need to be aware of? I realize there isn't a lot to go on here, but I don't have any more information. Thanks, Frank
Posted
by
Post not yet marked as solved
0 Replies
208 Views
{NSLocalizedDescription=Reached max retry count, AMSURL=https://amp-api.sandbox.apple.com/v1/catalog/us/in-apps?extend=hostedContent&filter%5BappBundleId%5D=com.dazn.staging&filter%5BofferName%5D=BR.AP.HO.01.STAG,BR.MP.HO.01.STAG,BR.NFL.APPLE.SPP.HO.01.STAG&l=en-US&omit%5Bresource%5D=autos&platform=iphone&with=adHocOffers, AMSStatusCode=401, NSLocalizedFailureReason=Task reached max retry count (0 / 0);}
Posted
by
Post marked as solved
2 Replies
263 Views
My app listens for transactions using: for await result in Transaction.updates { // check verification and process Transaction } The result coming from these updates has a jwsRepresentation field, which when decoded server-side reveals the price the user paid, discounted in the case of a custom offer code. How can I get this value without a server roundtrip? Transaction does not have price field, and the subscription product's price does not seem to include custom offer code pricing.
Posted
by
Post marked as solved
3 Replies
651 Views
After upgrading to iOS 17.4, when using SKProductsRequest to request product information from Apple, an error is returned. The error message is "4097 Couldn’t communicate with a helper application." At present, users who have upgraded to iOS 17.4 are unable to make payments. How can we solve this problem? Thank you!
Posted
by
Post marked as solved
2 Replies
387 Views
Hello, I'm trying to add in-app purchases to a macOS app but failing to load the local test products. From the docs and various examples I've found online, it should be pretty straightforward with StoreKit 2. So far, I've done the following: added in-app purchase capability created the local .storekit file added consumable and non-consumable products updated the scheme to use the test StoreKit configuration verified the products are present and can be purchase by using Debug > StoreKit > Manage Transactions to make direct purchases verified the product IDs are correct To simplify things I tried creating a barebones app. I created a new .storekit file and added a single non-consumable product with ID product1. I used the default view and just a task to retrieve the products: import SwiftUI import StoreKit struct ContentView: View { var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .task { let products = try? await Product.products(for: ["product1"]) print(products) } .padding() } } The print statement output is: Optional([]) I know I must be doing something wrong, but I'm completely missing it. I hope someone can help me. Thanks
Posted
by
Post not yet marked as solved
0 Replies
278 Views
Is it possible to have limited in-app quantity of an iap item? Let's say I have a premium gun which is limited to 100 quantity for the whole game. Is it possible to show the quantity remaining and hide once 100 quantity gets sold? Thanks!
Posted
by
Post not yet marked as solved
0 Replies
205 Views
Hi all, how do I get the originalTransactionId in an app after completed purchase. I use SubscriptionStoreView and onInAppPurchaseCompletion but I don't know how to extract the originalTransactionId. Any help would be appreciated. From https://developer.apple.com/documentation/appstoreserverapi/originaltransactionid I read "To get the original transaction identifier from your app, use the originalID property of the Transaction object that represents the in-app purchase. " So I assume I can extract the originalTransactionId in the code following onInAppPurchaseCompletion Regards Thomas S
Posted
by
Post not yet marked as solved
1 Replies
1.9k Views
Hello, I'm encountering an issue where my released app fails to launch only on iOS 17.4. The version of the app released through TestFlight works fine without any issues. Specifically, when the app installed from the App Store is launched on iOS 17.4, it immediately crashes. However, I've noticed the following: If I turn off the network connection, such as putting the device in Airplane Mode, the app launches successfully. Once the app is launched, I can re-enable the network connection, and the app continues to run without crashing. My app uses StoreKit2 for handling transactions and connections with the App Store. It initiates a connection to the App Store via StoreKit2 at launch. The primary difference between the TestFlight version and the production version is the App Store endpoint they connect to. This leads me to suspect that there might be an issue with the connection to the App Store. (Another possibility is that the app communicates with Firebase or Google Admob, so there could be an issue with these SDKs as well.) This issue only occurs in the production version, making it difficult to investigate. Are there any suggestions on what I can do to further diagnose this issue? You can download my app from here: https://apps.apple.com/us/app/repeatable-player-cut-loop/id616310281 I can provide the TestFlight URL if needed. Any help or guidance would be greatly appreciated.
Posted
by
Post not yet marked as solved
0 Replies
434 Views
Hey all, Tl;dr: In debug-build using a .storekit file, all is good. When uploading a release build to testflight, purchasing follow the expected flow, but it does not seem the subscription status is reflected. Full description I've implemented auto-renewable subscriptions in my app. In debug, I've setup a scheme to use products.storekit. When I run this scheme on my device, I can subscrbe, cancel etc, and it all works as expected. When I upload my release scheme to testflight, purchasing can be done, but changes do not seem to be reflected inside the app (i.e.e no features get unlocked). Same thing when I run this scheme on my device. This scheme has None set for Storekit configuration. When I set 'Storekit configuration' to the Products.storekit, file I can't make a purches at all: SubscriptionStoreView shows Subscription unavailable. The subscription is unavailable in the current storefront'. I've watched a number of WWDC sessions on the topic, read most (all?) of the documentation, and I just can't seem to find out what it is that needs to be done. Unfortunately, debugging has been rather cumbersome and sometimes impossible lately, especially when trying to debug a release build. Anyone can tell me what it is I am overlooking, or what piece of information or link I missed? Here is the relevant code (which works OK in debug) private func listenForTransactions() -> Task<Void, Error> { return Task.detached { for await anUpdate in Transaction.updates { do { let transaction = try self.checkVerified(anUpdate) await self.checkSubscriptionStatus() await transaction.finish() } } } } And here's checkSubscriptionStatus(): @MainActor private func checkSubscriptionStatus() async { var hasActiveSubscription = false do { for aStatus in try await Product.SubscriptionInfo.status(for: "718A7488") { let state = aStatus.state hasActiveSubscription = (state == .inGracePeriod) || (state == .subscribed) } } catch { print(error) } self.hasActiveSubscription = hasActiveSubscription }
Posted
by
Post not yet marked as solved
1 Replies
305 Views
Hi all, I've implemented in-app purchases to have auto-renewing subscriptions. However, when I use Xcode's Transaction Manager to expire a subscription, there seems to be no callback Transaction.updates or Transaction.currentEntitlements I can see an expiry through Transaction.all but I can't imagine the intention is to poll that while the app is running. What am I missing here?
Posted
by
Post not yet marked as solved
0 Replies
305 Views
I need to test my app's in-app purchases and I am having a very difficult time with it. I created a "Sandbox" account in App Store connect. Doing so was very frustrating. It told me multiple times that my password was "too simple", without ever explaining what the password rules are. Then it emailed me something so I could "validate" the account. Luckily I used an actual email address that I can receive mail on. I was expecting I could use any fake address since this is a Sandbox account. After that I started an iOS simulator and tried to log in. It immediately asked me to turn on two-factor authentication. I declined, and then it just said "User name or password is incorrect". I tried a couple more times, then I went back and actually turned on the two factor auth. But that didn't solve the problem. I still cannot log in. Can someone explain what I'm doing wrong here?
Posted
by
Post not yet marked as solved
2 Replies
251 Views
Hi All, I have this code and I would like to send the payload of the completed purchase to my server. How to do that? Regards Thomas S ` SubscriptionStoreView(...) .onInAppPurchaseCompletion { product, result in if case .success(.success(let transaction)) = result { print("Purchased successfully: \(transaction.signedDate)") // this looks good in Xcode // Pass payload of transaction to my server let url = URL(string: "... my server ...")! var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = ???? // how do I pass the payload of the transaction to the httpBody request.setValue("application/json", forHTTPHeaderField: "Content-Type") let task = URLSession.shared.dataTask(with: request) { data, response, error in let statusCode = (response as! HTTPURLResponse).statusCode if statusCode == 200 { print("SUCCESS") } else { print("FAILURE") } } task.resume() } else { print("Something else happened") } } `
Posted
by
Post not yet marked as solved
1 Replies
221 Views
Hi All, I use the SubscriptionStoreView and it displays a cancellation button in the upper right corner. How do I set a value of a local variable and dismiss the SubscriptionStoreView when the user clicks the cancellation button? Right now it seems as if the cancellation button is disabled and nothing happens when I press the cancellation button. Any help is appreciated. Regards Thomas S
Posted
by
Post not yet marked as solved
0 Replies
290 Views
Transaction.updates When testing on TestFlight, Transaction.updates emits payments that occur on the same device (usually within 1min after payment is finished), contradicting the docs: The asynchronous sequence that emits a transaction when the system creates or updates transactions that occur outside of the app or on other devices. Transaction.unfinished When testing on TestFlight, Transaction.unfinished contains finished payments from a different device (same App Store account). Docs: A sequence that emits unfinished transactions for the user. Both issues do not happen when testing with Xcode. Xcode 15.2, iOS 17.3, 17.4
Posted
by
Post not yet marked as solved
0 Replies
343 Views
Hi, We have Apple Search Ads for promoting our app. We are able fetch attribution records of those ads using AAAttribution inside app and send to our server and it is working fine. Now, we would like to receive Install-validation postbacks by setting NSAdvertisingAttributionReportEndpoint into our domain name and configured the server to receive HTTPS POST messages as per the instruction provided in the below link. Steps done Set NSAdvertisingAttributionReportEndpoint into our domain name Our server configured the server to receive HTTPS POST messages Made sure that updatePostbackConversionValue is called while app is launched. https://developer.apple.com/documentation/bundleresources/information_property_list/nsadvertisingattributionreportendpoint In our live app, we are keep getting attribution records using AAAttribution inside our app and sending them to our server. It is working fine now. But we did not receive any Install-validation postback yet into the URL provided in NSAdvertisingAttributionReportEndpoint. To troubleshoot this, i clicked Apple Search Ad of our app in App Store, i installed our app and opened it and in the console i can see the below error message. Error updating install attribution pingback for app: <OUR_APP_ID>, error: Error Domain=ASDErrorDomain Code=1208 "SKAdNetwork: No pingbacks found while attempting to register/ update." UserInfo={NSLocalizedDescription=SKAdNetwork: No pingbacks found while attempting to register/update.}, result: 0 I have attached console log for your reference. Is there any step i am missing to receive Install-validation postback in our server? Is there any way to validate domain name provided in nsadvertisingattributionreportendpoint so that we can check if any server configuration issue? Please advise on getting Install-validation postback in our server, Thank you.
Post not yet marked as solved
2 Replies
489 Views
I Implement a 'SubscriptionStoreView' using 'groupID' into a project (iOS is targeting 17.2 and macOS is targeting 14.1).Build/run the application locally (both production and development environments will work fine), however once the application is live on the AppStore in AppStoreConnect, SubscriptionStoreView no longer shows products and only shows 'Subscription Unavailable' and 'The subscription is unavailable in the current storefront.' - this message is shown live in production for both iOS and macOS targets. There is no log messages shown in the Console that indicate anything going wrong with StoreKit 2, but I haven't made any changes to my code and noticed this first start appearing about 5 days ago. I expect the subscription store to be visible to all users and for my products to display. My application is live on both the iOS and macOS AppStores, it passed App Review and I have users who have previously been able to subscribe and use my application, I have not pushed any new changes, so something has changed in StoreKit2 which is causing unexpected behaviour and for this error message to display. As 'SubscriptionStoreView' is a view provided by Apple, I'm really not sure on the pathway forward other than going back to StoreKit1 which I really don't want to do. Is there any further error information that can be provided on what might be causing this and how I can fix it? (I have created a feedback ticket FB13658521)
Posted
by
Post not yet marked as solved
0 Replies
237 Views
Good day! I have a problem with the presentCodeRedemptionSheet method. The problem is that when the sheet for entering the promotional code appears, I fill in the field with the promotional code and when I click the Redeem Code button, nothing happens, the button becomes inactive for some time, but nothing else happens, the promotional code is not applied. However, this behavior occurs every now and then, that is, to put it simply, this flow works only 1 time out of 5 times, and then not always. Please tell me, has anyone encountered this problem before?
Posted
by
Post not yet marked as solved
0 Replies
246 Views
The StoreKit offerCodeRedemption(isPresented:) view modifier is working fine for redeeming consumable offers from App Store Connect, but it fails to redeem subscription offers: When this same offer code is used in the App Store redemption flow, everything works as expected. So there's nothing wrong with the offer codes. Is there a trick to using offerCodeRedemption(isPresented:) when working with subscriptions rather than consumables? Any help is appreciated!
Posted
by
Post not yet marked as solved
0 Replies
274 Views
Testing in-app purchase with the App Store Connect / Xcode configuration file allows testing of approximately 43 non-consumable product errors. Testing my non-consumable in-app purchase with the new ProductView on my app for these various faults doses’t appear to be very promising. Loading errors: all result in a blank screen and spinning wheel without producing an alert. Purchase errors: Some of these work producing readable comments. Purchase (not entitled) and Purchase (system error) just produce alert boxes with an OK button. Purchase (network) produces the alert “The operation could not be completed. (NSURLErrorDomain error - 1009.)” Verification errors, App Store Sync, and App Transaction errors: These purchases are successful and produce no alerts. Am I missing a method that handles these errors? With ProductView do I need to use a do / catch block in my viewModel? Can I release this app for review working like this? struct StoreView: View { @StateObject private var store = StoreModel() var body: some View { GeometryReader { g in VStack { Spacer() Text("\(mainTitle)") .font(.title) .padding(.bottom, 25) ProductView(id: "xxxx") { _ in Image(systemName: "xxxx") .resizable() .scaledToFit() } placeholderIcon: { ProgressView() } .productViewStyle(.large) .overlay( RoundedRectangle(cornerRadius: 20) .stroke(Color( "darkGreen"), lineWidth: 5) .frame(width: g.size.width * 0.90, height: g.size.height * 0.35 ) ) .frame(maxWidth: .infinity, alignment: .center) Spacer() } } } } @MainActor final class StoreModel: ObservableObject { @Published private(set) var products: [Product] = [] @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = [] @Published var gotRefund = false // use to reset purchase private var updates: Task<Void, Never>? private var productIDs = ["xxxxx"] // app wide purchase status @AppStorage(StorageKeys.purStatus.rawValue) var storeStatus: Bool = false init() { updates = Task { for await update in StoreKit.Transaction.updates { if let transaction = try? update.payloadValue { await fetchActiveTransactions() // check transaction.revocationDate for refund if transaction.revocationDate != nil { self.storeStatus = false // requesting refund self.gotRefund = true } else { self.storeStatus = true // making purchase } await transaction.finish() } } } } // end init deinit { updates?.cancel() } // update the current entitlements func fetchActiveTransactions() async { var activeTransactions: Set<StoreKit.Transaction> = [] for await entitlement in StoreKit.Transaction.currentEntitlements { if let transaction = try? entitlement.payloadValue { activeTransactions.insert(transaction) } } self.activeTransactions = activeTransactions } }
Posted
by
Post not yet marked as solved
0 Replies
310 Views
I had everything working with Revenue Cat. Then my app got rejected for not loading subscriptions, which was odd because a previous built was rejected for wording on that same paywall. I checked, and realised I suddenly can't fetch products in testFlight either. I can only see products in Xcode using the store kit configuration file. I've found many issues like this online and everybody point to the same solutions (that seem to work for most), but here's what I tried so far: Checked that all my agreements in App Store Connect are active Checked that ids match between Xcode / revenue cat / App Store connect Store kit config file is syncing with App Store Connect correctly I removed revenue cat and used the store kit api directly to fetch products. The array of products is empty in all environments that don't have access to store kit config file. Checked status of all subscriptions (all waiting for review -- as they were when the paywall worked) Nothing seems to work... Any suggestions? Many thanks
Posted
by