IOS receipt validation with empty in_app array

Hi, i've been collecting the in app transaction receipts for all purchases within my app, and i end up detecting a discrepancy between what i register and what apple says i earned.


When i check the receipts validity, all of then came with status = 0, but, just a few return with the in_app array filled. I really don't know whats is happening but im registering way more purchases than i receiving from apple.


this is where i detect the purchase:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                // Load the receipt from the app bundle.
                [self checkReceipt:transaction];
                break;


and this is where i send the receipt

- (void)checkReceipt:(SKPaymentTransaction *)transaction {
    NSURL   *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData  *receipt    = [NSData dataWithContentsOfURL:receiptURL];
    if (!receipt) {
        / No local receipt -- handle the error. */
        [Util notifyWithTitle:[Strings appinFail] message:nil];
      
        // Remove the transaction from the payment queue.
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        return;
    }
  
    NSString *base64    = [receipt base64EncodedStringWithOptions:0];


Does any one know why the receipt in_app array is empty and why am i detecting a difference between what i sold and what apple said i sold?

Replies

In response to your finding, I can only respond as to suggest how the application should handle this situation. I will leave it to others to speculate as to why this is happening. I refer you to Tech Note 2413, QA - My app validates its receipt with the App Store via paymentQueue:updatedTransactions: after a successful purchase. However, the returned receipt contains an empty in_app array rather than the expected products.

<https://developer.apple.com/library/ios/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPT-MY_APP_VALIDATES_ITS_RECEIPT_WITH_THE_APP_STORE_VIA_PAYMENTQUEUE_UPDATEDTRANSACTIONS__AFTER_A_SUCCESSFUL_PURCHASE__HOWEVER__THE_RETURNED_RECEIPT_CONTAINS_AN_EMPTY_IN_APP_ARRAY_RATHER_THAN_THE_EXPECTED_PRODUCTS_>


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

You are grabbing the receipt within updatedTransactions so it is a current receipt; no need to refresh it.

The receipt does not contain an IAP - that means the user's receipt indicates they did not purchase the IAP. This is supported by your reports from Apple saying few users purchased your IAP. So your question becomes 'why is updatedTransactions being called with a SKPaymentTransactionStatePurchased but with a receipt indicating no purchase?' The answer to that question may be that you are being hacked. The user is pushing a call into updatedTransactions without having made any purchase. This would explain the receipt and Apple's sales figures differing from yours.


The receipt indicates no IAPs were purchased - why do you think they were? Why are you crediting the app if the receipt does not say an IAP was purchased?

I've just been checking the receipt because i detected a huge difference in my sales reports and the apple sales reports.


I only credit the IAP if the product is in the transaction returned by "- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions" with the result "SKPaymentTransactionStatePurchased", this is how Apple says on the documentation i should do, i really dont know what is happening, i tested this on different devices and on different Apple stores and all works fine, but, at the same time i report at least 10 times more sales than apple. both on google analytics and my personal reports, i cant belive this is from hack alone.


The problem is that im getting the delegate "- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions" success response with the product in the transaction without Apple registering a valid purchase.

You will want to read about IAP hacks - I suspect a simple search on the internet will show you how to hack your app and many of your users may be doing that. In iOS5 it was very easy to jailbreak an iPhone and push a call to updatedTransactions into the app with a transaction that made it look like the app executed a purchase. To stop that hack Apple showed developers how to examine that receipt. It used to be easy; it has now gotten a little harder. For a receipt to be valid it has to have status==0 and be for the item(s) in question, otherwise it is a receipt for a different purchase.You are testing the receipt and discovering they are only for the original app not for the IAP. Unfortunately, you are giving the user IAP credit for an IAP purchase that they most likely did not make.


The reason why Apple is not registering a valid purchase is because there is no valid purchase, just a hacked call to updatedTransactions. Validate the receipt and grant IAP credit only after the receipt has proven valid for the purchase of the IAP.

I am having the same issue.

What I do before I release the purchase, is that my server ( not the App ) is verifiing the receipt-data.

Check that "status" is equal to 0 and that the receipt and that "bundle_id" matches your app bundle id.

Also add code to your app so it does not work on jailbroken iPhones.

Check that each receipt-data is used ONCE, the hackers have purchased my app and keep sending the same recept multiple times.

Also in the response look at the field "in_app" it should contain an Array with the identifier of your "product_id", if it is empty I think that means they have purchased a consumable product that has already been consumed ( ie: they are stealing from you ).

I have heard that their is a russian website that you change your DNS entries and their server validates ( Fakes ) itunes.

Hope this help.

Mike, Bluestratus Ltd.

The same thing is happening to me.


I'm sending the app receipt to a server and validating it there within "updatedTransactions" so I don't need to refresh it, right?


For now I'm validating only the status to be zero as the Apple docs say. I would also validate the content of in-app array, but I want to be sure that getting an empty in-app array is not a problem of my app, so it's clear I'm being hacked.

If you only check status then you accomplish very little. An easy hack would push a call to updatedTransactions with no transaction receipt (and with a bit more sophistication the receipt might be a copy of a different receipt). Your app will obtain the apps current receipt (the one with no IAPs - or the copy of a different receipt) and push that forward. You will get status==0 for that receipt - it's valid it just says 'no IAPs' (or someone else's IAPs).


You need to check that the receipt shows a purchase of your IAP and you need to verfify that the transactionIdentifier for the receiot is unique. Only then can you be certain that the device is presenting a receipt that indicates a purchase of your IAP and that the receipt is not copied from a different purchase.