SatSale

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

commit 2642243f9bfa4700e2d07744c0f162c534a37f27
parent fe4cb535cc68ec2c48e36e1ef7676bf0ceba2963
Author: Nick <nicholas.w.farrow@gmail.com>
Date:   Fri, 12 Nov 2021 16:44:24 +1100

Add Lightning Address (#21)

* WIP lightning address server

* add lightning address config arg

* fix url

* Remove response codes and add import

* add identifier metadata

* change metadata string format

* Remove extra invoice print

* add lnd invoice memos

* Use memo for payRequest

* Add description hash

* Change lndgrpc library to sako0938's fork

* metadata and description hash

* add ability to modifiy description - new config arg

* Fix missing char

* switch min max sats

* Force HTTPS and fix min max payments
Diffstat:
Mconfig.py | 4++++
Agateways/lightning_address.py | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnode/lnd.py | 12++++++------
Mrequirements.txt | 2+-
Msatsale.py | 7+++++--
5 files changed, 80 insertions(+), 9 deletions(-)

diff --git a/config.py b/config.py @@ -60,6 +60,10 @@ connection_attempts = 3 # Generic redirect url after payment redirect = "https://github.com/nickfarrow/satsale" +# Lightning Address e.g. name@you.satsale.domain (think this requires https url) +lightning_address = None +lightning_address_comment = None # Defaults to: "Thank you for your support <3" + # DO NOT CHANGE THIS TO TRUE UNLESS YOU WANT ALL PAYMENTS TO AUTOMATICALLY # BE CONSIDERED AS PAID. free_mode = False diff --git a/gateways/lightning_address.py b/gateways/lightning_address.py @@ -0,0 +1,64 @@ +from flask import request +from flask_restplus import Resource, Api, Namespace, fields +import hashlib + +import config + +min_sats = 10**2 +max_sats = 10**6 + +# Following https://github.com/andrerfneves/lightning-address/blob/master/DIY.md + +description = config.lightning_address_comment +if description is None: + description = "Thank you for your support <3" + +metadata = "[[\"text/plain\", \"{}\"], [\"text/identifier\", \"{}\"]]".format(description, config.lightning_address.split("@")[0]) + +def add_ln_address_decorators(app, api, node): + class get_ln_address(Resource): + def get(self): + try: + print("Someone requested our ln address: {}!".format(config.lightning_address)) + resp = { + "callback": "https://{}/lnaddr".format(config.lightning_address.split("@")[1]), + "maxSendable": max_sats*10**3, + "minSendable": min_sats*10**3, + "metadata": metadata, + "tag": "payRequest" + } + return resp + + except Exception as e: + print(e) + return {"status": "ERROR", "reason": e} + + + + class init_ln_addr_payment(Resource): + def get(self): + if request.args.get("amount") is None: + return {"status": "ERROR", "reason": "You need to request an ?amount=MSATS"} + + amount_msats = int(request.args.get("amount")) + amount_btc = amount_msats / 10**(3+8) + + print("Received payment request from ln address for {} msats...".format(amount_msats)) + + description_hash = hashlib.sha256(metadata.encode()).digest() + + try: + invoice, _ = node.create_lnd_invoice(amount_btc, memo="lightning address payment", description_hash=description_hash) + print("Responding with invoice {}".format(invoice)) + return { + "pr": invoice, + "routes": [] + } + except Exception as e: + print(e) + return {"status": "ERROR", "reason": e} + + + api.add_resource(get_ln_address, "/.well-known/lnurlp/{}".format(config.lightning_address.split("@")[0])) + api.add_resource(init_ln_addr_payment, "/lnaddr") + return diff --git a/node/lnd.py b/node/lnd.py @@ -111,16 +111,16 @@ class lnd: return # Create lightning invoice - def create_lnd_invoice(self, btc_amount): + def create_lnd_invoice(self, btc_amount, memo=None, description_hash=None): # Multiplying by 10^8 to convert to satoshi units sats_amount = int(btc_amount * 10 ** 8) - res = self.lnd.add_invoice(value=sats_amount) + res = self.lnd.add_invoice(value=sats_amount, memo=memo, description_hash=description_hash) lnd_invoice = json.loads(MessageToJson(res)) - return lnd_invoice["payment_request"], lnd_invoice["r_hash"] + return lnd_invoice["paymentRequest"], lnd_invoice["rHash"] def get_address(self, amount, label): - address, r_hash = self.create_lnd_invoice(amount) + address, r_hash = self.create_lnd_invoice(amount, memo=label) return address, r_hash # Check whether the payment has been paid @@ -129,12 +129,12 @@ class lnd: MessageToJson(self.lnd.lookup_invoice(r_hash_str=b64decode(rhash).hex())) ) - if "amt_paid_sat" not in invoice_status.keys(): + if "amtPaidSat" not in invoice_status.keys(): conf_paid = 0 unconf_paid = 0 else: # Store amount paid and convert to BTC units - conf_paid = int(invoice_status["amt_paid_sat"]) * 10 ** 8 + conf_paid = int(invoice_status["amtPaidSat"]) * 10 ** 8 unconf_paid = 0 return conf_paid, unconf_paid diff --git a/requirements.txt b/requirements.txt @@ -13,4 +13,4 @@ PySocks==1.7.1 # For lightning (optional) setuptools==50.3.2 -lndgrpc==0.2.0 +lnd-grpc-client diff --git a/satsale.py b/satsale.py @@ -211,8 +211,6 @@ class complete_payment(Resource): if status["payment_complete"] != 1: return {"message": "You havent paid you stingy bastard"} - print(invoice) - # Call webhook to confirm payment with merchant if (invoice["webhook"] != None) and (invoice["webhook"] != ""): print("Calling webhook {}".format(invoice["webhook"])) @@ -301,5 +299,10 @@ if config.pay_method == "lnd": print("Connection to lightning node successful.") +if config.lightning_address is not None: + from gateways import lightning_address + lightning_address.add_ln_address_decorators(app, api, lightning_node) + + if __name__ == "__main__": app.run(debug=False)