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
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With