I am trying to connect to the coinspot API which has very shoddy documentation. Link below:
https://www.coinspot.com.au/api
It requires an HMAC SHA512 hash attached to the user's secret key, then a nonce key In the parameters of a POST request which is a timestamp turned to an integer to stay unique at each request. The header requires both the API key and the signature.
I use Alamofire's JSON encoded post request through coinspot's private API URL https://www.coinspot.com.au/api
Seems I get a success message for connecting but get an {status = invalid;} response.
The only 2 things I can think of are a wrong POST request (which doesn't seem the case, as if I don't add the nonce key in the parameters, I get a proper response requesting for one from the server), and the HMAC SHA-512 hash. I've taken the code from HMAC SHA512 using CommonCrypto in Swift 3.1
And modified it slightly to just include the SHA512 hashing.
Code below:
extension String{
var sha512:String{
return HMAC.hash(inp: self, algo: HMACAlgo.SHA512)
}
}
public struct HMAC{
static func hash(inp: String, algo: HMACAlgo)->String{
if let stringData = inp.data(using: String.Encoding.utf8, allowLossyConversion: false){
return hexStringFromData(input: digest(input: stringData as NSData, algo: algo))
}
return ""
}
private static func digest(input: NSData, algo: HMACAlgo)-> NSData{
let digestLength = algo.digestLength()
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA512(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private static func hexStringFromData(input: NSData)-> String{
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes{
hexString += String(format: "%02x", UInt8(byte))
}
return hexString
}
}
enum HMACAlgo{
case SHA512
func digestLength()->Int{
var result:CInt = 0
switch self{
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
The CommonCrypto library has been properly added with an objectiveC bridging file, and printing the result gives me a proper hashed signature.
I've contacted coinspot but they are swarmed with requests and they only have a cookie template response in regards to any technical support for their API.
Here's the Alamofire segment I placed under a function to call any command to the coinspot server:
func request(_ command: Router, params: [String: AnyObject]?, completion: @escaping (Result<JSON>)-> Void){
var parameters = ["nonce": Int(Date().timeIntervalSince1970*1000) as AnyObject]
if params != nil{
parameters = parameters + params!
}
let sign = customerSecret.sha512
print(sign)
let headers:HTTPHeaders = ["key": APIKey, "sign": sign]
do{
try Alamofire.request( command.asURL(), method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON{(response) in
guard let json = response.result.value else {
completion(.failure(CoinSpotError.networkError(error: response.result.error)))
return
}
print(response)
}
} catch {
print("error")
}
}
I think the above is correct. Router is just a set of addresses that call to the proper POST request for retrieving data.
Finally, I've changed the request to GET, got rid of both headers and parameters, and I get a status ok and the resulting JSON. I figured I'll just try a command on a browser and it worked,
The API does say every request must be a post method.