diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 1fd8a5932..f4570c7f6 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -95,6 +95,10 @@ 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) + 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.") + # put all parameters that don't change here PaymentKwargs = TypedDict( "PaymentKwargs", diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py index 0f5c74e35..5f8be4e2f 100644 --- a/lnbits/core/views/public_api.py +++ b/lnbits/core/views/public_api.py @@ -5,6 +5,7 @@ from urllib.parse import urlparse from fastapi import HTTPException from starlette.requests import Request +from starlette.responses import HTMLResponse from lnbits import bolt11 diff --git a/lnbits/extensions/lnaddress/lnurl.py b/lnbits/extensions/lnaddress/lnurl.py index 30b8fc5a4..471403153 100644 --- a/lnbits/extensions/lnaddress/lnurl.py +++ b/lnbits/extensions/lnaddress/lnurl.py @@ -1,4 +1,5 @@ import hashlib +import json from datetime import datetime, timedelta import httpx @@ -9,6 +10,7 @@ from lnurl import ( # type: ignore LnurlPayResponse, ) from starlette.requests import Request +from starlette.responses import HTMLResponse from . import lnaddress_ext from .crud import get_address, get_address_by_username, get_domain @@ -28,27 +30,29 @@ async def lnurl_response(username: str, domain: str, request: Request): if now > expiration: return LnurlErrorResponse(reason="Address has expired.").dict() - resp = LnurlPayResponse( - callback=request.url_for("lnaddress.lnurl_callback", address_id=address.id), - min_sendable=1000, - max_sendable=1000000000, - metadata=await address.lnurlpay_metadata(), - ) + resp = { + "tag": "payRequest", + "callback": request.url_for("lnaddress.lnurl_callback", address_id=address.id), + "metadata": await address.lnurlpay_metadata(domain=domain), + "minSendable": 1000, + "maxSendable": 1000000000, + } - return resp.dict() + print("RESP", resp) + return resp @lnaddress_ext.get("/lnurl/cb/{address_id}", name="lnaddress.lnurl_callback") async def lnurl_callback(address_id, amount: int = Query(...)): + print("PING") address = await get_address(address_id) - if not address: return LnurlErrorResponse(reason=f"Address not found").dict() amount_received = amount domain = await get_domain(address.domain) - + base_url = ( address.wallet_endpoint[:-1] if address.wallet_endpoint.endswith("/") @@ -67,7 +71,7 @@ async def lnurl_callback(address_id, amount: int = Query(...)): "out": False, "amount": int(amount_received / 1000), "description_hash": hashlib.sha256( - (await address.lnurlpay_metadata()).encode("utf-8") + (await address.lnurlpay_metadata(domain=domain.domain)).encode("utf-8") ).hexdigest(), "extra": {"tag": f"Payment to {address.username}@{domain.domain}"}, }, @@ -78,6 +82,7 @@ async def lnurl_callback(address_id, amount: int = Query(...)): except AssertionError as e: return LnurlErrorResponse(reason="ERROR") - resp = LnurlPayActionResponse(pr=r["payment_request"], routes=[]) + # resp = LnurlPayActionResponse(pr=r["payment_request"], routes=[]) + resp = {"pr": r["payment_request"], "routes": []} - return resp.dict() + return resp diff --git a/lnbits/extensions/lnaddress/models.py b/lnbits/extensions/lnaddress/models.py index eb0984572..6f21278ef 100644 --- a/lnbits/extensions/lnaddress/models.py +++ b/lnbits/extensions/lnaddress/models.py @@ -3,7 +3,7 @@ from typing import Optional from fastapi.params import Query from lnurl.types import LnurlPayMetadata -from pydantic.main import BaseModel # type: ignore +from pydantic.main import BaseModel class CreateDomain(BaseModel): @@ -49,8 +49,9 @@ class Addresses(BaseModel): paid: bool time: int - async def lnurlpay_metadata(self) -> LnurlPayMetadata: + async def lnurlpay_metadata(self, domain) -> LnurlPayMetadata: text = f"Payment to {self.username}" - metadata = [["text/plain", text]] - + identifier = f"{self.username}@{domain}" + metadata = [["text/plain", text], ["text/identifier", identifier]] + return LnurlPayMetadata(json.dumps(metadata)) diff --git a/lnbits/extensions/lnaddress/tasks.py b/lnbits/extensions/lnaddress/tasks.py index f962f4abb..9702c70b6 100644 --- a/lnbits/extensions/lnaddress/tasks.py +++ b/lnbits/extensions/lnaddress/tasks.py @@ -47,7 +47,7 @@ async def on_invoice_paid(payment: Payment) -> None: await payment.set_pending(False) await set_address_paid(payment_hash=payment.payment_hash) - await call_webhook_on_paid(payment.payment_hash) + await call_webhook_on_paid(payment_hash=payment.payment_hash) elif "renew lnaddress" == payment.extra.get("tag"): @@ -55,7 +55,7 @@ async def on_invoice_paid(payment: Payment) -> None: await set_address_renewed( address_id=payment.extra["id"], duration=payment.extra["duration"] ) - await call_webhook_on_paid(payment.payment_hash) + await call_webhook_on_paid(payment_hash=payment.payment_hash) else: return