StoreKit

RSS for tag

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

StoreKit Documentation

Post

Replies

Boosts

Views

Activity

Delay in response for sandbox environment
we are dealing with subscriptions using sandbox environment , we are depending our the backend server to tell if user is subscribed or not, but after each subscription or any action on subscription we have observed a delay in response from appstore to our backend server, which is restricting user to perform actions on the app, Is this delay expected behaviour? , is delay because we are using sandbox environment?, will it be the same in real time produtcion environment as well?
0
0
302
Dec ’23
Transaction.currentEntitlements never returns if there are no entitlements?
I'm uncertain about the reason behind Transaction.currentEntitlements not returning nil when there are no current entitlements. The challenge I'm facing is that, in the scenario where a trial period concludes, Transaction.currentEntitlements seems to perpetually withhold any response, leaving me without a means to discern the conclusion of the trial. I'm puzzled as to why this method doesn't simply return nil when no entitlements are detected. It would be immensely helpful if someone could shed light on the rationale behind this behavior or point out any potential errors in my understanding. Your insights would be greatly appreciated. Thanks.
0
0
536
Dec ’23
Not receiving REFUND notifications in sandbox
Hi, I am following https://developer.apple.com/documentation/storekit/transaction/testing_refund_requests and trying to test refund requests. I am able to trigger refund requests from my app (in sandbox) using https://developer.apple.com/documentation/swiftui/view/refundrequestsheet(for:ispresented:ondismiss:) I do see that the notification URL receives a NotificationTypeV2.CONSUMPTION_REQUEST but it's not receiving NotificationTypeV2.REFUND The product for which the refund is triggered is a consumable type Is this expected behavior? How can I test REFUND without this? Thanks
4
0
899
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
851
Dec ’23
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 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) } }
0
0
580
Dec ’23
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.8k
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.9k
Dec ’23
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
2
481
Dec ’23
Issue in tracking In App Purchase payment
We're facing some trouble with In App Purchases in our app. We are not able to confirm the purchase when users don't come back to the app right after purchasing an in app product as we will not be able to reach our backend. We were thinking, would it be possible to have a web hook that triggers after receiving the payment from the customer? This would help us keep track of purchases, even if users don't come back to the app immediately after purchasing. We configured consumable in app purchases in our app. We credit the same amount to user's in app wallet after doing an in app purchase. Any help or advice you can give on this would be awesome!
2
0
384
Jan ’24
Custom offer codes
Hi there! I've recently created custom offer codes to use in our promo campaigns. Our team faced the issue: when we try to redeem the code through the App Store - it doesn't work. Hovewer, the code does work when redeeming it through the link. My question is - can these custom codes be redeemed through the App Store? or is this basically impossible? I see the info on the developers page: Each code can be redeemed through a direct URL or within your app. - there is no word about the App Store, but probably someone has the experience in these codes. The source: https://developer.apple.com/news/?id=9sjl5wuv
1
0
808
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
577
Jan ’24
Any Apple Pay Device Token issued after Feb 2024 will no longer work for Standing Instruction
In this link https://support.cybersource.com/knowledgebase/knowledgearticle/?code=KA-04318 it says that recurring payments will stop working in Feb 2024 if you are using device generated tokens, so we need to migrate to Apple merchant tokents which are supossed to be better. Visa is making enhancements to network tokens to ensure their continued compatibility with follow-on transactions. Due to these changes, there is impact to a few use cases for Apple Pay recurring transactions. Beginning February 2024, the current device generated Apple Pay network tokens will no longer be applicable for Standing Instruction transactions. Standing Instruction use cases constitute recurring, installments, or unscheduled credential on file token usages. Fortunately, Apple has exposed a new recurring token type that is to be leveraged for any standing instruction transactions. This token type increases the longevity of Apple Pay tokens and are ideally suited for Standing Instruction merchant initiated transactions. I've been trying to find more information about this, but this link seems to be the only source for this topic, which confuses me because due to the proximity of the date, t here should be quite a few people concerned about it. I have react native app that uses the react-native-iap library, I was looking at the code and I we don't seem to be using a device generated token. But given the importance of payments, I want to be 100% sure this doesn't affect us. Is anyone really impacted by this, is the information even real? In which cases this requires changes? Thank you.
1
1
622
Jan ’24
App Reject many times
Hello, I have develop a app but continue to reject for 2 problems from resolve: For GUIDELINES 3.1.1.: I don't know how i can resolve, because there are more apps that is paymant using browser on app without use in-app purchase, example the apps SUPERENALOTTO when I pay, open a window browser with type of payments (especially out the app), the app ENI PLENITUDE when there is a bill to pay, can pay with Apple Pay on app but how can I specify the price on in-app purchase if the price is not fixed but occurs based on the cost of the bill. On in-app purchase there are the price that I have to insert from $0.99 to up. Please can you help me? Because, yesterday I have change on app the payment on browser but they rejected it anyway. For GUIDELINES 5.1.1: There are too every apps that can registered without specifying or explaining what registration is for, example the apps BADOO and LOVOO and NETFLIX there are only ACCESS, REGISTERED and PASSWORD DISMISSED...Can you help me with this too? Can I see a specific example? A screenshot? I've been studying and updating/editing for many days but they rejected me 10 times Thank you very much.
2
0
610
Jan ’24
App Store Server Notifications for FAMILY_SHARED ownership (and offer codes)
Hi, I have some questions about App Store Server Notifications that I can't seem to figure out. Specifically, those related to Family Shared purchases, that contain the 'FAMILY_SHARED' inAppOwnershipType. I'll focus on 'OFFER_REDEEMED' notifications, but I have the same questions for regular subscription purchases (with no offers involved): When a user redeems an offer code for a given subscription, do we receive a notifications for each of the family members? In that case, should we receive one with ownership 'PURCHASED', and the rest with the 'FAMILY_SHARED', or could we just receive notifications for 'FAMILY_SHARED' if that's the one who redeemed it? Assuming the redeemed offer was a free trial, whenever that user disables auto renewal, should we again receive one notification for each family member? We are certainly receiving disabled_renewal notifications with ownership type 'FAMILY_SHARED'. Is there any way of linking a given 'FAMILY_SHARED' ownership event/notification to the original or parent 'PURCHASED' notification/purchase? They usually come later in time, with no specific order. If we were to track the proceeds or active subscriptions, should we just ignore any notification with ownership 'FAMILY_SHARED', and focus on 'PURCHASED' ones ? In other words, is it safe to assume that we would always receive a notification of ownership 'PURCHASED', even if the one redeeming the offer code is a family member? (not sure if that is a possible scenario). Thank you.
1
0
665
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
525
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
658
Jan ’24
Failed to call API with error="Transaction id not found."
when i use app-store-server-library-java in sandbox AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment); TransactionInfoResponse transactionInfo = client.getTransactionInfo("***"); String signedTransactionInfo = transactionInfo.getSignedTransactionInfo(); then i got Failed to call API with error="Transaction id not found." howerver when i use https://sandbox.itunes.apple.com/verifyReceipt then i got success
3
0
622
Jan ’24