Base64 encoding weirdness for iOS7 in-app purchase receipt server verification - objective-c

I've found some very odd issues with base64 encoding of iOS7 receipts.
At the moment (Xcode5/iOS7) have 2 methods for getting a receipt for an in-app purchase:
Deprecated method that returns a single receipt. [SKPaymentTransaction transactionReceipt]
A bundle of all receipts from location appStoreReceiptURL
My App sells web site based services using a credit system which makes server receipt validation necessary. The App also sells downloadable extensions. So a mixture of consumables and non-consumables. The non-consumables are downloaded from Apple so no verification required. The consumables are packages of credits used for purchasing services on the website.
When iOS7 & XCode5 launched I updated my App but struggled with the new bundled receipt located at appStoreReceiptURL. With the new style, all receipts bundled in one, my server got back this error from Apple's verifyReceipt sandbox
[status] => 21002
[exception] => java.lang.IllegalArgumentException
I gave up after seening posts on stackoverflow saying others had experienced the same issue and that the opinion at the time was it was a bug, or yet to be added feature, on Apple's part.
However I've been seeing issues with using the old deprecated method which has forced me back to trying again. After a lot of debugging and searching I finally worked out some of what was going wrong - as well as getting it working but in a very ugly way which I'm not confident with going live with.
I would have thought that NSData (plain byte buffer) encoded output wouldn't differ much.
Here's the weird part I'm seeing.
The original deprecated single receipts can be base64 encode either as 64 character lines with \r\n on the end or without. Apple verification server doesn't care. It's happy either way.
For the new receipt bundle it won't work unless 2 things are done. First the base64 encoding cannot have line breaks. I notice Apple has added it's own base64 encoder into iOS7. This is what I'm using to get an encoded output that works for both receipt types.
NSString *receiptDataString = [transactionReceipt base64EncodedStringWithOptions:0];
The 2nd thing that needs to be done is to search and replace all space characters from the received encoded receipt bundle on the server. i.e.
$receiptdata = str_replace(' ', '+', $receiptdata);
I happened to noticed this was a difference between the receipts being received by my server.
Why I don't know? I'm using AFNetworking-1.3.2's JSONRequestOperationWithRequest
NSMutableArray *pairs = [[NSMutableArray alloc] initWithCapacity:0];
for (NSString *key in parameters) {
[pairs addObject:[NSString stringWithFormat:#"%#=%#", key, [parameters objectForKey:key]]];
}
postData = [[pairs componentsJoinedByString:#"&"] dataUsingEncoding:NSUTF8StringEncoding];
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"https://myserver.com/index.php"]];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
Chris Prince claims to have it working with AFNetworking 2 in his reply to this question
Why could these two NSData receipts cause encoding/decoding problems like I'm seeing? I've been as detailed and clear as I can be here to help others as I see there's a lot of folks feeling the same pain here with this new receipt method Apple's introduced.

The answer turned out to be due to
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding
not escaping all special characters like :/?#[]#!$&’()*+,;= etc.
The new receipt Apple provides, once base64 encoded, contains characters which need escaping but are not escaped by the above iOS lib escape encoder. Looking around I found one on here which works well. See below for working code. (Server doesn't need to parse receipt code now and can use it straight out of $_POST)
// Lifted from:
// http://stackoverflow.com/questions/2159341/nsstring-method-to-percent-escape-for-url
//
- (NSString *)urlEncodeValue:(NSString *)str {
NSString *result = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)str, NULL, CFSTR(":/?#[]#!$&’()*+,;="), kCFStringEncodingUTF8));
return result;
}
// Encode and pair basic parameters
NSMutableArray *pairs = [[NSMutableArray alloc] initWithCapacity:0];
NSString *part;
for (NSString *key in parameters) {
NSString *encodedValue = [[parameters objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
part = [NSString stringWithFormat: #"%#=%#", encodedKey, encodedValue];
[pairs addObject:part];
[pairs addObject:[NSString stringWithFormat:#"%#=%#", key, [parameters objectForKey:key]]];
}
// Receipt encoded and paired separately
receiptDataString = [self urlEncodeValue:receiptDataString];
part = [NSString stringWithFormat: #"receipt=%#", receiptDataString];
[pairs addObject:part];
// Post data.
postData = [[pairs componentsJoinedByString:#"&"] dataUsingEncoding:NSUTF8StringEncoding];

Related

NSstream write encoding issues

Im trying to send a string using NSoutputstream , however i cant seem to get the encoding right , using dataWithContentsOfURL works
im using a nodejs TCP server with actionHero library.
it works using netcat and telnet.
- (IBAction)sendText:(id)sender {
NSString *response = [NSString stringWithFormat:#"%#", [_sendTextField.text stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSLog(#"writing %#",response);
///////////////////////////// this line works/////////////////////////////////////////////////////
// NSData *data = [[NSData alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"]]];
///////////////////////////// this line doesnt work/////////////////////////////////////////////////////
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSUTF8StringEncoding]];
//%u returns a non zero value
NSLog(#"%u",[outputStream write:[data bytes] maxLength:[data length]]);
}
i get a null streamError from handle stream Event method
Not knowing the content of response, I can't give you a specific answer as to why NSUTF8StringEncoding doesn't work with it. Generally speaking, however, if there is a byte sequence in your content that is incompatible with UTF-8, you're going to get nil when you call -dataUsingEncoding:.
There's a strategy that I learned from reading Mike Ash's blog (look under the section "Fallbacks"), and it's served me pretty well in situations such as yours.
To briefly sum it up, first try using NSUTF8StringEncoding. If that doesn't work, try using NSISOLatin1StringEncoding. And if that doesn't work, try using NSMacOSRomanStringEncoding. Mike's blog has the rationale for this.
Found the answer to my own question. turns out the problem is in actionHero's on data method where it looks for a /n which is not provided by the ios application. appended a \n and its fine now

Issues displaying an emoji icon in iOS app

I am having trouble figuring out how to get the emoji icons to display in the iPhone app I am working on. The issue is that then when someone posts something to the db from the app it gets displayed in the app as a square. What the app is getting from the server where the emojicon should be is something like this \uf604
Do anyone have any idea what I am doing wrong? I have been search for a good answer but so far I haven't found anything exactly related.
//more info
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
self.memoArray = [returnString JSONValue];
//when adding it to the label
cell.memoLabel.text = [itemData objectForKey: #"memo"];
What I have figured out is that for example '\Uf604' needs to be '\U0001F604'.
It's a bug in Json parser SBJSON. I have been using SBJson classes, in several projects, using several versions, and found some problems with complex unicodes that are used by some Emoji chars. That icons are represented with a very long unicode, I don't remember exactly the format, but it is like a double unicode. Inside SBJson I found some bugs where it parses large unicodes.
I did modify a method and it now works, but when I went to Stig's SBJson repository to inform about that, I found an open issue about unicodes: https://github.com/stig/json-framework/issues/115
Nevertheless, my solution was very different. In the version I was using, I just had to comment in the method scanUnicodeChar: all this if with its body:
/*
if (hi >= 0xd800) { // high surrogate char?
... if-body ... ...
}
*/
If your version includes that method and that 'if', try to delete it.
For other versions, perhaps the solution is as easy. Find a method named getStringToken: and inside it, inside a switch-case '\' statement, try to comment this way:
/*
if (SBStringIsSurrogateHighCharacter(hi)) {
..... body ....
} else if ....... {
.... ....
} else {
*/
CFStringAppendCharacters((CFMutableStringRef)acc, &hi, 1);
/*
}
*/
I have not tested this last one and surely something more have to be done in order to make everything work well.
Please follow following steps:
Convert Emoji characters to base64 and send to server.
On server side save base64 in database without decode.
When you want to display Emoji on Application then retrieve same base64 data from server.
Decode retrieve string and display on app.
Your Emoji character will display properly.
Note: When you want to Show on webPage then Decode data when you display data on WebPage.
Update to the latest SBJson, this issue was fixed.
https://github.com/stig/json-framework/tags

Xcode HTTP GET Request

I am making an app in which you type a value into a textbox, and the app sends an HTTP GET request to my webserver, ie www.mywebserver.server.com/ihiuahdiuehfiuseh?' + textbox variable' However, I have no idea how to work with Xcode. I am experienced in basic PHP and HTML, and advanced C++, but I am so baffled by this Xcode stuff. In all other languages I have worked with, you could look up something like "how to play a sound file in (language)", and you will get something like "oh yeah just do play(mp3url). But, with Xcode, you have to initiate the connection, initiate the variables, etc etc. I bought 2 $30 books, but I am still so confused. So, back to the point, I just need the textbox numerical number to be parsed after the ? in the URl to be parsed as a variable.
This is an example of a non-http get synchronized
You can find more
-(void)aget:(NSString *)iurl{
NSURL*url = [NSURL URLWithString:iurl];
NSURLRequest *res = [NSURLRequest requestWithURL:url];
NSOperationQueue*que=[NSOperationQueue new];
[NSURLConnection sendAsynchronousRequest:res queue:que completionHandler:^(NSURLResponse*rep,NSData*data,NSError*err){
if ([data length]> 0 && err == nil) {
NSString* rel=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",rel);
}else{
NSLog(#"isnull");
}
}
];
}
Through this launch him
NSString * str = [self getDataFrom: # "your url"];
     NSLog (# "% #", str);
If you have a UITextFiled, you would do the following
NSString baseUrl = #"www.mywebserver.server.com/ihiuahdiuehfiuseh?"
NSString variable = textField.text;
NSString absoluteURL = [NSString stringWithFormat:#"%#%#", baseUrl, variable];
//Send the absolute variable now to the server
If you go for a solution like Omar Abdelhafith suggests, don't forget to url-encode your 'querystring'. There is a method in the string class for this: "stringByAddingPercentEscapesUsingEncoding", but it's not perfect.
I've recently used the solution suggested here: http://simonwoodside.com/weblog/2009/4/22/how_to_really_url_encode/.

Url encodings with greek characters

I get the html source of a page to a NSString like this
NSString* url = #"example url";
NSURL *urlRequest = [NSURL URLWithString:url];
NSError *err = nil;
NSString *response = [NSString stringWithContentsOfURL:urlRequest encoding:kCFStringEncodingUTF8 error:&err];
a part of the response is like : 2 \u00cf\u0083\u00cf\u0087\u00cf\u008c\u00ce\u00bb\u00ce\u00b9\u00ce\u00b1
How can i have the Greek characters shown as they should in the NSString response?
The encoding of the page is "charset=iso-8859-7"
Ahhh, I understand your question a little bit better now.
The Apple-supplied native implementation of NSString doesn't know what to do with iso-8859-7 encoding.
You have two options.
1)
Try requesting different encodings to [NSString stringWithContentsOfURL: encoding: error:] to see if one successfully loads. My first attempt would be with NSISOLatin1StringEncoding.
2)
I found a third party library (and NSString category extension) that does do iso-8859-7 conversion. But to get access to CkoCharset will cost you (or your client) $290 USD. It might be a worthwhile investment to save time & hassle.
https://chilkatsoft.com/charset-objc.asp
and documentation is here:
http://www.chilkatsoft.com/refdoc/objcCkoCharsetRef.html

Generate JSON object with transactionReceipt

I've been the past days trying to test my first in-app purchse iphone application. Unfortunately I can't find the way to talk to iTunes server to verify the transactionReceipt.
Because it's my first try with this technology I chose to verify the receipt directly from the iPhone instead using server support. But after trying to send the POST request with a JSON onbject created using the JSON api from google code, itunes always returns a strange response (instead the "status = 0" string I wait for).
Here's the code that I use to verify the receipt:
- (void)recordTransaction:(SKPaymentTransaction *)transaction {
NSString *receiptStr = [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"algo mas",#"receipt-data",nil];
NSString *jsonString = [jsonDictionary JSONRepresentation];
NSLog(#"string to send: %#",jsonString);
NSLog(#"JSON Created");
urlData = [[NSMutableData data] retain];
//NSURL *sandboxStoreURL = [[NSURL alloc] initWithString:#"https://sandbox.itunes.apple.com/verifyReceipt"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"https://sandbox.itunes.apple.com/verifyReceipt"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"will create connection");
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
maybe I'm forgetting something in the request's headers but I think that the problem is in the method I use to create the JSON object.
HEre's how the JSON object looks like before I add it to the HTTPBody :
string to send: {"receipt-data":"{\n\t\"signature\" = \"AUYMbhY
...........
D0gIjEuMCI7Cn0=\";\n\t\"pod\" = \"100\";\n\t\"signing-status\" = \"0\";\n}"}
The responses I've got:
complete response {
exception = "java.lang.IllegalArgumentException: Property list parsing failed while attempting to read unquoted string. No allowable characters were found. At line number: 1, column: 0.";
status = 21002;
}
Thanks a lot for your guidance.
I have just fixed that after 2 days of struggling. You have to encode receipt using Base64 before inserting into json object. Like that (Ruby):
dataForVerification = {"receipt-data" => Base64.encode64(receipt)}.to_json
Base64 is not mentioned anywhere in the official docs (at least for SDK 3.0), only on a couple of blogs.
For instance, here the guy encodes the receipt in Base64 before passing it to the PHP server, but does not decode it back in PHP, thus sending Base64-encoded string to iTunes.
Re: "21002: java.lang.IllegalArgumentException: propertyListFromString parsed an object, but there's still more text in the string.:"
I fixed a similar issue in my code by wrapping the receipt data in {} before encoding.
The resulting receipt looks like:
{
"signature" = "A[...]OSzQ==";
"purchase-info" = "ew[...]fQ==";
"pod" = "100";
"signing-status" = "0";
}
Here's the code I use:
receipt = "{%s}" % receipt // This step was not specified - trial and error
encoded = base64.b64encode(receipt)
fullpost = '{ "receipt-data" : "%s" }' % encoded
req = urllib2.Request(url, fullpost)
response = urllib2.urlopen(req)
Apple's Response:
{"receipt":{"item_id":"371235", "original_transaction_id":"1012307", "bvrs":"1.0", "product_id":"com.foo.cup", "purchase_date":"2010-05-25 21:05:36 Etc/GMT", "quantity":"1", "bid":"com.foo.messenger", "original_purchase_date":"2010-05-25 21:05:36 Etc/GMT", "transaction_id":"11237"}, "status":0}
Good luck!