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);
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:&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('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));
This is the error log output:
production - Array
(
[status] => 21002
[exception] => java.lang.IllegalArgumentException
)
sandbox - Array
(
[status] => 21002
[exception] => java.lang.IllegalArgumentException
)
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.
/YR
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();
curl_close($resource);
//fclose($fp);
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.
See Question&Answers more detail:
os