Michael Tsai's encyclopedic blog has discussed issues with Mac App Store receipt validation in 2019 and again in 2022. In summary, Apple's old sample code for developers was broken, some developers tried to fix it, and Apple later posted new sample code (at the same URL). However, I've recently run into a case that seems to call into question all extant sample code for Mac App Store receipt validation. Someone purchased my app Link Unshortener but was unable to launch it. The customer also mentioned being unable to launch another developer's Mac App Store app, Magnet. This suggested a problem with receipt validation. I learned that the ethernet port of the customer's Mac was fried as a result of electrical damage from a lightning strike. The Mac's motherboard was replaced, but afterward the customer still couldn't launch Magnet, and now they couldn't launch Link Unshortener either. It turns out that the Mac's ethernet port is now en11 rather than en0. Apple's old sample code checked only en0, and Apple's new sample code checks only en0 and en1, so that technique won't work. And the technique suggested by Chris Liscio won't work, because querying for kIOPrimaryInterface returned no results! The customer's Mac reported having no primary ethernet interface.
My solution was to query all built-in ethernet interfaces—in technical terms, kIOBuiltin devices of kIOEthernetInterfaceClass—and attempt to validate each interface's MAC address with the App Store receipt until a match was found. This might be the same technique suggested by Paulo Andrade, but that blog post contains no sample code.
Below is a snippet from my new Mac App Store receipt validation code.
mach_port_t master_port;
kern_return_t kernResult = IOMasterPort( MACH_PORT_NULL, &master_port );
if ( kernResult != KERN_SUCCESS )
return EXIT_FAILURE;
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
if ( matchingDict == NULL )
return EXIT_FAILURE;
CFMutableDictionaryRef propertyMatch = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
CFDictionarySetValue( propertyMatch, CFSTR(kIOBuiltin), kCFBooleanTrue );
CFDictionarySetValue( matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatch );
CFRelease( propertyMatch );
io_iterator_t iterator;
kernResult = IOServiceGetMatchingServices( master_port, matchingDict, &iterator ); // This actually releases matchingDict because CF_RELEASES_ARGUMENT
if ( kernResult != KERN_SUCCESS )
return EXIT_FAILURE;
BOOL hasMatch = NO;
do
{
io_object_t service = IOIteratorNext( iterator );
if ( service == IO_OBJECT_NULL )
break;
io_object_t parentService;
kernResult = IORegistryEntryGetParentEntry( service, kIOServicePlane, &parentService );
if ( kernResult == KERN_SUCCESS )
{
CFDataRef newMacAddress = (CFDataRef)IORegistryEntryCreateCFProperty( parentService, CFSTR( kIOMACAddress ), kCFAllocatorDefault, 0 );
if ( newMacAddress != NULL )
{
/* Validate MAS receipt and set hasMatch = YES if valid. */
CFRelease( newMacAddress );
}
IOObjectRelease( parentService );
}
IOObjectRelease( service );
} while ( !hasMatch );
IOObjectRelease( iterator );
I released a Link Unshortener update with the new code, which fixed my customer's problem. The Mac App Store receipt now validates, and the app launches. Moreover, I've received no reports of failures from other customers. So far, so good!
By the way, my Mac App Store receipt validation code doesn't require compiling and linking OpenSSL. To avoid that, I wrote a receipt parser using Apple's built-in CommonCrypto and Security frameworks. I won't post the full source code here, but if any Mac developers are interested, let me know. My code works only for upfront paid apps, however, not for In App Purchases.