IOS receipt validation error 21002

John Snow picture John Snow · Sep 29, 2015 · Viewed 18k times · Source

I'm trying to use receipt validation with my server side. Everything is ok, but sometimes I see strange: 10 times validation is OK, but on 11 i get 21002 error. I dont know what to do. Sometimes I get error 21002 when I validate receipt first time after launch app.

App side:

func validateReceipt(productID: String) {

    let receipt = NSData(contentsOfURL: NSBundle.mainBundle().appStoreReceiptURL!)!

    let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))

    let request = NSMutableURLRequest(URL: NSURL(string: "my_server_url")!)

    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    request.HTTPBody = receiptdata.dataUsingEncoding(NSUTF8StringEncoding)

    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary

        if (error != nil) {
            print(error!.localizedDescription)
            let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
            print("Error could not parse JSON: '\(jsonStr)'")
        }
        else {
            if let parseJSON = json {
                 if String(parseJSON["status"]! == "ok" {
                     //do something
                     print("Validate OK")
                        }else{
                            print("Validate NOK")
                    }
            }
            else {
                let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
                print("Receipt Error: \(jsonStr)")
            }
        }
    })

    task.resume()
}

server side php script:

function getReceiptData($receipt)
{
$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';

$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $receipt);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$errmsg = curl_error($ch);
curl_close($ch);
$msg = $response.' - '.$errno.' - '.$errmsg;
echo $response;
}

foreach ($_POST as $key=>$value){
$newcontent .= $key.' '.$value;
}

$new = trim($newcontent);
$new = trim($newcontent);
$new = str_replace('_','+',$new);
$new = str_replace(' =','==',$new);

if (substr_count($new,'=') == 0){
if (strpos('=',$new) === false){
    $new .= '=';
}
}

$new = '{"receipt-data":"'.$new.'"}';
$info = getReceiptData($new);

Everything I do based on example http://www.brianjcoleman.com/tutorial-receipt-validation-in-swift/

So, sometimes I feel that app send to serverside wrong receipt and php script cant parse it and I receive 21002 error status. Any suggestion?

Answer

null picture null · Sep 29, 2015

Try removing from receipt the characters '\n' and '\r' and replacing '+' with'%2B' before sending it to the server. Something like this:

 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
 NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
 NSString *receiptDataString = [receipt base64EncodedStringWithOptions:0];
 receiptDataString=[receiptDataString stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
 receiptDataString=[receiptDataString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
 receiptDataString=[receiptDataString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
 NSString *postDataString = [NSString stringWithFormat:@"receipt-data=%@", receiptDataString];
 NSString *length = [NSString stringWithFormat:@"%lu", (unsigned long)[postDataString length]];
 [request setValue:length forHTTPHeaderField:@"Content-Length"];
 [request setHTTPBody:[postDataString dataUsingEncoding:NSASCIIStringEncoding]];