SatSale

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 044272a46c228311dc603f0896c6c47399ef18fb
parent c8d563dccb159075c2468c675a251408e41f27c7
Author: NicholasFarrow <nicholas.w.farrow@gmail.com>
Date:   Sat, 16 Jan 2021 19:37:33 +1100

Implement lightning payments, working up until checking payment status

Diffstat:
MREADME.md | 14++++++++------
Minvoice/payment_invoice.py | 6+++++-
Mpay/lnd.py | 63+++++++++++++++++++++++++++++++++++----------------------------
Mrequirements.txt | 1+
4 files changed, 49 insertions(+), 35 deletions(-)

diff --git a/README.md b/README.md @@ -1,17 +1,19 @@ # BTCPyment -Existing non-custodial Bitcoin payment processors are bloated, difficult to install, and not easily customisable. BTCPyment strives to serve as an easily deployable, lightweight Bitcoin payment processor that keeps your coins with your keys by connecting to your own Bitcoin node. +Existing non-custodial Bitcoin payment processors are bloated, difficult to install, and not easily customisable. BTCPyment strives to serve as an easily deployable, lightweight Bitcoin payment processor that keeps your coins with your keys by connecting to your own Bitcoin node or Lightning network node. +## Demo [![demo](https://nickfarrow.com/assets/btcpyment.png)](https://node.nickfarrow.com/) -Currently, BTCPyment only provides donation buttons, but we plan to soon extend BTCPyment to handle payments from common webstores (shopify, woocommerce, etc). +BTCPyment is early in development and currently only provides donation buttons, but we plan to soon extend BTCPyment to handle payments from common webstores (shopify, woocommerce, etc). - BTCPyment makes donation buttons simple; using Python and Javascript to talk to your own Bitcoin node, with an easy iframe embed install. BTCPyment uses RPC to generate new addresses from your Bitcoin node, and monitors the payment status with your own copy of the blockchain. Soon, we hope to support lightning payments as well as function as a payment processor for a variety of web shops (woocommerce, shopify). + BTCPyment makes donation buttons simple; using Python and Javascript to talk to your own Bitcoin node, with an easy iframe embed install. BTCPyment uses RPC to generate new addresses from your Bitcoin node, and monitors the payment status with your own copy of the blockchain. We now support lightning payments also, with a woocommerce plugin under development. # Features * Lightweight, Python and Javascript talk to your own Bitcoin node via websockets and SSH. * Direct peer-to-peer payments without any middleman. No KYC, and greater privacy than donation systems with reused Bitcoin addresses. +* Recently added support for Lightning network (lnd)! * Natively supports all bitcoind node features (e.g. segwit) through RPC. -* QR codes, user chooses the minimum payment confirmations and payment expiry duration. +* QR codes, you choose the minimum payment confirmations and payment expiry duration. * Highly extendable, just take a look at the code! Optional code execution upon payment. * No shitcoin bloat. Bitcoin only. @@ -32,7 +34,7 @@ rpcport = "8332" username = "bitcoinrpc" password = "RPCPASSWORD" ``` -(You can find these in `~/.bitcoin/bitcoin.conf`). If your node is remote to your website, you can specify an SSH `tunnel_host = "pi@192.168.0.252"` that will forward `rpcport`. You may also need to set `rpcallowip=YOUR_SERVER_IP` in your `~/.bitcoin/bitcoin.conf`. +(You can find these in `~/.bitcoin/bitcoin.conf`). If your node is remote to your website, you can specify an SSH `tunnel_host = "pi@192.168.0.252"` that will forward `rpcport`. You may also need to set `rpcallowip=YOUR_SERVER_IP` in your `~/.bitcoin/bitcoin.conf`. If you want to use lightning network payments, see [Lightning instructions](docs/lightning.md)] ### Run BTCPyment Run BTCPyment with @@ -45,7 +47,7 @@ nohup gunicorn --worker-class eventlet -w 1 -b 0.0.0.0:8000 server:app > log.txt tail -f log.txt ``` -## Embed Donation Button +## Embed a Donation Button Now embed the donation button into your website: ```html <iframe src="http://YOUR_SERVER_IP:8000/" style="margin: 0 auto;display:block;height:320px;border:none;overflow:hidden;" scrolling="no"></iframe> diff --git a/invoice/payment_invoice.py b/invoice/payment_invoice.py @@ -21,7 +21,11 @@ class invoice: def create_qr(self): - qr_str = "BITCOIN:{}?amount={}&label={}".format(self.address.upper(), self.value, self.label) + if payment.pay_method = 'lnd': + qr_str = "{}".format(self.address.upper()) + else: + qr_str = "{}?amount={}&label={}".format(self.address.upper(), self.value, self.label) + img = qrcode.make(qr_str) img.save('static/qr_codes/{}.png'.format(self.uuid)) return diff --git a/pay/lnd.py b/pay/lnd.py @@ -13,34 +13,36 @@ class lnd(invoice): super().__init__(dollar_value, currency, label) print(self.__dict__) - from lnd_grpc import Client + from lndgrpc import LNDClient # Copy admin macaroon and tls cert to local machine - try: - tls_file = os.path.join(config.lnd_dir, "tls.cert") - macaroon_file = os.path.join(config.lnd_dir, "data/chain/bitcoin/mainnet/admin.macaroon") - subprocess.run(["scp", "{}:{}".format(config.tunnel_host, tls_file), "."]) - subprocess.run(["scp", "-r", "{}:{}".format(config.tunnel_host, macaroon_file), "."]) - - except Exception as e: - print(e) - print("Failed to copy tls and macaroon files to local machine.") + if (not os.path.isfile("tls.cert")) or (not os.path.isfile("admin.macaroon")): + print("Could not find tls.cert or admin.macaroon in BTCPyment folder. Attempting to download from lnd directory.") + try: + tls_file = os.path.join(config.lnd_dir, "tls.cert") + macaroon_file = os.path.join(config.lnd_dir, "data/chain/bitcoin/mainnet/admin.macaroon") + subprocess.run(["scp", "{}:{}".format(config.tunnel_host, tls_file), "."]) + subprocess.run(["scp", "-r", "{}:{}".format(config.tunnel_host, macaroon_file), "."]) + except Exception as e: + print(e) + print("Failed to copy tls and macaroon files to local machine.") + else: + print("Found tls.cert and admin.macaroon.") connection_str = "{}:{}".format(config.host, config.rpcport) - print("Attempting to connect to {}. This may take a few minutes...".format(connection_str)) + print("Attempting to connect to lightning node {}. This may take a few minutes...".format(connection_str)) for i in range(config.connection_attempts): try: # Require admin=True for creating invoices print("Attempting to initialise lnd rpc client...") - self.lnd = Client(grpc_host=config.host, - grpc_port=config.rpcport, - macaroon_path="admin.macaroon", - tls_cert_path="tls.cert") + self.lnd = LNDClient("{}:{}".format(config.host, config.rpcport), + macaroon_filepath="admin.macaroon", + cert_filepath="tls.cert") - # print("Getting lnd info...") - # info = self.lnd.get_info() - # print(info) + print("Getting lnd balance...") + info = self.lnd.channel_balance() + print(info) print("Successfully contacted lnd.") break @@ -52,15 +54,18 @@ class lnd(invoice): else: raise Exception("Could not connect to lnd. Check your gRPC / port tunneling settings and try again.") + print("Ready for payments requests.") def create_lnd_invoice(self, btc_amount): # Multiplying by 10^8 to convert to satoshi units sats_amount = int(btc_amount*10**8) res = self.lnd.add_invoice(value=sats_amount) self.lnd_invoice = json.loads(MessageToJson(res)) - self.hash = res.r_hash #self.lnd_invoice['r_hash'] - print("Create invoice response:") - print(res) + self.hash = self.lnd_invoice['r_hash'] + + print("Created lightning invoice:") + print(self.lnd_invoice) + return self.lnd_invoice['payment_request'] # self.lnd_invoice = json.loads(MessageToJson(self.lnd.add_invoice(sats_amount))) @@ -91,11 +96,15 @@ class lnd(invoice): print("Looking up invoice") # For some reason this does not work, I think lookup_invoice() may be broken # as it does not return the correct response that includes the amount paid among other fields. - print(self.lnd.list_invoices()) - print(self.lnd.lookup_invoice(r_hash=self.hash)) - print("Invoice ^") - print(type(self.hash)) - print(str(self.hash.hex())) + + invoice_status = json.loads(MessageToJson(self.lnd.lookup_invoice(r_hash_str=b64decode(self.hash).hex()))) + print(invoice_status) + + if 'amt_paid' not in invoice_status.items(): + conf_paid = 0 + unconf_paid = 0 + else: + print("WEEEEEEEEEEEEEEEEEEEE") # print(str(b64decode(self.hash.strip('\\')))) # invoice_status = json.loads(MessageToJson(self.lnd.lookup_invoice(self.hash))) @@ -103,6 +112,4 @@ class lnd(invoice): # print(self.lnd.lookup_invoice("8893044a07c2c5e2a50252f044224f297487242e05758a970d5ba28ece75f66d")) # subprocess.run([""]) - conf_paid = 0 #invoice_status['amt_paid'] - unconf_paid = 0 return conf_paid, unconf_paid diff --git a/requirements.txt b/requirements.txt @@ -7,3 +7,4 @@ requests==2.25.0 gunicorn==20.0.4 eventlet==0.30.0 Pillow==8.0.1 +lndgrpc==0.2.0