iosin-app-purchasermstore

Apple Receipt Device Hash Validation


I'm trying to validate that the receipt is for this particular device using the code from a popular library for Receipt Validation called RMStore:

NSUUID * uuid = [[UIDevice currentDevice] identifierForVendor];
uuid_t uuidBytes;
[uuid getUUIDBytes:uuidBytes];

NSMutableData * data = [[NSMutableData alloc] init];
[data appendBytes:uuidBytes length:sizeof(uuidBytes)];
[data appendData:_parsedReceipt.opaqueValue];
[data appendData:_parsedReceipt.bundleIdentifierData];

NSMutableData * computedHash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH];
SHA1(data.bytes, data.length, computedHash.mutableBytes);

return [computedHash isEqualToData:_parsedReceipt.hash];

But the two hashes are not equal. Is there something wrong with the code?

Edit

    SKReceiptRefreshRequest * request = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{SKReceiptPropertyIsRevoked: @YES}];
    [request setDelegate:self];
    [request start];

After I re-fetch the receipt once, the hashes start to match. This is the most bizarre behavior I have seen. Does anyone have an idea why this may happen?


Solution

  • As indicated in the answer from where you took that code, Apple recommends to refresh the receipt if validation fails. This is what RMStore does to validate a receipt/transaction:

    RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
    const BOOL verified = [self verifyTransaction:transaction inReceipt:receipt success:successBlock failure:nil]; // failureBlock is nil intentionally. See below.
    if (verified) return;
    
    // Apple recommends to refresh the receipt if validation fails on iOS
    [[RMStore defaultStore] refreshReceiptOnSuccess:^{
        RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
        [self verifyTransaction:transaction inReceipt:receipt success:successBlock failure:failureBlock];
    } failure:^(NSError *error) {
        [self failWithBlock:failureBlock error:error];
    }];