Browse Source

support description_hash across all APIs.

aiosqlite
fiatjaf 5 years ago
parent
commit
bc27293315
  1. 15
      lnbits/core/services.py
  2. 13
      lnbits/core/views/api.py
  3. 6
      lnbits/wallets/base.py
  4. 12
      lnbits/wallets/clightning.py
  5. 20
      lnbits/wallets/lnbits.py
  6. 10
      lnbits/wallets/lndgrpc.py
  7. 33
      lnbits/wallets/lndrest.py
  8. 9
      lnbits/wallets/lnpay.py
  9. 9
      lnbits/wallets/lntxbot.py
  10. 7
      lnbits/wallets/opennode.py

15
lnbits/core/services.py

@ -1,15 +1,18 @@
from typing import Optional, Tuple
from lnbits.bolt11 import decode as bolt11_decode # type: ignore
from lnbits.bolt11 import decode as bolt11_decode # type: ignore
from lnbits.helpers import urlsafe_short_hash
from lnbits.settings import WALLET
from .crud import get_wallet, create_payment, delete_payment
def create_invoice(*, wallet_id: str, amount: int, memo: str) -> Tuple[str, str]:
def create_invoice(*, wallet_id: str, amount: int, memo: str, description_hash: bytes) -> Tuple[str, str]:
try:
ok, checking_id, payment_request, error_message = WALLET.create_invoice(amount=amount, memo=memo)
ok, checking_id, payment_request, error_message = WALLET.create_invoice(
amount=amount, memo=memo, description_hash=description_hash
)
except Exception as e:
ok, error_message = False, str(e)
@ -35,11 +38,7 @@ def pay_invoice(*, wallet_id: str, bolt11: str, max_sat: Optional[int] = None) -
fee_reserve = max(1000, int(invoice.amount_msat * 0.01))
create_payment(
wallet_id=wallet_id,
checking_id=temp_id,
amount=-invoice.amount_msat,
fee=-fee_reserve,
memo=temp_id,
wallet_id=wallet_id, checking_id=temp_id, amount=-invoice.amount_msat, fee=-fee_reserve, memo=temp_id,
)
wallet = get_wallet(wallet_id)

13
lnbits/core/views/api.py

@ -1,5 +1,6 @@
from flask import g, jsonify, request
from http import HTTPStatus
from binascii import unhexlify
from lnbits.core import core_app
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
@ -27,13 +28,21 @@ def api_payments():
@api_validate_post_request(
schema={
"amount": {"type": "integer", "min": 1, "required": True},
"memo": {"type": "string", "empty": False, "required": True},
"memo": {"type": "string", "empty": False, "required": False},
"description_hash": {"type": "string", "empty": False, "required": False},
}
)
def api_payments_create_invoice():
if "description_hash" in g.data:
description_hash = unhexlify(g.data["description_hash"])
memo = ""
else:
description_hash = b""
memo = g.data["memo"]
try:
checking_id, payment_request = create_invoice(
wallet_id=g.wallet.id, amount=g.data["amount"], memo=g.data["memo"]
wallet_id=g.wallet.id, amount=g.data["amount"], memo=memo, description_hash=description_hash
)
except Exception as e:
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR

6
lnbits/wallets/base.py

@ -26,7 +26,7 @@ class PaymentStatus(NamedTuple):
class Wallet(ABC):
@abstractmethod
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
pass
@abstractmethod
@ -40,3 +40,7 @@ class Wallet(ABC):
@abstractmethod
def get_payment_status(self, checking_id: str) -> PaymentStatus:
pass
class Unsupported(Exception):
pass

12
lnbits/wallets/clightning.py

@ -7,20 +7,22 @@ import random
from os import getenv
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet, Unsupported
class CLightningWallet(Wallet):
def __init__(self):
if LightningRpc is None: # pragma: nocover
raise ImportError("The `pylightning` library must be installed to use `CLightningWallet`.")
self.l1 = LightningRpc(getenv("CLIGHTNING_RPC"))
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
if description_hash:
raise Unsupported("description_hash")
label = "lbl{}".format(random.random())
r = self.l1.invoice(amount*1000, label, memo, exposeprivatechannels=True)
r = self.l1.invoice(amount * 1000, label, memo, exposeprivatechannels=True)
ok, checking_id, payment_request, error_message = True, r["payment_hash"], r["bolt11"], None
return InvoiceResponse(ok, checking_id, payment_request, error_message)
@ -31,7 +33,7 @@ class CLightningWallet(Wallet):
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = self.l1.listinvoices(checking_id)
if r['invoices'][0]['status'] == 'unpaid':
if r["invoices"][0]["status"] == "unpaid":
return PaymentStatus(False)
return PaymentStatus(True)

20
lnbits/wallets/lnbits.py

@ -1,5 +1,6 @@
from os import getenv
from requests import get, post
from binascii import hexlify
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
@ -12,11 +13,16 @@ class LNbitsWallet(Wallet):
self.auth_admin = {"X-Api-Key": getenv("LNBITS_ADMIN_KEY")}
self.auth_invoice = {"X-Api-Key": getenv("LNBITS_INVOICE_KEY")}
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
r = post(
url=f"{self.endpoint}/api/v1/payments",
headers=self.auth_invoice,
json={"out": False, "amount": amount, "memo": memo}
json={
"out": False,
"amount": amount,
"memo": memo,
"description_hash": hexlify(description_hash).decode("ascii"),
},
)
ok, checking_id, payment_request, error_message = r.ok, None, None, None
@ -29,11 +35,7 @@ class LNbitsWallet(Wallet):
return InvoiceResponse(ok, checking_id, payment_request, error_message)
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = post(
url=f"{self.endpoint}/api/v1/payments",
headers=self.auth_admin,
json={"out": True, "bolt11": bolt11}
)
r = post(url=f"{self.endpoint}/api/v1/payments", headers=self.auth_admin, json={"out": True, "bolt11": bolt11})
ok, checking_id, fee_msat, error_message = True, None, 0, None
if r.ok:
@ -50,7 +52,7 @@ class LNbitsWallet(Wallet):
if not r.ok:
return PaymentStatus(None)
return PaymentStatus(r.json()['paid'])
return PaymentStatus(r.json()["paid"])
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.auth_invoice)
@ -58,4 +60,4 @@ class LNbitsWallet(Wallet):
if not r.ok:
return PaymentStatus(None)
return PaymentStatus(r.json()['paid'])
return PaymentStatus(r.json()["paid"])

10
lnbits/wallets/lndgrpc.py

@ -23,7 +23,7 @@ class LndWallet(Wallet):
self.auth_read = getenv("LND_READ_MACAROON")
self.auth_cert = getenv("LND_CERT")
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
lnd_rpc = lnd_grpc.Client(
lnd_dir=None,
macaroon_path=self.auth_invoice,
@ -33,7 +33,13 @@ class LndWallet(Wallet):
grpc_port=self.port,
)
lndResponse = lnd_rpc.add_invoice(memo=memo, value=amount, expiry=600, private=True)
lndResponse = lnd_rpc.add_invoice(
memo=memo,
description_hash=base64.b64encode(description_hash).decode("ascii"),
value=amount,
expiry=600,
private=True,
)
decoded_hash = base64.b64encode(lndResponse.r_hash).decode("utf-8").replace("/", "_")
print(lndResponse.r_hash)
ok, checking_id, payment_request, error_message = True, decoded_hash, str(lndResponse.payment_request), None

33
lnbits/wallets/lndrest.py

@ -1,5 +1,4 @@
from os import getenv
import os
import base64
from requests import get, post
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
@ -18,13 +17,17 @@ class LndRestWallet(Wallet):
self.auth_read = {"Grpc-Metadata-macaroon": getenv("LND_REST_READ_MACAROON")}
self.auth_cert = getenv("LND_REST_CERT")
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
r = post(
url=f"{self.endpoint}/v1/invoices",
headers=self.auth_invoice, verify=self.auth_cert,
json={"value": amount, "memo": memo, "private": True},
headers=self.auth_invoice,
verify=self.auth_cert,
json={
"value": amount,
"memo": memo,
"description_hash": base64.b64encode(description_hash).decode("ascii"),
"private": True,
},
)
print(self.auth_invoice)
@ -37,17 +40,19 @@ class LndRestWallet(Wallet):
r = get(url=f"{self.endpoint}/v1/payreq/{payment_request}", headers=self.auth_read, verify=self.auth_cert,)
print(r)
if r.ok:
checking_id = r.json()["payment_hash"].replace("/","_")
checking_id = r.json()["payment_hash"].replace("/", "_")
print(checking_id)
error_message = None
ok = True
return InvoiceResponse(ok, checking_id, payment_request, error_message)
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = post(
url=f"{self.endpoint}/v1/channels/transactions", headers=self.auth_admin, verify=self.auth_cert, json={"payment_request": bolt11}
url=f"{self.endpoint}/v1/channels/transactions",
headers=self.auth_admin,
verify=self.auth_cert,
json={"payment_request": bolt11},
)
ok, checking_id, fee_msat, error_message = r.ok, None, 0, None
r = get(url=f"{self.endpoint}/v1/payreq/{bolt11}", headers=self.auth_admin, verify=self.auth_cert,)
@ -59,9 +64,8 @@ class LndRestWallet(Wallet):
return PaymentResponse(ok, checking_id, fee_msat, error_message)
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
checking_id = checking_id.replace("_","/")
checking_id = checking_id.replace("_", "/")
print(checking_id)
r = get(url=f"{self.endpoint}/v1/invoice/{checking_id}", headers=self.auth_invoice, verify=self.auth_cert,)
print(r.json()["settled"])
@ -71,7 +75,12 @@ class LndRestWallet(Wallet):
return PaymentStatus(r.json()["settled"])
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = get(url=f"{self.endpoint}/v1/payments", headers=self.auth_admin, verify=self.auth_cert, params={"include_incomplete": "True", "max_payments": "20"})
r = get(
url=f"{self.endpoint}/v1/payments",
headers=self.auth_admin,
verify=self.auth_cert,
params={"include_incomplete": "True", "max_payments": "20"},
)
if not r.ok:
return PaymentStatus(None)

9
lnbits/wallets/lnpay.py

@ -1,3 +1,4 @@
import base64
from os import getenv
from requests import get, post
@ -15,11 +16,15 @@ class LNPayWallet(Wallet):
self.auth_read = getenv("LNPAY_READ_KEY")
self.auth_api = {"X-Api-Key": getenv("LNPAY_API_KEY")}
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
r = post(
url=f"{self.endpoint}/user/wallet/{self.auth_invoice}/invoice",
headers=self.auth_api,
json={"num_satoshis": f"{amount}", "memo": memo},
json={
"num_satoshis": f"{amount}",
"memo": memo,
"description_hash": base64.b64encode(description_hash).decode("ascii"),
},
)
ok, checking_id, payment_request, error_message = r.status_code == 201, None, None, None

9
lnbits/wallets/lntxbot.py

@ -1,5 +1,6 @@
from os import getenv
from requests import post
from binascii import hexlify
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
@ -13,8 +14,12 @@ class LntxbotWallet(Wallet):
self.auth_admin = {"Authorization": f"Basic {getenv('LNTXBOT_ADMIN_KEY')}"}
self.auth_invoice = {"Authorization": f"Basic {getenv('LNTXBOT_INVOICE_KEY')}"}
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
r = post(url=f"{self.endpoint}/addinvoice", headers=self.auth_invoice, json={"amt": str(amount), "memo": memo})
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
r = post(
url=f"{self.endpoint}/addinvoice",
headers=self.auth_invoice,
json={"amt": str(amount), "memo": memo, "description_hash": hexlify(description_hash).decode("ascii")},
)
ok, checking_id, payment_request, error_message = r.ok, None, None, None
if r.ok:

7
lnbits/wallets/opennode.py

@ -1,7 +1,7 @@
from os import getenv
from requests import get, post
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet, Unsupported
class OpenNodeWallet(Wallet):
@ -13,7 +13,10 @@ class OpenNodeWallet(Wallet):
self.auth_admin = {"Authorization": getenv("OPENNODE_ADMIN_KEY")}
self.auth_invoice = {"Authorization": getenv("OPENNODE_INVOICE_KEY")}
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
if description_hash:
raise Unsupported("description_hash")
r = post(
url=f"{self.endpoint}/v1/charges",
headers=self.auth_invoice,

Loading…
Cancel
Save