3434__author__ = 'gsibble'
3535
3636from oauth2client .client import AccessTokenRefreshError , OAuth2Credentials , AccessTokenCredentialsError
37-
37+ import re
3838import requests
3939import httplib2
4040import json
4141import os
4242import inspect
43-
43+ import hashlib
44+ import hmac
45+ import time
4446#TODO: Switch to decimals from floats
4547#from decimal import Decimal
4648
4749from coinbase .config import COINBASE_ENDPOINT
48- from coinbase .models import CoinbaseAmount , CoinbaseTransaction , CoinbaseUser , CoinbaseTransfer , CoinbaseError , CoinbasePaymentButton
50+ from coinbase .models import CoinbaseAmount , CoinbaseTransaction , CoinbaseUser , CoinbaseTransfer , CoinbaseError
4951
5052
5153class CoinbaseAccount (object ):
@@ -57,7 +59,8 @@ class CoinbaseAccount(object):
5759
5860 def __init__ (self ,
5961 oauth2_credentials = None ,
60- api_key = None ):
62+ api_key = None ,
63+ api_secret = None ):
6164 """
6265
6366 :param oauth2_credentials: JSON representation of Coinbase oauth2 credentials
@@ -96,6 +99,14 @@ def __init__(self,
9699 #Set our request parameters to be empty
97100 self .global_request_params = {}
98101
102+ elif api_key and api_secret :
103+
104+ self .api_key = api_key
105+ self .api_secret = api_secret
106+
107+ self .session .headers .update ({'ACCESS_KEY' : api_key })
108+
109+
99110 elif api_key :
100111 if type (api_key ) is str :
101112
@@ -167,7 +178,15 @@ def balance(self):
167178 """
168179
169180 url = COINBASE_ENDPOINT + '/account/balance'
170- response = self .session .get (url , params = self .global_request_params )
181+ if (self .api_secret ):
182+ nonce = int (time .time () * 1e6 )
183+ message = str (nonce ) + url
184+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
185+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
186+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
187+ response = self .session .get (url = url , data = None )
188+ else :
189+ response = self .session .get (url , params = self .global_request_params )
171190 results = response .json ()
172191 return CoinbaseAmount (results ['amount' ], results ['currency' ])
173192
@@ -179,8 +198,19 @@ def receive_address(self):
179198 :return: String address of account
180199 """
181200 url = COINBASE_ENDPOINT + '/account/receive_address'
182- response = self .session .get (url , params = self .global_request_params )
183- return response .json ()['address' ]
201+ if (self .api_secret ):
202+ nonce = int (time .time () * 1e6 )
203+ message = str (nonce ) + url
204+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
205+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
206+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
207+ response = self .session .get (url = url , data = None )
208+
209+ else :
210+ response = self .session .get (url , params = self .global_request_params )
211+
212+ results = response .json ()
213+ return str (results ['address' ])
184214
185215 @property
186216 def contacts (self ):
@@ -190,7 +220,15 @@ def contacts(self):
190220 :return: List of contacts in the account
191221 """
192222 url = COINBASE_ENDPOINT + '/contacts'
193- response = self .session .get (url , params = self .global_request_params )
223+ if (self .api_secret ):
224+ nonce = int (time .time () * 1e6 )
225+ message = str (nonce ) + url
226+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
227+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
228+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
229+ response = self .session .get (url = url , data = None )
230+ else :
231+ response = self .session .get (url , params = self .global_request_params )
194232 return [contact ['contact' ] for contact in response .json ()['contacts' ]]
195233
196234
@@ -205,7 +243,8 @@ def buy_price(self, qty=1):
205243 """
206244 url = COINBASE_ENDPOINT + '/prices/buy'
207245 params = {'qty' : qty }
208- params .update (self .global_request_params )
246+ if (self .api_secret == None ):
247+ params .update (self .global_request_params )
209248 response = self .session .get (url , params = params )
210249 results = response .json ()
211250 return CoinbaseAmount (results ['amount' ], results ['currency' ])
@@ -218,7 +257,8 @@ def sell_price(self, qty=1):
218257 """
219258 url = COINBASE_ENDPOINT + '/prices/sell'
220259 params = {'qty' : qty }
221- params .update (self .global_request_params )
260+ if (self .api_secret == None ):
261+ params .update (self .global_request_params )
222262 response = self .session .get (url , params = params )
223263 results = response .json ()
224264 return CoinbaseAmount (results ['amount' ], results ['currency' ])
@@ -244,7 +284,16 @@ def buy_btc(self, qty, pricevaries=False):
244284 "qty" : qty ,
245285 "agree_btc_amount_varies" : pricevaries
246286 }
247- response = self .session .post (url = url , data = json .dumps (request_data ), params = self .global_request_params )
287+ data = json .dumps (request_data )
288+ if (self .api_secret ):
289+ nonce = int (time .time () * 1e6 )
290+ message = str (nonce ) + url + data
291+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
292+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
293+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
294+ response = self .session .post (url = url , data = data )
295+ else :
296+ response = self .session .post (url = url , data = data , params = self .global_requestparams )
248297 response_parsed = response .json ()
249298 if response_parsed ['success' ] == False :
250299 return CoinbaseError (response_parsed ['errors' ])
@@ -263,7 +312,16 @@ def sell_btc(self, qty):
263312 request_data = {
264313 "qty" : qty ,
265314 }
266- response = self .session .post (url = url , data = json .dumps (request_data ), params = self .global_request_params )
315+ data = json .dumps (request_data )
316+ if (self .api_secret ):
317+ nonce = int (time .time () * 1e6 )
318+ message = str (nonce ) + url + data
319+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
320+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
321+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
322+ response = self .session .post (url = url , data = data )
323+ else :
324+ response = self .session .post (url = url , data = data , params = self .global_requestparams )
267325 response_parsed = response .json ()
268326 if response_parsed ['success' ] == False :
269327 return CoinbaseError (response_parsed ['errors' ])
@@ -300,7 +358,16 @@ def request(self, from_email, amount, notes='', currency='BTC'):
300358 }
301359 }
302360
303- response = self .session .post (url = url , data = json .dumps (request_data ), params = self .global_request_params )
361+ data = json .dumps (request_data )
362+ if (self .api_secret ):
363+ nonce = int (time .time () * 1e6 )
364+ message = str (nonce ) + url + data
365+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
366+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
367+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
368+ response = self .session .post (url = url , data = data )
369+ else :
370+ response = self .session .post (url = url , data = data , params = self .global_requestparams )
304371 response_parsed = response .json ()
305372 if response_parsed ['success' ] == False :
306373 pass
@@ -338,7 +405,16 @@ def send(self, to_address, amount, notes='', currency='BTC'):
338405 }
339406 }
340407
341- response = self .session .post (url = url , data = json .dumps (request_data ), params = self .global_request_params )
408+ data = json .dumps (request_data )
409+ if (self .api_secret ):
410+ nonce = int (time .time () * 1e6 )
411+ message = str (nonce ) + url + data
412+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
413+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
414+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
415+ response = self .session .post (url = url , data = data )
416+ else :
417+ response = self .session .post (url = url , data = data , params = self .global_requestparams )
342418 response_parsed = response .json ()
343419
344420 if response_parsed ['success' ] == False :
@@ -428,8 +504,8 @@ def get_user_details(self):
428504 url = COINBASE_ENDPOINT + '/users'
429505 response = self .session .get (url , params = self .global_request_params )
430506 results = response .json ()
431-
432- user_details = results ['users' ][0 ]['user' ]
507+
508+ user_details = results ['users' ], [0 ], ['user' ]
433509
434510 #Convert our balance and limits to proper amounts
435511 balance = CoinbaseAmount (user_details ['balance' ]['amount' ], user_details ['balance' ]['currency' ])
@@ -461,53 +537,17 @@ def generate_receive_address(self, callback_url=None):
461537 "callback_url" : callback_url
462538 }
463539 }
464- response = self .session .post (url = url , data = json .dumps (request_data ), params = self .global_request_params )
540+ data = json .dumps (request_data )
541+ if (self .api_secret ):
542+ nonce = int (time .time () * 1e6 )
543+ message = str (nonce ) + url + data
544+ signature = hmac .new (self .api_secret , message , hashlib .sha256 ).hexdigest ()
545+ self .session .headers .update ({'ACCESS_SIGNATURE' : signature })
546+ self .session .headers .update ({'ACCESS_NONCE' : nonce })
547+ response = self .session .post (url = url , data = data )
548+ else :
549+ response = self .session .post (url = url , data = data , params = self .global_requestparams )
465550 return response .json ()['address' ]
466551
467552
468- def create_button (self , name , price , price_currency = 'BTC' ,
469- button_type = 'buy_now' , callback_url = None ,
470- ** kwargs ):
471- """
472- Create a new payment button, page, or iframe.
473-
474- Some required parameters are documented, but the rest are supported
475- as keyword-arguments.
476-
477- See https://coinbase.com/api/doc/1.0/buttons/create.html for details.
478-
479- :param name: The name of the item to be purchased, donated for, or
480- subscribed.
481- :param price: The price, in whatever currency specified. Preferably a
482- string or Decimal.
483- :param price_currency: The ISO currency in which the price is listed.
484- Eg, BTC or USD.
485- :param button_type: Choices are 'buy_now', 'donation', and
486- 'subscription'
487- :param callback_url: The URL to receive instant payment notifications
488553
489- :return: The embeddable HTML string
490- """
491- url = COINBASE_ENDPOINT + '/buttons'
492- request_data = {
493- "button" : {
494- "name" :name ,
495- "price_string" :unicode (price ),
496- "price_currency_iso" :price_currency ,
497- "button_type" :button_type
498- }
499- }
500- if callback_url is not None :
501- request_data ['button' ]['callback_url' ] = callback_url
502- request_data ['button' ].update (kwargs )
503- response = self .session .post (url = url , data = json .dumps (request_data ),
504- params = self .global_request_params )
505- resp_data = response .json ()
506- if not resp_data ['success' ] or 'button' not in resp_data :
507- error_msg = 'Error creating button'
508- if 'errors' in resp_data :
509- error_msg += ':' + u'\n ' .join (resp_data )
510- else :
511- error_msg += '.'
512- raise RuntimeError (error_msg )
513- return CoinbasePaymentButton (** resp_data ['button' ])
0 commit comments