StoreKit

RSS for tag

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

StoreKit Documentation

Posts under StoreKit tag

345 Posts
Sort by:
Post not yet marked as solved
6 Replies
1k Views
Does anyone know how to test failed IAP flows? There is documentation here: https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox/testing_failed_in-app_purchases_and_subscription_renewals which starts with the instructions Sign in to the App Store using a Sandbox Apple ID. Go to Settings > App Store > Sandbox Account > Manage > Account Settings. Disable the Allow Purchases & Renewals setting. This setting is enabled by default. But there is no "Allow Purchases & Renewals" option (or any other option) in the Accounts settings
Posted
by
Post marked as solved
3 Replies
1.3k Views
I am trying to test Ask to Buy for consumable product. When the transaction was approved, I can get verified transaction in Transaction.updates, meaning the process stops at a breakpoint in Transaction.updates. However, when I call Transaction.currentEntitlements after that, I cannot handle transaction as consumable. Am I missing anything? Here is my code. func newTransactionListenerTask() -> Task<Void, Never> { Task(priority: .background) { for await verificationResult in Transaction.updates { guard case .verified(let transaction) = verificationResult,             transaction.revocationDate == nil         else { return }         // Breakpoint stops here await refreshPurchasedProducts()         await transaction.finish()         }    } } func refreshPurchasedProducts() async { for await verificationResult in Transaction.currentEntitlements { guard case .verified(let transaction) = verificationResult else { return } switch transaction.productType { case .consumable: // This code is not called             updateConsumablePurchaseCount(productID: transaction.productID)         default:             break }     } }
Posted
by
Post not yet marked as solved
1 Replies
1.6k Views
I am using local receipt validation and the SKReceiptRefreshRequest API to restore purchases. When my iOS 16.1 users tap "restore purchase", the call fails. Notably, it does not ask the users to log into their iTunes account (it normally does) and my app logs the following error: <SKReceiptRefreshRequest: 0x281b0ad20>: Finished refreshing receipt with error: Error Domain=ASDErrorDomain Code=603 "Request throttled" UserInfo={NSLocalizedFailureReason=Unified receipt is valid and current, NSLocalizedDescription=Request throttled, AMSServerErrorCode=0} These errors started showing up after the release of 16.1 and appear to be limited to that specific version of iOS. The relevant code has not changed in years and I have iOS 15 and iOS 16.0 users who are currently able to restore purchases without issue. Also, I am not able to reproduce the issue in the sandbox but I am able to do so in production. I'm a bit at a loss. Why would my request be "throttled" and, further, why only on iOS 16.1 and only in production? Any thoughts on what could be happening here? Any help is much appreciated. Thank you!
Posted
by
Post not yet marked as solved
5 Replies
1.1k Views
Hi, I'm trying to locate the app store receipt in the apple watch in order to send it to my server to verify. I'm using the Original API for In-App Purchase because my app needs to support watchOS 7.0 My watchOS app is an independent app still pending to release to App Store. I'm testing using TestFlight, and I have one-month subscription in state "Ready to submit". The process of purchase finishes fine in the apple watch -sandbox success screen- but then, when I invoke SKReceiptRefreshRequest in order to ask Apple for the Bundle.main.appStoreReceiptURL to download in my device and send it to my server to verify, I always get the following error: <SKReceiptRefreshRequest: 0x16df52c0>: Finished refreshing receipt with error: Error Domain=ASDErrorDomain Code=507 "Invalid container for application: file:///private/var/containers/Bundle/Application/8BAF3B0E-45FC-465A-92E2-CFB1507A33E1/IvooxAppleWatch%20WatchKit%20App.app/PlugIns/IvooxAppleWatch%20WatchKit%20Extension.appex/" UserInfo={NSDebugDescription=Invalid container for application: file:///private/var/containers/Bundle/Application/8BAF3B0E-45FC-465A-92E2-CFB1507A33E1/MyApp%20WatchKit%20App.app/PlugIns/MyApp%20WatchKit%20Extension.appex/} And the receipt isn't there, in the path Bundle.main.appStoreReceiptURL. I tried looking for both receipt and sandboxReceipt. No luck. I even tried to list the contents of StoreKit but that directory does not exist on my watch according FileManager. I cannot get rid of the error above. Any help is highly appreciated. Thanks in advance.
Posted
by
Post not yet marked as solved
2 Replies
2.0k Views
I've been reading through the documents and I feel like each particular doc jumps over the piece of info that I need. It explains how to create a Sandbox Test User and I've done that. I did plenty of IAP work many years ago, but things have changed enough that it's not working as I expect. When I read the docs, I feel like I can't find the missing piece. When I test in a simulator with Xcode, it does my IAP transactions in the Xcode environment. That makes sense. https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox This page says: To run your app using your Sandbox Apple ID, build and run your app from Xcode. But when I test on a physical device installing directly from Xcode, it also does my IAP transactions in the Xcode environment. I thought this would put me in the Sandbox environment, but it didn't. The initial purchase view controller didn't reference the Sandbox at all and the "You're All Set" alert shows Environment: Xcode. When I put a test build into TestFlight and test using that version, my IAP transactions are happening in production using my real Apple Id (but I'm not actually charged for any transactions). I assumed that testing in TestFlight would give me the Sandbox environment, but it happens in the Production environment. (The purchase view controller doesn't show Sandbox and the "You're All Set" alert doesn't show Sandbox.) So, how do I test in the Sandbox? Every way I try to test either puts me in the Xcode environment or the Production Environment. What am I missing? I've logged out of my real Apple id on my phone and then logged in with the Sandbox User credentials I created in AppStore Connect. But when I tried to test the app, by installing from Xcode it still says I'm in the Xcode test environment. When I create transactions, those transactions show up in the Xcode Transaction Manager. How do I test in the Sandbox environment?
Posted
by
Post not yet marked as solved
11 Replies
2.2k Views
Hi community: When I try to access subscription settings, after filling in my sandbox user's password, a message "cannot connect" appears, just like below. Does someone know about a workaround to cancel or reset the subscription from the iPhone? Thanks in advance
Posted
by
Post not yet marked as solved
11 Replies
2.7k Views
I'm using the iOS simulator with a StoreKit configuration file. I can see that there have been transactions while the app has been closed, but my StoreKit 2 listener is never called with those updates to be able to finish them When I open my app from a cold start. I've added a listener on application(_:didFinishLaunching:launchOptions:) like this: func startObservingTransactions() { task = Task(priority: .background) { for await result in Transaction.updates { if case .verified(let transaction) = result { await transaction.finish() } } } } But the Transaction.updates loop never gets called (have added breakpoints to check). It's only ever called when a purchase is made, or subsequent transaction renewals when the app is open. Only then it will get the previously unfinished transactions. Steps to reproduce: Create an app with a StoreKit config file (with sped up transactions) to purchase an item Make a purchase then quit the app Wait for a bit for more transactions to be made while the app is closed. Open the app from a cold start and none of the transactions will be finished by the listener in your app. Cancel the subscription via the transaction manager. Close and open the app from a cold start. The first transaction will be finished by the listener but none of the others will be. In Apple's docs it says If your app has unfinished transactions, the listener receives them immediately after the app launches Why is this not the case?
Posted
by
Post not yet marked as solved
2 Replies
570 Views
Hello! I am working on a reader app that supports 4 languages: Danish, Finnish, Norwegian, and Sweeden. The content's language is either the phone's preferred language, if it is one of the 4, or is chosen by the user on the first app start. As per Apple's guideline, I implemented the ExternalLinkAccount flow, and for localizing the URL I added SKExternalLinkAccount into the Info.plist file. For debug purposes, the default URL is google.com. This is how it looks: <dict> <key>dk</key> <string>https://historienet.dk/abonnement</string> <key>fi</key> <string>https://historianet.fi/tilaa</string> <key>no</key> <string>https://historienet.no/abonner</string> <key>se</key> <string>https://varldenshistoria.se/prenumera</string> <key>*</key> <string>https://google.com</string> </dict> I have a question and a problem: The problem is that on iOS 16+, the URL opened after pressing the Continue button on the ExternalLinkAccount sheet is always the default one, regardless of the app's and phone's language and region. On what does it rely to display the localized/regional version of the URL? For debugging it, I tried obtaining the Norway or Sweedan URL on the simulator by changing the app's language and region from the scheme, the simulator's language and region from Settings, and tested it on a real device with the region and language changed to Norway/Finland using a TesFlight build. I had no success. Can I control/influence which region is selected? Because the user can select a language for the app content, it would be ideal to reflect it on the URL opened by the ExternalLinkAccount flow.
Posted
by
Post marked as solved
5 Replies
3.5k Views
Dear all, This is my first post in this forum - and, in fact, my first app, too! I'm glad to be here, and thanks in advance for your help. I'm looking to offer an app for a one-time payment. I'd also like people to be able to try the app for a week. It seems that the "Pricing and Availability" section in App Store Connect is not the right place to configure this kind of offer. It does allow me to set a one-time price, but I cannot find a trial-period there (or am I missing something?) Two different strategies seem possible here: Using in-app-purchases: make the actual app free, but ask users after a week to buy a non-consumable IAP. The problem with that: I need to verify that the app has been installed for seven days ... even if it has been uninstalled at some point. Using subscriptions: There is a "free trial" option for subscriptions. But after that free trial, subscriptions are being payed periodically. I'd rather have the user only pay once for lifetime-access. Some apps seem to use strategy 1 - I believe the "Lap swim" app does. But still it seems like a bit of a hack - is there a more elegant way to achieve this?
Posted
by
Post not yet marked as solved
11 Replies
5.8k Views
Hi, thanks for reading my question. I need help with some odd behaviour with product.purchase() not triggering a confirmation dialog after a subscription has expired and trying to purchase it again. Seeing this in iOS 16.2 and 15.7.2 (haven't tried any other versions) on actual devices, not in simulator. I'm using a sandbox user on the sandbox environment (not using the local store kit config file testing option). Using a newly created sandbox user, first subscription purchase goes through just fine, dialog box pops up, login with sandbox user, get confirmation of purchase and then Transaction.currentEntitlements has one item as expected. It auto renews for 12 times (each time Transaction.currentEntitlements contains the correct results) and then expires, as expected for sandbox. Transaction.currentEntitlements is then also empty, as expected. All good so far. Now I want to test purchasing it again...Call product.purchase() again to renew/start a new subscription and nothing happens, no confirm purchase dialog box pops up at all. The purchase function simply exits BUT returns success (as in the following gets called) but in self.updatePurchasedProducts(), Transaction.currentEntitlements is empty. case let .success(.verified(transaction)):      // Successful purchase       await transaction.finish()      await self.updatePurchasedProducts() if I instead go to Settings->App Store->Sandbox User-> Manage Subscriptions and renew the subscription there, instead of in my app, then Transaction.currentEntitlements has a new entry and all is good again. Alternatively, if I create yet another new sandbox user and logout of the old one I was using, I am once again able to purchase from within the app, so .purchase() once again works as normal. Is there something I am missing about expired subscriptions and trying to purchase them again in the app? Is this a sandbox issue and in production I'll have no problem? The sandbox user has purchasing enabled in Settings->App Store. I've also tried calling AppStore.sync() (which is in my "Restore Purchase" button) before calling product.purchase() after the subscription stops renewing, expires and this issue comes up, doesn't resolve it. Also have a less important question, the initial call to product.purchase(), the one that works as expected, has a bit of a delay before the confirmation dialog pops up, a few seconds, which will probably result in the user clicking the buy button again thinking it didn't work. Is a bit of a delay normal for sandbox? Will it be ok in production? When it fails, and I have to renew in Settings->AppStore->Sandbox user, there's also a bit of a delay after I return to my app, 5-15 or so seconds, before the transaction observer fires and currentEntitlements is checked again, is there a way to reduce this delay? Thank you! Colin @MainActor class IAPManager: NSObject, ObservableObject {  // removed other functions.....   func purchase(_ product: Product) async throws {    let result = try await product.purchase()     switch result {    case let .success(.verified(transaction)):      // Successful purchase       await transaction.finish()      await self.updatePurchasedProducts()    case let .success(.unverified(_, error)):       break     case .pending:       break     case .userCancelled:       break     @unknown default:       break   } }  func updatePurchasedProducts() async {     for await result in Transaction.currentEntitlements {       guard case .verified(let transaction) = result else {         continue       }       if transaction.revocationDate == nil {         self.purchasedProductIDs.insert(transaction.productID)       } else {         self.purchasedProductIDs.remove(transaction.productID)       }     }   } }
Posted
by
Post not yet marked as solved
4 Replies
1.4k Views
Hey, in our app we show post-purchase flow when a user purchases a subscription and its appearance should depend on the type of purchase: upgrade, downgrade or crossgrade. I found a way how to get this type on the backend side but can not figure out how to get this within the app. I see that Transaction has isUpgraded property but it is always false even if I move from a lower service plan to a higher one. So, I have two questions: Is this actually possible to know on the client when the user upgrades, downgrades or crossgrades? If yes, then how? Thanks
Posted
by
Post not yet marked as solved
2 Replies
775 Views
When performing StoreKit 2 operations in my Mac app, even very simple ones like AppStore.sync() a small but steady percentage of users experiences the following error: systemError(Error Domain=NSCocoaErrorDomain Code=4097 \"connection to service named com.apple.storekitagent\" UserInfo={NSDebugDescription=connection to service named com.apple.storekitagent}) Rebooting or re-downloading the App from the Mac App Store, as well as signing out and in again on the Mac App Store does not solve the issue. Strangely enough all of the affected users I'm aware of were initially able to perform In-App purchases via the said app. Without any updates or anything else however suddenly after a restart of the app the above error gets thrown by the system. As I have not found any way to solve this issue yet and the said customers are consequently left without service even though they are paying customers, I am desperate to find help with this issue. Given the simplicity of the AppStore.sync() method for example I can't imagine the solution to lie in code, but I might of course be wrong. Thank you, Bastian
Posted
by
Post marked as solved
7 Replies
1.5k Views
Hi I'm using presentCodeRedemptionSheet() method to display a sheet that enables users to redeem subscription offer codes. When calling the SKPaymentQueue.default().presentCodeRedemptionSheet() method on real app store app it will present redemption sheet and after entering the offer code in it is displaying screen where Cancel and Redeem buttons do not work. As seen in the attached picture. What could be the reason for this and what solutions can be found to solve the problem? Please someone help it is really frustrating. Environment: iOS 16.3
Posted
by
Post not yet marked as solved
2 Replies
1.3k Views
The sample code provided in https://developer.apple.com/wwdc21/10114 doesn't appear to call finish() on unverified transactions, and I haven't been able to find any documentation regarding what to do with unfinished transactions. However, Apple has always emphasized the importance of finishing transactions, and since a transaction object is provided even with the unverified state, I'd love some guidance!
Posted
by
Post not yet marked as solved
4 Replies
1.1k Views
Hi, Apple just rejected my app because IAP is not working. It was working yesterday of course but today I am receiving this error: ["msg": "Purchase failed. Error Domain=SKErrorDomain Code=0 \"An unknown error occurred\" UserInfo={NSLocalizedDescription=An unknown error occurred, NSUnderlyingError=0x2810320a0 {Error Domain=ASDServerErrorDomain Code=3004 \"We are temporarily unable to process your request.\" UserInfo={NSLocalizedDescription=We are temporarily unable to process your request.}}}", "error": Error Domain=SKErrorDomain Code=0 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred, NSUnderlyingError=0x2810320a0 {Error Domain=ASDServerErrorDomain Code=3004 "We are temporarily unable to process your request." UserInfo={NSLocalizedDescription=We are temporarily unable to process your request.}}}] I know that yesterday App Store - Receipt Verification - was in outage. I am wondering if this is same problem? Does it work for you?
Posted
by
Post not yet marked as solved
2 Replies
685 Views
I do the following steps: Connects to the sandbox account through the settings Installing the app from a test flight Trying to subscribe to the app payment pop-up opens with my Apple ID and not with the sandbox account
Posted
by
Post not yet marked as solved
1 Replies
743 Views
Hi We are adding subscriptions auto-renewable in our app. Subscription will be one month, auto-renewable. We are testing in sandbox. Results of Transaction.currentEntitlements and Transaction.all are sometimes not available : for example, we buy a subscription, we always well receive the hook sent (responseBodyV2DecodedPayload is correct), so subscription is active BUT when we ask Transaction.currentEntitlements, result is no subscription available, neither to Transaction.all (for this one, last transaction is not present) - this problem happens in around 20% of tests. possibly, once the auto-renew is done (5 minutes later, because of sandbox) everything works fine OR not Is it possible that in sandbox, behavior is a bit "random", not 100% safe ? Thanks for any help :)
Posted
by
Post not yet marked as solved
1 Replies
880 Views
I am working on implementing in-app purchases for my iOS app, specifically auto-renewable subscriptions. I've been trying to understand the differences between transaction.webOrderLineItemID and transaction.id and how they can be used in managing subscriptions. Both of these properties seem to provide unique identifiers for transactions, but I am unclear about the specific benefits of using webOrderLineItemID over id. Can you please provide clarification on the following points? What are the exact use cases where using webOrderLineItemID is more beneficial than id when managing auto-renewable subscriptions? Can different transactions have the same value for webOrderLineItemID? If not, how does it provide additional granularity or context compared to id? I appreciate any insights you can provide on this topic, as I want to ensure that I am using the appropriate identifiers for managing auto-renewable subscriptions in my app. Thank you!
Posted
by
Post not yet marked as solved
1 Replies
583 Views
I have looked high and low all and cannot find an answer or solution. I have an app that is primarily used by Mac users. I am looking to implement promo codes in the coming weeks. The presentCodeRedemptionSheet call does not work for Mac Catalyst apps saying "This function doesn’t affect Mac apps built with Mac Catalyst" in the documentation. AppStore.presentOfferCodeRedeemSheet also does not work for Mac Catalyst. If it is the case that you can't redeem in-app, that is fine if I can direct users to the Mac App Store to redeem promo codes, but I cannot find a way to do that either. I can only find a route to redeem gift cards. I have also tried clicking on a specific promo code link (something like https://apps.apple.com/redeem?ctx=offercodes&id=00000000&code=PROMOCODE) and that just redirects to the Gift Card redemption screen in the Mac App Store. So is there any way for a Mac only user (a user that does not have an iPhone or iPad) to use app promo codes? Thanks!
Posted
by