Validating iOS in-app non-subscription purchases

We just launched the iOS version of our app that includes consumable non-subscription in-app purchases. The first day live we already had fake in-app purchases from hacked iOS devices. Juce does not provide a means to get the signed receipt data for an in-app purchase that can be sent to our server to then be validated with Apple’s server, as Apple outlines here:

https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html

So I made some minor changes to the in-app purchasing code in order to get the signed receipt data sent to our listener via the productPurchaseFinished() callback.

In juce/juce_product_unlocking/in_app_purchases/juce_InAppPurchases.h I added the purchaseReceipt to the Purchase struct (inserted at line 88):

    /** CNS: for iOS only: this is the purchase receipt that should be sent to the application's server for verifying the purchase with Apple's server. */
    String purchaseReceipt;

In juce/juce_product_unlocking/native/juce_ios_InAppPurchases.cpp I added code in processTransactionFinish() to get and set the purchase receipt (starting at line 431):

    // CNS: get the purchase receipt to send along with the other purchase data
    String purchaseReceipt;
    if (success)
    {
        auto* receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];

        if (auto* receiptData = [NSData dataWithContentsOfURL: receiptURL])
        {
            NSString* receiptString = [receiptData base64EncodedStringWithOptions:0];
            purchaseReceipt = nsStringToJuce(receiptString);
        }
    }

    // CNS: added the purchaseReceipt to the Purchase object
    Purchase purchase { orderId, productId, packageName, purchaseTime, {}, purchaseReceipt };

Now our listener callback gets the signed receipt data, passes it to our server, and our server does real-time validation with Apple’s server. This validation has to come from our server since a connection from the client device to Apple cannot be trusted.

which OS were you supporting? is iOS12 and 11 jailbroken?

We support iOS 11 and up. We log all invalid purchase attempts. The offending users have devices with iOS versions of 11.1, 11.2.6, 11.3.1 and 11.4.1.