Asked  7 Months ago    Answers:  5   Viewed   51 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);
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 - 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.

/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.

 Answers

74

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, March 31, 2021
 
CBroe
answered 7 Months ago
68

Quote from The Swift Programming Language, which answers your question:

“Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:”

Safety check 1 “A designated initializer must ensure that all of the “properties introduced by its class are initialized before it delegates up to a superclass initializer.”

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11

Tuesday, June 1, 2021
 
medhybrid
answered 5 Months ago
81

You should first correct your validating events this way:

private void textBox1_Validating(object sender, CancelEventArgs e)
{
    Regex regex1 = new Regex(@"^[a-zA-Z]+$");
    if (!regex1.IsMatch(textBox1.Text))
    {
        //To set validation error
        errorProvider1.SetError(textBox1, "Nosaukums nedr?skt satur?t ciparus!");
        //To say the state of control in invalid
        e.Cancel = true;
    }
    else
    {
        //To clear the validation error
        this.errorProvider1.SetError(this.textBox1, "");
    }
}

Then you should use ValidateChildren method to check if there is a validation error or not, then you can get a list of all errors and show to user this way:

private void button1_Click(object sender, EventArgs e)
{
    if (this.ValidateChildren())
    {
        //Here the form is in valid state
        //Do what you need when the form is valid
    }
    else
    {
        var listOfErrors = this.errorProvider1.ContainerControl.Controls.Cast<Control>()
                               .Select(c => this.errorProvider1.GetError(c))
                               .Where(s => !string.IsNullOrEmpty(s))
                               .ToList();
        MessageBox.Show("Please correct validation errors:n - " +
            string.Join("n - ", listOfErrors.ToArray()),
            "Error",  
            MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

A Sample screenshot:

enter image description here

Note:

  • You should not use Clear method of error provider to set valid state to control, you should use SetError, for example this.errorProvider1.SetError(textBox2, "");
  • You should call e.Cancel=true when there is a validation error.
  • In sample codes I assume that all your controls including the error provider placed directly on your form and not in a container control.
  • I also recommend to change validation behavior of form by setting AutoValidate property of form to EnableAllowFocusChange in design time or by code in Load event of form this way:

To change validation behavior of form:

this.AutoValidate = System.Windows.Forms.AutoValidate.EnableAllowFocusChange;
Friday, June 11, 2021
 
Eddas
answered 5 Months ago
38

Here is a deprecated transaction (single transaction) style receipt:

ewoJInNpZ25hdHVyZSIgPSAiQW5USlN6UUFqZWhXWW1ucWxvZk9ZVnFyWEo1MVVOWnI5Ly8ySFhxM01COWkyYVBqVmlsdjM4aXhtWm9PLzlZZlBsUkhZRHVzWFQySXBZYkRzNHBGWk53L21RTDFUemtJSWV0WWVhNE95anVWNUtsdUVCNExLVm9sN25tSGZkMjdISTZQTTZqQkRaS0xtcGt0bU5WQ21mbmhlVCtqbE1qTHg3ZVpLakhTRmhsUkFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NCdXA0K1BBaG0vTE1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEUwTURZd056QXdNREl5TVZvWERURTJNRFV4T0RFNE16RXpNRm93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNbVRFdUxnamltTHdSSnh5MW9FZjBlc1VORFZFSWU2d0Rzbm5hbDE0aE5CdDF2MTk1WDZuOTNZTzdnaTNvclBTdXg5RDU1NFNrTXArU2F5Zzg0bFRjMzYyVXRtWUxwV25iMzRucXlHeDlLQlZUeTVPR1Y0bGpFMU93QytvVG5STStRTFJDbWVOeE1iUFpoUzQ3VCtlWnRERWhWQjl1c2szK0pNMkNvZ2Z3bzdBZ01CQUFHamNqQndNQjBHQTFVZERnUVdCQlNKYUVlTnVxOURmNlpmTjY4RmUrSTJ1MjJzc0RBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkRZZDZPS2RndElCR0xVeWF3N1hRd3VSV0VNNk1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQWVhSlYyVTUxcnhmY3FBQWU1QzIvZkVXOEtVbDRpTzRsTXV0YTdONlh6UDFwWkl6MU5ra0N0SUl3ZXlOajVVUllISytIalJLU1U5UkxndU5sMG5rZnhxT2JpTWNrd1J1ZEtTcTY5Tkluclp5Q0Q2NlI0Szc3bmI5bE1UQUJTU1lsc0t0OG9OdGxoZ1IvMWtqU1NSUWNIa3RzRGNTaVFHS01ka1NscDRBeVhmN3ZuSFBCZTR5Q3dZVjJQcFNOMDRrYm9pSjNwQmx4c0d3Vi9abEwyNk0ydWVZSEtZQ3VYaGRxRnd4VmdtNTJoM29lSk9PdC92WTRFY1FxN2VxSG02bTAzWjliN1BSellNMktHWEhEbU9Nazd2RHBlTVZsTERQU0dZejErVTNzRHhKemViU3BiYUptVDdpbXpVS2ZnZ0VZN3h4ZjRjemZIMHlqNXdOelNHVE92UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUxTFRFeExURTNJREE0T2pNME9qUXhJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakUwTkRjNE1qRXlPREV3TURBaU93b0pJblZ1YVhGMVpTMXBaR1Z1ZEdsbWFXVnlJaUE5SUNKa1pEQmxNek5sWW1ZMU5tSmlNV1l6WVdRMFl6SmtZbUZoTjJFd1lqTXpZV00yT0dJd1pUZzFJanNLQ1NKdmNtbG5hVzVoYkMxMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaU1UQXdNREF3TURFNE1EWXpOakl5TmlJN0Nna2laWGh3YVhKbGN5MWtZWFJsSWlBOUlDSXhORFE0TXpRMk9EZ3hNREF3SWpzS0NTSjBjbUZ1YzJGamRHbHZiaTFwWkNJZ1BTQWlNVEF3TURBd01ERTRNVEk1T0Rjek5DSTdDZ2tpYjNKcFoybHVZV3d0Y0hWeVkyaGhjMlV0WkdGMFpTMXRjeUlnUFNBaU1UUTBOemMzT0RBNE1UQXdNQ0k3Q2draWQyVmlMVzl5WkdWeUxXeHBibVV0YVhSbGJTMXBaQ0lnUFNBaU1UQXdNREF3TURBek1Ea3pPRGN4TXlJN0Nna2lZblp5Y3lJZ1BTQWlNU0k3Q2draWRXNXBjWFZsTFhabGJtUnZjaTFwWkdWdWRHbG1hV1Z5SWlBOUlDSTBSVFZGTVVFelFpMUdNREZCTFRRNE5UVXRPREl3UmkxR016UTVSakV5TkRJeE5EZ2lPd29KSW1WNGNHbHlaWE10WkdGMFpTMW1iM0p0WVhSMFpXUXRjSE4wSWlBOUlDSXlNREUxTFRFeExUSXpJREl5T2pNME9qUXhJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkltbDBaVzB0YVdRaUlEMGdJakV3TWpnNU5UQTNPVGNpT3dvSkltVjRjR2x5WlhNdFpHRjBaUzFtYjNKdFlYUjBaV1FpSUQwZ0lqSXdNVFV0TVRFdE1qUWdNRFk2TXpRNk5ERWdSWFJqTDBkTlZDSTdDZ2tpY0hKdlpIVmpkQzFwWkNJZ1BTQWllV1ZoY214NUlqc0tDU0p3ZFhKamFHRnpaUzFrWVhSbElpQTlJQ0l5TURFMUxURXhMVEU0SURBME9qTTBPalF4SUVWMFl5OUhUVlFpT3dvSkltOXlhV2RwYm1Gc0xYQjFjbU5vWVhObExXUmhkR1VpSUQwZ0lqSXdNVFV0TVRFdE1UY2dNVFk2TXpRNk5ERWdSWFJqTDBkTlZDSTdDZ2tpWW1sa0lpQTlJQ0pqYjIwdWJXSmhZWE41TG1sdmN5NWtaVzF2SWpzS0NTSndkWEpqYUdGelpTMWtZWFJsTFhCemRDSWdQU0FpTWpBeE5TMHhNUzB4TnlBeU1Eb3pORG8wTVNCQmJXVnlhV05oTDB4dmMxOUJibWRsYkdWeklqc0tDU0p4ZFdGdWRHbDBlU0lnUFNBaU1TSTdDbjA9IjsKCSJlbnZpcm9ubWVudCIgPSAiU2FuZGJveCI7CgkicG9kIiA9ICIxMDAiOwoJInNpZ25pbmctc3RhdHVzIiA9ICIwIjsKfQ==

And here is a Grand Unified Receipt (multiple transactions):

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

Both from sandbox (sorry can't give you any production ones). Enjoy.

Edit: It occurs to me that would won't be able to communicate to Apple's servers with these receipt without my shared secret (not something I am willing to disclose).

However you can decode them, if you're using ruby I wrote a gem called ItunesReceiptDecoder. I also have a gem called ItunesReceiptValidator that also provides the remote calls to Apple.

Sunday, August 8, 2021
 
stand
answered 3 Months ago
36

As of iOS 7, SKMutablePayment has an applicationUsername property that can be set when a payment is made. And, SKPaymentQueue has the restoreCompletedTransactionsWithApplicationUsername: method (link).

Apple's guidance is to hash your server-side user ID and pass that up on purchase and restore. So, in your case, if iTunes User A purchases the product it will be bound to your server-side user ID for that purchaser. Then, if iTunes User B attempts to restore, restoration will fail, because they would still be passing up the same server-side user ID.

You'll also have to track (on the server) that your server-side user ID has purchased the product. Otherwise, if iTunes User B attempts to restore you'll need to know the difference between they having logged into the device with a different iTunes account and they having never purchased. And of course, you'll also want to prevent the same server-side user from purchasing the product twice, under different iTunes accounts.

Caveats:

  • If you already have purchases made in production, this most likely won't work.
  • iOS 7+
  • The "already purchased" scenario I mentioned above.
Wednesday, September 29, 2021
 
shin
answered 3 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :