Viewed   165 times

I'm converting an app from iOS6 to iOS7. Before I used the deprecated transactionReceipt method so now I'm trying the recommended methods to retrieve the receipt, and then encode in base 64:

NSData *working = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
// Tried 64 or 76 chars/line and LF or CR line endings
NSString *receipt = [working base64EncodedStringWithOptions:kNilOptions];

The above is the only change in the code. Below is how I validate it, no changes:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
         NSMutableString *url = [NSMutableString string];

         [url appendFormat:@"%@", WEB_SERVICE];
         [url appendFormat:@"receipt=%@", receipt];

         NSStringEncoding encoding;
         NSError *error = [NSError new];
         NSURL *URL = [NSURL URLWithString:url];
         NSString *json = [NSString stringWithContentsOfURL:URL usedEncoding:&encoding error:&error];

         // check json and error
         // ... code omitted

On the server side, this is the PHP code I use to verify the receipt, no change other than trying the sandbox for any error:

// Encode as JSON
$json = json_encode(array('receipt-data' => $receipt));
// Try production first, if it doesn't work, then try the sandbox
$working = postJSONToURL('', $json, false);
error_log('production - '.print_r($working, true));
if (@$working['status'] !== 0) // === 21007)
    $working = postJSONToURL('', $json, true);
error_log('sandbox - '.print_r($working, true));

This is the error log output:

production - Arrayn(n    [status] => 21002n    [exception] => java.lang.IllegalArgumentExceptionn)n
sandbox - Arrayn(n    [status] => 21002n    [exception] => java.lang.IllegalArgumentExceptionn)n

It looks like I'm throwing all kinds of exceptions over at Apple!

Again the only difference is how the receipt is retrieved and encoded. Has anyone encountered this problem and fixed it?

Thanks for reading.


As requested, code for PostJSONToURL:

function postJSONToURL($url, $json, $disableSSLVerify = false)
    $resource = curl_init($url);
    curl_setopt($resource, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($resource, CURLOPT_POSTFIELDS, $json);
    curl_setopt($resource, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($resource, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: '.strlen($json)));
    curl_setopt($resource, CURLOPT_HEADER, 0);
    if ($disableSSLVerify)
        curl_setopt($resource, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($resource, CURLOPT_SSL_VERIFYPEER, 0);
    //curl_setopt($resource, CURLOPT_VERBOSE, true);
    //curl_setopt($resource, CURLOPT_STDERR, $fp = fopen('/tmp/curl_output'.rand(1000, 9999).'.txt', 'w'));
    $contents = json_decode(curl_exec($resource), true);
    if (!$contents)
        $contents = array();
    return $contents;

New details after some experimenting, have determined that sending the existing data as base 64 encoded is likely encroaching on some internal limit. If it exceeds some internal limit, the data isn't even sent, it fails locally on the device, below that, it is sent. Columns are: data format, size of encoded data, whether it reached the server:

raw receipt data         5K  N/A
base64 no options     6.66K  yes
base64 76 chars/line  6.75K  no
base64 64 chars/line  6.77K  no
hex coded               10K  no

Note that the difference between successfully sending and not sending is less than 100 bytes.



I've had this problem and looked everywhere, including on Apple's development forums. Apple will give a couple of stock replies, and that's it. I think it's a bug on Apple's side. Validation locally on the device will work, so try to convert to that. If you absolutely must use server side validation, only transactionReceipt seems to work right now.

The function is just deprecated, not banned, so I would just use it and hope Apple approves of the app. In fact, it's what I just did, fingers crossed, waiting for approval.

You can turn off the warning in Xcode by bracketing your code like this:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// code using transactionReceipt
#pragma clang diagnostic pop
Wednesday, August 3, 2022

There seems to be a bug going on just now. Our logs show increased errors in both production and sandbox. So far we haven't seen any solution yet.


This has been fixed.

Voting to close this issue to prevent confusing future similar problems.

Monday, December 12, 2022

You'll have to switch the cURL request to use TLS 1.2 in order to use the PayPal sandbox. I'm in the same boat, and there's no way around it, unfortunately. They just activated the change on the sandbox environment a few days ago.

Tuesday, August 9, 2022

Use AutoLayout and fix the position of the button to the bottom right corner.

Friday, September 2, 2022

I believe you're looking for SKStoreProductViewController.

Step 1, Import StoreKit:

#import <StoreKit/StoreKit.h>

Step 2, conform to the SKStoreProductViewControllerDelegate protocol.

@interface ViewController : UIViewController<SKStoreProductViewControllerDelegate>

Step 3, show the StoreKit view controller at your convenience:

SKStoreProductViewController *productVC = [[SKStoreProductViewController alloc] init];
productVC.delegate = self;

NSDictionary *productParameters = @{ SKStoreProductParameterITunesItemIdentifier: @"376558836" };
[productVC loadProductWithParameters:productParameters completionBlock:^(BOOL result, NSError *error) {
    if (error) {
        // sad face :(

    if (result) {
        [self presentViewController:productVC animated:NO completion:nil];

Step 4: Change 376558836 to be the app ID of the apps you want users to buy. That's one of my apps. You should buy it ;)

Step 5: Profit!

Sunday, September 11, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :