Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify receipt for in App purchase

I have been playing around with in app purchases for a few days, everything works fine up until the point where I try to validate the receipt with the app store, as i am constantly getting back an invalid status.

I am passing the receipt data to my PHP server then forwarding from there to the app store and once I get a valid response I intend to add the receipt data to my database.

The store kit programming guide and the class references are less than useless for this particular area as they don't really give you any sort of example, I did find one useful article which helped me out a bit but something is still wrong.

Basically I am wondering if someone who has receipt validation working would be willing to share their code as I'm getting nowhere.

Thanks

like image 966
Andy Avatar asked Aug 19 '09 10:08

Andy


People also ask

How do I verify my Apple receipt?

Use the production URL https://buy.itunes.apple.com/verifyReceipt when your app is live in the App Store. For more information on these endpoints, see verifyReceipt. Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code.

Does Apple email the receipts of in app purchases?

You only get an email receipt, you don't get one via post, and you will get one for all purchases (including in-app purchases), and whether it's paid for via your balance, credit card or a combination of both.


1 Answers

First, there are a few typos in the posted code. Try this. (Disclaimer: Refactoring et. al is left as an exercise for the readership!)

- (BOOL)verifyReceipt:(SKPaymentTransaction *)transaction {     NSString *jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];           NSString *completeString = [NSString stringWithFormat:@"http://url-for-your-php?receipt=%@", jsonObjectString];                    NSURL *urlForValidation = [NSURL URLWithString:completeString];            NSMutableURLRequest *validationRequest = [[NSMutableURLRequest alloc] initWithURL:urlForValidation];                   [validationRequest setHTTPMethod:@"GET"];              NSData *responseData = [NSURLConnection sendSynchronousRequest:validationRequest returningResponse:nil error:nil];       [validationRequest release];     NSString *responseString = [[NSString alloc] initWithData:responseData encoding: NSUTF8StringEncoding];     NSInteger response = [responseString integerValue];     [responseString release];     return (response == 0); }  - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length {     static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";      NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];     uint8_t *output = (uint8_t *)data.mutableBytes;      for (NSInteger i = 0; i < length; i += 3) {         NSInteger value = 0;         for (NSInteger j = i; j < (i + 3); j++) {             value <<= 8;              if (j < length) {                 value |= (0xFF & input[j]);             }         }          NSInteger index = (i / 3) * 4;         output[index + 0] =                    table[(value >> 18) & 0x3F];         output[index + 1] =                    table[(value >> 12) & 0x3F];         output[index + 2] = (i + 1) < length ? table[(value >> 6)  & 0x3F] : '=';         output[index + 3] = (i + 2) < length ? table[(value >> 0)  & 0x3F] : '=';     }      return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; } 

You can make these Internal methods on the class that handles your SKPaymentTransactionObserver messages:

@interface YourStoreClass (Internal) - (BOOL)verifyReceipt:(SKPaymentTransaction *)transaction; - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length; @end 

Note: You could use something like libcrypto to handle base64 encoding, but then you're looking at export restrictions and extra steps at app approval time. But I digress ...

Then, wherever you intend to kick-off recording the transaction on your remote server, call verifyReceipt: with your transaction and make sure it comes back positive.

Meanwhile, on your server, here's some super-stripped-down PHP to handle things:

$receipt = json_encode(array("receipt-data" => $_GET["receipt"])); // NOTE: use "buy" vs "sandbox" in production. $url = "https://sandbox.itunes.apple.com/verifyReceipt"; $response_json = call-your-http-post-here($url, $receipt); $response = json_decode($response_json);  // Save the data here!  echo $response->status; 

Where call-your-http-post-here is your favorite HTTP post mechanism. (cURL is one possible choice. YMMV. PHP.net has the scoop!)

One thing that has me slightly concerned is the length of the payload in the URL going from the app to the server (via GET). I forget if there's a length issue there per the RFCs. Maybe it's OK, or maybe it's server-specific. (Readers: Advisement welcome on this part!)

There may also be some balking at making this a synchronous request. You may want to post it asynchronously and put up the ol' UIActivityIndicatorView or some other HUD. Case in point: That initWithData:encoding: call takes a loooooong time for me. A few seconds, which is a small eternity in iPhone land (or anywhere else online, for that matter). Showing some sort of indeterminate progress indicator may be advisable.

like image 167
Joe D'Andrea Avatar answered Oct 13 '22 19:10

Joe D'Andrea