I hope you have more luck on this than I have.
As the title suggests I am trying to hit a private API endpoint on bitstamp.net. https://www.bitstamp.net/api/#api-authentication try as I might I cannot get the signature to make sense. I have followed the docs as close as I know how, I have spent hours trying to learn as much python as possible and decyphering the docs, as there is no JavaScript example.
The signature is supposed to be a sha256 hmac of the API secret and the message variable which is a backslash seperated string that I cannot find anything wrong with anymore.
I use the below link to query the server, the apikey and secret will be encoded and decoded in production when this all works.
http://localhost:3001/sha256/jorw1007/QGcJKrhenfqOML5cOpTsLOe9IbEPsJnN/bKXiqtYHawJ7DAUZIHviAPoRrp0zhfIv
const crypto = require('crypto');
app.get("/sha256/:id/:key/:secret", cors(), (request, response, next) => {
const APIkey = "BITSTAMP" + " " + request.params.key;
const APIsecret = request.params.secret;
const urlHost = "192.168.0.120:3001"; // where is querying the API from bitstamp? in this case its the server localhost:3001
const urlPath = "/api/v2/balance/";
const urlQuery = "";
const UTC = Date.now().toString();
const nonce = randomBytes(18).toString("hex").toLowerCase();
const nonce2 = "f93c979d-b00d-43a9-9b9c-fd4cd9547fa6"
const contentType = "application/x-www-form-urlencoded";
const version = "v2";
const payload = urlencoded({"offset": "1"})
let message = `${APIkey}\\POST\\${urlHost}\\${urlPath}\\${urlQuery}\\${nonce}\\${UTC}\\${version}\\`
// const encodedMsg = encodeURI(message) ///why are we encoding?
// const signature = createHmac("sha256", APIsecret ).update(JSON.stringify(encodedMsg) );
// const sigDigested = signature.digest('hex')
var signature = crypto.createHmac('sha256', APIsecret).update(encodeURI(message));
// to lowercase hexits
const digestedHash = signature.digest('hex');
console.log(message)
const headers = {
"X-Auth": APIkey,
"X-Auth-Signature": digestedHash,
"X-Auth-Nonce": nonce,
"X-Auth-Timestamp": UTC,
"X-Auth-Version": version,
};
fetch("https://www.bitstamp.net/api/v2/balance/", {
headers,
method: "POST",
data : payload
})
.then((res) => res.json())
.then((json) => response.send(json))
.catch((err) => console.error(err));
});
I keep getting error 'API0005' which means the signature is invalid.
Please could someone point me in the right direction?
thank you
After countless effort I finally managed to get it work. The code I will be posting is in C# but it very much resembles that of JavaScript.
protected async Task<string> SendRequest(string @base, string endpoint, EndpointHttpMethod method, Dictionary<string, object> parameters = null, bool signed = true)
{
var resiliencyStrategy = ExchangeTools.DefineAndRetrieveResiliencyStrategy("Bitstamp");
parameters ??= new Dictionary<string, object>();
var payloadString = string.Empty;
var queryParameters = string.Empty;
var contentType = string.Empty;
if (method == EndpointHttpMethod.GET)
{
queryParameters = ExchangeTools.BuildQuery(parameters);
endpoint += !string.IsNullOrWhiteSpace(queryParameters) ? $"?{queryParameters}" : string.Empty;
}
else
{
payloadString = ExchangeTools.BuildQuery(parameters, true);
contentType = !string.IsNullOrWhiteSpace(payloadString) ? "application/x-www-form-urlencoded" : string.Empty;
}
using var client = new HttpClient();
using var httpRequestMessage = new HttpRequestMessage(new HttpMethod(method.ToString()), endpoint);
client.BaseAddress = new Uri(Endpoints.BasePrefix + @base);
if (signed)
{
var apiKey = $"BITSTAMP {publicKey}";
var version = "v2";
var nonce = Guid.NewGuid();
var timestamp = ExchangeTools.GetUnixTimestamp(1000);
var msg = $"{apiKey}{method}{@base}{endpoint}{queryParameters}{contentType}{nonce}{timestamp}{version}{payloadString}";
var signature = ExchangeTools.Sign(msg, privateKey, HashingAlgorithm.HMACSHA256, ByteEncoding.ASCII, SignatureEncoding.HexString);
httpRequestMessage.Headers.Add("X-Auth", apiKey);
httpRequestMessage.Headers.Add("X-Auth-Signature", signature);
httpRequestMessage.Headers.Add("X-Auth-Nonce", nonce.ToString()); ;
httpRequestMessage.Headers.Add("X-Auth-Timestamp", timestamp.ToString()); ;
httpRequestMessage.Headers.Add("X-Auth-Version", version);
if(parameters.Count > 0)
{
httpRequestMessage.Content = new FormUrlEncodedContent(parameters.ToDictionary(kv => kv.Key, kv => kv.Value.ToString()));
}
}
var response = await resiliencyStrategy.ExecuteAsync(() => client.SendAsync(httpRequestMessage));
var result = await response.Content.ReadAsStringAsync();
return result;
}
Let me know if you need further clarification. My best guess as to why yours is not working comes down to conditionally setting the payload and queryParam depending on what parameters you have and whether the request is a GET or POST.