StoreKit Test

RSS for tag

Create and automate tests in Xcode for your app's submission and in-app purchase transactions.

StoreKit Test Documentation

Posts under StoreKit Test tag

69 Posts
Sort by:
Post not yet marked as solved
3 Replies
968 Views
Hello, What is the key for In-App Purchases entitlement I can add to my app.entitlements file in my project, so that I can autonomously enable the In-App Purchase capability? I have searched far a wide for this, however, it's unclear where it can be located. I know I can enable this capability manually by opening Xcode -> Selecting the "Signing & Capabilities" tab -> selecting "+ Capability" -> selecting "In-App Purchase" capability. However, this is not really an ideal solution for adding the capability to my app, especially when automated processes for building, testing, distributing via CI/CD are integrated. It would beneficial to be able to reference some documentation or resources for enabling capabilities (or any other build settings) autonomously in a project as opposed to having to manually click my way through enabling them. Looking forward to hearing back. Thanks!
Posted
by coltkenn.
Last updated
.
Post not yet marked as solved
1 Replies
421 Views
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!
Posted Last updated
.
Post not yet marked as solved
0 Replies
481 Views
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") }
Posted Last updated
.
Post not yet marked as solved
0 Replies
392 Views
I wrote a StoreKit unit test and set the renewal rate to .oneRenewalEveryTwoSeconds for the test session. But now my App expires and renews every two seconds when running normally, even though the StoreKit Configuration file is set to "Real Time." Changing it to anything else doesn't change the renewal rate. I've tried cleaning the build folder. If, however, I set my SKTestSession explicitly to .realTime and run the test again, then my app behaves. However, again, it doesn't matter what I set the Configuration Settings to. Anyone know where this information is stored?
Posted
by JetForMe.
Last updated
.
Post not yet marked as solved
0 Replies
337 Views
We are doing a Swift Package to manage our iap with storekit 2. This packages also contains the unit tests for the code. (as well as all the required files .storekit, certificate, ...) when calling SKTestSession.buyProduct(identifier: options:) we catch an error : Error Domain=SKErrorDomain Code=0 "(null)" Note when trying exactly the same code but in a project instead of a package that works perfectly. Any idea what could it be? The code is really simple : var testSession: SKTestSession! override func setUpWithError() throws { testSession = try SKTestSession(configurationFileNamed: "Products") } func testExample() async throws { do { let productID = "nonConsumable.crystal.tier1" try await testSession.buyProduct(identifier: productID) } catch { XCTFail("An error occured during purchase : \(error)") } } }
Posted Last updated
.
Post not yet marked as solved
1 Replies
339 Views
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) } }
Posted
by akinjobie.
Last updated
.
Post not yet marked as solved
0 Replies
309 Views
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 } }
Posted
by naticio.
Last updated
.
Post not yet marked as solved
1 Replies
367 Views
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.
Posted
by Marvin.
Last updated
.
Post not yet marked as solved
2 Replies
552 Views
Hi, I currently have an introductory offer set up in App Store Connect, which is in the 'Ready to Submit' state. When testing with the StoreKit configuration testing for Xcode, the introductory offer appears correctly, and I can successfully make and cancel purchases. However, when testing on a device without the StoreKit configuration, using a new sandbox account, the introductory offer does not appear, though the introductory price is visible for subscription. Could you advise on how to ensure the introductory offer is displayed in this scenario? Using Storekit 2
Posted Last updated
.
Post not yet marked as solved
2 Replies
1.6k Views
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.
Posted Last updated
.
Post not yet marked as solved
0 Replies
358 Views
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 am checking Status.RenewalInfo's gracePeriodExpiration and expirationReason values produce nil. How do I exit the inBillingRetry state? I'm new to in-app purchases. Thanks. Here's the relevant code: ... @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) } }
Posted
by akinjobie.
Last updated
.
Post not yet marked as solved
1 Replies
692 Views
I am developing my first app and having issues understanding how SubscriptionStoreView works. I used some business logic available from Apple, but I see other resources that insinuate the StoreKit views can handle all of the business logic itself, and all I need is the config file. Can anyone confirm? When I'm previewing SubscriptionStoreView, It just says, "The subscription is unavailable in the current storefront," and I cannot find what that means or what to fix.
Posted
by newguy1.
Last updated
.
Post marked as solved
12 Replies
10k Views
When testing In-App Purchases in Xcode with a .storekit file, I can delete past purchase transactions, so I can re-test the purchase experience. I've switched to using a Sandbox tester and made purchases. However, I cannot find how to delete previous purchase transactions made in the sandbox so I can re-run the tests. Is this possible?
Posted
by Todd2.
Last updated
.
Post not yet marked as solved
0 Replies
304 Views
I have a unit test case to test subscription plan change scenario, that is working until iOS 16, but the test failed starting iOS 17 and above. Test case setup: let session = try SKTestSession(configurationFileNamed: "IAPTestConfiguration") session.disableDialogs = true session.clearTransactions() // Buy a monthly subscription first try session.buyProduct(productIdentifier: "test.apple.test_mobile.1m") if let transaction = session.allTransactions().first { let id = transaction.identifier // and disable monthly subscription try session.disableAutoRenewForTransaction(identifier: id) } // buy a yearly subscription to create a subscription change scenario. try session.buyProduct(productIdentifier: "test.apple.test_mobile.1y")` Now this is the logic I have to determine that there is going to be a plan change: if let autoRenewPreference = renewalInfo.autoRenewPreference, autoRenewPreference != renewalInfo.currentProductID, renewalInfo.willAutoRenew { // means subscription change } until iOS 16, the above test setup resulted in : renewalInfo.autoRenewPreference = "test.apple.test_mobile.1y" renewalInfo.currentProductID = "test.apple.test_mobile.1m" renewalInfo.willAutoRenew = true But in iOS 17, the above setup seems to be not working because, the second time I try to buy a product seems to have no effect. // This has no effect, when there is already an active subscription, in our case "test.apple.test_mobile.1m" . try session.buyProduct(productIdentifier: "test.apple.test_mobile.1y") I expect there should be at least two transactions with my current setup, in iOS 16 I get 2 transactions when I query session.allTransactions() at the end, but in iOS 17 I only get 1 transaction, which is the first buyProduct, the last buyProduct seems to have no effect, as I do not get any callback in the SKPaymentQueue updateTransactions method for the last buyProduct call. In iOS 16, I get callbacks in SKPaymnetQueue for both buyProduct calls. I also tried to use async buyProduct in iOS 17, and updated my test case accordingly, but it resulted in the same behaviour. Please let me know if you need any other detail that could help sort this issue.
Posted
by Rupika.
Last updated
.
Post not yet marked as solved
0 Replies
283 Views
x.storekit:1:1 unexpected character ‘{’ x.storekit:11:1 unexpected character ‘"’ I get hundreds of errors similar to this when trying to build my application.
Posted
by Wleksion.
Last updated
.
Post not yet marked as solved
2 Replies
1.7k Views
Hello all, Created and tested successfully auto renewal subscription products in my app. Both products offer 7 days free trial. Once purchased for the first time, isEligibleForIntroOffer value was true and when I tried to subscribe again later, the value is false - everything worked as expected. However, when I reset the eligibility for my sandbox user through the edit subscription screen, I do see that the Free Trial offering becomes available again, as expected, but when I test it in the app, the value for isEligibleForIntroOffer is still false. The value is false for the group (Product.SubscriptionInfo.isEligibleForIntroOffer(for: renewableSubscription.subscriptionGroupID)) and also for the product (renewableSubscription.isEligibleForIntroOffer). iOS 15.4. Is this a bug? Can I still trust this property value as the only source to check whether the user is eligible for intro in production? Thanks a lot.
Posted
by DudiSG.
Last updated
.
Post not yet marked as solved
0 Replies
369 Views
I was trying to write unit test of ask to buy for SKDemo app. Even if I set SKTestSession.askToBuyEnabled = true, I got transaction state as .purchased after I call SKTestSession.buyProduct(identifier:). import StoreKitTest import XCTest final class SKDemoTests: XCTestCase { private var session: SKTestSession! override func setUp() async throws { session = try .init(SKTestSession(configurationFileNamed: "Products")) session.disableDialogs = true session.resetToDefaultState() session.clearTransactions() } func test() async throws { session.askToBuyEnabled = true try await session.buyProduct(identifier: "consumable.fuel.octane89") XCTAssertEqual(session.allTransactions().first!.state, .deferred) // Gets error here. The actual state I get is .purchased } } I was using Xcode 15.0.1 (15A507), Simulator iPhone 15 Pro iOS 17.0.1 (21A342) I couldn't find the problem so I'm happy to hear any solutions.
Posted
by ozumin.
Last updated
.
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 ozumin.
Last updated
.
Post not yet marked as solved
3 Replies
1.1k Views
When calling SKPaymentQueue.default().restoreCompletedTransactions() during unit testing it always fails. It calls the restoreCompletedTransactionsFailedWithError with an error message: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={NSLocalizedDescription=The network connection was lost., NSUnderlyingError=0x600000eb2790 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={_kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-4, NSErrorPeerAddressKey={length = 28, bytes = 0x1c1ef212 00000000 00000000 00000000 ... 00000001 00000000 }}}} This is/was working fine in iOS 16 and Xcode 14. Anyone else seen this issue?
Posted
by YishaiR.
Last updated
.