예제 #1
0
    def calculate_fiat_amount(self, satoshis):
        """
        Calculates the fiat amount that X satoshis gets you right now.
        """

        fiat_btc = BTCTransaction.get_btc_market_price(self.currency_code)
        markup_fee = fiat_btc * self.get_cashout_percent_markup() / 100.00
        fiat_btc = fiat_btc - markup_fee
        fiat_total = fiat_btc * satoshis_to_btc(satoshis)
        return math.floor(fiat_total * 100) / 100
예제 #2
0
    def send_btc(self,
                 satoshis_to_send,
                 destination_btc_address,
                 destination_email_address=None,
                 notes=None):
        """
        Send satoshis to a destination address or email address.
        CB requires a fee for txns < .001 BTC, so this method will
        automatically include txn fees for those.

        Returns a tuple of the form (some or all may be none):
            btc_txn, sent_btc_obj, api_call, err_str
        """

        msg = "Can't have both a destination email and BTC address. %s | %s" % (
            destination_email_address, destination_btc_address)
        assert not (destination_email_address and destination_btc_address), msg

        msg = 'Must send to a destination email OR BTC address'
        assert destination_email_address or destination_btc_address, msg

        dest_addr_to_use = None

        if destination_btc_address:
            dest_addr_to_use = destination_btc_address
            send_btc_dict = {
                'destination_btc_address': destination_btc_address
            }

            msg = '%s is not a valid bitcoin address' % destination_btc_address
            assert is_valid_btc_address(destination_btc_address), msg

        if destination_email_address:
            dest_addr_to_use = destination_email_address
            send_btc_dict = {'destination_email': destination_email_address}

            msg = '%s is not a valid email address' % destination_email_address
            # FIXME: implement

        btc_to_send = satoshis_to_btc(satoshis_to_send)

        SEND_URL = 'https://coinbase.com/api/v1/transactions/send_money'

        body = 'transaction[to]=%s&transaction[amount]=%s' % (dest_addr_to_use,
                                                              btc_to_send)
        post_params = {'to': dest_addr_to_use, 'amount': btc_to_send}

        if satoshis_to_send <= btc_to_satoshis(
                .001) and not destination_email_address:
            # https://coinbase.com/api/doc/1.0/transactions/send_money.html
            # Coinbase pays transaction fees on payments greater than or equal to 0.001 BTC.
            # But for smaller amounts you have to add your own
            # For some reason, coinbase requires 2x fees of .2 mBTC vs (.1 mBTC)
            body += '&transaction[user_fee]=0.0002'
            post_params['user_fee'] = 0.0002

        if notes:
            # TODO: url encode this?
            body += '&transaction[notes]=' + notes
            post_params['notes'] = notes

        r = get_cb_request(url=SEND_URL,
                           api_key=self.api_key,
                           api_secret=self.api_secret,
                           body=body)

        # Log the API call
        api_call = APICall.objects.create(api_name=APICall.COINBASE_SEND_BTC,
                                          url_hit=SEND_URL,
                                          response_code=r.status_code,
                                          post_params=post_params,
                                          api_results=r.content,
                                          merchant=self.merchant,
                                          credential=self)

        self.handle_status_code(r.status_code)

        resp_json = json.loads(r.content)

        if resp_json.get('error') or resp_json.get('errors'):
            err_str = resp_json.get('error')
            # combine the two
            if resp_json.get('errors'):
                if err_str:
                    err_str += ' %s' % resp_json.get('errors')
                else:
                    err_str = resp_json.get('errors')

            # this assumes all error messages here are safe to display to the user
            return None, None, api_call, err_str

        transaction = resp_json['transaction']

        satoshis = -1 * btc_to_satoshis(transaction['amount']['amount'])

        txn_hash = transaction['hsh']

        # Record the Send
        send_btc_dict.update({
            'credential': self,
            'txn_hash': txn_hash,
            'satoshis': satoshis,
            'transaction_id': transaction['id'],
            'notes': notes,
        })
        sent_btc_obj = CBSSentBTC.objects.create(**send_btc_dict)

        if txn_hash:
            return BTCTransaction.objects.create(
                txn_hash=txn_hash, satoshis=satoshis,
                conf_num=0), sent_btc_obj, api_call, None
        else:
            # Coinbase seems finicky about transaction hashes
            return None, sent_btc_obj, api_call, None
예제 #3
0
    def request_cashout(self, satoshis_to_sell):
        SELL_URL = 'https://coinbase.com/api/v1/sells'

        btc_to_sell = satoshis_to_btc(satoshis_to_sell)
        body_to_use = 'qty=%s' % btc_to_sell

        r = get_cb_request(
            url=SELL_URL,
            api_key=self.api_key,
            api_secret=self.api_secret,
            body=body_to_use,
        )

        # Log the API call
        APICall.objects.create(api_name=APICall.COINBASE_CASHOUT_BTC,
                               url_hit=SELL_URL,
                               response_code=r.status_code,
                               api_results=r.content,
                               post_params={'qty': btc_to_sell},
                               merchant=self.merchant,
                               credential=self)

        self.handle_status_code(r.status_code)

        resp_json = json.loads(r.content)

        success = resp_json['success']
        assert success is True, '%s: %s' % (success, resp_json.get('errors'))

        transfer = resp_json['transfer']

        status = transfer['status']
        assert status.lower() in ('pending', 'created'), status

        btc_obj = transfer['btc']
        assert btc_obj['currency'] == 'BTC', btc_obj

        satoshis = btc_to_satoshis(btc_obj['amount'])
        assert satoshis == satoshis_to_sell, btc_obj['amount']

        currency_to_recieve = transfer['total']['currency']

        fiat_fees_in_cents = 0
        for fee_key in transfer['fees']:
            fee = transfer['fees'][fee_key]
            fiat_fees_in_cents += int(fee['cents'])
            msg = '%s != %s' % (fee['currency_iso'], currency_to_recieve)
            assert fee['currency_iso'] == currency_to_recieve, msg
        fiat_fees = fiat_fees_in_cents / 100.0

        cbs_sell_btc = CBSSellBTC.objects.create(
            coinbase_code=transfer['code'])

        return CBSSellBTC.objects.create(
            credential=self,
            cbs_sell_btc=cbs_sell_btc,
            satoshis=satoshis,
            currency_code=currency_to_recieve,
            fees_in_fiat=fiat_fees,
            to_receive_in_fiat=float(transfer['total']['amount']),
        )
예제 #4
0
 def calculate_exchange_rate(self):
     return float(self.fiat_amount) / satoshis_to_btc(self.satoshis)
예제 #5
0
    def send_btc(self, satoshis_to_send, destination_btc_address):
        """
        Returns a tuple of the form (some or all may be none):
            btc_txn, sent_btc_obj, api_call, err_str
        """

        msg = '%s is not a valid bitcoin address' % destination_btc_address
        assert is_valid_btc_address(destination_btc_address), msg

        btc_to_send = satoshis_to_btc(satoshis_to_send)
        SEND_URL = 'https://www.bitstamp.net/api/bitcoin_withdrawal/'

        trading_obj = self.get_trading_obj()

        try:
            post_params = {
                'amount': btc_to_send,
                'address': destination_btc_address
            }
            withdrawal_info = trading_obj.bitcoin_withdrawal(**post_params)

            withdrawal_id = withdrawal_info['id']
            msg = "%s is not an int, it's a %s" % (withdrawal_id,
                                                   type(withdrawal_id))
            assert type(withdrawal_id) is int, msg

            # Log the API call
            api_call = APICall.objects.create(
                api_name=APICall.BITSTAMP_SEND_BTC,
                url_hit=SEND_URL,
                response_code=200,
                post_params=post_params,
                api_results=str(withdrawal_info),
                merchant=self.merchant,
                credential=self)

            self.mark_success()

        except Exception as e:
            # Log the API Call
            api_call = APICall.objects.create(
                api_name=APICall.BITSTAMP_SEND_BTC,
                url_hit=SEND_URL,
                response_code=0,  # not accurate
                post_params=post_params,
                api_results=str(e),
                merchant=self.merchant,
                credential=self)

            self.mark_failure()
            print 'Error was: %s' % e
            # TODO: this assumes all error messages here are safe to display to the user
            return None, None, api_call, str(e)

        sent_btc_obj = BTSSentBTC.objects.create(
            credential=self,
            satoshis=satoshis_to_send,
            destination_btc_address=destination_btc_address,
            withdrawal_id=withdrawal_id,
            status='0',
        )

        # This API doesn't return a TX hash on sending bitcoin :(
        return None, sent_btc_obj, api_call, None