Download File From short URL - cocoa-touch

I want to download a sound file from a short URL (like: www.adjix.com)
When I try from normal link, it's OK, but from short URL, how first redirect and then download
I use this part of code to create request:
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlToSound]];
NSURLConnection *theConnection =[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
self.receiveData = [[NSMutableData data] retain];
}
And this code to view header:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[receiveData setLength:0];
if ([response isKindOfClass:[NSHTTPURLResponse self]] ) {
NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields];
NSLog(#"headers: %#", headers);
}
}
When I try to download directly, link to MP3 header is:
"Accept-Ranges" = bytes;
Connection = "Keep-Alive";
"Content-Length" = 21316;
"Content-Type" = "audio/mpeg";
Date = "Sat, 07 Feb 2009 16:01:34 GMT";
Etag = "\"2d810-5344-7dda240\"";
"Keep-Alive" = "timeout=15, max=100";
"Last-Modified" = "Sat, 25 Jun 2005 12:26:41 GMT";
Server = Apache;
When I try to download file with a short URL (adjix.com/3na3), the header is:
"Cache-Control" = "max-age=60";
Connection = close;
"Content-Length" = 692;
"Content-Type" = "text/html";
Date = "Sat, 07 Feb 2009 19:18:23 GMT";
Expires = "Sat, 07 Feb 2009 19:19:23 GMT";
Server = "Apache/1.3.41 (Darwin) mod_ssl/2.8.31 OpenSSL/0.9.7l";

Can you provide more information? In particular, it would be good to mention the classes you're using to perform the HTTP request, as well as what breaks when you use the redirected URL.

You may want to try the GTMHTTPFetcher class, which is part of Google Toolbox for Mac. It handles redirection for you.

NSURLConnection has a delegate method which you can implement specifically to handle redirects. However, it may be that the server you are connecting to is behaving differently because of your request's user agent string.

Related

NSURLCache: inconsistent behaviour

I was observirng some strange behaviour of my app sometime caching responses and sometime not caching them (all the responses have Cache-Control: max-age=600).
The test is simple: I did a test.php script that was just setting the headers and returning a simple JSON:
<?php
header('Content-Type: application/json');
header('Cache-Control: max-age=600');
?>
{
"result": {
"employeeId": "<?php echo $_GET['eId']; ?>",
"dateTime": "<?php echo date('Y-m-d H:i:s'); ?>'" }
}
This is the response I get from the PHP page:
HTTP/1.1 200 OK
Date: Thu, 28 Nov 2013 11:41:55 GMT
Server: Apache
X-Powered-By: PHP/5.3.17
Cache-Control: max-age=600
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json
{
"result": {
"employeeId": "",
"dateTime": "2013-11-28 11:41:55'"
}
}
Then I've created a simple app and added AFNetworking library.
When I call the script with few parameters, the cache works properly:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *params = #{
#"oId": #"4011",
#"eId": self.firstTest ? #"1" : #"0",
#"status": #"2031",
};
[manager GET:#"http://www.mydomain.co.uk/test.php" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
NSLog(#"Cache current memory usage (after call): %d", [cache currentMemoryUsage]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
But when I increase the number of parameters, like:
NSDictionary *params = #{
#"organizationId": #"4011",
#"organizationId2": #"4012",
#"organizationId3": #"4013",
#"organizationId4": #"4014",
#"organizationId5": #"4015",
#"organizationId6": #"4016",
#"eId": self.firstTest ? #"1" : #"0",
#"status": #"2031",
};
it doesn't work anymore and it execute a new request each time it is called.
I've done many tests and it seems to me that it is related to the length of the URL, because if I includes this set of params:
NSDictionary *params = #{
#"oId": #"4011",
#"oId2": #"4012",
#"oId3": #"4013",
#"oId4": #"4014",
#"oId5": #"4015",
#"oId6": #"4016",
#"eId": self.firstTest ? #"1" : #"0",
#"status": #"2031",
};
It works!!
I've done many tests and that's the only pattern I've found...
To exclude AFNetworking from the equation, I've created another test program that uses NSURLConnection only and I can see the same behaviour so it's not AFNetworking and definitely NSURLCache. This is the other test:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.mydomain.co.uk/test.php?eId=%#&organizationId=4011&organizationId2=4012&organizationId3=4013&organizationId4=4014&organizationId5=4015&organizationId6=4016", self.firstTest ? #"1" : #"0"]]; // doesn't work
//NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.mydomain.co.uk/test.php?eId=%#&oId=4011&oId2=4012&oId3=4013&oId4=4014&oId5=4015&oId6=4016", self.firstTest ? #"1" : #"0"]]; // work
//NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.mydomain.co.uk/test.php?eId=%#", self.firstTest ? #"1" : #"0"]]; // work
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error == nil) {
// Parse data here
NSString *responseDataStr = [NSString stringWithUTF8String:[data bytes]];
NSLog(#"Response data: %#", responseDataStr);
}
I've also tried to establish how many characters in the URL will trigger the problem but even in this case I've got strange results:
This one is 112 characters long and it doesn't work:
http://www.mydomain.co.uk/test.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&orgaId4=4
This one is 111 characters long and it works:
http://www.mydomain.co.uk/test.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&orgId4=4
Ive renamed the PHP script to see if the first part of the URL would matter and I've got a strange behaviour again:
This one is 106 characters long and it doesn't work:
http://www.mydomain.co.uk/t.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&org=40
This one is 105 characters long and it works:
http://www.mydomain.co.uk/t.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&org=4
So I've removed 3 characters from the page name and I've got a working threshold 6 characters lower.
Any suggestion?
Thanks,
Dem
I am witnessing something similar with certain responses not being cached by NSURLCache and I have come up with another possible reason:
In my case I have been able to ascertain that the responses not being cached are the ones that are returned using Chunked transfer-encoding. I've read elsewhere that NSURLCache should cache those after iOS 6 but for some reason it doesn't in my case (iOS 7.1 and 8.1).
I see that your example response shown here, also has the Transfer-Encoding: chunked header.
Could it be that some of your responses are returned with chunked encoding (those that are not cached) and some are not (those that are cached)?
My back-end is also running PHP on Apache and I still can't figure out why it does that...
Probably some Apache extension...
Anyway, I think it sounds more plausible than the request URL length scenario.
EDIT:
It's been a while, but I can finally confirm that in our case, it is the chunked transfer encoding that causes the response not to be cached. I have tested that with iOS 7.1, 8.1, 8.3 and 8.4.
Since I understand that it is not always easy to change that setting on your server, I have a solution to suggest, for people who are using AFNetworking 2 and subclassing AFHTTPSessionManager.
You could add your sub-class as an observer for AFNetworking's AFNetworkingTaskDidCompleteNotification, which contains all the things you will need to cache the responses yourself. That means: the session data task, the response object and the response data before it has been processed by the response serializer.
If your server uses chunked encoding for only a few of its responses, you could add code in -(void)didCompleteTask: to only cache responses selectively. So for example you could check for the transfer-encoding response header, or cache the response based on other criteria.
The example HTTPSessionManager sub-class below caches all responses that return any data:
MyHTTPSessionManager.h
#interface MyHTTPSessionManager : AFHTTPSessionManager
#end
MyHTTPSessionManager.m
#import "MyHTTPSessionManager.h"
#implementation MyHTTPSessionManager
+ (instancetype)sharedClient {
static MyHTTPClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:_sharedClient selector:#selector(didCompleteTask:) name:AFNetworkingTaskDidCompleteNotification object:nil];
});
return _sharedClient;
}
- (void)didCompleteTask:(NSNotification *)notification {
NSURLSessionDataTask *task = notification.object;
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
NSData *responseData = notification.userInfo[AFNetworkingTaskDidCompleteResponseDataKey];
if (!responseData.length) {
// Do not cache empty responses.
// You could place additional checks above to cache responses selectively.
return;
}
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:responseData];
[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:task.currentRequest];
}
I tried to come up with some sort of cleaner solution, but it seems that AFNetworking does not provide a callback or a delegate method that returns everything we need early enough - that is, before it has been serialized by the response serializer.
Hope people will find this helpful :)
Did you try to configure
NSURLRequestCachePolicy
for NSURLRequest
+ (id)requestWithURL:(NSURL *)theURL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval
These constants are used to specify interaction with the cached responses.
enum
{
NSURLRequestUseProtocolCachePolicy = 0,
NSURLRequestReloadIgnoringLocalCacheData = 1,
NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4,
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,
NSURLRequestReturnCacheDataDontLoad = 3,
NSURLRequestReloadRevalidatingCacheData = 5
};
typedef NSUInteger NSURLRequestCachePolicy;
You could investigate what your cached response is from the sharedURLCache by subclassing NSURLProtocol and overriding startLoading:
add in AppDelegate application:didFinishLaunchingWithOptions:
[NSURLProtocol registerClass:[CustomURLProtocol class]];
Then create a subclass of NSURLProtocol (CustomURLProtol) and override startLoading
- (void)startLoading
{
self.cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
if (self.cachedResponse) {
[self.client URLProtocol:self
didReceiveResponse:[self.cachedResponse response]
cacheStoragePolicy:[self.cachedResponse storagePolicy]];
[self.client URLProtocol:self didLoadData:[self.cachedResponse data]];
}
[self.client URLProtocolDidFinishLoading:self];
}
self.cachedResponse is a property NSCachedURLResponse i've added. You can see if anything is wrong with any cachedResponse here.

POST request with AFNetworking 2.0 - AFHTTPSessionManager

Hej,
I am struggling with doing a POST request to the parse REST API. I am using AFNetworking 2.0. My code for the AFHTTPSessionManager Subclass looks as follows:
+ (ParseAPISession *)sharedSession {
static ParseAPISession *sharedSession = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSession = [[ParseAPISession alloc] initWithBaseURL:[NSURL URLWithString:kSDFParseAPIBaseURLString]];
});
return sharedSession;
}
And:
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (self) {
[self.requestSerializer setValue:kSDFParseAPIApplicationId forHTTPHeaderField:#"X-Parse-Application-Id"];
[self.requestSerializer setValue:kSDFParseAPIKey forHTTPHeaderField:#"X-Parse-REST-API-Key"];
}
return self;
}
I am doing the request like this:
[[ParseAPISession sharedSession] POST:#"ClassName" parameters: [NSDictionary dictionaryWithObjectsAndKeys:#"name", #"name", nil]
success:^(NSURLSessionDataTask *task, id abc) {
NSLog(#"%#", abc);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#", error);
}];
Doing this I always get this kind of error:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x8c72420 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
Since the GET Request works like a charm I am quite confused why I can’t POST something. Can anybody halp me with this problem?
Best regards!
UPDATE
Happily after testing around a lot this Error message isn't displayed anymore, unfortuatelly another appeared:
<NSHTTPURLResponse: 0x8b96d40>
{ URL: https://api.parse.com/1/users }
{ status code: 400,
headers {
"Access-Control-Allow-Origin" = "*";
"Access-Control-Request-Method" = "*";
"Cache-Control" = "no-cache";
Connection = "keep-alive";
"Content-Length" = 130;
"Content-Type" = "application/json; charset=utf-8";
Date = "Wed, 30 Oct 2013 20:01:58 GMT";
Server = "nginx/1.4.2";
"Set-Cookie" = "_parse_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlNjIxZjUxMzY3NWVhZWJmMDYyYWYwMGJiZTQ3MThmMWE%3D--851bd31b07e7dba2c5f83bb13a8d801ecbea42c4; domain=.parse.com; path=/; expires=Fri, 29-Nov-2013 20:01:58 GMT; secure; HttpOnly";
Status = "400 Bad Request";
"X-Runtime" = "0.060910";
"X-UA-Compatible" = "IE=Edge,chrome=1";
} }
Can anyone tell me what the Status: 400 Bad Request is telling me and how I can get rid of it?
This error means that your POST request went through, the server is successfully returning you some data, which NSJSONSerialization is having trouble parsing.
You probably need to set your AFJSONResponseSerializer to allow JSON fragments.
In the init method of your AFHTTPSessionManager subclass:
AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[self setResponseSerializer:responseSerializer];
If this doesn't work, you probably have an encoding issue. From the NSJSONSerialization class reference:
The data must be in one of the 5 supported encodings listed in the JSON specification: UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE. The data may or may not have a BOM. The most efficient encoding to use for parsing is UTF-8, so if you have a choice in encoding the data passed to this method, use UTF-8.
Check the encoding type sent by your server.
Finally, you can either set breakpoints inside of AFNetworking, or set up AFNetworkActivityLogger, which will log requests as they are sent and received to your console. This tool is incredibly helpful for debugging this type of issue.
This worked for me :
in the AFHTTPSessionManager subclass initialise it with the following serialisers:
[self setRequestSerializer:[AFHTTPRequestSerializer serializer]];
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];

ASIHttprequest with Apache CXF Restful webservices

I am working on a project involving Apache CXF at server side providing RESTful webservice, taking in and out in JSON format. At the client side is an Mac OSX app with ASIHTTPRequest and SBJson. I had various issues in the last few days, and was not able to find out a solution.
At server side:
#Override
#POST
#Path("/testService/")
#Consumes(MediaType.APPLICATION_JSON)
public Boolean service1(SomeMetaData metaData)
{
return this.testMetaData(metaData);
}
At client side:
NSString *requestURLString = [NSString stringWithFormat:#"%#%#", serverURL, #"/webServices/rest/testService"];
NSURL *url = [NSURL URLWithString:requestURLString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setValidatesSecureCertificate:NO];
[request setRequestMethod:#"POST"];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
NSString* jsonMetaData = [[SomeMetaData proxyForJson] JSONRepresentation];
NSMutableData *requestBody = [[NSMutableData alloc] initWithData:[jsonMetaData dataUsingEncoding:NSUTF8StringEncoding]];
[request setPostBody:requestBody];
[request startAsynchronous];
[request setCompletionBlock:^{
NSLog(#"Response: %#", [request responseString]);
}];
[request setFailedBlock:^{
NSLog(#"Failed: %#", [request error]);
}];
The JSON string generated from the metadata object is something like this:
{
"metaData":
{
"name":"test.txt",
"remoteKey":"4",
"remoteShare":"test1"
}
}
The client and server are in different physical computer but within the same LAN.
First issue:
An error domain error randomly appears in the console. There is no apparent pattern for its appearance, but it's guaranteed to show up with my first attempt.
Failed: Error Domain=ASIHTTPRequestErrorDomain Code=1 "A connection failure occurred" UserInfo=0x10013a030 {NSLocalizedDescription=A connection failure occurred, NSUnderlyingError=0x100190cf0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1005.)"}
Second issue:
This appears to be a json parser error which I don't really understand why. The container at the server side and the client side has identical structure.
Response for getExistsFileRequest: JAXBException occurred : unexpected element (uri:"", local:"metaData"). Expected elements are <{}someMetaData>. unexpected element (uri:"", local:"metaData"). Expected elements are <{}someMetaData>.
This kind of issue appears to be only happen when I have parameters in my request. My other GET web services with no input parameter works perfectly fine.
I have been stucked on this for days. Any suggestions will be really appreciated!
In case anyone is wondering, there is a root path defined for all web services on server side, so it's not likely to be a problem in this case.
*Another edit: * Request/Response headers at server side
[ERROR] 500 - POST /webServices/rest/testService (192.168.1.29) 199 bytes
Request headers
Host: 192.168.1.206
User-Agent: ASIHTTPRequest (Macintosh; Mac OS X 10.7.2; en_CA)
Content-Length: 240
Content-Type: application/json
Accept-Encoding: gzip
Authorization: Basic cmthbmc6Um9LYTEyMyE=
Connection: close
Response headers
Connection: close
Content-Type: text/plain
Date: Thu, 22 Dec 2011 18:55:23 GMT
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: JSESSIONID=1uxr8b377s5xs;Path=/
JAXBException is in this xml namespace (javax.xml.bind.JAXBException).
Are you trying to parse JSON as XML?
That's not JSON
{
metaData = {
name = "test.txt";
remoteKey = 4;
remoteShare = test1;
};
}
Did you mean :
{
"metaData": {
"name":"test.txt",
"remoteKey":"4",
"remoteShare":"test1"
}
}
(If you did, update your question and I'll delete this answer ;)

Posting JSON as the body of a POST request using AFHTTPClient

I am trying to find a way, using AFNetworking, to set the Content-Type header to be application/json and to POST with JSON in the body. The methods that I'm seeing in the documentation (postPath and requestWithMethod) both take a dictionary of parameters, which I assume is encoded in the standard form syntax. Does anyone know of a way to instruct AFHTTPClient to use JSON for the body, or do I need to write the request on my own?
I went ahead and checked out the latest AFNetworking from their master branch. Out of the box I was able to get the desired behavior. I looked and it seems like a recent change (October 6th) so you might just need to pull the latest.
I wrote the following code to make a request:
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://localhost:8080/"]];
[client postPath:#"hello123" parameters:[NSDictionary dictionaryWithObjectsAndKeys:#"v1", #"k1", #"v2", #"k2", nil]
success:^(id object) {
NSLog(#"%#", object);
} failure:^(NSHTTPURLResponse *response, NSError *error) {
NSLog(#"%#", error);
}];
[client release];
Under my proxy I can see the raw request:
POST /hello123 HTTP/1.1
Host: localhost:8080
Accept-Language: en, fr, de, ja, nl, it, es, pt, pt-PT, da, fi, nb, sv, ko, zh-Hans, zh-Hant, ru, pl, tr, uk, ar, hr, cs, el, he, ro, sk, th, id, ms, en-GB, ca, hu, vi, en-us;q=0.8
User-Agent: info.evanlong.apps.TestSample/1.0 (unknown, iPhone OS 4.3.2, iPhone Simulator, Scale/1.000000)
Accept-Encoding: gzip
Content-Type: application/json; charset=utf-8
Accept: */*
Content-Length: 21
Connection: keep-alive
{"k2":"v2","k1":"v1"}
From the AFHTTPClient source you can see that JSON encoding is the default based on line 170, and line 268.
For me, json was NOT the default encoding. You can manually set it as the default encoding like this:
(using Evan's code)
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://localhost:8080/"]];
[client setParameterEncoding:AFJSONParameterEncoding];
[client postPath:#"hello123" parameters:[NSDictionary dictionaryWithObjectsAndKeys:#"v1", #"k1", #"v2", #"k2", nil]
success:^(id object) {
NSLog(#"%#", object);
} failure:^(NSHTTPURLResponse *response, NSError *error) {
NSLog(#"%#", error);
}];
[client release];
the crucial part:
[client setParameterEncoding:AFJSONParameterEncoding];

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!