Question

I've looked through SO and Google and haven't found a similiar issue to this. I feel like the answer is staring me in the face and I just need another set of eyes.

I'm using AFNetworking to connect to the Stripe.com API. Specifically I'm using AFHTTPClient postPath to send data to an endpoint, charges. Stripe requires the request to be encoded as application/x-www-form-urlencoded so I can't use JSON encoding.

The problem I'm running into is that I have a Charge object and a Card object. Card is a property on Charge and I convert both Charge and Card to NSDictionary's (Card is an dictionary inside of the Charge dictionary) and then pass them in as the parameters on the request like so:

NSDictionary *parameters = [ChargeRequest convertToDictionary:request];

[[StripeAPIClient sharedClient] postPath:@"charges" parameters:parameters 
success:^(AFHTTPRequestOperation *operation, id responseObject) 
{        
    NSLog(@"Response: %@", responseObject);
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error) {        
    NSLog(@"Error: %@", [error localizedDescription]);
    NSLog(@"Response: %@", operation.responseString);
}];

When I do this, with AFHttpClient's parameterEncoding property set to AFFormURLParameterEncoding, Stripe returns this error:

"error": {
    "message": "Invalid token id: {\n    \"exp_month\" = 10;\n    \"exp_year\" = 2016;\n      number = 4242111111111111;\n}",
    "type": "invalid_request_error"
}

The values in the error are specifically the key/values on the Card object after converting it. Here is the code I use for the conversion:

   return [[NSDictionary alloc] initWithObjectsAndKeys:request.number, @"number", [NSNumber numberWithInt:10], @"exp_month", [NSNumber numberWithInt:2016], @"exp_year", nil];

Any advice on what do to get rid of the invalid tokens being put in this NSDictionary? Am I focusing on the wrong thing?

Thanks!

Was it helpful?

Solution

AFNetworking support only AFFormURLParameterEncoding with 1 level of parameters.

I'm writing a fix for that

replace AFQueryStringFromParametersWithEncoding implementation in AFHTTPClient by

extern NSArray * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey, NSStringEncoding encoding);
extern NSArray * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding encoding);
extern NSArray * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding encoding);
extern NSArray * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key, NSStringEncoding encoding);

NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding) {
    NSMutableArray *mutableParameterComponents = [NSMutableArray array];
    [mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersAtBaseKeyWithEncoding(parameters,nil,encoding)];   
    return [mutableParameterComponents componentsJoinedByString:@"&"];
}

NSArray * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey, NSStringEncoding encoding)
{
    NSMutableArray *mutableParameterComponents = [NSMutableArray array];

    if([parameters isKindOfClass:[NSDictionary class]]) {
        [mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(parameters,baseKey,encoding)];
    }
    else if([parameters isKindOfClass:[NSArray class]]) {
        [mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(parameters,baseKey,encoding)];
    }
    else {
        [mutableParameterComponents addObject:AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(parameters,baseKey,encoding)];
    } 

    return mutableParameterComponents;
}

NSArray * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding encoding)
{
    NSMutableArray *mutableParameterComponents = [NSMutableArray array];

    id key = nil;

    NSEnumerator *enumerator = [parameters keyEnumerator];
    while ((key = [enumerator nextObject])) {
        NSString *newKey = baseKey?[NSString stringWithFormat:@"%@[%@]",baseKey,key]:key;
        [mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersAtBaseKeyWithEncoding([parameters valueForKey:key],newKey,encoding)];
    }

    return mutableParameterComponents;
}

NSArray * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding encoding)
{
    NSMutableArray *mutableParameterComponents = [NSMutableArray array];

    for (id value in parameters) {
        NSString* newKey = [NSString stringWithFormat:@"%@[]",newKey];
        [mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersAtBaseKeyWithEncoding(value,newKey,encoding)];
    }

    return mutableParameterComponents;
}

NSArray * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key, NSStringEncoding encoding)
{
    return [NSString stringWithFormat:@"%@=%@", AFURLEncodedStringFromStringWithEncoding([key description], encoding), AFURLEncodedStringFromStringWithEncoding([parameter description], encoding)];
}

I'm just writing the same code for multipart requests and submit a pull request to AFNetworking

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top