我在使用Swift访问私有API端点时遇到问题。我试过使用Python,因为我很有经验,我能够从API中提取数据。我不知道发生了什么。Gemini API在他们的文档中没有任何Swift实现,所以很难弄清楚。我也试过使用ChatGPT,但我仍然有问题。
func generateNonce() -> String {
let timestamp = String(Int(Date().timeIntervalSince1970 * 1000)) // Get the current timestamp in milliseconds
let uniqueId = UUID().uuidString // Generate a unique identifier
// Concatenate the timestamp and unique identifier to create the nonce
let nonce = timestamp + uniqueId
return nonce
}
func signPayload(payload: String, secret: String) -> String {
if let payloadData = payload.data(using: .utf8), let secretData = secret.data(using: .utf8) {
var result = [UInt8](repeating: 0, count: Int(CC_SHA384_DIGEST_LENGTH))
payloadData.withUnsafeBytes { payloadPtr in
secretData.withUnsafeBytes { secretPtr in
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA384), secretPtr.baseAddress, secretData.count, payloadPtr.baseAddress, payloadData.count, &result)
}
}
let signatureData = Data(result)
let signature = signatureData.map { String(format: "%02x", $0) }.joined()
return signature
} else {
return ""
}
}
func base64Encode(payload: String) -> String? {
if let payloadData = payload.data(using: .utf8) {
return payloadData.base64EncodedString()
}
return nil
}
let apiKey = MyAPIKEY
let apiSecret = MYAPISecret
let baseUrl = "https://api.gemini.com/v1/notionalbalances/usd"
let endpoint = "/v1/notionalbalances/usd"
let account = "primary"
func performAPICall() async throws -> Crypto {
let url = URL(string: baseUrl)!
let nonce = generateNonce()
let payload = "\(nonce)+\(endpoint)+\(account)"
// let payload = "["request": "/v1/notionalbalances/usd", "nonce" : nonce, "account" : "primary"]"
// let payload = """
// {
// "request": "/v1/notionalbalances/usd",
// "nonce": "\(nonce)",
// "account": "\(account)"
// }
// """
let signature = signPayload(payload: payload, secret: apiSecret)
var request = URLRequest(url:url)
request.httpMethod = "POST"
request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
request.setValue("0", forHTTPHeaderField: "Content-Length")
request.setValue(apiKey, forHTTPHeaderField: "X-GEMINI-APIKEY")
request.setValue(base64Encode(payload: payload), forHTTPHeaderField: "X-GEMINI-PAYLOAD")
request.setValue(signature, forHTTPHeaderField: "X-GEMINI-SIGNATURE")
request.setValue("no-cache", forHTTPHeaderField: "Cache-Control")
// request.setValue(generateNonce(), forHTTPHeaderField: "nonce")
let (data, response) = try await URLSession.shared.data(from: url)
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
print("Response Headers: \(httpResponse.allHeaderFields)")
}
let wrapper = try JSONDecoder().decode(Crypto.self, from: data)
return wrapper
}
Task {
var crypto = try await performAPICall()
}
我不断得到这个错误:
验证码:404响应标题:[AnyHashable(“Vary”):Origin,AnyHashable(“Content-Length”):111,AnyHashable(“服务器”):nginx,AnyHashable(“Content-Type”):application/json,AnyHashable(“Date”):星期六,2023年9月30日16:21:07 GMT]
1条答案
按热度按时间72qzrwbm1#
在文档中,它说
X-GEMINI-SIGNATURE
头必须是hex(HMAC_SHA384(base64(payload), key=api_secret))
,但这不是您的代码发送的内容。您发送
hex(HMAC_SHA384(payload, key=api_secret))
,然后对在X-GEMINI-PAYLOAD
报头中发送的有效负载进行base64编码。另外,你的有效载荷不正确。您正在使用
"\(nonce)+\(endpoint)+\(account)"
-它将类似于<nonce>/v1/notionalbalances/usd<account>
,但它需要是一个JSON文档。您创建
nonce
的方式也可能不符合文档。它指出:如果你没有选择基于时间的随机数,那么随机数必须是一个永远不会重复的数字,并且必须在请求之间增加。
虽然你使用时间戳作为nonce的第一部分,所以它总是会增加,doucumentation说它需要一个整数(大概是以10为底),所以我不认为160+位的十六进制值会起作用。使用时间戳可能更安全。
你也要去所有的麻烦,设置您的
URLRequest
,但然后不使用它。您需要调用URLSession.shared.data(for: request)
,而不是URLSession.shared.data(from: url)
建议代码: