class Controller: def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.expected_amount = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.single_screen_mode = settings['single_screen_mode'] self.green_addresses = settings['green_addresses'] def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect(self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect(self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay(os.environ['POS'] + '/data/customer_display.html', self.single_screen_mode) if not self.single_screen_mode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.single_screen_mode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % (cur_amount, currency, self.exchange_rate, currency, self.exchange_rate_source) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) self.expected_amount = '%s BTC' % amount_str imgdata = self.create_img_data(self.current_address, amount_str) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ (self.expected_amount, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) def create_img_data(self, address, amount_str): (_, size, img) = qrencode.encode("bitcoin:%s?amount=%s&label=" % (address, amount_str)) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid): if not hasattr(self, 'app'): return # not yet read # emit signal, so we can process this on the Qt GUI thread self.app.emit(QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), txid) def _new_transaction_received(self, txid): # check if we are waiting for a payment if self.current_address == "": return # check if the txid looks sane before passing it # to bitcoind (for security reasons; might be overly # paranoid, but can't hurt) if re.search("^[a-f0-9]*$", txid) == None: return tx_info = self.bitcoind.gettransaction(txid) address_found = False for detail in tx_info['details']: if self.current_address == detail['address']: amount_received = detail['amount'] address_found = True if not address_found: return msg = "Transaction to %s with amount %s (of %s expected) received." % (self.current_address, amount_received, self.expected_amount) (from_green_address, green_address_msg) = self.green_address_check(txid) if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script('show_payment_received()') self.current_address = "" def green_address_check(self, txid): found = False msg = "" origins = self.get_origins(txid) for origin in origins: if origin in self.green_addresses: found = True msg = self.green_addresses[origin] break return (found, msg) def get_origins(self, txid): try: origins = [] raw_tx = self.bitcoind.getrawtransaction(txid, 1) vins = raw_tx['vin'] for vin in vins: raw_tx = self.bitcoind.getrawtransaction(vin['txid'], 1) for vout in raw_tx['vout']: if vin['vout'] == vout['n']: origins.extend(vout['scriptPubKey']['addresses']) return origins except JSONRPCException: return [] def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): if not hasattr(self, 'app'): return # not yet read self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)
class Controller: def __init__(self, settings, nfc_broadcast): self.nfc_broadcast = nfc_broadcast self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.single_screen_mode = settings['single_screen_mode'] self.green_addresses = settings['green_addresses'] self.bt_addr = None def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect( self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect( self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay('data/customer_display.html', self.single_screen_mode) if not self.single_screen_mode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.single_screen_mode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % ( cur_amount, currency, self.exchange_rate, currency, self.exchange_rate_source) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) btc_uri = self.create_btc_uri(self.current_address, amount_str, self.bt_addr) imgdata = self.create_img_data(btc_uri) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ ('%s BTC' % amount_str, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) self.nfc_broadcast.set_btc_uri(btc_uri) def create_btc_uri(self, address, amount_str, bt_addr): btc_uri = "bitcoin:%s?amount=%s" % (address, amount_str) if bt_addr != None: bt_addr_stripped = bt_addr.translate(None, ':') btc_uri += "&bt=%s" % bt_addr_stripped return btc_uri def create_img_data(self, btc_uri): (_, size, img) = qrencode.encode(btc_uri) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid): if not hasattr(self, 'app'): return # not yet read # emit signal, so we can process this on the Qt GUI thread self.app.emit( QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), txid) def _new_transaction_received(self, txid): # check if we are waiting for a payment if self.current_address == "": return # check if the txid looks sane before passing it # to bitcoind (for security reasons; might be overly # paranoid, but can't hurt) if re.search("^[a-f0-9]*$", txid) == None: return tx_info = self.bitcoind.gettransaction(txid) output_addresses = [] for detail in tx_info['details']: output_addresses.append(detail['address']) if self.current_address not in output_addresses: return msg = "Transaction to %s received." % self.current_address (from_green_address, green_address_msg) = self.green_address_check(txid) if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script('show_payment_received()') self.current_address = "" def bluetooth_available(self, bt_addr): self.bt_addr = bt_addr def new_transaction_via_bluetooth(self, tx): try: self.bitcoind.sendrawtransaction(tx) except JSONRPCException: # ignore, if this did not work - we might # have already received the transaction in # a different way pass def green_address_check(self, txid): found = False msg = "" origins = self.get_origins(txid) for origin in origins: if origin in self.green_addresses: found = True msg = self.green_addresses[origin] break return (found, msg) def get_origins(self, txid): try: origins = [] raw_tx = self.bitcoind.getrawtransaction(txid, 1) vins = raw_tx['vin'] for vin in vins: raw_tx = self.bitcoind.getrawtransaction(vin['txid'], 1) for vout in raw_tx['vout']: if vin['vout'] == vout['n']: origins.extend(vout['scriptPubKey']['addresses']) return origins except JSONRPCException: return [] def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') self.merchant_gui.update_status("System ready.") # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): if not hasattr(self, 'app'): return # not yet read self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)
class Controller: def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.singleScreenMode = settings['single-screen-mode'] def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect( self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect( self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay('data/customer_display.html', self.singleScreenMode) if not self.singleScreenMode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.singleScreenMode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % ( cur_amount, currency, self.exchange_rate, self.exchange_rate_source, currency) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) imgdata = self.create_img_data(self.current_address, amount_str) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ ('%s BTC' % amount_str, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) def create_img_data(self, address, amount_str): (_, size, img) = qrencode.encode("bitcoin:%s?amount=%s&label=" % (address, amount_str)) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid, output_addresses, from_green_address, green_address_msg): # emit signal, so we can process this on the Qt GUI thread self.app.emit( QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), (txid, output_addresses, from_green_address, green_address_msg)) def _new_transaction_received(self, data): (_, output_addresses, from_green_address, green_address_msg) = data if self.current_address != "" and self.current_address in output_addresses: msg = "Transaction to %s received." % self.current_address if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script( 'show_payment_received()') self.current_address = "" def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)
class Controller: def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.singleScreenMode = settings['single-screen-mode'] def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect(self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect(self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay('data/customer_display.html', self.singleScreenMode) if not self.singleScreenMode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.singleScreenMode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % (cur_amount, currency, self.exchange_rate, self.exchange_rate_source, currency) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) imgdata = self.create_img_data(self.current_address, amount_str) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ ('%s BTC' % amount_str, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) def create_img_data(self, address, amount_str): (_, size, img) = qrencode.encode("bitcoin:%s?amount=%s&label=" % (address, amount_str)) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid, output_addresses, from_green_address, green_address_msg): # emit signal, so we can process this on the Qt GUI thread self.app.emit(QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), (txid, output_addresses, from_green_address, green_address_msg)) def _new_transaction_received(self, data): (_, output_addresses, from_green_address, green_address_msg) = data if self.current_address != "" and self.current_address in output_addresses: msg = "Transaction to %s received." % self.current_address if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script('show_payment_received()') self.current_address = "" def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)