StoreKit

RSS for tag

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

StoreKit Documentation

Post

Replies

Boosts

Views

Activity

Unfinished transactions not being emitted on start of app
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?
14
2
4.0k
Dec ’22
Subscription is still waiting-for-review while app update is deplyoed
Hi. A couple of days ago I've released my new iOS app version update including subscription(in-app-purchase). App is successfully updated and deployed on store but App Store Connect says subscription is still waiting-for-review, and users cannot subscribe our paid features. (API says it's an error; invalid product id) On App Store Connect, it says "A subscription has been returned and is highlighted in the table below." in subscription section. But there's no clue what am I supposed to do next to fix this. I even have received the E-Mail says app update and subscription is successfully reviewed. I am so confused can anyone help me with this?
6
0
1.8k
Dec ’22
Can you preserve "early adopter pricing"?
Hi there, This may be a dumb question but I'm trying to confirm this before we go down this road. Essentially, we are looking to have one premium subscription available to users within our app. A yearly subscription and that's it. Since our app is brand new, we are looking to offer some "early adopter pricing" where early users can sign up for premium and lock in their yearly subscription at a fraction of what we will eventually charge. Will we be able to preserve pricing for select users while also making sure that new users aren't able to access that price? I was reading some articles and saw mention of "unpreserving users subscription pricing" so I figured there must be the functionality to preserve then? Thanks for the help!
2
0
988
Dec ’22
StoreKit2: .purchase() not working after expiry of subscription in app, but renewing in AppStore sandbox does...
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)       }     }   } }
12
8
7.7k
Jan ’23
How to know when user upgrades/downgraded/crossgrades their subscription on the client
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
4
1
1.8k
Jan ’23
presentCodeRedemptionSheet issue
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
8
2
2k
Feb ’23
Suddenly receiving error 21002 from verifyReceipt endpoint for sandbox receipt
We started getting error code 21002 from the verifyReceipt endpoint today for any receipt passed to it: {     "status": 21002 } I have created a new sandbox tester, and cleared and reinstalled the application before attempting the purchase. I'm seeing this response through RevenueCat's receipt validation tool (https://www.revenuecat.com/app-store-receipt-validation/) and from a direct cURL command: curl --location --request POST 'https://sandbox.itunes.apple.com/verifyReceipt' \ --header 'content-type: application/json' \ --data-raw '{     "receipt-data":"{RECEIPT_DATA}",     "password":"{SECRET}",     "exclude-old-transactions":"false" }' This was working as of yesterday, but seems to be failing for anyone who has tried today. Has anyone else run into issues with this?
28
7
7.9k
Feb ’23
StoreKit 2 - Is it necessary to finish unverified transactions?
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!
2
0
1.7k
Feb ’23
Clarification on the use of webOrderLineItemID vs. id for managing auto-renewable subscriptions
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!
1
0
1.3k
Mar ’23
Free Trial Billing Errors
Hi everyone! I recently launched a hard paywall in my app and have had a fair few people each day sign up to the 3 day free trial. Around 55% of users remain after the 3 day trial but I am finding that when it ends around 20-25% of the users are running into 'billing errors' meaning they end up not paying at the end of the trial despite never cancelling. The information coming through from Apple ranges from 'insufficient credit', 'incorrect details' and 'unknown' - but the % is way higher than Has anyone come across this issue before at such a high % and if so, is there anything we can do to try and improve this number? Thank you!
2
0
782
Mar ’23
No way to close the page for subscription Offer Code processing invoked by "SKPaymentQueue.default().presentCodeRedemptionSheet()".
Problem was observed on the published App because the IAP offer code processing is only done on the published App. I implemented the ".presentCodeRedemptionSheet()" command by tapping Offer Code button. Then the typed in offer code was processed by this page and the offer was successfully purchased from AppStore. After detecting the purchased status, executed "SKPaymentQueue.default().finishTransaction(transaction)". I thought that finishTransaction() would close the offer code processing page that is not written in document. However the page for offer code processing page was still displayed and was never closed. The only way to close the input page for offer code is to manually tapping "Cancel" button. Since there is no way to close the offer code processing page, I added ".popToRootViewController(animated: true)" in order to close all pages and views after detection of purchased status but failed to close the offer code processing page managed by Apple. I need to know how to programmatically close the offer code processing page invoked by ".presentCodeRedemptionSheet()". I got no response from FeedBackAssistant and Apple Developer Technical Support. Thank you.
4
0
504
Apr ’23
App Store Server Notifications not always sent to my server
We have implemented an auto-renewing subscription as an in-app-purchase for our iOS application. We are consuming the App Store Server Notifications for subscription transactions in order to update the user's account (and thereby maintain their 'Pro' access to our application). Sometimes those notifications never come to our server, and there is no evidence that they were even attempted to be sent to us. We have had some users report to our Customer Support team that they have successfully made a purchase of the subscription, but that they were not granted 'Pro' access. For the large majority of users this is not happening and all is well, but for some users the notifications just never come from the App Store Server API. We keep a record of all notifications that we receive from the App Store, and for these users we never received the "SUBSCRIBED" event. We have checked the Notification History API and there are no reports of any failure to send notifications to our server. We have checked our server logs for any sign of failure to receive incoming web requests, and there is no sign of these missing notifications. We have verified that our server supports ATS. We are keeping the transaction.originalID for all our users who are subscribed to the auto-renewing subscription. We have used this value to do some lookups into the transaction history and subscription status of the users who are being affected. Here is an example of our findings from those lookups: From the transaction history endpoint, we received an error: “Invalid transaction identifier”. From the subscription status endpoint, we received a response with the information for that user's active, valid subscription. We never received any App Store Server Notifications about this user’s subscription, and the transaction history API tells us it is an invalid transaction ID. We believe that the fact that the subscription status API returned the information showing that this user’s subscription is active and valid, and that the notification history API shows no sign of a failure to send us notifications about that subscription, shows that the App Store Server API never attempted to send us any notification for this user’s subscription. The same is true for a significant number of other users of our service. Can anyone help us determine what is going on, and how to best support these customers? It seems as though there was never an attempt to send these notifications to our server, but our users provide proof that they do in fact have an active subscription, for which they have paid (receipt email from Apple with a valid order ID).
8
7
2.7k
Apr ’23
Transaction fails with ASDServerErrorDomain Code=3504 in production
Suddenly in-app purchases of already productive IAPs are failing in the production with the error code: NSUnderlyingError=0x283b42e20 {Error Domain=ASDServerErrorDomain Code=3504 "找不到此项目。" UserInfo={NSLocalizedDescription=找不到此项目。}}} Environment: Production OS Version:iOS 15.6.1 I confirm that there have been no prohibited sales areas for the product I want to know What does Code 3504 represent? Where can I find documentation related to error codes?
10
5
2.3k
Apr ’23
Problem with presentCodeRedemptionSheet() not displaying keyboard
Hello, I have a question regarding the presentCodeRedemptionSheet() function. I'm implementing an offer code input feature in a Unity app and currently testing it on TestFlight. However, when I tap the text field, the keyboard doesn't appear as shown in the attached image. If I flip the iPhone, the keyboard then shows up. Is this phenomenon specific to TestFlight or Sandbox? I am testing this issue on the following two environments, and the problem occurs in both cases. The iPhones are models sold in Japan. iPhone 11 Pro, iOS 16.4.1 iPhone 12 Pro, iOS 15.7.1 Any help or suggestions would be greatly appreciated. Thank you!
1
1
604
Apr ’23
Auto-Renew of Subscription stops after 12 renews, how to allow more autorenew in TestFlight?
I'm trying to test my IAP subscriptions. Testing in the Sandbox environment has been kind of a nightmare, because it sometimes returns out of date information on the phone and the server events are either never delivered or delivered very late, etc. It makes testing nigh impossible. So I'm trying to test via TestFlight, because that is, at least, working in the production environment and delivers events more quickly and seems to return the correct data when I query the StoreKit2 APIs. However, it's using my REAL Apple Id for the purchases (no charges though), and after it has auto-renewed my subscription 12 times... then it NEVER will auto-renew ever again (as far as I can tell). Even if I resubscribe, the subscription immediately dies at the end of that period. It's making testing impossible again... and I can't find a way to clear TestFlight purchases similar to the way I can clear purchases for Sandbox users. At this point, I have -no- confidence that my Apple IAP Subscriptions are going to work correctly and I don't seem to have a way that I can test them properly in order to gain that confidence. Is there any way to clear purchases in TestFlight or any way to allow more than 12 renewals in TestFlight?
2
2
1.7k
Apr ’23
Error in remote proxy while processing transaction: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.storekitd"
I am working on a billing implementation in UnityIAP. Recently, when I press the billing button on iOS, I get an Unknown error the first time, I checked the logs and found the following error. <SKPaymentQueue: 0x2823b65c0>: Error in remote proxy while processing transaction: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.storekitd" UserInfo={NSDebugDescription=connection to service named com.apple.storekitd}\ Special conditions, When this is occurring, addTransactionObserver is being called after the purchase order. If addTransactionObserver is called for a paymentQueue during the purchase process, would this error be called? Or if there is any problem on the AppleStore side, I would appreciate it if you could let me know. Thank you in advance.
3
0
2.1k
Apr ’23
StoreKit2: Transaction.currentEntitlements returns expired subscriptions (Testing locally with shortened sub periods)
Hi, title says it all: I have Transaction.currentEntitlements returning expired subscriptions (testing both transaction expirationDate & RenewalState). Environment: local via .storekit file. Subscription duration is shortened for testing. Could it be the issue? The sub duration is normally 1 year. The documentation says it should only returns active subscription (RenewalState.subscribed) or in grace period (RenewalState.inGracePeriod).
2
1
1.5k
May ’23
Tracking clicks on SKOverlay
I'm currently working with SKOverlay in my iOS app and I'm trying to track user clicks on the overlay. I've explored a few solutions but haven't been able to find a reliable method for capturing these clicks. I would appreciate some guidance or suggestions on how to achieve this. Any advice or code examples would be greatly appreciated. Thank you in advance for your help!
1
0
868
May ’23
macOS TestFlight app using Xcode environment for IAP
I don't know how this happened or how to reset this, but I can't test the StoreKit part of my TestFlight app anymore. When I try to retrieve or buy products (IAPs), I get all kinds of strange behavior. I think it is caused by storekitagent thinking the app is in "Xcode sandbox environment". One of the errors from storekitagent looks like this: [51852D62] [LoadSubscriptionStatusTask]: Subscription status request failed with error - Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={AMSStatusCode=0, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <468756B3-DA53-4FED-B35C-E093954C27D8>.<1>, NSErrorFailingURLKey=http://localhost:49828/inApps/subscriptions?guid=98DD60024C21&reason=push, _kCFStreamErrorDomainKey=10, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <468756B3-DA53-4FED-B35C-E093954C27D8>.<1>" ), NSLocalizedDescription=A server with the specified hostname could not be found., NSErrorFailingURLStringKey=http://localhost:49828/inApps/subscriptions?guid=98DD60024C21&reason=push, NSUnderlyingError=0x122f638d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: lo0, _kCFStreamErrorCodeKey=-72000, _kCFStreamErrorDomainKey=10}}, _kCFStreamErrorCodeKey=-72000} It looks like it's trying to connect to a local server instead of contacting Apple's App Store servers. I'm on the latest macOS and Xcode. Any help is greatly appreciated:) Thanks!
4
1
1.5k
Jun ’23