Trying to Replicate a Post make from the Chrome REST Console to Prosper.com using the REST SDK

Mar 1, 2015 at 5:44 PM
Hello,

I am trying to create a program to interface with the API of the peer-to-peer lending site, Prosper.com. I am able to successfully post a loan request from the REST Console of Chrome, but I am unable to duplicate this request within my C++ program and I'm hoping someone can help identify my mistake(s).

Here are the parameters I entered into the REST Console:

Request URI: https://api.prosper.com/api/Invest/
Request Method: POST
Request Timeout: 60 seconds
Content-Type: application/x-www-form-urlencoded
Request Parameters:
amount, 25.00
listingId, 2430877
Authorization Header: Basic EncodedStringHere

When I clicked the "Send" button, I received a response that the request was a success and the loan was made. Here is the request header reported by the REST Console:

Content-Type: application/x-www-form-urlencoded
Authorization: Basic EncodedStringHere
Accept: /
Connection: keep-alive
Origin: chrome-extension: //rest-console-id
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36

and here is the request body:

Request Url: https://api.prosper.com/api/Invest/
Request Method: POST
Status Code: 200
Params: {
"amount": "25.00",
"listingId": "2430877"
}

So I figured that if I could just duplicate this header and body from within my C++ program, that it would also work, but I get a return code of 400. Here is my code:
http_client client(U("https://api.prosper.com"));
http_request req(methods::POST);
req.set_request_uri(U("api/Invest"));
req.headers().add(header_names::authorization, U("Basic EncodedStringHere"));
req.headers().add(header_names::content_type, U("application/x-www-form-urlencoded"));
req.headers().add(header_names::connection, U("keep-alive"));
req.headers().add(header_names::accept, U("*/*"));
wchar_t parmString[] = L"Params: {\n   \"listingId\": \"2430877\",\n   \"amount\": \"25.00\"\n}";
wchar_t bodyString[1000];
swprintf(bodyString,L"Request Url: https://api.prosper.com/api/Invest/\nRequest Method: POST\nStatus Code: 200\n%s",parmString);
req.set_body(bodyString);
http_response response = client.request(req).get();
if(response.status_code() == status_codes::OK)
{
  auto body = response.extract_string();    
  AfxMessageBox(CString(body.get().c_str()));
}
else {
    CString errorString;
    errorString.Format("Error Code: %d", response.status_code());
    AfxMessageBox(errorString);
    AfxMessageBox(CString(bodyString));
} 
The last line displays the body, which I have confirmed is identical to the body reported by the REST Console.

Any help would be greatly appreciated.

Gary
Mar 3, 2015 at 3:32 AM
Hi Gary,

One issue that I can spot is you aren't actually setting any of the URI query parameters. The variable parmString isn't used to set any information on the outgoing http_request object. According to the documentation for the Prosper invest API it looks like you need to pass them as query parameters. I recommend using our uri_builder, it should make it easy to put everything together.

Steve
Mar 4, 2015 at 4:03 AM
Hi Steve,

Thanks much for your reply. Is there any sample code available where I could get a better understanding of the process involved in building a URI query parameter?

Gary
Mar 5, 2015 at 5:33 PM
Hi Gary,

In general I'd recommend learning about the different URI components if you aren't familiar with them. Otherwise I think it is pretty straightforward. For examples the BingRequest sample creates a URI with a single key value pair. Also you could look at the uri_builder test cases as well for more examples.

Steve
Mar 5, 2015 at 6:54 PM
Hi Steve,

Thanks again for the useful information. Here is my latest code:
http_client client(U("https://api.prosper.com"));
http_request req(methods::POST);
req.headers().add(header_names::authorization, U("Basic EncodedStringHere"));
req.headers().add(header_names::content_type, U("application/x-www-form-urlencoded"));
uri_builder myBuilder(U("api/Invest"));
myBuilder.append_query(L"listingId", L"2588100");
myBuilder.append_query(L"amount", L"25.00");
req.set_request_uri(myBuilder.to_uri());
http_response response = client.request(req).get();
if(response.status_code() == status_codes::OK)
{
  auto body = response.extract_string();    
  AfxMessageBox(CString(body.get().c_str()));
}
else {
    CString errorString;
    errorString.Format("Error Code: %d", response.status_code());
    AfxMessageBox(errorString);
} 
Still getting a return code of 400. If you or anyone else sees any flaws in the code, please let me know. Otherwise, I think I'll just put the project aside and keep making the loans manually for now.

Gary
Mar 5, 2015 at 11:05 PM
Hi Gary,

I didn't look over the Prosper API so I would double check there. Are you not getting any more information in the HTTP response body about what the actual problem is? Other options you can try to print the http_request class out to a string and it will give you a pretty good idea about what the outgoing request looks like, take a look at the http_request::to_string() API. You also could use an HTTP debugging proxy/tool to see the request as it goes out across the wire.

Steve
Mar 6, 2015 at 1:33 AM
Hey Steve,
I didn't look over the Prosper API so I would double check there.
Unfortunately, the documentation for the Investing portion of the API consists of only one page:

https://api.prosper.com/Invest

I contacted their support department to see if they had any other resources like sample code, etc. and they said that was all there was.
Are you not getting any more information in the HTTP response body about what the actual problem is?
The only additional information provided in the response body is "Invalid Arguments".
Other options you can try to print the http_request class out to a string...
Good idea. Here is what it produced:

"POST api/Invest?listingId=2463757&amount=25.00 HTTP/1.1\r\nAuthorization: Basic EncodedStringHere\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: cpprestsdk/2.4.0\r\n\r\n"

The only thing that looks suspicious to me is the "HTTP/1.1". Could that be getting included with the definition for amount?
You also could use an HTTP debugging proxy/tool to see the request as it goes out across the wire.
That may be my next step. Ideally, I would like to use such a tool to see the request that works from the Chrome Console and compare it with what is being sent from my program.

Thanks once again for all of the great help.

Gary