To handle the upcoming App Store receipt signing certificate changes I switched my Mac app to use the new recommended APIs about a month ago at the beginning of December 2024. The transition worked seamlessly for most users except a few ones that only needed to re-enter their Mac App Store credentials.
My app is available discounted to Educational Institutions who use the Apple School Manager system to purchase and manage app on student's devices. Starting Jan. 7h this week, several schools contacted me that the app fails receipt validation. Reinstalling the app from scratch doesn't help.
Is this some kind of server side failure from Apple's side? Are there special cases to consider on my side when verifying receipts for educational purchases? (I didn't find anything in the documentation)
Here is my (simple) receipt validation code:
import StoreKit
extension AppDelegate {
func validateReceipt() {
Task {
do {
let verificationResult = try await AppTransaction.shared
switch verificationResult {
case let .verified(appTransaction):
Log.i("Receipt verification succeeded.", tag: "🧾")
case let .unverified(appTransaction, verificationError):
Log.w("Receipt verification failed with error: \(verificationError.localizedDescription)", tag: "🧾")
showVerificationFailureAlert()
}
} catch {
Log.w("Failed to retrieve AppTransaction: \(error.localizedDescription)", tag: "🧾")
showVerificationFailureAlert()
}
}
}
private func refreshReceipt() {
Task {
do {
let result = try await AppTransaction.refresh()
switch result {
case let .verified(appTransaction):
Log.i("Receipt refreshed and verified successfully.", tag: "🧾")
case let .unverified(appTransaction, verificationError):
Log.w("Refreshed receipt verification failed with error: \(verificationError.localizedDescription)", tag: "🧾")
showVerificationFailureAlert()
}
} catch {
Log.w("Failed to refresh AppTransaction: \(error.localizedDescription)", tag: "🧾")
showVerificationFailureAlert()
}
}
}
private func showVerificationFailureAlert() {
DispatchQueue.main.async {
let alert = NSAlert()
alert.messageText = "Receipt Verification Failed"
alert.informativeText = "Unable to verify the app receipt."
alert.alertStyle = .critical
// Add "Retry" and "Cancel" buttons
alert.addButton(withTitle: "Retry")
alert.addButton(withTitle: "Cancel")
let response = alert.runModal()
if response == .alertFirstButtonReturn {
self.refreshReceipt()
} else {
NSApp.terminate(nil)
}
}
}
}
let verificationResult = try await AppTransaction.shared
AppTransaction doesn't support MDM. To fetch and validate receipt for MDM, you must use the StoreKit1 APIs and workflow. For more information, see Use the Original API to support certain features.
The verifyReceipt endpoint is deprecated. To validate receipts on your server, follow the steps in Validating receipts on the device on your server. To validate in-app purchases on your server without using receipts, call the App Store Server API to get Apple-signed transaction and subscription information for your customers, or verify the AppTransaction and Transaction signed data that your app obtains. You can also get the same signed transaction and subscription information from the App Store Server Notifications V2.