Golang vs PHP https calls with headers different results - php

I am trying to implement Vault of Satoshi's API in Google App Engine Go. Their reference API is in PHP:
<?php
$serverURL = 'https://api.vaultofsatoshi.com';
$apiKey = 'ENTER_YOUR_API_KEY_HERE';
$apiSecret = 'ENTER_YOUR_API_SECRET_HERE';
function usecTime() {
list($usec, $sec) = explode(' ', microtime());
$usec = substr($usec, 2, 6);
return intval($sec.$usec);
}
$url = 'https://api.vaultofsatoshi.com';
$endpoint = '/info/currency';
$url = $serverURL . $endpoint;
$parameters= array();
$parameters['nonce'] = usecTime();
$data = http_build_query($parameters);
$httpHeaders = array(
'Api-Key: ' . $apiKey,
'Api-Sign:' . base64_encode(hash_hmac('sha512', $endpoint . chr(0) . $data, $apiSecret)),
);
// Initialize the PHP curl agent
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, "something specific to me");
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
?>
My Go code looks like this:
func GenerateSignatureFromValues(secretKey string, endpoint string, values url.Values) string {
query:=[]byte(values.Encode())
toEncode:=[]byte(endpoint)
toEncode = append(toEncode, 0x00)
toEncode = append(toEncode, query...)
key:=[]byte(secretKey)
hmacHash:=hmac.New(sha512.New, key)
hmacHash.Write(toEncode)
answer := hmacHash.Sum(nil)
return base64.StdEncoding.EncodeToString(([]byte(strings.ToLower(hex.EncodeToString(answer)))))
}
func Call(c appengine.Context) map[string]interface{} {
serverURL:="https://api.vaultofsatoshi.com"
apiKey:="ENTER_YOUR_API_KEY_HERE"
apiSecret:="ENTER_YOUR_API_SECRET_HERE"
endpoint:="/info/order_detail"
tr := urlfetch.Transport{Context: c}
values := url.Values{}
values.Set("nonce", strconv.FormatInt(time.Now().UnixNano()/1000, 10))
signature:=GenerateSignatureFromValues(apiSecret, endpoint, values)
req, _:=http.NewRequest("POST", serverURL+endpoint, nil)
req.Form=values
req.Header.Set("Api-Key", apiKey)
req.Header.Set("Api-Sign", signature)
resp, err:=tr.RoundTrip(req)
if err != nil {
c.Errorf("API post error: %s", err)
return nil
}
defer resp.Body.Close()
body, _:= ioutil.ReadAll(resp.Body)
result := make(map[string]interface{})
json.Unmarshal(body, &result)
return result
}
Both of those pieces of code generate the same signature for the same input. However, when I run the PHP code (with the proper Key and Secret), the server responds with a proper response, but while I run the Go code, the server responds with "Invalid signature". This error indicates that the HTTP request generated by Go must be somehow malformed - either HTTP Header's values are wrong (if the header values are completely missing a different error appears), or the way the POST fields are encoded is wrong for some reason.
Can anyone help me find some reason why those two pieces of code generate different HTTP requests and how can I make Go generate requests like the PHP code?

See the documentation for Request.Form:
// Form contains the parsed form data, including both the URL
// field's query parameters and the POST or PUT form data.
// This field is only available after ParseForm is called.
// The HTTP client ignores Form and uses Body instead.
Form url.Values
Specifically "HTTP client ignores Form and uses Body instead."
With this line:
req, _:= http.NewRequest("POST", serverURL+endpoint, nil)
You should use this instead of nil:
bytes.NewBufferString(values.Encode())
Also keep in mind that the order of map is not guaranteed. url.Values is map[string][]string. So you should be using Encode() once and use the same result in the body and signature. There is a chance that by using Encode() twice the order could be different. This is an important difference between Go and PHP.
You should also make a habit of handling error instead of ignoring it.

Related

GoLang WebServer sends description of param's struct on Json Response

So here is the deal : I have been working on a huge system (PHP) for a couple years, and now, I decided to give up part of heavy jobs for golang scripts.
So far, I replicated a few php scripts to a go version. Then, I am able to benchmark which option is better ( ok, I know go is faster, but I need curl or sockets to comunication, so, I have to check if it is still worth ) .
One of the scripts just generate a random code, check if this new code is already in use ( on mysql db ), if not, record the new code and return it, if is already in use, just recursive call the function again until find an exclusive code. pretty simple one.
I already had this code generator in php, so, wrote new one in go to be called as http/post with json params.
Using linux terminal, I call it as
curl -H [headers] -d [jsondata] [host]
and I get back a pretty simple json
{"locator" : "XXXXXX"}
After, I wrote a simple php script to call the scripts and check how long each took to complete, something like :
<?php
public function indexAction()
{
$phpTime = $endPHP = $startPHP =
$goTime = $endGO = $startGO = 0;
// my standard function already in use
ob_start();
$startPHP = microtime(true);
$MyCodeLocator = $this->container->get('MyCodeLocator')->Generate(22, 5);
$endPHP = microtime(true);
ob_end_clean();
sleep(1);
// Lets call using curl
$data = array("comon" => "22", "lenght" => "5");
$data_string = json_encode($data);
ob_start();
$startGO = microtime(true);
$ch = curl_init('http://localhost:8888/dev/fibootkt/MyCodeGenerator');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
curl_close($ch);
$endGO = microtime(true);
ob_end_clean();
$result_rr = json_decode($result);
// tst just echo the variable in a nice way, second parameter means no kill execution
tst($result, 1); // HERE IS MY PROBLEM, please read below
tst($result_rr, 1); // IT SHOW NULL
sleep(1);
// just to have params for comparision, always try by shell script
ob_start();
$startShell = microtime(true);;
$exec = "curl -H \"Content-Type: application/json\" -d '{\"comon\":\"22\"}' http://localhost:8888/dev/fibootkt/MyCodeGenerator";
$output = shell_exec($exec);
$endShell = microtime(true);
ob_end_clean();
tst(json_decode($output),1); // here show a stdclass with correct value
tst($output,1); // here shows a json typed string ready to be converted
// and here it is just for show the execution time ...
$phpTime = $endPHP - $startPHP;
$goTime = $endGO - $startGO ;
$shellTime = $endShell - $startShell;
tst("php " . $phpTime, 1);
tst("curl ". $goTime, 1);
tst("shell " . $shellTime, 1);
And I get the results from GO :
By Shell Script :
{"locator" : "DPEWX22"}
So, this one is pretty and easy decode to a stdobj.
But, using curl, the operation is faster ! So, I want to use it.
But, the curl request responds something like :
{"Value":"string","Type":{},"Offset":26,"Struct":"CodeLocatorParams","Field":"lenght"}
{"locator":"DPEWX22"}
And when I try to decode it, I get a null as result !!!
CodeLocatorParams the struct type I use in go to get the post params, as show below
so, here is my question : Why GO is returning this ? how to avoid it.
I have another similar go script which take no params and responds a similar json ( but in this case, a qrcode image path ) and it works fine !
My go function:
type CodeLocatorParams struct {
Comon string `json:"comon"`
Lenght int `json:"lenght"`
}
func Generate(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
if err != nil {
fmt.Println(err)
panic(err)
}
if err := r.Body.Close(); err != nil {
panic(err)
}
// retrieve post data and set it to params which is CodeLocatorParams type
var params CodeLocatorParams
if err := json.Unmarshal(data, &params ); err != nil {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(422) // unprocessable entity
if err := json.NewEncoder(w).Encode(err); err != nil {
fmt.Println(err)
panic(err)
}
}
var result struct{
Locator string `json:"locator"`
}
// here actually comes the func that generates random code and return it as string, but for now, just set it static
result.Locator = "DPEWX22"
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(w).Encode(result)
}
There is an error parsing the incoming JSON. This error is written to the response as {"Value":"string","Type":{},"Offset":26,"Struct":"CodeLocatorParams","Field":"lenght"}. The handler continues to execute and writes the normal response {"locator":"DPEWX22"}.
Here's how what to fix:
After writing error response, return from the handler.
The input JSON has lenght as a string value. Either change the struct field from int to string or modify the input to pass an integer.

Parsing json data using php from youtube data api v3

I am using Youtube data api v3 to retrieve data for search query using the below url
https://www.googleapis.com/youtube/v3/search?part=snippet&q=text&key=apikey&maxResults=25.
I am getting json response, while i am using json decode to parse the json data, i am getting empty result, can any one tell me how to retrieve the data
I am using below php code to parse
$videourl="https://www.googleapis.com/youtube/v3/search?part=snippet&q=hello&key=apikey&maxResults=25";
$json = file_get_contents($videourl);
$data = json_decode($json,true);
print_r($data);
obtaining the json response from the below url:
https://www.googleapis.com/youtube/v3/search?part=snippet&q=hello&key=apikey&maxResults=25
if (extension_loaded('curl')) {
# create a new cURL resource
$ch = curl_init();
# set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://googleapis.com/.....");
curl_setopt($ch, CURLOPT_HEADER, 0);
# Setting cURL's option to return the webpage data
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
# grab URL and pass it to the browser
if($json = curl_exec($ch)) {
if(!($data = #json_decode($json)) instanceof stdClass) {
trigger_error('Unable to decode json. '. print_r(error_get_last(), true));
}
}
else trigger_error('CUrl Error:'.curl_error($ch));
# close cURL resource, and free up system resources
curl_close($ch);
}
else trigger_error('CUrl unsupported', E_USER_WARNING);
You should test the return value of file_get_contents. Be careful that this function will return FALSE in case of error (and not NULL). So you should test the return value with === FALSE in order to not be confused by empty values.
In your case, if you are trying to access the exact url that you posted in your question, google is returning a code 400 (bad request), this is why you get a return of FALSE.

Decoding JSON after sending using PHP cUrl

I've researched everywhere and cannot figure this out.
I am writing a test cUrl request to test my REST service:
// initialize curl handler
$ch = curl_init();
$data = array(
"products" => array ("product1"=>"abc","product2"=>"pass"));
$data = json_encode($data);
$postArgs = 'order=new&data=' . $data;
// set curl options
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postArgs);
curl_setopt($ch, CURLOPT_URL, 'http://localhost/store/rest.php');
// execute curl
curl_exec($ch);
This works fine and the request is accepted by my service and $_Post is populated as required, with two variables, order and data. Data has the encoded JSON object. And when I print out $_Post['data'] it shows:
{"products":{"product1":"abc","product2":"pass"}}
Which is exactly what is expected and identical to what was sent in.
When I try to decode this, json_decode() returns nothing!
If I create a new string and manually type that string, json_decode() works fine!
I've tried:
strip_tags() to remove any tags that might have been added in the http post
utf8_encode() to encode the string to the required utf 8
addslashes() to add slashes before the quotes
Nothing works.
Any ideas why json_decode() is not working after a string is received from an http post message?
Below is the relevant part of my processing of the request for reference:
public static function processRequest($requestArrays) {
// get our verb
$request_method = strtolower($requestArrays->server['REQUEST_METHOD']);
$return_obj = new RestRequest();
// we'll store our data here
$data = array();
switch ($request_method) {
case 'post':
$data = $requestArrays->post;
break;
}
// store the method
$return_obj->setMethod($request_method);
// set the raw data, so we can access it if needed (there may be
// other pieces to your requests)
$return_obj->setRequestVars($data);
if (isset($data['data'])) {
// translate the JSON to an Object for use however you want
//$decoded = json_decode(addslashes(utf8_encode($data['data'])));
//print_r(addslashes($data['data']));
//print_r($decoded);
$return_obj->setData(json_decode($data['data']));
}
return $return_obj;
}
Turns out that when JSON is sent by cURL inside the post parameters & quot; replaces the "as part of the message encoding. I'm not sure why the preg_replace() function I tried didn't work, but using html_entity_decode() removed the &quot and made the JSON decode-able.
old:
$return_obj->setData(json_decode($data['data']));
new:
$data = json_decode( urldecode( $data['data'] ), true );
$return_obj->setData($data);
try it im curious if it works.

writing cURL like function in a rails app

I'm trying to convert this PHP cURL function to work with my rails app. The piece of code is from an SMS payment gateway that needs to verify the POST paramters. Since I'm a big PHP noob I have no idea how to handle this problem.
$verify_url = 'http://smsgatewayadress';
$fields = '';
$d = array(
'merchant_ID' => $_POST['merchant_ID'],
'local_ID' => $_POST['local_ID'],
'total' => $_POST['total'],
'ipn_verify' => $_POST['ipn_verify'],
'timeout' => 10,
);
foreach ($d as $k => $v)
{
$fields .= $k . "=" . urlencode($v) . "&";
}
$fields = substr($fields, 0, strlen($fields)-1);
$ch = curl_init($verify_url); //this initiates a HTTP connection to $verify_url, the connection headers will be stored in $ch
curl_setopt($ch, CURLOPT_POST, 1); //sets the delivery method as POST
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); //The data that is being sent via POST. From what I can see the cURL lib sends them as a string that is built in the foreach loop above
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //This verifies if the target url sends a redirect header and if it does cURL follows that link
curl_setopt($ch, CURLOPT_HEADER, 0); //This ignores the headers from the answer
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //This specifies that the curl_exec function below must return the result to the accesed URL
$result = curl_exec($ch); //It ransfers the data via POST to the URL, it gets read and returns the result
if ($result == true)
{
//confirmed
$can_download = true;
}
else
{
//failed
$can_download = false;
}
}
if (strpos($_SERVER['REQUEST_URI'], 'ipn.php'))
echo $can_download ? '1' : '0'; //we tell the sms sever that we processed the request
I've googled a cURL lib counterpart in Rails and found a ton of options but none that I could understand and use in the same way this script does.
If anyone could give me a hand with converting this script from php to ruby it would be greatly appreciated.
The most direct approach might be to use the Ruby curb library, which is the most straightforward wrapper for cURL. A lot of the options in Curl::Easy map directly to what you have here. A basis might be:
url = "http://smsgatewayadress/"
Curl::Easy.http_post(url,
Curl::PostField.content('merchant_ID', params[:merchant_ID]),
# ...
)

Callback from API not happening after posting parameters to API URL from server side

API integration description
The API needs a form to be posted to the API URL with some input fields and a customer token. The API processes and then posts response to a callback.php file on my server. I can access the posted vals using $_POST in that file. That's all about the existing method and it works fine.
Requirement
To hide the customer token value from being seen from client side. So I started with sending server side post request.
Problem
I tried with many options but the callback is not happening -
1) CURL method
$ch = curl_init(API_URL);
$encoded = '';
$_postArray['customer_token'] = API_CUSTOMER_TOKEN;
foreach($_postArray as $name => $value)
{
$encoded .= urlencode($name).'='.urlencode($value).'&';
}
// chop off last ampersand
$encoded = substr($encoded, 0, strlen($encoded)-1);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded);
$resp = curl_exec($ch);
curl_close($ch);
echo $resp;
$resp echoes 1 if the line curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); is removed but the callback does not happen. I am setting a session variable in the callback script to verify.Is it needed that the API be synchronous in order to use curl method, so that curl_exec returns the response?
2) without CURL as given in Posting parameters to a url using the POST method without using a form
But the callback is not happening.
I tried with the following code too, but looks like my pecl is not installed properly because the HttpRequest() is not defined.
$req = new HttpRequest($apiUrl, HttpRequest::METH_POST);
$req->addQueryData($params);
try
{
$r->send();
if ($r->getResponseCode() == 200)
{
echo "success";
// success!
}
else
{
echo "failure";
// got to the API, the API returned perhaps a RESTful response code like 404
}
}
catch (HttpException $ex)
{
// couldn't get to the API (probably)
}
Please help me out! I just need to easily send a server side post request and get the response in the callback file.
Try to debug your request using the curl_get_info() function:
$header = curl_getinfo($ch);
print_r($header);
Your request might be OK but it my result in an error 404.
EDIT: If you want to perform a post request, add this to your code:
curl_setopt($ch, CURLOPT_POST, true);
EDIT: Something else I mentioned at your code: You used a '1' at the 'CURLOPT_RETURNTRANSFER' but is should be 'true':
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
At least this is how I usually do it, and you never know if the function will also understand a '1' as 'true';
EDIT: The real problem: I copy-pasted your source and used it on one of my pages getting this error:
Warning: urlencode() expects parameter 1 to be string, array given in C:\xampp\htdocs\phptests\test.php on line 8
The error is in this line:
foreach($_postArray as $name => $value)
$_postArray is an array with one value holding the other values and you need either another foreach or you simple use this:
foreach($_postArray['customer_token'] as $name => $value)
As discussed in the previous question, the callback is an entirely separate thing from your request. The callback also will not have your session variables, because the remote API is acting as the client to the callback script and has its own session.
You should really show some API documentation here. Maybe we're misunderstanding each other but as far as I can see, what you are trying to do (get the callback value in the initial CURL request) is futile, and doesn't become any less futile by asking twice.

Categories