StoreKit

RSS for tag

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

StoreKit Documentation

Post

Replies

Boosts

Views

Activity

Use of the App Store Server API in a production environment.
When submitting an app, reviewers are aware that they are using the In App Purchase in Sandbox environment. Therefore, we recognize that when processing an app to run the App Store Server API from the app, the production App Store Server API must be run, and if it is a failure, the Snadbox API must be run. As follows. https://developer.apple.com/documentation/appstoreserverapi#3820693 This would result in two http communications, so is there any more efficient way to do this?
1
0
336
Aug ’24
Suggestions for Improving Server Notifications for In-App Purchases
Dear Apple Development Team, I would like to draw attention to certain aspects of working with Server Notifications for In-App Purchases that could be improved to enhance development convenience and API efficiency. 1. Lack of Information on Non-Consumable Purchases in Server Notifications Currently, Server Notifications do not provide information about non-consumable purchases. This creates certain inconveniences when validating such purchases on the server. It would be extremely useful to have the ability to verify non-consumable purchases in the same way as subscriptions. Moreover, there is currently no way to obtain information about the amounts paid for non-consumable purchases, even with additional API requests. This limitation significantly complicates financial reporting and analytics for apps that utilize non-consumable purchases. While we can obtain information about the amount paid by the user for a subscription, we have no equivalent capability for non-consumable purchases. Adding this information to Server Notifications or providing an API endpoint to retrieve it would greatly improve our ability to track and analyze non-consumable purchase data without relying on client-side reporting. 2. Inconsistency in Token and Signature Handling There is some inconsistency in the approaches to authentication and verification between various Apple APIs. For example: When using Sign In with Apple, the approach with keyid is applied for JWT verification. In Server Notifications for In-App Purchases, certificate information is repeatedly duplicated in each notification. This leads to the need to implement different methods of JWT verification depending on the API being used. Additionally, the current approach with Server Notifications results in data redundancy: the useful payload is about 1.5 KB, while repetitive certificate information takes up about 17 KB in each notification. Unifying authentication and verification approaches across different APIs could significantly simplify development and improve data processing efficiency. We would appreciate consideration of these suggestions for API improvement. This could substantially simplify developers' work and increase the efficiency of integrating Apple services into applications. Thank you for your attention to this matter.
1
0
356
Aug ’24
Testing StoreKit refunds always returns an error, but items get refunded anyway
Hi all, I'm using StoreKit views for my in app store. While testing locally with a local storekit config, I can display the refund sheet for the correct product and tap refund, but my onRefundDismiss always handles the .failure case. The error messages I get have been non descriptive, i.e "Unable to Request Refund". Weirdly enough, I can confirm through transaction manager that the refund does go through, it's just my onDismiss function is getting a failure case for some reason. Any help is appreciated. The code below // Somewhere in body MyView() .refundRequestSheet(for: storeModel.productId ?? 0, isPresented: $isShowRefund, onDismiss: onRefundDismiss) // onRefundDismiss private func onRefundDismiss(result: Result<StoreKit.Transaction.RefundRequestStatus, StoreKit.Transaction.RefundRequestError>){ switch result { case .success(let refundStatus): switch refundStatus { case .success: storeModel.handleBlockRefund() // Some function I call case .userCancelled: break @unknown default: break } case .failure(let errorVal): alertTitle = "Refund failed" alertMsg = errorVal.localizedDescription } }
0
0
301
Aug ’24
How to determine active subscription by inspecting receipts
As a part of moving my app to a subscription model, I'm now struggling with allowing the user to manage their active subscription. I have the infrastructure in place to purchase, restore and query all the receipt data from Apple's servers and am currently testing with sandbox accounts. What I'm struggling with is knowing what subscription APPLE thinks the user is subscribed to. This is only a problem when the user wants to change their subscription but either upgrading or downgrading. What can happen is that a user might decide to change their subscription (to take affect after their current subscription expires, etc.) I want to be able to show the user what they are subscribed to, but for the life of me, I don't know how to do it. It's not always the "most recently purchased and still active" subscription as determined by inspecting the receipts. Obviously I could remove the ability to change the subscription plan from within the app... or just allow only ONE product per group. But that feels wrong. Does anyone know how to simply ask Apple (programatically) "What product is the user currently subscribed to?" Apple's rules say only one product per group, but I can't find this info documented anywhere.
1
0
194
Aug ’24
In-App Purchase Receipt Issue: Incorrect in_app Transaction Data Compared to latest_receipt_info
Hello, I’m encountering an issue with an iOS in-app purchase receipt where the in_app array contains the previous transaction’s data, rather than the most recent one. However, the correct transaction details appear in the latest_receipt_info array. According to my understanding, the in_app array should contain the most recent transaction details, but in my case, it does not. Here’s the anonymized receipt data for reference: { "receipt": { "receipt_type": "Production", "adam_id": ..., "app_item_id": ..., "bundle_id": "...", "application_version": "...", "download_id": ..., "version_external_identifier": ..., "receipt_creation_date": "2024-08-20 12:52:28 Etc/GMT", "receipt_creation_date_ms": "1724158348000", "receipt_creation_date_pst": "2024-08-20 05:52:28 America/Los_Angeles", "request_date": "2024-08-25 03:27:31 Etc/GMT", "request_date_ms": "1724556451959", "request_date_pst": "2024-08-24 20:27:31 America/Los_Angeles", "original_purchase_date": "2015-10-09 07:04:21 Etc/GMT", "original_purchase_date_ms": "1444374261000", "original_purchase_date_pst": "2015-10-09 00:04:21 America/Los_Angeles", "original_application_version": "1.1.3449", "in_app": [ { "quantity": "1", "product_id": "...279K", "transaction_id": "500001835761582", "original_transaction_id": "500001835761582", "purchase_date": "2024-08-20 12:52:27 Etc/GMT", "purchase_date_ms": "1724158347000", "purchase_date_pst": "2024-08-20 05:52:27 America/Los_Angeles", "original_purchase_date": "2024-08-20 12:52:27 Etc/GMT", "original_purchase_date_ms": "1724158347000", "original_purchase_date_pst": "2024-08-20 05:52:27 America/Los_Angeles", "is_trial_period": "false", "in_app_ownership_type": "PURCHASED" } ] }, "environment": "Production", "latest_receipt_info": [ { "quantity": "1", "product_id": "...155K", "transaction_id": "500001841402403", "original_transaction_id": "500001841402403", "purchase_date": "2024-08-25 03:27:28 Etc/GMT", "purchase_date_ms": "1724556448000", "purchase_date_pst": "2024-08-24 20:27:28 America/Los_Angeles", "original_purchase_date": "2024-08-25 03:27:28 Etc/GMT", "original_purchase_date_ms": "1724556448000", "original_purchase_date_pst": "2024-08-24 20:27:28 America/Los_Angeles", "is_trial_period": "false", "in_app_ownership_type": "PURCHASED" } ] } As shown, the in_app array contains a transaction with a purchase_date of 2024-08-20, while the latest_receipt_info array correctly reflects the most recent transaction with a purchase_date of 2024-08-25. Is this behavior expected, or is it an issue that needs addressing? Any insights or suggestions on how to resolve this would be greatly appreciated. Thank you!
0
2
199
Aug ’24
Un-understandable error thrown when calling Product.products(for:))
Hi, Whenever trying to call Product.products(for: list) where list contains ids of my subscriptions, I get the following error. I can't seem to catch this error regardless of how i try to implement do/catch, and it only happens on real IDS, if i use an ID that doesn't exist, then it just returns an empty list and doens't crash. I haven't deployed my app yet, and it's my first app, so I'm not sure if it may be an issue with the subscriptions not being approved yet. I do have all of my agreements signed / bank accounts setup, so i'm not sure. libswiftCore.dylib`swift_willThrow: -> 0x1a10b9f58 <+0>: pacibsp 0x1a10b9f5c <+4>: str x19, [sp, #-0x20]! .... @MainActor class PurchaseManager: ObservableObject { private let productIds = ["00"] @Published private(set) var products: [Product] = [] private var productsLoaded = false func loadProducts() async throws { guard !self.productsLoaded else { return } self.products = try await Product.products(for: ["com.one_dollar"]) self.productsLoaded = true print("Products") print(self.products) } .... } where i'm calling it: struct AmountSelectionView: View { @EnvironmentObject var purchaseManager: PurchaseManager // Add this line var body: some View { HStack(spacing: 16) { ... } .padding() .padding(.top, -30) .task { Task { print("Loading Products") do { try await purchaseManager.loadProducts() } catch { print("error") print(error) } } } } }
3
0
364
Aug ’24
IAP for Listings
Hello, I am making an e-commerce app. We plan to charge user's to make a listing for 15% of the sale price capped at £35, and are trying to figure out if we need to use IAP? From what I can find, other companies in the same area don't use them - for example Autotrader takes you off their app to there website when you try to make a listing, and you pay for the listing there. Any advice would be greatly appreciated.
2
0
322
Aug ’24
Removing In-App Purchases, consequences and questions.
Hi, I have a question regarding a set of existing in app purchases as we are planning on making a set of changes to our application. For context we are a B2B application that sells a web based tool directly to businesses. Really due to historical reasons we made recurring subscriptions available in the iOS app store and have a number of tiers users can select. Our service is getting a little more complicated, we offer a lot of stuff outside of the app store and looking at the rules: 3.1.3(f) Free Stand-alone Apps: Free apps acting as a stand-alone companion to a paid web based tool (e.g. VoIP, Cloud Storage, Email Services, Web Hosting) do not need to use in-app purchase, provided there is no purchasing inside the app, or calls to action for purchase outside of the app. It would make a lot of sense for our App to become a companion to our main service. As far as I understand this means what we need to remove the App Store subscriptions, there is no call to action in app to buy outside of the app store. However, what happens to the existing subscribers to the service? By removing the in app purchases from the app will these existing subscribers still continue to be subscribed? We don't have that many users that buy in app but we don't want to mess up their experience. For example, we don't want these subscriptions to just cancel and their access be removed. Also is this a fair understanding of the rules as we don't want to fall foul of them. I think this is a similar question to this thread, but obviously want to check before making this change. Thank you!
2
0
280
Aug ’24
Is it expected that both "trialPeriod" and "introOfferPeriod" are true in IAP Receipts?
It seems that starting from around 2024-08-20T10:28:00 UTC, both the "trialPeriod" and "introOfferPeriod" fields are set to true in the receipt data of free-trial subscriptions. Before that time only trialPeriod was set to true while introOfferPeriod was false. Just want to confirm whether this is an expected and permanent change? Thanks.
0
0
250
Aug ’24
Request review without having a view controller?
SKStoreReviewController.requestReview() is deprecated in favor of AppStore.requestReview(in:). The problem is that AppStore.requestReview(in:) requires passing in a NSViewController, and in some contexts, this is not possible. For example, a menu bar app that only has a NSMenu when you click it. It has no NSViewController that could be passed in. How are we supposed to request a review for such apps? FB14887376
1
1
260
Aug ’24
Resuming purchases back off slow back off many times
Multiple calls restorePurchase first time normal, the second is very slow, wait 2 minutes to have the order back drop back, and each time this function - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *) transactions are executed twice this is executed once - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue really magical!
0
0
171
Aug ’24
In app purchase max price limit and buy multiple products in a single transaction
Hi All, We are developing an educational platform similar to Udemy and are integrating in-app purchases for our mobile app to list it on the App Store. We have a few questions regarding in-app purchases: Price Limit: We understand that there is a maximum price limit of $1000 for any single in-app purchase product on the App Store. Is there any way to obtain an exception to this limit for our use case, as our client requires it? Multiple Products in a Single Transaction: Is it possible to purchase multiple products in a single transaction? We have a large number of products, and creating bundles in App Store Connect is not feasible for us. Are there alternative methods to achieve this? Dynamic Discounts and Offers: Can we apply discounts and offers to our products dynamically? We need to be able to modify pricing and promotions based on various factors. What are the best practices for implementing this?
1
0
317
Aug ’24
Managing Auto-Renewable-Subscriptions between custom accounts and Apple ID
My app has its own membership system, but I’m encountering a conflict with the subscription management provided by Apple ID. Here’s the issue: Suppose a user creates Account A and starts an auto-renewable subscription X, which is set to expire on 2024-09-15 at 12:00. This subscription can be managed within the iPhone settings. Later, the user logs out of Account A and logs into Account B, and then tries to purchase auto-renewable subscription X, the iPhone displays a popup message stating that the user is already subscribed to this item, preventing them from starting an auto-renewable subscription for Account B. I’ve tested other apps, such as iQIYI, and noticed that they do not have this issue. How can I resolve this problem? Are there any resources or documentation that can help? I’m currently using the StoreKit2 API.
1
0
215
Aug ’24
AMSServerErrorCode=3504
<SKPaymentQueue: 0x3037e64a0>: Payment completed with error: Error Domain=ASDServerErrorDomain Code=3504 "This item cannot be found." UserInfo={storefront-country-code=USA, AMSServerErrorCode=3504, client-environment-type=Sandbox, NSLocalizedFailureReason=This item cannot be found.}
1
0
298
Aug ’24
Will Apple reject my app for using SKStoreProductViewController for in-app updates?
I'm currently working on an iOS app where I want to provide users with an in-app update feature. For this, I'm considering using SKStoreProductViewController to present the App Store page of my app, allowing users to update it directly. However, I'm concerned about whether Apple might reject my app during the review process for using this method. Is using SKStoreProductViewController for in-app updates acceptable according to Apple's guidelines, or is there a better approach to handle in-app updates?" This is the code below func openAppStore() { let storeViewController = SKStoreProductViewController() storeViewController.delegate = self let appStoreURL = URL(string: "https://apps.apple.com/app/id333903271")! let parameters = [SKStoreProductParameterITunesItemIdentifier: "333903271"] storeViewController.loadProduct(withParameters: parameters) { _, error in if error != nil { UIApplication.shared.open(appStoreURL) } else { self.viewController?.present(storeViewController, animated: true) } } }
0
0
276
Aug ’24
Is it technically feasible for iOS app to support programmatic creation of subscriptions such that one user can subscribe to multiple users?
I’m developing an app similar to Patreon where creators can decide if they want to charge either $5 or $10 a month for users to be able to view their content (each creator can only have one subscription price defined) I came across information that the only way users can subscribe to multiple subscriptions (each from a different creator) in one app, those subscriptions need to be defined in their own Subscription Group. However, it seems that Subscription Groups need to be manually, pre-defined through an online portal and go through an approval process. So the number of Subscription Groups created would have to match the number of creators. This wouldn’t scale because I need the ability for those subscriptions to be created on the fly when new creators sign up. Am I understanding that correctly? If so, how can I get around this?
1
0
334
Aug ’24