Question

I am trying to implement the following PHP code in Google App Engine Go:

<?php

function api_query(array $req = array()) {
        $key = '90294318da0162b082c3d27126be80c3873955f9';

        $req['method'] = 'getinfo';
        $req['nonce'] = 1394503747386411;

        // generate the POST data string
        $post_data = http_build_query($req, '', '&');
        $sign = '75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348';

        // generate the extra headers
        $headers = array(
                'Sign: '.$sign,
                'Key: '.$key,
        );

        // our curl handle (initialize if required)
        static $ch = null;
        if (is_null($ch)) {
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; Cryptsy API PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
        }
        curl_setopt($ch, CURLOPT_URL, 'https://api.cryptsy.com/api');
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

        // run the query
        $res = curl_exec($ch);

        if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch));
        $dec = json_decode($res, true);
        if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists');

        echo "<pre>".print_r($dec, true)."</pre>";
        return $dec;
}

api_query();

When executed, the code returns a JSON array of values. I tried implementing the same code in Golang:

func PrivateCall(c appengine.Context) (map[string]interface{}, error) {
    AuthAPI := "https://api.cryptsy.com/api"
    APIKey := "90294318da0162b082c3d27126be80c3873955f9"
    tr := urlfetch.Transport{Context: c}
    values := url.Values{}
    values.Set("method", "getinfo")
    values.Set("nonce", "1394503747386411")

    signature := "75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348"

    req, err := http.NewRequest("POST", AuthAPI+"?"+values.Encode(), nil)
    if err != nil {
        c.Infof("API - Call - error 2 - %s", err.Error())
        return nil, err
    }
    req.Header.Set("Key", APIKey)
    req.Header.Set("Sign", signature)

    c.Infof("req - %v", req)
    resp, err := tr.RoundTrip(req)
    if err != nil {
        c.Errorf("API post error: %s", err)
        return nil, err
    }
    defer resp.Body.Close()
    //reading response
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        c.Errorf("API read error: could not read body: %s", err)
        return nil, err
    }
    result := make(map[string]interface{})
    //unmarshalling JSON response
    err = json.Unmarshal(body, &result)
    if err != nil {
        c.Infof("Unmarshal: %v", err)
        c.Infof("%s", body)
        return nil, err
    }
    return result, nil
}

I am getting an error saying "Unable to Authorize Request - Check Your Post Data". Does anyone see what could've caused this error? At the moment my best guess is that perhaps the request header in Go is a map[string][]string, while in PHP it appears to be an array...

Was it helpful?

Solution

As suggested by LeGEC, you're putting the POST data onto the end of the URL as if it were a GET request.

Try replacing

values := url.Values{}
values.Set("method", "getinfo")
values.Set("nonce", "1394503747386411")

signature := "75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348"

req, err := http.NewRequest("POST", AuthAPI+"?"+values.Encode(), nil)

with

data := struct {
    method string
    nonce  string
}{
    "getinfo",
    "1394503747386411",
}
signature := "75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348"
postData, err := json.Marshal(data)
if err != nil {
    return nil, err
}
buf := bytes.NewBuffer(postData)
req, err := http.Post(AuthAPI, "application/json", buf)

OTHER TIPS

https://github.com/ctfang/http_build_query

data := map[string]interface{}{
    "int":     1,
    "str":     "str",
    "arr_int": []int16{1, -2, 4},
    "m_arr": map[string][]int16{
        "test": []int16{1, -2, 4},
    },
    "m_m": []interface{}{
        map[string]string{"mo1": "v", "mo2": "v2"},
        map[string]string{"mo2": "v"},
    },
    "m_m_m": map[string]interface{}{
        "mm": struct{ Name string }{"张三"},
    },
}
str := Encode(data)

// echo 
// int=1&str=str&arr_int[]=1&arr_int[]=-2&arr_int[]=4&m_arr[test][0]=1&m_arr[test][1]=-2&m_arr[test][2]=4&m_m[0][mo1]=v&m_m[0][mo2]=v2&m_m[1][mo2]=v&m_m_m[mm][Name]=张三

Here is what you need and here are the few things you missed.

  • When working with https you need to set the TLSClientConfig
  • Missing X- prefix in custom headers
  • values.Encode() is the the wrong position.

Example: See https://www.cryptsy.com/pages/api

var (
    urlStr = "https://api.cryptsy.com/api"
    key    = "YOUR KEY"
    secret = "YOUR SECRECT"
    agent  = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36"
)

func main() {

    client := &http.Client{Transport: &http.Transport{
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }}

    values := url.Values{}
    //  $req['method'] = $method;
    values.Set("method", "getinfo")

    // $mt = explode(' ', microtime());
    // $req['nonce'] = $mt[1];
    values.Set("nonce", time.Nanosecond.String())

    //  $post_data = http_build_query($req, '', '&');
    encoded := values.Encode()

    mac := hmac.New(sha512.New, []byte(secret))
    mac.Write([]byte(encoded))

    //$sign = hash_hmac("sha512", $post_data, $secret);
    sign := fmt.Sprintf("%x", mac.Sum(nil))

    req, err := http.NewRequest("POST", urlStr, bytes.NewBufferString(encoded))

    if err != nil {
        log.Fatalln(err)
    }

    // generate the extra headers
    // $headers = array(
    // 'Sign: '.$sign,
    // 'Key: '.$key,
    // );
    req.Header.Set("X-Sign", sign)
    req.Header.Set("X-Key", key)

    //  curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; Cryptsy API PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
    req.Header.Set("User-Agent", agent)
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Content-Length", strconv.Itoa(len(encoded)))

    //       $res = curl_exec($ch);
    resp, err := client.Do(req)

    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(resp.Status)

    data, _ := ioutil.ReadAll(resp.Body)
    fmt.Printf("%s", data)
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top