Posts under App & System Services topic

Post

Replies

Boosts

Views

Activity

How do win-back offers displayed for discounted subscriptions?
In all the illustrations of win-back offers, I see an example of "Get 3 months off, then $X/month", as seen below. First, I'm not exactly clear how each configuration translates into an actual offer in practice: If I want to offer 3 months off on an annual subscription, ie. only if the user pays for the annual offer (basically a 25% discount), is that possible? If I set a "Free" type, of 3 months, I guess that would allow the user to cancel before paying for the annual, correct? If I set a "Pay up front" type, with a 25% discount, how would that show up to the user on the App Store? Secondly, is eligibility to an offer determined by the user elapsing on the same subscription or any subscription in the same subscription group? Thank you
0
0
316
Jan ’25
Handling failed transactions in Storekit 2
When simulating a Storekit error like an invalid device verification or others of that type, should we finish a failed transaction? When I test with a Storekit configuration file, all failed transactions persist after every restart. The Apple-provided sample code for Storekit 2 has transactions finished only when they are successful.
0
0
203
Jan ’25
Apple Pay Merchant Token on Web
Hello. I have a few questions about the implementation of Apple Pay payments on websites. Could you help me From the documentation: Apple Pay issues an Apple Pay Merchant Token if the user’s payment network supports merchant-specific payment tokens. Otherwise, Apple Pay issues a device token for the payment request. How can we determine whether a token is a merchant token or a device token? Is it possible to determine this by any of the token fields? https://developer.apple.com/documentation/passkit/payment-token-format-reference Is it possible to understand this in other ways? Can I make recurring payments with the device token if it was issued instead of the merchant token? Is it necessary to include the tokenNotificationURL when generating a merchant token, or can we generate one without specifying it? What does the applicationExpirationDate field in the merchant token represent? Is this the date when the device token or merchant token expires and payments cannot be made with it?
0
0
359
Jan ’25
IAP refund decision changed
We integrated App Store Server notification, to get notified about CONSUMPTION_REQUESTS and REFUND notifications. In the data, we noticed same transactionId have multiple REFUND decisions, usually REFUND_DECLINED and then REFUND. Why is that? Did user contact customer support ? For the second (or later) REFUND decision, CONSUMPTION_REQUEST notifications are usually not sent, but thats not always the case. Sometimes, REFUND decision are the same. Sometimes, we get even 3 or more REFUND related notifications for same transactionId, e.g: 2024-12-02: REFUND_DECLINED 2024-12-05: REFUND_DECLINED 2024-12-12: REFUND Do user request refund again ? Do they contact customer support ? But I can not explain why sometimes status it REFUND at first, but then later REFUND_DECLINED. Thank you already in advance:)
0
0
365
Jan ’25
Inconsistent notification coming from AppStore Servers
I encountered a scenario involving a subscription and need to determine if it's a problem or an expected outcome. Here are the details: My service received a notification from Apple of type DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED. The status field received on the payload was equal to 1 - Active. (2024-12-19T15:34:53.801) My service again received a DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED. But the status field received was 2 - Expired. (2024-12-19T23:34:57.527) My service received an EXPIRED with subtype VOLUNTARY notification. (2024-12-19T23:35:01.669) Is the event 2 an inconsistent event? Since we are receiving a notification that means the auto renew was disabled when the subscription was already expired.
0
0
329
Jan ’25
API to check Core Spotlight storage limit
There's a 128mb limit for donating items to core spotlight. As far as I understand, there's a warning that shows in the Xcode console when either approaching or hitting that limit. It would be great if there was an API to check the current status of available storage for QA purposes to see if we're either donating too much or can donate more. Thanks!
0
0
276
Jan ’25
Business model changes by using the app transaction
Hello, I’m trying to change my business model within the app, and following Apple’s documentation guidelines HERE I created this task in the main view of the app. It seems to work perfectly in the simulator, on physical devices, and on TestFlight. However, after releasing it to production and uploading the new version to the App Store, it doesn’t work, and all users, whether new or existing, are asked to subscribe. In the console, it appears to retrieve the transactions correctly, but in production, I’m not sure how to view the console or see what it’s retrieving. Here the sandbox receipt I obtained AppTransaction.shared obtained: { "applicationVersion" : "1", "bundleId" : "com.anestesiaIB.Drugs-Infusion-Calc", "deviceVerification" : "6M0Nnw14nSEOBVTPE\/\/EfnWSwLm7LFSlrpFEwxgH74SBHp5dSzBEm896Uvo42mwr", "deviceVerificationNonce" : "8a8238c0-0aee-41e6-bfb0-1cfc52b70fb6", "originalApplicationVersion" : "1.0", "originalPurchaseDate" : 1375340400000, "receiptCreationDate" : 1737577840917, "receiptType" : "Sandbox", "requestDate" : 1737577840917 } This are the processing log while verified the receipt New business model change: 1.7 Original versionéis components: ["1", "0"] Major version: 1, Minor version: 0 This user is premium. Original version: 1.0 This is my task... .task { do { let shared = try await AppTransaction.shared if case .verified(let appTransaction) = shared { let newBusinessModelVersion = (1, 7) // Representado como (major, minor) let versionComponents = appTransaction.originalAppVersion.split(separator: ".") if let majorVersion = versionComponents.first.flatMap({ Int($0) }), let minorVersion = versionComponents.dropFirst().first.flatMap({ Int($0) }) { if (majorVersion, minorVersion) < newBusinessModelVersion { self.premiumStatus.isPremium = true isPremium = true } else { let customerInfo = try await Purchases.shared.customerInfo() self.premiumStatus.isPremium = customerInfo.entitlements["premium"]?.isActive == true isPremium = self.premiumStatus.isPremium } } else { print("Error: obteining version components") } } else { print("Not verified") } } catch { print("Error processing transaction: \(error.localizedDescription)") } }
0
0
332
Jan ’25
[In-App Purchase] first trial but "is_trial_period" false
This question is about In-App Purchase. This is an inquiry from one of our customers. We have set up a free trial. This is your first time using the service, but you have stated that you have been charged. Document. https://developer.apple.com/documentation/appstorereceipts/is_trial_period 「You can use this value to determine whether the specific record is in a subscription trial period. If a previous subscription period in the receipt has the value "true" for either the is_trial_period or is_in_intro_offer_period keys, the user is not eligible for a free trial or introductory price within that subscription group.」 Our expectation is that is_trial_period is true. Receipt is not contain is_trial_period : true or is_in_intro_offer_period : true. Only one case has occurred. Other customers are no problem.
0
0
416
Dec ’24
Can users create their own subscription plans?
I'm considering developing an app where users can create their own subscription plans by freely setting their prices, similar to YouTube's membership feature. I understand that in-app purchases must be used to unlock features within the app. With that in mind, I searched for APIs to enable this functionality but couldn't find relevant information. When I contacted Apple directly, they mentioned that they couldn't provide specific answers unless the app is under review. If anyone has knowledge about the following points, I would greatly appreciate your response: Is it possible to implement a feature similar to YouTube's membership using in-app purchase APIs? If it's not feasible with in-app purchases, is it allowed to use external payment services like Stripe?
0
0
315
Dec ’24
How do I finish and track Consumable IAPs with StoreKit 2?
Hi, I'm attempting to use StoreKit 2 and SwiftUI to add a tip jar to my iOS app. I've successfully added consumable IAPs for each of my tip sizes, and used ProductView to show these on my tip jar screen. However, I am at a loss on how to do the following things: How and when do I finish the consumable IAP transaction? I see the finish() function in the documentation, but I am not sure how I can call it given that ProductView is handling the purchase for me (I have no access to a Transaction object). How can I track the amount of consumable IAPs the user has purchased across all their devices? I want to show the user the amount of money they have tipped in total. I have added SKIncludeConsumableInAppPurchaseHistory to my Info.plist and set it to YES as suggested here: https://forums.developer.apple.com/forums/thread/687199 This is my first time using StoreKit 2 (until now, I was using StoreKit 1), so I would really appreciate any advice and guidance you can provide. Thanks!
0
1
267
Jan ’25
inApps/v2/history/ of AppleStoreServerAPI
https://developer.apple.com/documentation/appstoreserverapi/get-v2-history-_transactionid I would like to inquire about the detailed triggers for updating receipts in this API specification. Recently, I was using this API with sort=DESCENDING&revoked=false to retrieve the expiration date of the most recent receipt and determine the subscription status. However, for some reason, an old receipt with an earlier expiration date appeared as the first receipt, and I would like to know the reason for this. Can you provide information on what specific events or actions trigger the updating of receipts in this API? Also, regarding https://developer.apple.com/documentation/appstoreserverapi/status, will statuses 3 and 4 not be returned in the response unless the billing grace period is enabled in the App Store? -- Japanese こちらのAPI仕様ですが、どのようなトリガーでレシートが更新されるのか詳細に伺いたいです。 先日このAPIを使用してsort=DESCENDING&revoked=falseで一番最初のレシートの有効期限を取得して課金状態か判断していたのですが、どういったわけか一番最初のレシートに古い有効期限のレシートが入ってきたので理由を知りたいです。 また、https://developer.apple.com/documentation/appstoreserverapi/status のステータスはAppleStoreで請求猶予期間を有効化しないと3,4はレスポンスされませんか?
0
0
341
Jan ’25
swiftdata model polymorphism?
I have a SwiftData model where I need to customize behavior based on the value of a property (connectorType). Here’s a simplified version of my model: @Model public final class ConnectorModel { public var connectorType: String ... func doSomethingDifferentForEveryConnectorType() { ... } } I’d like to implement doSomethingDifferentForEveryConnectorType in a way that allows the behavior to vary depending on connectorType, and I want to follow best practices for scalability and maintainability. I’ve come up with three potential solutions, each with pros and cons, and I’d love to hear your thoughts on which one makes the most sense or if there’s a better approach: **Option 1: Use switch Statements ** func doSomethingDifferentForEveryConnectorType() { switch connectorType { case "HTTP": // HTTP-specific logic case "WebSocket": // WebSocket-specific logic default: // Fallback logic } } Pros: Simple to implement and keeps the SwiftData model observable by SwiftUI without any additional wrapping. Cons: If more behaviors or methods are added, the code could become messy and harder to maintain. **Option 2: Use a Wrapper with Inheritance around swiftdata model ** @Observable class ParentConnector { var connectorModel: ConnectorModel init(connectorModel: ConnectorModel) { self.connectorModel = connectorModel } func doSomethingDifferentForEveryConnectorType() { fatalError("Not implemented") } } @Observable class HTTPConnector: ParentConnector { override func doSomethingDifferentForEveryConnectorType() { // HTTP-specific logic } } Pros: Logic for each connector type is cleanly organized in subclasses, making it easy to extend and maintain. Cons: Requires introducing additional observable classes, which could add unnecessary complexity. **Option 3: Use a @Transient class that customizes behavior ** protocol ConnectorProtocol { func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel) } class HTTPConnectorImplementation: ConnectorProtocol { func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel) { // HTTP-specific logic } } Then add this to the model: @Model public final class ConnectorModel { public var connectorType: String @Transient public var connectorImplementation: ConnectorProtocol? // Or alternatively from swiftui I could call myModel.connectorImplementation.doSomethingDifferentForEveryConnectorType() to avoid this wrapper func doSomethingDifferentForEveryConnectorType() { connectorImplementation?.doSomethingDifferentForEveryConnectorType(connectorModel: self) } } Pros: Decouples model logic from connector-specific behavior. Avoids creating additional observable classes and allows for easy extension. Cons: Requires explicitly passing the model to the protocol implementation, and setup for determining the correct implementation needs to be handled elsewhere. My Questions Which approach aligns best with SwiftData and SwiftUI best practices, especially for scalable and maintainable apps? Are there better alternatives that I haven’t considered? If Option 3 (protocol with dependency injection) is preferred, what’s the best way to a)manage the transient property 2) set the correct implementation and 3) pass reference to swiftdata model? Thanks in advance for your advice!
0
0
475
Jan ’25
"InvalidProviderToken" with APN push service when using code-generated token
Problem We have successfully set up push notifications using Apple APN service, that is push notifications work when using a token generated using the JSON Web Token Generator in the Push Notification console. However, we get an "InvalidProviderToken" error when creating using our own token using the following code. The Key and TeamID is definitely correct (obviously, censored in the below code). When pasting our token in the JSON Web Token Validator in the Push Notification console we get the error „Invalid signing key“. We merely pasted our secret key in our setNewTokenIfNeeded code, separated on four lines using the “““ style. Does anyone know why this error happens? Given that it works when we upload our .p8 file to the JSON Web Token Generator and we simply paste the text of this file (excluding the lines with "-----BEGIN/END PRIVATE KEY-----") I guess our secret key is correct? Code to generate token fileprivate var currentToken: String? fileprivate var currentTokenCreateTime: Date? fileprivate func setNewTokenIfNeeded() { // Ensure, token is at least 20 minutes but at most 60 minutes old if let currentTokenCreateTime = currentTokenCreateTime { let ageOfTokenInSeconds = abs(Int(currentTokenCreateTime.timeIntervalSinceNow)) NSLog("Age of token: \(Int(ageOfTokenInSeconds / 60)) minutes.") if ageOfTokenInSeconds <= 20 * 60 { return } } // Generate new token NSLog("Renewing token.") let secret = """ ABCABCABCABCABCABCABCABCABCABCABCABC+ABCABC+ABCABCABC+ABCABCAB/+ ABCABCABCABCABCABCABCABCABCABCABCABC+ABCABC+ABCABCABC+ABCABCAB/+ ABCABCABCABCABCABCABCABCABCABCABCABC+ABCABC+ABCABCABC+ABCABCAB/+ ABCABCAB """ let privateKey = SymmetricKey(data: Data(secret.utf8)) let headerJSONData = try! JSONEncoder().encode(Header()) let headerBase64String = headerJSONData.urlSafeBase64EncodedString() let payloadJSONData = try! JSONEncoder().encode(Payload()) let payloadBase64String = payloadJSONData.urlSafeBase64EncodedString() let toSign = Data((headerBase64String + "." + payloadBase64String).utf8) let signature = HMAC<SHA256>.authenticationCode(for: toSign, using: privateKey) let signatureBase64String = Data(signature).urlSafeBase64EncodedString() let token = [headerBase64String, payloadBase64String, signatureBase64String].joined(separator: ".") currentToken = token currentTokenCreateTime = Date() } fileprivate struct Header: Encodable { let alg = "ES256" let kid: String = "ABCABCABC" // Key (censored here) } fileprivate struct Payload: Encodable { let iss: String = "ABCABCABC" // Team-ID (censored here) let iat: Int = Int(Date().timeIntervalSince1970) } extension Data { func urlSafeBase64EncodedString() -> String { return base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=", with: "") } } Code to send the push notification func SendPushNotification(category: ConversationCategory, conversationID: UUID, title: String, subTitle: String?, body: String, devicesToSendTo: [String]) { // Für alle Felder s. https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification let payload = [ "aps": [ "alert": [ "title": title, "subtitle" : subTitle ?? "", "body": body ], "category" : category.rawValue, "mutable-content": 1 ], "conversationID": conversationID.uuidString ] as [String : Any] // Ggf. Token setzen setNewTokenIfNeeded() guard let currentToken = currentToken else { NSLog("Token not initialized.") return } NSLog(currentToken) // Notification an alle angegebenen Devices schicken let bundleID = "com.TEAMID.APPNAME" for curDeviceID in devicesToSendTo { NSLog("Sending push notification to device with ID \(curDeviceID).") let apnServerURL = "https://api.sandbox.push.apple.com:443/3/device/\(curDeviceID)" var request = URLRequest(url: URL(string: apnServerURL)!) request.httpMethod = "POST" request.allHTTPHeaderFields = [ "authorization": "bearer " + currentToken, "apns-id": UUID().uuidString, "apns-topic": bundleID, "apns-priority": "10", "apns-expiration": "0" ] request.httpBody = try! JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted) URLSession(configuration: .ephemeral).dataTask(with: request) { data, response, error in if let error = error { NSLog(error.localizedDescription) } if let data = data { NSLog(String(data: data, encoding: .utf8)!) } }.resume() } } On a similar note, some people seem to encounter this error when using the prettyPrinted option for the JSON serialization (i.e., in request.httpBody = try! JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted). Could this be the culprit, given our secret key contains „/„ and „+“? Many thanks!
0
0
492
Jan ’25
Does the Apple Store support variable recurring payments like Stripe?
Stripe offers variable payment structures, also known as "irregular recurring payments," which include: Usage-based billing: Charges amounts based on usage during the billing cycle (e.g., minutes used or energy consumed). Quantity-based billing: Charges a pre-agreed amount based on quantity (e.g., number of users in a subscription). Is it possible to implement this type of billing in the Apple Store for apps? How would variations in amounts be handled?
0
0
356
Jan ’25
App cannot fetch any resource after a while
Hi everyone, We came an issue that, In some scenarios in our app we cannot fetch any resources from device (Photo and Contact). One case we catched is putting app in background and spending time in other commonly used apps and coming back to our app cause this issue but there is a small chance that get this issue during using the application. In cell, we are trying to fetch the image like this imageFetchTask = Task { let image = await CompositionRoot.shared.photosManager.image(requestType: .imageCollections, forId: photoAsset.photoId) self.photoImageView.image(image) } and inner layers of this code we get the PHAsset and request image PHAsset.firstAsset(for: id) let manager = PHImageManager.default() manager.requestImage(for: asset, targetSize: request.targetSize, contentMode: .aspectFill, options: request.options) { (image, info) in continuation.resume(returning: image) } We figured out that issue not happening only in Photos also Contacts and any web request. So any help according to this situation is well appreciated. Thanks
0
0
474
Dec ’24
Response of the server-side verifyReceipt endpoint when using offer codes
Hi, We use the (now deprecated) server-side receipt verification API (*1) with the app we are maintaining and there are some points I would like confirm on how its response changes based on whether the purchase being processed was a subscription that used an offer code or not. I am particularly concerned about the following: Are there any properties of the response that are added or missing? Is there any property indicating that an offer code was used? If there is, which field is that and what values does it take? Are there any special steps or options required when processing the receipt which used an offer code on the server side? *1 https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
0
0
250
Jan ’25
Gathering Required Information for Troubleshooting Tap to Pay on iPhone
While troubleshooting Tap to Pay on iPhone, it is essential that the developer is able to collect logs on their device and check those logs for error messages. This is also essential when reporting issues to Apple. To gather the required data for your own debugging as well as reporting issues, please perform the following steps on the test device: Install the Tap to Pay profile on your iPhone. Reproduce the issue and make a note of the timestamp when the issue occurred, while optionally capturing screenshots or video. Gather a sysdiagnose on the same iPhone. Create a Feedback Assistant report with the following information: The bundle ID The serial number of the device. For iOS, open Settings > General > About (tap and hold to copy). The SEID (Secure Element Identifier) of the device, represented as a HEX encoded string. For iOS, open Settings > General > About > SEID (tap and hold to copy). The sysdiagnose gathered after reproducing the issue. The timestamp (including timezone) of when the issue was reproduced. Screenshots or videos of errors and unexpected behaviors (optional). Submitting Your Feedback After your submission to Feedback Assistant is complete, please respond to your post with the Feedback ID. Once received, I can begin my investigation and determine if this issue is caused by an error within your app, a configuration issue within your developer account, app configuration, or an underlying system bug. Cheers, Paris X Pinkney |  WWDR | DTS Engineer
0
0
431
Mar ’25