mirror of
https://github.com/lnbits/lnbits.git
synced 2025-10-03 18:04:36 +02:00
support description_hash across all APIs.
This commit is contained in:
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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"])
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user