back
This commit is contained in:
parent
d6f145eee9
commit
228717195b
|
@ -48,10 +48,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
|
|||
origins = ["*"]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
CORSMiddleware, allow_origins=origins, allow_methods=["*"], allow_headers=["*"]
|
||||
)
|
||||
|
||||
g().config = lnbits.settings
|
||||
|
|
107
lnbits/bolt11.py
107
lnbits/bolt11.py
|
@ -116,7 +116,6 @@ def decode(pr: str) -> Invoice:
|
|||
return invoice
|
||||
|
||||
|
||||
|
||||
def encode(options):
|
||||
""" Convert options into LnAddr and pass it to the encoder
|
||||
"""
|
||||
|
@ -131,27 +130,31 @@ def encode(options):
|
|||
addr.paymenthash = unhexlify(options.paymenthash)
|
||||
|
||||
if options.description:
|
||||
addr.tags.append(('d', options.description))
|
||||
addr.tags.append(("d", options.description))
|
||||
if options.description_hashed:
|
||||
addr.tags.append(('h', options.description_hashed))
|
||||
addr.tags.append(("h", options.description_hashed))
|
||||
if options.expires:
|
||||
addr.tags.append(('x', options.expires))
|
||||
addr.tags.append(("x", options.expires))
|
||||
|
||||
if options.fallback:
|
||||
addr.tags.append(('f', options.fallback))
|
||||
addr.tags.append(("f", options.fallback))
|
||||
|
||||
for r in options.route:
|
||||
splits = r.split('/')
|
||||
route=[]
|
||||
splits = r.split("/")
|
||||
route = []
|
||||
while len(splits) >= 5:
|
||||
route.append((unhexlify(splits[0]),
|
||||
unhexlify(splits[1]),
|
||||
int(splits[2]),
|
||||
int(splits[3]),
|
||||
int(splits[4])))
|
||||
route.append(
|
||||
(
|
||||
unhexlify(splits[0]),
|
||||
unhexlify(splits[1]),
|
||||
int(splits[2]),
|
||||
int(splits[3]),
|
||||
int(splits[4]),
|
||||
)
|
||||
)
|
||||
splits = splits[5:]
|
||||
assert(len(splits) == 0)
|
||||
addr.tags.append(('r', route))
|
||||
assert len(splits) == 0
|
||||
addr.tags.append(("r", route))
|
||||
return lnencode(addr, options.privkey)
|
||||
|
||||
|
||||
|
@ -159,21 +162,22 @@ def lnencode(addr, privkey):
|
|||
if addr.amount:
|
||||
amount = Decimal(str(addr.amount))
|
||||
# We can only send down to millisatoshi.
|
||||
if amount * 10**12 % 10:
|
||||
raise ValueError("Cannot encode {}: too many decimal places".format(
|
||||
addr.amount))
|
||||
if amount * 10 ** 12 % 10:
|
||||
raise ValueError(
|
||||
"Cannot encode {}: too many decimal places".format(addr.amount)
|
||||
)
|
||||
|
||||
amount = addr.currency + shorten_amount(amount)
|
||||
else:
|
||||
amount = addr.currency if addr.currency else ''
|
||||
amount = addr.currency if addr.currency else ""
|
||||
|
||||
hrp = 'ln' + amount
|
||||
hrp = "ln" + amount
|
||||
|
||||
# Start with the timestamp
|
||||
data = bitstring.pack('uint:35', addr.date)
|
||||
data = bitstring.pack("uint:35", addr.date)
|
||||
|
||||
# Payment hash
|
||||
data += tagged_bytes('p', addr.paymenthash)
|
||||
data += tagged_bytes("p", addr.paymenthash)
|
||||
tags_set = set()
|
||||
|
||||
for k, v in addr.tags:
|
||||
|
@ -181,30 +185,36 @@ def lnencode(addr, privkey):
|
|||
# BOLT #11:
|
||||
#
|
||||
# A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields,
|
||||
if k in ('d', 'h', 'n', 'x'):
|
||||
if k in ("d", "h", "n", "x"):
|
||||
if k in tags_set:
|
||||
raise ValueError("Duplicate '{}' tag".format(k))
|
||||
|
||||
if k == 'r':
|
||||
if k == "r":
|
||||
route = bitstring.BitArray()
|
||||
for step in v:
|
||||
pubkey, channel, feebase, feerate, cltv = step
|
||||
route.append(bitstring.BitArray(pubkey) + bitstring.BitArray(channel) + bitstring.pack('intbe:32', feebase) + bitstring.pack('intbe:32', feerate) + bitstring.pack('intbe:16', cltv))
|
||||
data += tagged('r', route)
|
||||
elif k == 'f':
|
||||
route.append(
|
||||
bitstring.BitArray(pubkey)
|
||||
+ bitstring.BitArray(channel)
|
||||
+ bitstring.pack("intbe:32", feebase)
|
||||
+ bitstring.pack("intbe:32", feerate)
|
||||
+ bitstring.pack("intbe:16", cltv)
|
||||
)
|
||||
data += tagged("r", route)
|
||||
elif k == "f":
|
||||
data += encode_fallback(v, addr.currency)
|
||||
elif k == 'd':
|
||||
data += tagged_bytes('d', v.encode())
|
||||
elif k == 'x':
|
||||
elif k == "d":
|
||||
data += tagged_bytes("d", v.encode())
|
||||
elif k == "x":
|
||||
# Get minimal length by trimming leading 5 bits at a time.
|
||||
expirybits = bitstring.pack('intbe:64', v)[4:64]
|
||||
while expirybits.startswith('0b00000'):
|
||||
expirybits = bitstring.pack("intbe:64", v)[4:64]
|
||||
while expirybits.startswith("0b00000"):
|
||||
expirybits = expirybits[5:]
|
||||
data += tagged('x', expirybits)
|
||||
elif k == 'h':
|
||||
data += tagged_bytes('h', hashlib.sha256(v.encode('utf-8')).digest())
|
||||
elif k == 'n':
|
||||
data += tagged_bytes('n', v)
|
||||
data += tagged("x", expirybits)
|
||||
elif k == "h":
|
||||
data += tagged_bytes("h", hashlib.sha256(v.encode("utf-8")).digest())
|
||||
elif k == "n":
|
||||
data += tagged_bytes("n", v)
|
||||
else:
|
||||
# FIXME: Support unknown tags?
|
||||
raise ValueError("Unknown tag {}".format(k))
|
||||
|
@ -215,26 +225,31 @@ def lnencode(addr, privkey):
|
|||
#
|
||||
# A writer MUST include either a `d` or `h` field, and MUST NOT include
|
||||
# both.
|
||||
if 'd' in tags_set and 'h' in tags_set:
|
||||
if "d" in tags_set and "h" in tags_set:
|
||||
raise ValueError("Cannot include both 'd' and 'h'")
|
||||
if not 'd' in tags_set and not 'h' in tags_set:
|
||||
if not "d" in tags_set and not "h" in tags_set:
|
||||
raise ValueError("Must include either 'd' or 'h'")
|
||||
|
||||
|
||||
# We actually sign the hrp, then data (padded to 8 bits with zeroes).
|
||||
privkey = secp256k1.PrivateKey(bytes(unhexlify(privkey)))
|
||||
sig = privkey.ecdsa_sign_recoverable(bytearray([ord(c) for c in hrp]) + data.tobytes())
|
||||
sig = privkey.ecdsa_sign_recoverable(
|
||||
bytearray([ord(c) for c in hrp]) + data.tobytes()
|
||||
)
|
||||
# This doesn't actually serialize, but returns a pair of values :(
|
||||
sig, recid = privkey.ecdsa_recoverable_serialize(sig)
|
||||
data += bytes(sig) + bytes([recid])
|
||||
|
||||
return bech32_encode(hrp, bitarray_to_u5(data))
|
||||
|
||||
|
||||
class LnAddr(object):
|
||||
def __init__(self, paymenthash=None, amount=None, currency='bc', tags=None, date=None):
|
||||
def __init__(
|
||||
self, paymenthash=None, amount=None, currency="bc", tags=None, date=None
|
||||
):
|
||||
self.date = int(time.time()) if not date else int(date)
|
||||
self.tags = [] if not tags else tags
|
||||
self.unknown_tags = []
|
||||
self.paymenthash=paymenthash
|
||||
self.paymenthash = paymenthash
|
||||
self.signature = None
|
||||
self.pubkey = None
|
||||
self.currency = currency
|
||||
|
@ -242,13 +257,13 @@ class LnAddr(object):
|
|||
|
||||
def __str__(self):
|
||||
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
|
||||
hexlify(self.pubkey.serialize()).decode('utf-8'),
|
||||
self.amount, self.currency,
|
||||
", ".join([k + '=' + str(v) for k, v in self.tags])
|
||||
hexlify(self.pubkey.serialize()).decode("utf-8"),
|
||||
self.amount,
|
||||
self.currency,
|
||||
", ".join([k + "=" + str(v) for k, v in self.tags]),
|
||||
)
|
||||
|
||||
|
||||
|
||||
def _unshorten_amount(amount: str) -> int:
|
||||
"""Given a shortened amount, return millisatoshis"""
|
||||
# BOLT #11:
|
||||
|
|
|
@ -30,8 +30,7 @@ async def get_account(
|
|||
user_id: str, conn: Optional[Connection] = None
|
||||
) -> Optional[User]:
|
||||
row = await (conn or db).fetchone(
|
||||
"SELECT id, email, pass as password FROM accounts WHERE id = ?", (
|
||||
user_id,)
|
||||
"SELECT id, email, pass as password FROM accounts WHERE id = ?", (user_id,)
|
||||
)
|
||||
|
||||
return User(**row) if row else None
|
||||
|
@ -305,8 +304,7 @@ async def delete_expired_invoices(conn: Optional[Connection] = None,) -> None:
|
|||
except:
|
||||
continue
|
||||
|
||||
expiration_date = datetime.datetime.fromtimestamp(
|
||||
invoice.date + invoice.expiry)
|
||||
expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry)
|
||||
if expiration_date > datetime.datetime.utcnow():
|
||||
continue
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ async def pay_invoice(
|
|||
if max_sat and invoice.amount_msat > max_sat * 1000:
|
||||
raise ValueError("Amount in invoice is too high.")
|
||||
|
||||
wallet = await get_wallet(wallet_id, conn=conn)
|
||||
wallet = await get_wallet(wallet_id, conn=conn)
|
||||
|
||||
# put all parameters that don't change here
|
||||
PaymentKwargs = TypedDict(
|
||||
|
@ -141,15 +141,19 @@ async def pay_invoice(
|
|||
|
||||
# do the balance check if internal payment
|
||||
if internal_checking_id:
|
||||
wallet = await get_wallet(wallet_id, conn=conn)
|
||||
wallet = await get_wallet(wallet_id, conn=conn)
|
||||
assert wallet
|
||||
if wallet.balance_msat < 0:
|
||||
raise PermissionError("Insufficient balance.")
|
||||
|
||||
# do the balance check if external payment
|
||||
else:
|
||||
if invoice.amount_msat > wallet.balance_msat - (wallet.balance_msat / 100 * 2):
|
||||
raise PermissionError("LNbits requires you keep at least 2% reserve to cover potential routing fees.")
|
||||
if invoice.amount_msat > wallet.balance_msat - (
|
||||
wallet.balance_msat / 100 * 2
|
||||
):
|
||||
raise PermissionError(
|
||||
"LNbits requires you keep at least 2% reserve to cover potential routing fees."
|
||||
)
|
||||
|
||||
if internal_checking_id:
|
||||
# mark the invoice from the other side as not pending anymore
|
||||
|
@ -326,8 +330,7 @@ async def check_invoice_status(
|
|||
if not payment.pending:
|
||||
return status
|
||||
if payment.is_out and status.failed:
|
||||
print(
|
||||
f" - deleting outgoing failed payment {payment.checking_id}: {status}")
|
||||
print(f" - deleting outgoing failed payment {payment.checking_id}: {status}")
|
||||
await payment.delete()
|
||||
elif not status.pending:
|
||||
print(
|
||||
|
|
|
@ -77,7 +77,9 @@ async def api_update_wallet(
|
|||
@core_app.get("/api/v1/payments")
|
||||
async def api_payments(wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||
await get_payments(wallet_id=wallet.wallet.id, pending=True, complete=True)
|
||||
pendingPayments = await get_payments(wallet_id=wallet.wallet.id, pending=True, exclude_uncheckable=True)
|
||||
pendingPayments = await get_payments(
|
||||
wallet_id=wallet.wallet.id, pending=True, exclude_uncheckable=True
|
||||
)
|
||||
for payment in pendingPayments:
|
||||
await check_invoice_status(
|
||||
wallet_id=payment.wallet_id, payment_hash=payment.payment_hash
|
||||
|
@ -198,8 +200,7 @@ async def api_payments_create(
|
|||
invoiceData: CreateInvoiceData = Body(...),
|
||||
):
|
||||
if wallet.wallet_type < 0 or wallet.wallet_type > 2:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail="Key is invalid")
|
||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Key is invalid")
|
||||
|
||||
if invoiceData.out is True and wallet.wallet_type == 0:
|
||||
if not invoiceData.bolt11:
|
||||
|
@ -379,16 +380,14 @@ async def api_lnurlscan(code: str):
|
|||
params.update(callback=url) # with k1 already in it
|
||||
|
||||
lnurlauth_key = g().wallet.lnurlauth_key(domain)
|
||||
params.update(
|
||||
pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
|
||||
params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
|
||||
else:
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(url, timeout=5)
|
||||
if r.is_error:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
|
||||
detail={"domain": domain,
|
||||
"message": "failed to get parameters"},
|
||||
detail={"domain": domain, "message": "failed to get parameters"},
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -418,8 +417,7 @@ async def api_lnurlscan(code: str):
|
|||
|
||||
if tag == "withdrawRequest":
|
||||
params.update(kind="withdraw")
|
||||
params.update(fixed=data["minWithdrawable"]
|
||||
== data["maxWithdrawable"])
|
||||
params.update(fixed=data["minWithdrawable"] == data["maxWithdrawable"])
|
||||
|
||||
# callback with k1 already in it
|
||||
parsed_callback: ParseResult = urlparse(data["callback"])
|
||||
|
@ -509,18 +507,21 @@ async def api_list_currencies_available():
|
|||
|
||||
|
||||
class ConversionData(BaseModel):
|
||||
from_: str = Field('sat', alias="from")
|
||||
from_: str = Field("sat", alias="from")
|
||||
amount: float
|
||||
to: str = Query('usd')
|
||||
to: str = Query("usd")
|
||||
|
||||
|
||||
@core_app.post("/api/v1/conversion")
|
||||
async def api_fiat_as_sats(data: ConversionData):
|
||||
output = {}
|
||||
if data.from_ == 'sat':
|
||||
if data.from_ == "sat":
|
||||
output["sats"] = int(data.amount)
|
||||
output["BTC"] = data.amount / 100000000
|
||||
for currency in data.to.split(','):
|
||||
output[currency.strip().upper()] = await satoshis_amount_as_fiat(data.amount, currency.strip())
|
||||
for currency in data.to.split(","):
|
||||
output[currency.strip().upper()] = await satoshis_amount_as_fiat(
|
||||
data.amount, currency.strip()
|
||||
)
|
||||
return output
|
||||
else:
|
||||
output[data.from_.upper()] = data.amount
|
||||
|
|
|
@ -43,9 +43,7 @@ async def home(request: Request, lightning: str = None):
|
|||
|
||||
|
||||
@core_html_routes.get(
|
||||
"/extensions",
|
||||
name="core.extensions",
|
||||
response_class=HTMLResponse,
|
||||
"/extensions", name="core.extensions", response_class=HTMLResponse
|
||||
)
|
||||
async def extensions(
|
||||
request: Request,
|
||||
|
|
|
@ -171,6 +171,7 @@ async def require_admin_key(
|
|||
else:
|
||||
return wallet
|
||||
|
||||
|
||||
async def require_invoice_key(
|
||||
r: Request,
|
||||
api_key_header: str = Security(api_key_header),
|
||||
|
@ -184,7 +185,8 @@ async def require_invoice_key(
|
|||
# If wallet type is not invoice then return the unauthorized status
|
||||
# This also covers when the user passes an invalid key type
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invoice (or Admin) key required."
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invoice (or Admin) key required.",
|
||||
)
|
||||
else:
|
||||
return wallet
|
||||
|
|
|
@ -52,7 +52,7 @@ async def lnurl_callback(address_id, amount: int = Query(...)):
|
|||
amount_received = amount
|
||||
|
||||
domain = await get_domain(address.domain)
|
||||
|
||||
|
||||
base_url = (
|
||||
address.wallet_endpoint[:-1]
|
||||
if address.wallet_endpoint.endswith("/")
|
||||
|
@ -71,7 +71,9 @@ async def lnurl_callback(address_id, amount: int = Query(...)):
|
|||
"out": False,
|
||||
"amount": int(amount_received / 1000),
|
||||
"description_hash": hashlib.sha256(
|
||||
(await address.lnurlpay_metadata(domain=domain.domain)).encode("utf-8")
|
||||
(await address.lnurlpay_metadata(domain=domain.domain)).encode(
|
||||
"utf-8"
|
||||
)
|
||||
).hexdigest(),
|
||||
"extra": {"tag": f"Payment to {address.username}@{domain.domain}"},
|
||||
},
|
||||
|
|
|
@ -53,5 +53,5 @@ class Addresses(BaseModel):
|
|||
text = f"Payment to {self.username}"
|
||||
identifier = f"{self.username}@{domain}"
|
||||
metadata = [["text/plain", text], ["text/identifier", identifier]]
|
||||
|
||||
|
||||
return LnurlPayMetadata(json.dumps(metadata))
|
||||
|
|
|
@ -36,7 +36,7 @@ async def display(domain_id, request: Request):
|
|||
|
||||
wallet = await get_wallet(domain.wallet)
|
||||
url = urlparse(str(request.url))
|
||||
|
||||
|
||||
return lnaddress_renderer().TemplateResponse(
|
||||
"lnaddress/display.html",
|
||||
{
|
||||
|
@ -45,6 +45,6 @@ async def display(domain_id, request: Request):
|
|||
"domain_domain": domain.domain,
|
||||
"domain_cost": domain.cost,
|
||||
"domain_wallet_inkey": wallet.inkey,
|
||||
"root_url": f"{url.scheme}://{url.netloc}"
|
||||
"root_url": f"{url.scheme}://{url.netloc}",
|
||||
},
|
||||
)
|
||||
|
|
|
@ -24,7 +24,15 @@ async def create_lnurldevice(data: createLnurldevice,) -> lnurldevices:
|
|||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(lnurldevice_id, lnurldevice_key, data.title, data.wallet, data.currency, data.device, data.profit,),
|
||||
(
|
||||
lnurldevice_id,
|
||||
lnurldevice_key,
|
||||
data.title,
|
||||
data.wallet,
|
||||
data.currency,
|
||||
data.device,
|
||||
data.profit,
|
||||
),
|
||||
)
|
||||
return await get_lnurldevice(lnurldevice_id)
|
||||
|
||||
|
@ -63,7 +71,9 @@ async def get_lnurldevices(wallet_ids: Union[str, List[str]]) -> List[lnurldevic
|
|||
|
||||
|
||||
async def delete_lnurldevice(lnurldevice_id: str) -> None:
|
||||
await db.execute("DELETE FROM lnurldevice.lnurldevices WHERE id = ?", (lnurldevice_id,))
|
||||
await db.execute(
|
||||
"DELETE FROM lnurldevice.lnurldevices WHERE id = ?", (lnurldevice_id,)
|
||||
)
|
||||
|
||||
########################lnuldevice payments###########################
|
||||
|
||||
|
@ -102,19 +112,23 @@ async def update_lnurldevicepayment(
|
|||
(*kwargs.values(), lnurldevicepayment_id),
|
||||
)
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?", (lnurldevicepayment_id,)
|
||||
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?",
|
||||
(lnurldevicepayment_id,),
|
||||
)
|
||||
return lnurldevicepayment(**row) if row else None
|
||||
|
||||
|
||||
async def get_lnurldevicepayment(lnurldevicepayment_id: str) -> lnurldevicepayment:
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?", (lnurldevicepayment_id,)
|
||||
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?",
|
||||
(lnurldevicepayment_id,),
|
||||
)
|
||||
return lnurldevicepayment(**row) if row else None
|
||||
|
||||
|
||||
async def get_lnurlpayload(lnurldevicepayment_payload: str) -> lnurldevicepayment:
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE payload = ?", (lnurldevicepayment_payload,)
|
||||
"SELECT * FROM lnurldevice.lnurldevicepayment WHERE payload = ?",
|
||||
(lnurldevicepayment_payload,),
|
||||
)
|
||||
return lnurldevicepayment(**row) if row else None
|
||||
return lnurldevicepayment(**row) if row else None
|
||||
|
|
|
@ -105,10 +105,7 @@ async def lnurl_v1_params(
|
|||
paymentcheck = await get_lnurlpayload(p)
|
||||
if device.device == "atm":
|
||||
if paymentcheck:
|
||||
return {
|
||||
"status": "ERROR",
|
||||
"reason": f"Payment already claimed",
|
||||
}
|
||||
return {"status": "ERROR", "reason": f"Payment already claimed"}
|
||||
|
||||
if len(p) % 4 > 0:
|
||||
p += "=" * (4 - (len(p) % 4))
|
||||
|
@ -174,27 +171,28 @@ async def lnurl_v1_params(
|
|||
}
|
||||
|
||||
|
||||
|
||||
@lnurldevice_ext.get(
|
||||
"/api/v1/lnurl/cb/{paymentid}",
|
||||
status_code=HTTPStatus.OK,
|
||||
name="lnurldevice.lnurl_callback",
|
||||
)
|
||||
async def lnurl_callback(request: Request, paymentid: str = Query(None), pr: str = Query(None), k1: str = Query(None)):
|
||||
async def lnurl_callback(
|
||||
request: Request,
|
||||
paymentid: str = Query(None),
|
||||
pr: str = Query(None),
|
||||
k1: str = Query(None),
|
||||
):
|
||||
lnurldevicepayment = await get_lnurldevicepayment(paymentid)
|
||||
device = await get_lnurldevice(lnurldevicepayment.deviceid)
|
||||
if not device:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="lnurldevice not found."
|
||||
)
|
||||
if pr:
|
||||
if pr:
|
||||
if lnurldevicepayment.id != k1:
|
||||
return {"status": "ERROR", "reason": "Bad K1"}
|
||||
if lnurldevicepayment.payhash != "payment_hash":
|
||||
return {
|
||||
"status": "ERROR",
|
||||
"reason": f"Payment already claimed",
|
||||
}
|
||||
return {"status": "ERROR", "reason": f"Payment already claimed"}
|
||||
lnurldevicepayment = await update_lnurldevicepayment(
|
||||
lnurldevicepayment_id=paymentid, payhash=lnurldevicepayment.payload
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ from lnbits.db import Database
|
|||
|
||||
db2 = Database("ext_lnurlpos")
|
||||
|
||||
|
||||
async def m001_initial(db):
|
||||
"""
|
||||
Initial lnurldevice table.
|
||||
|
@ -59,7 +60,8 @@ async def m002_redux(db):
|
|||
(row[0], row[1], row[2], row[3], row[4], "pos", 0),
|
||||
)
|
||||
for row in [
|
||||
list(row) for row in await db2.fetchall("SELECT * FROM lnurlpos.lnurlpospayment")
|
||||
list(row)
|
||||
for row in await db2.fetchall("SELECT * FROM lnurlpos.lnurlpospayment")
|
||||
]:
|
||||
await db.execute(
|
||||
"""
|
||||
|
@ -76,4 +78,4 @@ async def m002_redux(db):
|
|||
(row[0], row[1], row[3], row[4], row[5], row[6]),
|
||||
)
|
||||
except:
|
||||
return
|
||||
return
|
||||
|
|
|
@ -33,12 +33,15 @@ class lnurldevices(BaseModel):
|
|||
return cls(**dict(row))
|
||||
|
||||
def lnurl(self, req: Request) -> Lnurl:
|
||||
url = req.url_for("lnurldevice.lnurl_response", device_id=self.id, _external=True)
|
||||
url = req.url_for(
|
||||
"lnurldevice.lnurl_response", device_id=self.id, _external=True
|
||||
)
|
||||
return lnurl_encode(url)
|
||||
|
||||
async def lnurlpay_metadata(self) -> LnurlPayMetadata:
|
||||
return LnurlPayMetadata(json.dumps([["text/plain", self.title]]))
|
||||
|
||||
|
||||
class lnurldevicepayment(BaseModel):
|
||||
id: str
|
||||
deviceid: str
|
||||
|
|
|
@ -41,10 +41,13 @@ async def displaypin(request: Request, paymentid: str = Query(None)):
|
|||
)
|
||||
status = await api_payment(lnurldevicepayment.payhash)
|
||||
if status["paid"]:
|
||||
await update_payment_status(checking_id=lnurldevicepayment.payhash, pending=True)
|
||||
await update_payment_status(
|
||||
checking_id=lnurldevicepayment.payhash, pending=True
|
||||
)
|
||||
return lnurldevice_renderer().TemplateResponse(
|
||||
"lnurldevice/paid.html", {"request": request, "pin": lnurldevicepayment.pin}
|
||||
)
|
||||
return lnurldevice_renderer().TemplateResponse(
|
||||
"lnurldevice/error.html", {"request": request, "pin": "filler", "not_paid": True}
|
||||
"lnurldevice/error.html",
|
||||
{"request": request, "pin": "filler", "not_paid": True},
|
||||
)
|
||||
|
|
|
@ -48,7 +48,9 @@ async def api_lnurldevice_create_or_update(
|
|||
async def api_lnurldevices_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
|
||||
try:
|
||||
return [{**lnurldevice.dict()} for lnurldevice in await get_lnurldevices(wallet_ids)]
|
||||
return [
|
||||
{**lnurldevice.dict()} for lnurldevice in await get_lnurldevices(wallet_ids)
|
||||
]
|
||||
except:
|
||||
return ""
|
||||
|
||||
|
@ -71,7 +73,8 @@ async def api_lnurldevice_retrieve(
|
|||
|
||||
@lnurldevice_ext.delete("/api/v1/lnurlpos/{lnurldevice_id}")
|
||||
async def api_lnurldevice_delete(
|
||||
wallet: WalletTypeInfo = Depends(require_admin_key), lnurldevice_id: str = Query(None)
|
||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||
lnurldevice_id: str = Query(None),
|
||||
):
|
||||
lnurldevice = await get_lnurldevice(lnurldevice_id)
|
||||
|
||||
|
|
|
@ -13,10 +13,12 @@ lnurlpayout_ext: APIRouter = APIRouter(prefix="/lnurlpayout", tags=["lnurlpayout
|
|||
def lnurlpayout_renderer():
|
||||
return template_renderer(["lnbits/extensions/lnurlpayout/templates"])
|
||||
|
||||
|
||||
from .tasks import wait_for_paid_invoices
|
||||
from .views import * # noqa
|
||||
from .views_api import * # noqa
|
||||
|
||||
|
||||
def lnurlpayout_start():
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
|
||||
|
|
|
@ -6,14 +6,23 @@ from . import db
|
|||
from .models import lnurlpayout, CreateLnurlPayoutData
|
||||
|
||||
|
||||
async def create_lnurlpayout(wallet_id: str, admin_key: str, data: CreateLnurlPayoutData) -> lnurlpayout:
|
||||
async def create_lnurlpayout(
|
||||
wallet_id: str, admin_key: str, data: CreateLnurlPayoutData
|
||||
) -> lnurlpayout:
|
||||
lnurlpayout_id = urlsafe_short_hash()
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO lnurlpayout.lnurlpayouts (id, title, wallet, admin_key, lnurlpay, threshold)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(lnurlpayout_id, data.title, wallet_id, admin_key, data.lnurlpay, data.threshold),
|
||||
(
|
||||
lnurlpayout_id,
|
||||
data.title,
|
||||
wallet_id,
|
||||
admin_key,
|
||||
data.lnurlpay,
|
||||
data.threshold,
|
||||
),
|
||||
)
|
||||
|
||||
lnurlpayout = await get_lnurlpayout(lnurlpayout_id)
|
||||
|
@ -22,13 +31,19 @@ async def create_lnurlpayout(wallet_id: str, admin_key: str, data: CreateLnurlPa
|
|||
|
||||
|
||||
async def get_lnurlpayout(lnurlpayout_id: str) -> Optional[lnurlpayout]:
|
||||
row = await db.fetchone("SELECT * FROM lnurlpayout.lnurlpayouts WHERE id = ?", (lnurlpayout_id,))
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM lnurlpayout.lnurlpayouts WHERE id = ?", (lnurlpayout_id,)
|
||||
)
|
||||
return lnurlpayout(**row) if row else None
|
||||
|
||||
|
||||
async def get_lnurlpayout_from_wallet(wallet_id: str) -> Optional[lnurlpayout]:
|
||||
row = await db.fetchone("SELECT * FROM lnurlpayout.lnurlpayouts WHERE wallet = ?", (wallet_id,))
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM lnurlpayout.lnurlpayouts WHERE wallet = ?", (wallet_id,)
|
||||
)
|
||||
return lnurlpayout(**row) if row else None
|
||||
|
||||
|
||||
async def get_lnurlpayouts(wallet_ids: Union[str, List[str]]) -> List[lnurlpayout]:
|
||||
if isinstance(wallet_ids, str):
|
||||
wallet_ids = [wallet_ids]
|
||||
|
@ -42,4 +57,6 @@ async def get_lnurlpayouts(wallet_ids: Union[str, List[str]]) -> List[lnurlpayou
|
|||
|
||||
|
||||
async def delete_lnurlpayout(lnurlpayout_id: str) -> None:
|
||||
await db.execute("DELETE FROM lnurlpayout.lnurlpayouts WHERE id = ?", (lnurlpayout_id,))
|
||||
await db.execute(
|
||||
"DELETE FROM lnurlpayout.lnurlpayouts WHERE id = ?", (lnurlpayout_id,)
|
||||
)
|
||||
|
|
|
@ -2,11 +2,13 @@ from sqlite3 import Row
|
|||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class CreateLnurlPayoutData(BaseModel):
|
||||
title: str
|
||||
lnurlpay: str
|
||||
threshold: int
|
||||
|
||||
|
||||
class lnurlpayout(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
|
|
|
@ -26,28 +26,33 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
# Check its got a payout associated with it
|
||||
lnurlpayout_link = await get_lnurlpayout_from_wallet(payment.wallet_id)
|
||||
if lnurlpayout_link:
|
||||
|
||||
|
||||
# Check the wallet balance is more than the threshold
|
||||
|
||||
|
||||
wallet = await get_wallet(lnurlpayout_link.wallet)
|
||||
if wallet.balance < lnurlpayout_link.threshold + (lnurlpayout_link.threshold*0.02):
|
||||
if wallet.balance < lnurlpayout_link.threshold + (
|
||||
lnurlpayout_link.threshold * 0.02
|
||||
):
|
||||
return
|
||||
|
||||
|
||||
# Get the invoice from the LNURL to pay
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
url = await api_payments_decode({"data": lnurlpayout_link.lnurlpay})
|
||||
if str(url["domain"])[0:4] != "http":
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="LNURL broken")
|
||||
try:
|
||||
r = await client.get(
|
||||
str(url["domain"]),
|
||||
timeout=40,
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="LNURL broken"
|
||||
)
|
||||
try:
|
||||
r = await client.get(str(url["domain"]), timeout=40)
|
||||
res = r.json()
|
||||
try:
|
||||
r = await client.get(
|
||||
res["callback"] + "?amount=" + str(int((wallet.balance - wallet.balance*0.02) * 1000)),
|
||||
res["callback"]
|
||||
+ "?amount="
|
||||
+ str(
|
||||
int((wallet.balance - wallet.balance * 0.02) * 1000)
|
||||
),
|
||||
timeout=40,
|
||||
)
|
||||
res = r.json()
|
||||
|
@ -65,6 +70,9 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
except (httpx.ConnectError, httpx.RequestError):
|
||||
return
|
||||
except Exception:
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Failed to save LNURLPayout")
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN,
|
||||
detail="Failed to save LNURLPayout",
|
||||
)
|
||||
except:
|
||||
return
|
||||
return
|
||||
|
|
|
@ -10,10 +10,17 @@ from lnbits.core.views.api import api_payment, api_payments_decode
|
|||
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
|
||||
|
||||
from . import lnurlpayout_ext
|
||||
from .crud import create_lnurlpayout, delete_lnurlpayout, get_lnurlpayout, get_lnurlpayouts, get_lnurlpayout_from_wallet
|
||||
from .crud import (
|
||||
create_lnurlpayout,
|
||||
delete_lnurlpayout,
|
||||
get_lnurlpayout,
|
||||
get_lnurlpayouts,
|
||||
get_lnurlpayout_from_wallet,
|
||||
)
|
||||
from .models import lnurlpayout, CreateLnurlPayoutData
|
||||
from .tasks import on_invoice_paid
|
||||
|
||||
|
||||
@lnurlpayout_ext.get("/api/v1/lnurlpayouts", status_code=HTTPStatus.OK)
|
||||
async def api_lnurlpayouts(
|
||||
all_wallets: bool = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
|
||||
|
@ -28,23 +35,33 @@ async def api_lnurlpayouts(
|
|||
@lnurlpayout_ext.post("/api/v1/lnurlpayouts", status_code=HTTPStatus.CREATED)
|
||||
async def api_lnurlpayout_create(
|
||||
data: CreateLnurlPayoutData, wallet: WalletTypeInfo = Depends(get_key_type)
|
||||
):
|
||||
):
|
||||
if await get_lnurlpayout_from_wallet(wallet.wallet.id):
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Wallet already has lnurlpayout set")
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN,
|
||||
detail="Wallet already has lnurlpayout set",
|
||||
)
|
||||
return
|
||||
url = await api_payments_decode({"data": data.lnurlpay})
|
||||
if "domain" not in url:
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="LNURL could not be decoded")
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="LNURL could not be decoded"
|
||||
)
|
||||
return
|
||||
if str(url["domain"])[0:4] != "http":
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not valid LNURL")
|
||||
return
|
||||
lnurlpayout = await create_lnurlpayout(wallet_id=wallet.wallet.id, admin_key=wallet.wallet.adminkey, data=data)
|
||||
lnurlpayout = await create_lnurlpayout(
|
||||
wallet_id=wallet.wallet.id, admin_key=wallet.wallet.adminkey, data=data
|
||||
)
|
||||
if not lnurlpayout:
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Failed to save LNURLPayout")
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="Failed to save LNURLPayout"
|
||||
)
|
||||
return
|
||||
return lnurlpayout.dict()
|
||||
|
||||
|
||||
@lnurlpayout_ext.delete("/api/v1/lnurlpayouts/{lnurlpayout_id}")
|
||||
async def api_lnurlpayout_delete(
|
||||
lnurlpayout_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||
|
@ -57,26 +74,34 @@ async def api_lnurlpayout_delete(
|
|||
)
|
||||
|
||||
if lnurlpayout.wallet != wallet.wallet.id:
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your lnurlpayout.")
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="Not your lnurlpayout."
|
||||
)
|
||||
|
||||
await delete_lnurlpayout(lnurlpayout_id)
|
||||
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
||||
|
||||
|
||||
@lnurlpayout_ext.get("/api/v1/lnurlpayouts/{lnurlpayout_id}", status_code=HTTPStatus.OK)
|
||||
async def api_lnurlpayout_check(
|
||||
lnurlpayout_id: str, wallet: WalletTypeInfo = Depends(get_key_type)
|
||||
):
|
||||
):
|
||||
lnurlpayout = await get_lnurlpayout(lnurlpayout_id)
|
||||
payments = await get_payments(
|
||||
wallet_id=lnurlpayout.wallet, complete=True, pending=False, outgoing=True, incoming=True
|
||||
wallet_id=lnurlpayout.wallet,
|
||||
complete=True,
|
||||
pending=False,
|
||||
outgoing=True,
|
||||
incoming=True,
|
||||
)
|
||||
result = await on_invoice_paid(payments[0])
|
||||
return
|
||||
|
||||
# get payouts func
|
||||
# lnurlpayouts = await get_lnurlpayouts(wallet_ids)
|
||||
# for lnurlpayout in lnurlpayouts:
|
||||
# payments = await get_payments(
|
||||
# wallet_id=lnurlpayout.wallet, complete=True, pending=False, outgoing=True, incoming=True
|
||||
# )
|
||||
# await on_invoice_paid(payments[0])
|
||||
|
||||
# get payouts func
|
||||
# lnurlpayouts = await get_lnurlpayouts(wallet_ids)
|
||||
# for lnurlpayout in lnurlpayouts:
|
||||
# payments = await get_payments(
|
||||
# wallet_id=lnurlpayout.wallet, complete=True, pending=False, outgoing=True, incoming=True
|
||||
# )
|
||||
# await on_invoice_paid(payments[0])
|
||||
|
|
|
@ -15,6 +15,7 @@ satspay_ext: APIRouter = APIRouter(prefix="/satspay", tags=["satspay"])
|
|||
def satspay_renderer():
|
||||
return template_renderer(["lnbits/extensions/satspay/templates"])
|
||||
|
||||
|
||||
from .tasks import wait_for_paid_invoices
|
||||
from .views import * # noqa
|
||||
from .views_api import * # noqa
|
||||
|
|
|
@ -115,7 +115,7 @@ async def check_address_balance(charge_id: str) -> List[Charges]:
|
|||
pass
|
||||
if charge.lnbitswallet:
|
||||
invoice_status = await api_payment(charge.payment_hash)
|
||||
|
||||
|
||||
if invoice_status["paid"]:
|
||||
return await update_charge(charge_id=charge_id, balance=charge.amount)
|
||||
row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,))
|
||||
|
|
|
@ -28,4 +28,3 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
|
||||
await payment.set_pending(False)
|
||||
await check_address_balance(charge_id=charge.id)
|
||||
|
||||
|
|
|
@ -32,5 +32,6 @@ async def display(request: Request, charge_id):
|
|||
)
|
||||
wallet = await get_wallet(charge.lnbitswallet)
|
||||
return satspay_renderer().TemplateResponse(
|
||||
"satspay/display.html", {"request": request, "charge": charge, "wallet_key": wallet.inkey}
|
||||
"satspay/display.html",
|
||||
{"request": request, "charge": charge, "wallet_key": wallet.inkey},
|
||||
)
|
||||
|
|
|
@ -25,14 +25,15 @@ from .models import CreateCharge
|
|||
|
||||
#############################CHARGES##########################
|
||||
|
||||
|
||||
@satspay_ext.post("/api/v1/charge")
|
||||
async def api_charge_create(
|
||||
data: CreateCharge,
|
||||
wallet: WalletTypeInfo = Depends(require_invoice_key)
|
||||
data: CreateCharge, wallet: WalletTypeInfo = Depends(require_invoice_key)
|
||||
):
|
||||
charge = await create_charge(user=wallet.wallet.user, data=data)
|
||||
return charge.dict()
|
||||
|
||||
|
||||
@satspay_ext.put("/api/v1/charge/{charge_id}")
|
||||
async def api_charge_update(
|
||||
data: CreateCharge,
|
||||
|
|
|
@ -285,5 +285,6 @@ async def get_fiat_rate_satoshis(currency: str) -> float:
|
|||
async def fiat_amount_as_satoshis(amount: float, currency: str) -> int:
|
||||
return int(amount * (await get_fiat_rate_satoshis(currency)))
|
||||
|
||||
|
||||
async def satoshis_amount_as_fiat(amount: float, currency: str) -> float:
|
||||
return float(amount / (await get_fiat_rate_satoshis(currency)))
|
||||
|
|
|
@ -2,6 +2,7 @@ import asyncio
|
|||
import json
|
||||
import httpx
|
||||
from os import getenv
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, AsyncGenerator
|
||||
import hashlib
|
||||
from ..bolt11 import encode
|
||||
|
@ -13,60 +14,65 @@ from .base import (
|
|||
Wallet,
|
||||
)
|
||||
|
||||
|
||||
class FakeWallet(Wallet):
|
||||
"""https://github.com/lnbits/lnbits"""
|
||||
async def status(self) -> StatusResponse:
|
||||
print("This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits.")
|
||||
return StatusResponse(
|
||||
None,
|
||||
21000000000,
|
||||
|
||||
print(
|
||||
"The FakeWallet backend is for using LNbits as a centralised, stand-alone payment system."
|
||||
)
|
||||
return StatusResponse(None, 21000000000)
|
||||
|
||||
async def create_invoice(
|
||||
self,
|
||||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
|
||||
options.amount = amount
|
||||
options.timestamp = datetime.now().timestamp()
|
||||
class options:
|
||||
def __init__(self, amount, timestamp, payments_hash, privkey, memo, ):
|
||||
self.name = name
|
||||
self.age = age
|
||||
async def status(self) -> StatusResponse:
|
||||
|
||||
randomHash = hashlib.sha256(b"some random data").hexdigest()
|
||||
options.payments_hash = hex(randomHash)
|
||||
options.privkey = "v3qrevqrevm39qin0vq3r0ivmrewvmq3rimq03ig"
|
||||
options = {
|
||||
"amount": amount,
|
||||
"timestamp": datetime.now().timestamp(),
|
||||
"payments_hash": randomHash,
|
||||
"privkey": "v3qrevqrevm39qin0vq3r0ivmrewvmq3rimq03ig",
|
||||
"memo": "",
|
||||
"description_hashed": "",
|
||||
}
|
||||
if description_hash:
|
||||
options.description_hashed = description_hash
|
||||
else:
|
||||
options.memo = memo
|
||||
payment_request = encode(options)
|
||||
print(payment_request)
|
||||
checking_id = randomHash
|
||||
|
||||
return InvoiceResponse(ok, checking_id, payment_request)
|
||||
|
||||
async def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
||||
|
||||
|
||||
return ""
|
||||
|
||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||
|
||||
return ""
|
||||
return ""
|
||||
|
||||
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||
|
||||
|
||||
return ""
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
url = f"{self.endpoint}/api/v1/payments/sse"
|
||||
print("lost connection to lnbits /payments/sse, retrying in 5 seconds")
|
||||
await asyncio.sleep(5)
|
||||
yield ""
|
||||
|
||||
|
||||
#invoice = "lnbc"
|
||||
#invoice += str(data.amount) + "m1"
|
||||
#invoice += str(datetime.now().timestamp()).to_bytes(35, byteorder='big'))
|
||||
#invoice += str(hashlib.sha256(b"some random data").hexdigest()) # hash of preimage, can be fake as invoice handled internally
|
||||
#invoice += "dpl" # d then pl (p = 1, l = 31; 1 * 32 + 31 == 63)
|
||||
#invoice += "2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq" #description, how do I encode this?
|
||||
#invoice += str(hashlib.sha224("lnbc" + str(data.amount) + "m1").hexdigest())
|
||||
# invoice = "lnbc"
|
||||
# invoice += str(data.amount) + "m1"
|
||||
# invoice += str(datetime.now().timestamp()).to_bytes(35, byteorder='big'))
|
||||
# invoice += str(hashlib.sha256(b"some random data").hexdigest()) # hash of preimage, can be fake as invoice handled internally
|
||||
# invoice += "dpl" # d then pl (p = 1, l = 31; 1 * 32 + 31 == 63)
|
||||
# invoice += "2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq" #description, how do I encode this?
|
||||
# invoice += str(hashlib.sha224("lnbc" + str(data.amount) + "m1").hexdigest())
|
||||
|
|
|
@ -23,8 +23,7 @@ class LndRestWallet(Wallet):
|
|||
endpoint = getenv("LND_REST_ENDPOINT")
|
||||
endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
|
||||
endpoint = (
|
||||
"https://" +
|
||||
endpoint if not endpoint.startswith("http") else endpoint
|
||||
"https://" + endpoint if not endpoint.startswith("http") else endpoint
|
||||
)
|
||||
self.endpoint = endpoint
|
||||
|
||||
|
@ -103,10 +102,7 @@ class LndRestWallet(Wallet):
|
|||
r = await client.post(
|
||||
url=f"{self.endpoint}/v1/channels/transactions",
|
||||
headers=self.auth,
|
||||
json={
|
||||
"payment_request": bolt11,
|
||||
"fee_limit": lnrpcFeeLimit,
|
||||
},
|
||||
json={"payment_request": bolt11, "fee_limit": lnrpcFeeLimit},
|
||||
timeout=180,
|
||||
)
|
||||
|
||||
|
@ -183,8 +179,7 @@ class LndRestWallet(Wallet):
|
|||
except:
|
||||
continue
|
||||
|
||||
payment_hash = base64.b64decode(
|
||||
inv["r_hash"]).hex()
|
||||
payment_hash = base64.b64decode(inv["r_hash"]).hex()
|
||||
yield payment_hash
|
||||
except (OSError, httpx.ConnectError, httpx.ReadError):
|
||||
pass
|
||||
|
|
|
@ -20,11 +20,10 @@ class VoidWallet(Wallet):
|
|||
raise Unsupported("")
|
||||
|
||||
async def status(self) -> StatusResponse:
|
||||
print("This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits.")
|
||||
return StatusResponse(
|
||||
None,
|
||||
0,
|
||||
print(
|
||||
"This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits."
|
||||
)
|
||||
return StatusResponse(None, 0)
|
||||
|
||||
async def pay_invoice(self, bolt11: str) -> PaymentResponse:
|
||||
raise Unsupported("")
|
||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "lnbits",
|
||||
"name": "lnbits-legend",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
Loading…
Reference in New Issue
Block a user