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:
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.