StoreKit

RSS for tag

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

Posts under StoreKit tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

StoreKit 2: Issue with Subscription Validation in Sandbox
I'm opening this post because I've encountered a perplexing issue in my application utilizing StoreKit 2 with the Sandbox environment for subscription validation. My app makes a server call each time it opens to verify if there's an active subscription. The problem arose after successfully making a purchase in the Sandbox. When I clear history from the Sandbox user and reopen the app, it resends a request to check the subscription, indicating that the user is still subscribed even though the purchases were deleted. Has anyone encountered a similar issue? if I testing it with transaction manager in Xcode it working well. ` 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) print("# purchased") } else { self.purchasedProductIDs.remove(transaction.productID) print("# canceled") } } } Thank you very much!
0
0
567
Jan ’24
Can I show expiry date and cancel button in StoreKit Views?
I am looking at StoreKit Views, new in iOS 17. In a SubscriptionStoreView, is it possible to show the user their current subscription's renewal/expiry date, or to present a cancel button? According to https://developer.apple.com/documentation/storekit/storebuttonkind/4203108-cancellation , storeButton for: .cancellation is "A type of button for canceling a subscription.", but actually it just shows a (X) button at the top right to dismiss the view - and this is what the WWDC video presenter (2023 session 10013) seems to believe it should do (around 28:30, "The cancellation button shows a platform-appropriate button to dismiss the view"). Is that a documentation bug? It seems that I can show renewal/expiry dates and a cancellation button if I use AppStore.showManageSubscriptions(...). But this is rather disjointed. Shouldn't I be able to show all of this in one place? Does my UI need two separate buttons for these overlapping features? Or am I missing something?
3
0
538
Jan ’24
Beta testers have to keep subscribing to recurring subscriptions
I have an app that I distribute to beta testers via TestFlight. The app has auto-renewable subscriptions. In the sandbox environment the subscriptions expire at an accelerated rate; every hour. They renew up to 12 times. This means that my testers have to re-subscribe every 12 hours. Is there a way to make the sandbox operate at realtime for certain users?
0
0
521
Jan ’24
Mac Appstore StoreKit 2 - validate purchase & where is purchase receipt cached?
This is re-posted from this Stack Overflow post. I am looking at validating the purchase of a paid app from Mac AppStore. Based on this WWDC video about StoreKit 2, I am attempting to this with AppTransaction. I have not found meaningful high-level documentation about this specific use case beyond that. My approach is to first get the "cached" AppTransaction by calling AppTransaction.shared. If that is not there I proceed to getting it from Apple, via AppTransaction.refresh(). If they don't have it, or when the network is down, the user automagically gets the familiar "log in to your store account" UI that has been around as long as the Mac AppStore. Once I have the AppTransaction I use it to verify we are on the right device, using code like this, where the returned Bool represents validation success: guard let deviceVID = AppStore.deviceVerificationID?.uuidString.lowercased() else { return false } let nonce = appTransaction.deviceVerificationNonce.uuidString.lowercased() let combo = nonce + deviceVID let digest = SHA384.hash(data: Data(combo.utf8)) return (digest == appTransaction.deviceVerification) My first question is: Does that look like the right approach? Is there something else I should do, or check? My second question is around testing this approach. Refreshing the AppTransaction in the sandbox invariably yields a valid item, even if the app version does not yet exist in AppStoreConnect. This is also the case when I log out in the App Store app on the Mac. This makes me think it is using my AppleID which I am logged into in System Settings. Does that sound right? I would like to be able to remove / delete the cached AppTransactions - where might I find those on the system? Thanks for everyone's help!
1
0
804
Jan ’24
Non-StoreKit Free Trial Guidelines
I've seen several posts (mostly from 3+ years ago) asking about implementing a "home-rolled" free trial for subscriptions in iOS. I'm still a bit confused though as there doesn't seem to be a definitive answer. I'm wanting to try to increase registration -> free trial conversion by offering users a time-based free trial from my backend rather than going through StoreKit. It will be clear up front that it's a subscription service and has a cost, but they won't need to agree to the auto payment after X days. Is anyone currently doing this without getting rejected or is this still considered a no-no by Apple?
3
0
329
Jan ’24
Verifying In-App Purchases with iOS App Sandbox
The iOS App Sandbox plays a pivotal role in ensuring the secure testing of In-App Purchases (IAPs) without impacting real users. To utilize the sandbox, set up your app's IAPs in App Store Connect. Begin by importing the StoreKit framework and fetching product information using SKProductsRequest. Once products are retrieved, initiate purchases through SKPaymentQueue. Implement the SKPaymentTransactionObserver to handle transaction updates, distinguishing between successful, failed, and restored purchases. When testing, remember to use test user accounts and sign in to the App Store with a test account on your device. This ensures a controlled environment for validating IAPs during development. URL - https://www.controlf5.in/ios-mobile-app-development-company/ swift Copy code import StoreKit class YourViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver { func fetchProducts() { let productIdentifiers: Set<String> = ["com.yourapp.product1", "com.yourapp.product2"] let request = SKProductsRequest(productIdentifiers: productIdentifiers) request.delegate = self request.start() } func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { let products = response.products // Handle retrieved products } func request(_ request: SKRequest, didFailWithError error: Error) { // Handle error } func purchaseProduct(product: SKProduct) { let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .purchased: // Handle successful purchase case .failed: // Handle failed purchase case .restored: // Handle restored purchase default: break } } } }
0
0
324
Jan ’24
How to use Sandbox for IAP testing
Need some help here. I've got an iPhone 11 PM on 16.6.1 and I'm trying to test Family Sharing IAPs. However, I can't seem to test via the Sandbox environment (which I need to validate receipt handling for Family Sharing). The app is running locally on my device and was built straight from Xcode. When I tap to make a purchase in my app (which uses StoreKit2, if that makes any difference) a sheet pops up with a purchase button which, when tapped, immediately completes and I get the following dialog box: "Your purchase was successful" [Environment: Xcode] How do I get my app to use the Sandbox environment? All documentation suggestions when I tap my purchase button I should simply be presented with a login modal and then, after the purchase has completed, be able to see my Sandbox credentials under Settings -> App Store. At the moment no dialog is presented (the purchase completes immediately) and the entire "Sandbox Account" section is missing from Settings -> App Store. Any help will be greatly appreciated!
1
0
598
Jan ’24
Affiliate/offer mechanism with non-consumable in-app purchases
I have a MacOS application that has a non-consumable in-app purchase that unlocks an enhanced version of the app (in other words, it's a one-time upgrade purchased inside of the app). The transaction is handled using StoreKit 2 and the app is distributed on the MacOS App Store. I would like to add an affiliate-like program where certain people can promote my application and receive a percentage of the earnings from users that buy the in-app purchase as a result of their promotion. In order to correctly distribute earnings, I need some way to track that a purchase from a user is linked to a certain affiliate. Research led me to offer codes but I quickly realised that these are only valid for subscriptions (which my purchase is not), and, besides, it seems they are not supported on MacOS. Another constraint that I have is that my application should not make any external network requests (apart from those to Apple servers, like StoreKit), so I cannot use something like Firebase for a custom offer implementation. I'm not sure what the best way to achieve this is. One way I thought of is to create one non-consumable in-app purchase/product for each affiliate and use a deep link to associate a user with that product. Then, I'll know which affiliate each user comes from based on the product that was used during the purchase. The only problem with this is that products need to be aded at compile-time so each new affiliate I add would require me to publish a new app version. I'm wondering whether there's a better way to do this?
0
0
351
Jan ’24
SKAN not receiving postbacks correctly / wrong attribution to "direct"
SKAN / SkAdNetwork Error ERROR: We never received any http request from the "skan.ourdomain.com" as expected. ERROR: All the attributions(YES! ALL OF THE INSTALLS) in the Google Analytics / Singular / AppsFlyer / Twitter / Facebook, shows the source are "direct organic". We Did These We use Google Ads and Twitter Ads and Facebook Ads to promote our apps. We spent enough money, and got thousands of paid installs from these ads. We set the NSAdvertisingAttributionReportEndpoint to "skan.ourdomain.com". We call the SKAdNetwork.updatePostbackConversionValue(1). in "AppDelegate" and "Subscribe" source codes. And from our app logs, we see there are SKAN_UPDATE_CONVERSION_VALUE_OK. Source Code if #available(iOS 15.4, *) { SKAdNetwork.updatePostbackConversionValue(1) { err in if let err = err { Tracker.shared.reportEvent(.SKAN_UPDATE_CONVERSION_VALUE_FAIL, name: err.localizedDescription, value: 1) } else { Tracker.shared.reportEvent(.SKAN_UPDATE_CONVERSION_VALUE_OK) } } } else { SKAdNetwork.registerAppForAdNetworkAttribution() Tracker.shared.reporxtEvent( .SKAN_UPDATE_CONVERSION_VALUE_OLD_VERSION, name: "AppDelegate") }
0
1
670
Jan ’24
How to exit `inBillingRetryPeriod` state in Storekit2
I'm currently testing subscriptions in Sandbox. In AppstoreConnect, I set a grace period of 3 days. I subscribed for a service which expired and now it's inBillingRetryPeriod state. I thought it had to do with my payment method. After updating my payment method, it still remains in that state. I checked Status.RenewalInfo's gracePeriodExpiration and expirationReason values but both produced nil. How do I exit the inBillingRetry state? I'm new to in-app purchases. Thanks. Here's the relevant code that updates subscription status: @MainActor func updateSubscriptionStatus() async { do { guard let product = storeManager.renewables.first, let statuses = try await product.subscription?.status else {return} var highestProduct: Product? = nil var highestStatus: Product.SubscriptionInfo.Status? = nil for status in statuses { switch status.state { case .expired, .revoked: continue default: let verifiedRenewalInfo = try storeManager.checkVerified(status.renewalInfo) //Find the first subscription in the store that matches id on the `status.renewalInfo` guard let newSubscription = storeManager.renewables.first(where: {$0.id == verifiedRenewalInfo.autoRenewPreference}) else { continue } guard let currentProduct = highestProduct else { highestProduct = newSubscription highestStatus = status // next status continue } let currentProductTier = storeManager.tierDuration(for: currentProduct.id) let newTier = storeManager.tierDuration(for: newSubscription.id) if newTier > currentProductTier { //updated product and status highestProduct = newSubscription highestStatus = status } } } currentSubscription = highestProduct // currentSubscription is an @State status = highestStatus // status is an @State if let mySubcriptionStatus = status, case .verified(let renewalInfo) = highestStatus?.renewalInfo { print(mySubcriptionStatus.state) // StoreKit.Product.SubscriptionInfo.RenewalState(rawValue: 3))-- inBillingRetry. print(renewalInfo.expirationReason) // nil print(renewalInfo.gracePeriodExpirationDate) // nil } } catch { print(error) } }
1
0
482
Jan ’24
Storekit2 debug: subscription doesn't exist and code still append a subscription??!! BUG??
I am really frustrated with storekit2, is it me or is it an Apple bug? the subscription doesn't exist LITERALLY and the code still append a subscription to my currentSubscriptions!! Unfortunatelly I cannot attach an image but the susbcrioption doesn't exist in the storekit debug window but the code still append a valid subscription! https://stackoverflow.com/questions/77783897/storekit2-subscription-is-not-existant-and-still-append-subscription-bug // update the customers products @MainActor func updateCustomerProductStatus() async { var purchasedSubs: [Product] = [] var purchasedIAP: [Product] = [] //iterate through all the user's purchased products for await result in Transaction.currentEntitlements { do { //again check if transaction is verified let transaction = try checkVerified(result) //Check the `productType` of the transaction and get the corresponding product from the store. switch transaction.productType { case .consumable: if let iap = iaps.first(where: { $0.id == transaction.productID }) { purchasedIAP.append(iap) } case .autoRenewable: if let subscription = subscriptions.first(where: { $0.id == transaction.productID }) { //SUBSCRIPTION DOESN'T EXIST AND STILL GETS APPENDED!! purchasedSubs.append(subscription) } default: break } } catch { //storekit has a transaction that fails verification, don't delvier content to the user print("Transaction failed verification") } //finally assign the purchased products self.purchasedIAPs = purchasedIAP self.purchasedSubscriptions = purchasedSubs } }
0
0
412
Jan ’24
Store Kit 2 not availiable in Sandbox environment
I have implemented Store Kit for my Swift UI App. I defined all products in app store connect (auto-renewables & non-renewables). I tested everything in Xcode and it seems to run fine. However i want to test it in Sandbox to be able to check the server side dependencies. After creating Sandbox users and logging in to those accounts on my physical device, i am still only able to do payments in Xcode ("[Environment: Xcode]"). I added the In-App Purchase Capability to my project in the Signing & Capability Targets and made sure the app runs in debug mode. So according to the docs (https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox) everything seems to be set-up.
1
0
469
Jan ’24
need for 'restore purchase'
It seems that with StoreKit and using .currentEntitlementTask, there is no use for a 'restore purchases' button. Purchase information is restored automatically. But in the app review process, the reviewer thinks that there is still need for a 'restore purchase' option. What should the 'restore purchases' action do? Is this documented for StoreKit in some place?
2
0
496
Jan ’24
Checking In-App Subscription Status in Keyboard Extension
Hi all, I have an app which is essentially a keyboard extension. Some features of the keyboard require a subscription to work, and this subscription can be bought in the main app. My question is, can the keyboard extension use StoreKit 2 to verify the validity of a subscription created in the main app? If this isn't possible (due to sandboxing or whatever), I suppose I can just write status information that's shared using an app group, but the problem with this is that the app is likely to run seldomly. This mean that if the subscription is cancelled or lapses, the main app may not see it for a while, and the keyboard will continue to functions even though it has expired. One other thing I've thought of is maybe to share the receipt of the purchase with the extension, and have the keyboard sync and verify the receipt periodically with Apple's servers. Is this the proper way to do it? Thanks!
0
1
385
Dec ’23
NSCocoaErrorDomain 4097: connection to service named com.apple.storekitagent
We had a few users reporting this issue where our app is unable to connect to StoreKit. Failed product request from the App Store server: systemError(Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.storekitagent" UserInfo={NSDebugDescription=connection to service named com.apple.storekitagent}) This occurs when calling Product.products(for:). Some users mentioned they had to restart their Mac in safe mode to make the error go away, some had DNS cache issues and clearing those helped, or some found the culprit to be Adguard. What could be causing this error as it is not very clear what's causing it?
4
0
1.3k
Feb ’24
Profile "SKAdNetwork Developer Testing" has an invalid signature
Hi, I am trying to download to my iPhone SKADNetwork profile and every time it pops us this error: Profile Error: Profile "SKAdNetwork Developer Testing" has an invalid signature. We need this profile for testing. Can you please help? In case it is relevant: my company renewed the yearly membership a few days ago and I have Developer Mode activated on my iPhone. Thanks and have a great day!
4
1
794
Jan ’24
How to migration from Original StoreKit to StoreKit2
Hello. I have a question about migrating from Original StoreKit to StoreKit2. In my app, there are two in-app purchase products implemented using Original StoreKit: Product A and Product B. For each product, there are separate backend servers, and due to certain reasons, we need to migrate one of those servers to use the AppStore Server API instead of the VerifyReceipt API. During this migration, the iOS app needs to have code that combines both Original StoreKit and StoreKit2 for each product. I heard that combining StoreKit2 with the VerifyReceipt API is not recommended. To avoid doing so, I believe the implementation would look like the below. Is this approach problematic? Is it not recommended by Apple? If anyone has successfully implemented a similar migration and has insights, I would appreciate hearing about it. Code implemented using Original StoreKit SKPaymentQueue.default().add(payment) func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions.filter({ $0.payment.productIdentifier.hasPrefix("...Original StoreKit product only...")}) { switch transaction.transactionState { case .purchasing: break case .deferred: print(Messages.deferred) case .purchased: handlePurchased(transaction) // send base64 encoded receipt file. case .failed: handleFailed(transaction) case .restored: handleRestored(transaction) @unknown default: fatalError(Messages.unknownPaymentTransaction) } } } Code implemented using StoreKit2 let result: Product.PurchaseResult = try await product.purchase() switch result { case .success(let verification): let transaction = try checkVerified(verification) // send transaction id // ... await transaction.finish() case .pending: break case .userCancelled: break @unknown default: break } func observeTransactions() -> Task<Void, Error> { return Task(priority: .background) { for await result in Transaction.unfinished { do { let transaction = try self.checkVerified(result) guard transaction.productID.hasPrefix("... StoreKit2 product only...") else { return } // send transaction id // ... await transaction.finish() } catch { // ... } } } }
2
0
1.2k
Dec ’23
How to check if the refund for consumable IAP for apple is refunded fully or partially and its amount?
We want to know whether the refund requested by the user for the consumable IAP of Apple is refunded fully or partially. I can get the revocation date on when the refund was processed but I also want to know whether the user got a refund fully or partially and its amount as well if possible. we tried to get transaction info and also the refund history of App Store Server API but we are only getting the revocation date and revocation reason we also want to know if the refund was processed as fully or partially and how much money did the user got back on refund successful. Also checked the webhook data we get for REFUND notificationType, we don't get back any field that helps us identify whether refund was full or partial and its amount as well.
1
0
677
Dec ’23