diff --git a/lnurl.py b/lnurl.py index 4445e79..a1f2935 100644 --- a/lnurl.py +++ b/lnurl.py @@ -7,7 +7,7 @@ from lnbits.core.services import create_invoice from lnbits.utils.exchange_rates import get_fiat_rate_satoshis - +from lnbits.wallets import get_funding_source from . import lnurlp_ext from .crud import ( get_or_create_lnurlp_settings, @@ -92,7 +92,7 @@ async def api_lnurl_callback( wallet_id=link.wallet, amount=int(amount / 1000), memo=link.description, - unhashed_description=unhashed_description, + unhashed_description=None if get_funding_source().__class__.__name__ == "OpenNodeWallet" else unhashed_description, extra=extra, ) diff --git a/models.py b/models.py index 070655f..29f8a18 100644 --- a/models.py +++ b/models.py @@ -106,3 +106,8 @@ def lnurlpay_metadata(self) -> LnurlPayMetadata: metadata = [["text/plain", self.description]] return LnurlPayMetadata(json.dumps(metadata)) + + + +class PayLnurlWData(BaseModel): + lnurl: str diff --git a/tasks.py b/tasks.py index f21651a..4d1ced4 100644 --- a/tasks.py +++ b/tasks.py @@ -24,10 +24,12 @@ async def wait_for_paid_invoices(): while True: payment = await invoice_queue.get() + await on_invoice_paid(payment) async def on_invoice_paid(payment: Payment): + if payment.extra.get("tag") != "lnurlp": return diff --git a/views_api.py b/views_api.py index 6da2899..9bbf8ce 100644 --- a/views_api.py +++ b/views_api.py @@ -1,7 +1,7 @@ import json from http import HTTPStatus from typing import Optional - +import httpx from fastapi import Depends, Query, Request from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl from starlette.exceptions import HTTPException @@ -9,7 +9,7 @@ from lnbits.core.crud import get_user, get_wallet from lnbits.decorators import WalletTypeInfo, check_admin, get_key_type, require_admin_key, require_invoice_key from lnbits.utils.exchange_rates import currencies, get_fiat_rate_satoshis - +from lnurl import decode as decode_lnurl from . import lnurlp_ext from .crud import ( create_pay_link, @@ -26,7 +26,7 @@ from .services import check_lnaddress_format from .helpers import parse_nostr_private_key from .lnurl import api_lnurl_response -from .models import CreatePayLinkData, LnurlpSettings +from .models import CreatePayLinkData, LnurlpSettings, PayLnurlWData # redirected from /.well-known/lnurlp @@ -238,3 +238,66 @@ async def api_update_settings(data: LnurlpSettings) -> LnurlpSettings: @lnurlp_ext.delete("/api/v1/settings", dependencies=[Depends(check_admin)]) async def api_delete_settings() -> None: await delete_lnurlp_settings() + + + +@lnurlp_ext.post( + "/api/v1/links/{link_id}/invoices/{payment_request}/pay", status_code=HTTPStatus.OK +) +async def api_link_pay_invoice( + lnurl_data: PayLnurlWData, payment_request: str, link_id: str +): + tpos = await get_pay_link(link_id) + + if not tpos: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Link does not exist." + ) + + lnurl = ( + lnurl_data.lnurl.replace("lnurlw://", "") + .replace("lightning://", "") + .replace("LIGHTNING://", "") + .replace("lightning:", "") + .replace("LIGHTNING:", "") + ) + + if lnurl.lower().startswith("lnurl"): + lnurl = decode_lnurl(lnurl) + else: + lnurl = "https://" + lnurl + + async with httpx.AsyncClient() as client: + try: + headers = {"user-agent": "lnbits/lnurlp"} + r = await client.get(lnurl, follow_redirects=True, headers=headers) + if r.is_error: + lnurl_response = {"success": False, "detail": "Error loading"} + else: + resp = r.json() + if resp["tag"] != "withdrawRequest": + lnurl_response = {"success": False, "detail": "Wrong tag type"} + else: + r2 = await client.get( + resp["callback"], + follow_redirects=True, + headers=headers, + params={ + "k1": resp["k1"], + "pr": payment_request, + }, + ) + resp2 = r2.json() + if r2.is_error: + lnurl_response = { + "success": False, + "detail": "Error loading callback", + } + elif resp2["status"] == "ERROR": + lnurl_response = {"success": False, "detail": resp2["reason"]} + else: + lnurl_response = {"success": True, "detail": resp2} + except (httpx.ConnectError, httpx.RequestError): + lnurl_response = {"success": False, "detail": "Unexpected error occurred"} + + return lnurl_response