iOSでbitbank APIの認証を通す

ビットバンク(bitbank)で逆指値注文ができるようになりましたね。
自分の場合bitbankは長期投資のガチ保用で使っていましたが、これを機会にアクティブに使ってみようかと思い、iPhoneで損益を見るアプリを作ろうかと考え中です。

試しにbitbankのAPIをお試しで使ってみようとしたところ、Pythonやnode.jsの公式ライブラリは公開されているのですが、iOS版は存在しません。自前でREST APIの呼び出しを実装する必要がありまが、最初の認証がなんとも厄介で、かなりハマったのでその時のメモです。

認証に失敗すると 20001 が返ってくるのですが、理由を示す情報が含まれていないため試行錯誤するしかありません。

```json:エラーレスポンスの例
{
    "data" : { "code" : 20001 },
    "success" : 0
}
```

# 認証のポイント
- UNIXタイムスタンプ(timeIntervalSince1970)をそのままIntに変換すると、少数部が丸められインクリメントされないため、連続で呼び出すと認証に失敗する。1000とか10000倍してからIntに変換することで回避できる。
- 署名に指定するクエリパラメータは ? を付ける
- クエリのパスは　/v1 で始める
- bitbankで取得したAPIシークレットキーを使って、HMAC-SHA256 形式で署名する

## 約定履歴の認証の例

```swift:swift
  let privateEndpoint = "https://api.bitbank.cc"
  let path = "/v1/user/spot/trade_history"
  let queryParam = "?pair=btc_jpy"

  let urlString = privateEndpoint + path + queryParam
  let url = URL(string: urlString)!
  var request = URLRequest(url: url)
        
  let date: Date = Date()
  let nonce = String(Int(date.timeIntervalSince1970 * 10000))

  let signature = makeSignature(secret: apiSecret, nonce: nonce, path: path, queryParam: queryParam)
```

# サンプル全文
GETリクエストは標準APIを使用しています。
HMAC-SHA256の署名方法は@kohei1218さんの[記事](https://qiita.com/kohei1218/items/91c4691fd9e1caf6dfde)を参考にさせていただきました。

```swift:swift
import UIKit
import CryptoSwift

class ViewController: UIViewController {
    let publicEndpoint = "https://public.bitbank.cc"
    let privateEndpoint = "https://api.bitbank.cc"
    
    let apiKey = "bitbankのポータルサイトで取得したAPIキー"
    let apiSecret = "bitbankのポータルサイトで取得したシークレット"

    override func viewDidLoad() {
        super.viewDidLoad()
        
        getPrivateTradeHistory()
    }

    func getPrivateTradeHistory() {
        let path = "/v1/user/spot/trade_history"
        let queryParam = "?pair=btc_jpy"

        let urlString = privateEndpoint + path + queryParam
        let url = URL(string: urlString)!
        var request = URLRequest(url: url)
        
        let date: Date = Date()
        let nonce = String(Int(date.timeIntervalSince1970 * 10000))

        let signature = makeSignature(secret: apiSecret, nonce: nonce, path: path, queryParam: queryParam)

        request.httpMethod = "GET"
        request.allHTTPHeaderFields = ["ACCESS-KEY": apiKey]
        request.allHTTPHeaderFields = ["ACCESS-NONCE": nonce]
        request.allHTTPHeaderFields = ["ACCESS-SIGNATURE": signature]

        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard let data = data else { return }
            do {
                let object = try JSONSerialization.jsonObject(with: data, options: [])
                print(object)
            } catch let error {
                print(error)
            }
        }
        task.resume()
    }

    func makeSignature(secret: String, nonce: String, path: String, queryParam: String = "") -> String {
        let str = nonce + path + queryParam
        print(str)
        let bytes = str.bytes
        var signature = ""
        do {
            let hmac = try HMAC(key: secret, variant: .sha256).authenticate(bytes)
            signature = hmac.toHexString()
        } catch let error {
            print(error)
        }
        return signature
    }
}

extension StringProtocol {
    var bytes: [UInt8] { .init(utf8) }
}

```
# 参考
- [bitbank.cc 公式APIドキュメント](https://github.com/bitbankinc/bitbank-api-docs/blob/master/README_JP.md)
- [ウィジェットでBitbankの自分の資産を確認できるようにした](https://qiita.com/kohei1218/items/91c4691fd9e1caf6dfde)