From a4decf9bf2ba9b615b4949536b5a17ec58d5fb80 Mon Sep 17 00:00:00 2001 From: Abhirama M Date: Thu, 27 Jun 2024 14:09:57 +0530 Subject: [PATCH 1/4] Init --- lnurl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnurl.py b/lnurl.py index 4445e79..1e4bc4f 100644 --- a/lnurl.py +++ b/lnurl.py @@ -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, extra=extra, ) From ebd814e7bbeb04de1e75d7f43a19c1be025a2ea2 Mon Sep 17 00:00:00 2001 From: Abhirama M Date: Thu, 27 Jun 2024 14:10:21 +0530 Subject: [PATCH 2/4] changed manifest --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index cc0c3d2..c97cee5 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "repos": [ { "id": "lnurlp", - "organisation": "lnbits", + "organisation": "kiddokodes", "repository": "lnurlp" } ] From 8ea357c5f858194bc73b628e97d59c8c20138b3a Mon Sep 17 00:00:00 2001 From: Abhirama M Date: Mon, 1 Jul 2024 17:24:54 +0530 Subject: [PATCH 3/4] Cleaned up the code --- lnurl.py | 4 ++-- manifest.json | 2 +- tasks.py | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lnurl.py b/lnurl.py index 1e4bc4f..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=None, + unhashed_description=None if get_funding_source().__class__.__name__ == "OpenNodeWallet" else unhashed_description, extra=extra, ) diff --git a/manifest.json b/manifest.json index c97cee5..cc0c3d2 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "repos": [ { "id": "lnurlp", - "organisation": "kiddokodes", + "organisation": "lnbits", "repository": "lnurlp" } ] 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 From e420c7d37e505c61ce0a314a5d7bce4bc5c7d364 Mon Sep 17 00:00:00 2001 From: Abhirama M Date: Wed, 28 Aug 2024 19:17:26 +0530 Subject: [PATCH 4/4] feat: nfc handler experimental --- models.py | 5 ++++ views_api.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 3 deletions(-) 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/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