New Receipt Validation Sample Code
Apple (via Thomas Zoechling):
The following example code illustrates how to retrieve an identifier in macOS for validating an App Store receipt. Use the data returned to by the
copy_mac_address
function to validate the App Store receipt.In the following Swift code, the
io_service
function uses IOKit to retrieve network interfaces as an optional IOKit object. Thecopy_mac_address
function looks up an appropriate network interface and returns the hardware address from the IOKit object as optional CFData.
Apple’s sample code for this had been broken at least since Catalina. I still think there should just be an API for validating and parsing receipts, instead of encouraging developers to either reinvent the wheel or incorporate open source code of questionable quality.
Previously:
Update (2022-06-08): macOS 13 adds AppTransaction.
Update (2022-06-10): John Siracusa:
Still no simple Apple API to validate Mac App Store receipts, though, right?
No. And I’m not sire there will be on as it’s a semi-antithesis of DRM checking: have your check in multiple places that can’t be stubbed out by one change to the binary especially if it would apply to every binary.
I just don’t want to have to statically link OpenSSL into my dinky app and then copy/paste a bunch of code I don’t understand. I’m not trying to stop someone with a hex editor. I’m trying to stop casual piracy.
This new API is for making feature selections betterer based on date purchased without needing to roll a new app entry on the App Store for the new version.
3 Comments RSS · Twitter
Broken how..? I‘m only aware of a rare issue where the wrong network interface is being checked causing the validation to fail.
Apple sample code is different to internal implementation in AppleMediaServices.framework (+[AMSDevice macAddressData]).
Sample Code
if wantBuiltIn == CFBooleanGetValue(isBuiltIn) {
return candidate
}
Internal Code
if CFBooleanGetValue(isBuiltIn) == true {
return candidate
} else if wantBuiltIn == false {
return candidate
} else { //interface is not build in but we want build in
continue
}
Another difference is:
guard let service = io_service(named: "en0", wantBuiltIn: true)
?? io_service(named: "en1", wantBuiltIn: false) //instead of true
?? io_service(named: "en0", wantBuiltIn: false)
Due to the difference I would even rename wantBuiltIn to shouldFailIfNotBuiltIn
https://github.com/IdeasOnCanvas/AppReceiptValidator/issues/83#issuecomment-1966283436