title: iOS7 - receipts not validating at sandbox - error 21002 (java.lang.IllegalArgumentException)
body: <p>I'm converting an app from iOS6 to iOS7. Before I used the deprecated <code>transactionReceipt</code> method so now I'm trying the recommended methods to retrieve the receipt, and  then encode in base 64:</p>
  
  <pre><code>NSData *working = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
  // Tried 64 or 76 chars/line and LF or CR line endings
  NSString *receipt = [working base64EncodedStringWithOptions:kNilOptions];
  </code></pre>
  
  <p>The above is the only change in the code. Below is how I validate it, no changes:</p>
  
  <pre><code>dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  dispatch_async(queue,
       ^{
           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:&amp;encoding error:&amp;error];
  
           // check json and error
           // ... code omitted
      }
  </code></pre>
  
  <p>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:</p>
  
  <pre><code>// Encode as JSON
  $json = json_encode(array('receipt-data' =&gt; $receipt));
  // Try production first, if it doesn't work, then try the sandbox
  $working = postJSONToURL('https://buy.itunes.apple.com/verifyReceipt', $json, false);
  error_log('production - '.print_r($working, true));
  if (@$working['status'] !== 0) // === 21007)
      $working = postJSONToURL('https://sandbox.itunes.apple.com/verifyReceipt', $json, true);
  error_log('sandbox - '.print_r($working, true));
  </code></pre>
  
  <p>This is the error log output:</p>
  
  <pre><code>production - Array\n(\n    [status] =&gt; 21002\n    [exception] =&gt; java.lang.IllegalArgumentException\n)\n
  sandbox - Array\n(\n    [status] =&gt; 21002\n    [exception] =&gt; java.lang.IllegalArgumentException\n)\n
  </code></pre>
  
  <p>It looks like I'm throwing all kinds of exceptions over at Apple!</p>
  
  <p>Again the only difference is how the receipt is retrieved and encoded. Has anyone encountered this problem and fixed it?</p>
  
  <p>Thanks for reading.</p>
  
  <p>/YR</p>
  
  <p>As requested, code for PostJSONToURL:</p>
  
  <pre><code>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();
      curl_close($resource);
      //fclose($fp);
      return $contents;
  }
  </code></pre>
  
  <p>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:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>Note that the difference between successfully sending and not sending is less than 100 bytes.</p>