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.
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Post
Replies
Boosts
Views
Activity
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
}
}
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 {
...
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!
Hey everyone 👋
I am wondering how to create a dynamic multiple subscriptions in the same app
scenario -> the app has multiple creators [Streamer] and user can subscribe monthly to each one as a separate subscription.
how to develop this approach using in-app purchases?
thanks
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.
<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.}
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.
I'm trying to test in-app purchases in my app in the sandbox environment. I've purchased a one-time purchase in the app, but no purchases are visible under "Manage Transactions..." in Xcode. Deleting the app from the device as well as deleting the directories ~/Library/Caches/com.apple.appstoreagent and ~/Library/Caches/com.apple.storekitagent does not remove the purchase from the app. This makes it impossible to fully test purchases in the app.
Hello everyone,
I am trying to implementing the In-App Purchase in one of my iOS App and I am getting the following error when I test it into iPhone, but when I test the same app into the iPad I didn't get any error and it works properly.
Error Domain=SKErrorDomain Code=0 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred, NSUnderlyingError=0x282da9860 {Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={NSUnderlyingError=0x282d02940 {Error Domain=AMSErrorDomain Code=203 "Bag Load Failed" UserInfo={NSLocalizedFailureReason=Unable to retrieve p2-product-offers-batch-limit because we failed to load the bag., NSLocalizedDescription=Bag Load Failed, NSUnderlyingError=0x282d02af0 {Error Domain=AMSErrorDomain Code=203 "Bag Load Failed" UserInfo=0x2836ca380 (not displayed)}}}}}}
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
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.
When verifying the receipt to 'https://sandbox.itunes.apple.com/verifyReceipt', the status code 21199 is always returned. Even if the verification is performed again after an interval, the status code 21199 is still returned. How can this problem be solved?
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!
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?
We would like to provide auto-renew subscription in our app and we have these questions:
when provide offer codes for auto-renewable subscriptions using presentCodeRedemptionSheet() to let the user enter redeem code
is it ok we put the redeem code in clipboard so that user just need to paste to enter ?
we should present a screen that let user to choose which duration to subscribe, so that we can provide proper redeem code for user to use in system redeem sheet (call out by presentCodeRedemptionSheet()), am I right ?
if user apply the redeem code, the system redeem sheet will emit the resulting transaction as if purchase the corresponding subscription by in-app purchase, am I right ?
P.S.
As we can't test redeem an offer code for an auto-renewable subscription in sandbox, so we don't know the exact flow of it and need to ask in forum.
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) } } }
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?
PLEASE HELP
MY PHONE SCREEN TURN GREEN
WITHOUT ANY PHYSICAL DAMAGE
what can I use direct bank transfer for buying premium account in my app?
because I found an application in the App Store that without a marker provides in-app purchases, but when the application is opened, this application offers an upgrade to a premium account to be able to access all features and the purchase process by bank transfer to the application owner's account.
so now we can use our bank account directly to process payment in app without using in app purchases system by apple, right?