support description_hash across all APIs.
This commit is contained in:
parent
61f736878c
commit
bc27293315
10 changed files with 89 additions and 45 deletions
|
|
@ -7,9 +7,12 @@ from lnbits.settings import WALLET
|
||||||
from .crud import get_wallet, create_payment, delete_payment
|
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:
|
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:
|
except Exception as e:
|
||||||
ok, error_message = False, str(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))
|
fee_reserve = max(1000, int(invoice.amount_msat * 0.01))
|
||||||
create_payment(
|
create_payment(
|
||||||
wallet_id=wallet_id,
|
wallet_id=wallet_id, checking_id=temp_id, amount=-invoice.amount_msat, fee=-fee_reserve, memo=temp_id,
|
||||||
checking_id=temp_id,
|
|
||||||
amount=-invoice.amount_msat,
|
|
||||||
fee=-fee_reserve,
|
|
||||||
memo=temp_id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
wallet = get_wallet(wallet_id)
|
wallet = get_wallet(wallet_id)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from flask import g, jsonify, request
|
from flask import g, jsonify, request
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
from binascii import unhexlify
|
||||||
|
|
||||||
from lnbits.core import core_app
|
from lnbits.core import core_app
|
||||||
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
||||||
|
|
@ -27,13 +28,21 @@ def api_payments():
|
||||||
@api_validate_post_request(
|
@api_validate_post_request(
|
||||||
schema={
|
schema={
|
||||||
"amount": {"type": "integer", "min": 1, "required": True},
|
"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():
|
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:
|
try:
|
||||||
checking_id, payment_request = create_invoice(
|
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:
|
except Exception as e:
|
||||||
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class PaymentStatus(NamedTuple):
|
||||||
|
|
||||||
class Wallet(ABC):
|
class Wallet(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
|
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|
@ -40,3 +40,7 @@ class Wallet(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Unsupported(Exception):
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,20 @@ import random
|
||||||
|
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
|
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet, Unsupported
|
||||||
|
|
||||||
|
|
||||||
class CLightningWallet(Wallet):
|
class CLightningWallet(Wallet):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if LightningRpc is None: # pragma: nocover
|
if LightningRpc is None: # pragma: nocover
|
||||||
raise ImportError("The `pylightning` library must be installed to use `CLightningWallet`.")
|
raise ImportError("The `pylightning` library must be installed to use `CLightningWallet`.")
|
||||||
|
|
||||||
self.l1 = LightningRpc(getenv("CLIGHTNING_RPC"))
|
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())
|
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
|
ok, checking_id, payment_request, error_message = True, r["payment_hash"], r["bolt11"], None
|
||||||
|
|
@ -31,7 +33,7 @@ class CLightningWallet(Wallet):
|
||||||
|
|
||||||
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||||
r = self.l1.listinvoices(checking_id)
|
r = self.l1.listinvoices(checking_id)
|
||||||
if r['invoices'][0]['status'] == 'unpaid':
|
if r["invoices"][0]["status"] == "unpaid":
|
||||||
return PaymentStatus(False)
|
return PaymentStatus(False)
|
||||||
return PaymentStatus(True)
|
return PaymentStatus(True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from requests import get, post
|
from requests import get, post
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
|
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_admin = {"X-Api-Key": getenv("LNBITS_ADMIN_KEY")}
|
||||||
self.auth_invoice = {"X-Api-Key": getenv("LNBITS_INVOICE_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(
|
r = post(
|
||||||
url=f"{self.endpoint}/api/v1/payments",
|
url=f"{self.endpoint}/api/v1/payments",
|
||||||
headers=self.auth_invoice,
|
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
|
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)
|
return InvoiceResponse(ok, checking_id, payment_request, error_message)
|
||||||
|
|
||||||
def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
||||||
r = post(
|
r = post(url=f"{self.endpoint}/api/v1/payments", headers=self.auth_admin, json={"out": True, "bolt11": bolt11})
|
||||||
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
|
ok, checking_id, fee_msat, error_message = True, None, 0, None
|
||||||
|
|
||||||
if r.ok:
|
if r.ok:
|
||||||
|
|
@ -50,7 +52,7 @@ class LNbitsWallet(Wallet):
|
||||||
if not r.ok:
|
if not r.ok:
|
||||||
return PaymentStatus(None)
|
return PaymentStatus(None)
|
||||||
|
|
||||||
return PaymentStatus(r.json()['paid'])
|
return PaymentStatus(r.json()["paid"])
|
||||||
|
|
||||||
def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||||
r = get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.auth_invoice)
|
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:
|
if not r.ok:
|
||||||
return PaymentStatus(None)
|
return PaymentStatus(None)
|
||||||
|
|
||||||
return PaymentStatus(r.json()['paid'])
|
return PaymentStatus(r.json()["paid"])
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class LndWallet(Wallet):
|
||||||
self.auth_read = getenv("LND_READ_MACAROON")
|
self.auth_read = getenv("LND_READ_MACAROON")
|
||||||
self.auth_cert = getenv("LND_CERT")
|
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_rpc = lnd_grpc.Client(
|
||||||
lnd_dir=None,
|
lnd_dir=None,
|
||||||
macaroon_path=self.auth_invoice,
|
macaroon_path=self.auth_invoice,
|
||||||
|
|
@ -33,7 +33,13 @@ class LndWallet(Wallet):
|
||||||
grpc_port=self.port,
|
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("/", "_")
|
decoded_hash = base64.b64encode(lndResponse.r_hash).decode("utf-8").replace("/", "_")
|
||||||
print(lndResponse.r_hash)
|
print(lndResponse.r_hash)
|
||||||
ok, checking_id, payment_request, error_message = True, decoded_hash, str(lndResponse.payment_request), None
|
ok, checking_id, payment_request, error_message = True, decoded_hash, str(lndResponse.payment_request), None
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from os import getenv
|
from os import getenv
|
||||||
import os
|
|
||||||
import base64
|
import base64
|
||||||
from requests import get, post
|
from requests import get, post
|
||||||
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
|
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_read = {"Grpc-Metadata-macaroon": getenv("LND_REST_READ_MACAROON")}
|
||||||
self.auth_cert = getenv("LND_REST_CERT")
|
self.auth_cert = getenv("LND_REST_CERT")
|
||||||
|
|
||||||
|
def create_invoice(self, amount: int, memo: str = "", description_hash: bytes = b"") -> InvoiceResponse:
|
||||||
def create_invoice(self, amount: int, memo: str = "") -> InvoiceResponse:
|
|
||||||
|
|
||||||
r = post(
|
r = post(
|
||||||
url=f"{self.endpoint}/v1/invoices",
|
url=f"{self.endpoint}/v1/invoices",
|
||||||
headers=self.auth_invoice, verify=self.auth_cert,
|
headers=self.auth_invoice,
|
||||||
json={"value": amount, "memo": memo, "private": True},
|
verify=self.auth_cert,
|
||||||
|
json={
|
||||||
|
"value": amount,
|
||||||
|
"memo": memo,
|
||||||
|
"description_hash": base64.b64encode(description_hash).decode("ascii"),
|
||||||
|
"private": True,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
print(self.auth_invoice)
|
print(self.auth_invoice)
|
||||||
|
|
||||||
|
|
@ -44,10 +47,12 @@ class LndRestWallet(Wallet):
|
||||||
|
|
||||||
return InvoiceResponse(ok, checking_id, payment_request, error_message)
|
return InvoiceResponse(ok, checking_id, payment_request, error_message)
|
||||||
|
|
||||||
|
|
||||||
def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
||||||
r = post(
|
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
|
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,)
|
r = get(url=f"{self.endpoint}/v1/payreq/{bolt11}", headers=self.auth_admin, verify=self.auth_cert,)
|
||||||
|
|
@ -59,7 +64,6 @@ class LndRestWallet(Wallet):
|
||||||
|
|
||||||
return PaymentResponse(ok, checking_id, fee_msat, error_message)
|
return PaymentResponse(ok, checking_id, fee_msat, error_message)
|
||||||
|
|
||||||
|
|
||||||
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||||
checking_id = checking_id.replace("_", "/")
|
checking_id = checking_id.replace("_", "/")
|
||||||
print(checking_id)
|
print(checking_id)
|
||||||
|
|
@ -71,7 +75,12 @@ class LndRestWallet(Wallet):
|
||||||
return PaymentStatus(r.json()["settled"])
|
return PaymentStatus(r.json()["settled"])
|
||||||
|
|
||||||
def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
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:
|
if not r.ok:
|
||||||
return PaymentStatus(None)
|
return PaymentStatus(None)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import base64
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from requests import get, post
|
from requests import get, post
|
||||||
|
|
||||||
|
|
@ -15,11 +16,15 @@ class LNPayWallet(Wallet):
|
||||||
self.auth_read = getenv("LNPAY_READ_KEY")
|
self.auth_read = getenv("LNPAY_READ_KEY")
|
||||||
self.auth_api = {"X-Api-Key": getenv("LNPAY_API_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(
|
r = post(
|
||||||
url=f"{self.endpoint}/user/wallet/{self.auth_invoice}/invoice",
|
url=f"{self.endpoint}/user/wallet/{self.auth_invoice}/invoice",
|
||||||
headers=self.auth_api,
|
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
|
ok, checking_id, payment_request, error_message = r.status_code == 201, None, None, None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from requests import post
|
from requests import post
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
|
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_admin = {"Authorization": f"Basic {getenv('LNTXBOT_ADMIN_KEY')}"}
|
||||||
self.auth_invoice = {"Authorization": f"Basic {getenv('LNTXBOT_INVOICE_KEY')}"}
|
self.auth_invoice = {"Authorization": f"Basic {getenv('LNTXBOT_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}/addinvoice", headers=self.auth_invoice, json={"amt": str(amount), "memo": memo})
|
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
|
ok, checking_id, payment_request, error_message = r.ok, None, None, None
|
||||||
|
|
||||||
if r.ok:
|
if r.ok:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from requests import get, post
|
from requests import get, post
|
||||||
|
|
||||||
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
|
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet, Unsupported
|
||||||
|
|
||||||
|
|
||||||
class OpenNodeWallet(Wallet):
|
class OpenNodeWallet(Wallet):
|
||||||
|
|
@ -13,7 +13,10 @@ class OpenNodeWallet(Wallet):
|
||||||
self.auth_admin = {"Authorization": getenv("OPENNODE_ADMIN_KEY")}
|
self.auth_admin = {"Authorization": getenv("OPENNODE_ADMIN_KEY")}
|
||||||
self.auth_invoice = {"Authorization": getenv("OPENNODE_INVOICE_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(
|
r = post(
|
||||||
url=f"{self.endpoint}/v1/charges",
|
url=f"{self.endpoint}/v1/charges",
|
||||||
headers=self.auth_invoice,
|
headers=self.auth_invoice,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue