Я реализую API обмена Coinbase, используя пользовательскую аутентификацию в request-python. Следующий код работает со всеми (аутентифицированными) вызовами на основе GET, но не работает для всех аутентифицированных вызовов на основе POST (я не пробовал с глаголами DELETE или UPDATE). Я не понимаю, почему подпись не будет работать для обоих, потому что полезная нагрузка timestamp + method + path
для GET и timestamp + method + path + body
для PUT, поэтому пользовательская аутентификация кажется правильной. Что-то не так с добавлением тела и изменением GET на POST. Спасибо!
Вы можете получить ключи API для опробования здесь: https://gdax.com/settings
import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase
class CoinbaseAuth(AuthBase):
SIGNATURE_HTTP_HEADER = 'CB-ACCESS-SIGN'
TIMESTAMP_HTTP_HEADER = 'CB-ACCESS-TIMESTAMP'
KEY_HTTP_HEADER = 'CB-ACCESS-KEY'
PASSPHRASE_HTTP_HEADER = 'CB-ACCESS-PASSPHRASE'
def __init__(self, api_key, secret_key, passphrase):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase
def __call__(self, request):
#Add headers
request.headers[CoinbaseAuth.KEY_HTTP_HEADER] = self.api_key
request.headers[CoinbaseAuth.PASSPHRASE_HTTP_HEADER] = self.passphrase
timestamp = str(time.time())
request.headers[CoinbaseAuth.TIMESTAMP_HTTP_HEADER] = timestamp
#add signature
method = request.method
path = request.path_url
content = request.body
message = timestamp + method + path
if content:
message += content
hmac_key = base64.b64decode(self.secret_key)
sig = hmac.new(hmac_key, message, hashlib.sha256)
sig_b64 = sig.digest().encode("base64").rstrip("\n")
#Add signature header
request.headers[CoinbaseAuth.SIGNATURE_HTTP_HEADER] = sig_b64
return request
#Get your keys here: https://gdax.com/settings
key = 'KEY GOES HERE'
secret = 'SECRET GOES HERE'
passphrase = 'PASSPHRASE GOES HERE'
api_url = 'https://api.gdax.com:443/'
auth = CoinbaseAuth(API_KEY, API_SECRET, API_PASS)
#GETs work, shows account balances
r = requests.get(api_url + 'accounts', auth=auth)
print r.json()
#POSTs fail: {message: 'invalid signature'}
order = {}
order['size'] = 0.01
order['price'] = 100
order['side'] = 'buy'
order['product_id'] = 'BTC-USD'
r = requests.post(api_url + 'orders', data=json.dumps(order), auth=auth)
print r.json()
И вывод:
ПОЛУЧИТЬ вызов: 200: [{u'available': .......}]
Почтовый вызов: 400: {u'message': u'invalid signature'}
РЕДАКТИРОВАТЬ: POSTing 'a' вместо действительных данных в кодировке JSON приводит к той же ошибке подписи (а не к ошибке декодирования JSON с сервера), поэтому я не думаю, что это то, как я формирую данные. Примечательно, что если я опускаю тело -- request.post(..., data='',...)
--- сервер отвечает соответствующим образом {u'message': u'Missing product_id'}
.