diff --git a/lnbits/core/models/wallets.py b/lnbits/core/models/wallets.py index 72415be0..5ce65fc4 100644 --- a/lnbits/core/models/wallets.py +++ b/lnbits/core/models/wallets.py @@ -7,11 +7,11 @@ from datetime import datetime, timezone from enum import Enum from ecdsa import SECP256k1, SigningKey +from lnurl import encode as lnurl_encode from pydantic import BaseModel, Field from lnbits.db import FilterModel from lnbits.helpers import url_for -from lnbits.lnurl import encode as lnurl_encode from lnbits.settings import settings diff --git a/lnbits/core/services/__init__.py b/lnbits/core/services/__init__.py index d9a45184..2d385cb7 100644 --- a/lnbits/core/services/__init__.py +++ b/lnbits/core/services/__init__.py @@ -2,7 +2,7 @@ from .funding_source import ( get_balance_delta, switch_to_voidwallet, ) -from .lnurl import fetch_lnurl_pay_request +from .lnurl import fetch_lnurl_pay_request, get_pr_from_lnurl from .notifications import enqueue_admin_notification, send_payment_notification from .payments import ( calculate_fiat_amounts, @@ -55,6 +55,7 @@ __all__ = [ "fetch_lnurl_pay_request", "get_balance_delta", "get_payments_daily_stats", + "get_pr_from_lnurl", "pay_invoice", "send_payment_notification", "service_fee", diff --git a/lnbits/core/services/lnurl.py b/lnbits/core/services/lnurl.py index 9f3b978c..38b82bae 100644 --- a/lnbits/core/services/lnurl.py +++ b/lnbits/core/services/lnurl.py @@ -1,11 +1,35 @@ -from lnurl import LnurlPayActionResponse -from lnurl import execute_pay_request as lnurlp +from lnurl import ( + LnurlPayActionResponse, + LnurlPayResponse, + LnurlResponseException, + execute_pay_request, + handle, +) from lnbits.core.models import CreateLnurlPayment from lnbits.settings import settings from lnbits.utils.exchange_rates import fiat_amount_as_satoshis +async def get_pr_from_lnurl(lnurl: str, amount_msat: int) -> str: + res = await handle(lnurl, user_agent=settings.user_agent, timeout=10) + if not isinstance(res, LnurlPayResponse): + raise LnurlResponseException( + "Invalid LNURL response. Expected LnurlPayResponse." + ) + res2 = await execute_pay_request( + res, + msat=str(amount_msat), + user_agent=settings.user_agent, + timeout=10, + ) + if not isinstance(res, LnurlPayActionResponse): + raise LnurlResponseException( + "Invalid LNURL pay response. Expected LnurlPayActionResponse." + ) + return res2.pr + + async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionResponse: """ Pay an LNURL payment request. @@ -21,9 +45,9 @@ async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionRes else: amount_msat = data.amount - return await lnurlp( + return await execute_pay_request( data.res, msat=str(amount_msat), user_agent=settings.user_agent, - timeout=5, + timeout=10, ) diff --git a/lnbits/lnurl.py b/lnbits/lnurl.py deleted file mode 100644 index 802d9308..00000000 --- a/lnbits/lnurl.py +++ /dev/null @@ -1,66 +0,0 @@ -from http import HTTPStatus -from typing import Callable - -from fastapi import HTTPException, Request, Response -from fastapi.responses import JSONResponse -from fastapi.routing import APIRoute -from lnurl import LnurlErrorResponse, decode, encode, handle -from loguru import logger - -from lnbits.exceptions import InvoiceError, PaymentError - - -class LnurlErrorResponseHandler(APIRoute): - """ - Custom APIRoute class to handle LNURL errors. - LNURL errors always return with status 200 and - a JSON response with `status="ERROR"` and a `reason` key. - Helps to catch HTTPException and return a valid lnurl error response - - Example: - withdraw_lnurl_router = APIRouter(prefix="/api/v1/lnurl") - withdraw_lnurl_router.route_class = LnurlErrorResponseHandler - """ - - def get_route_handler(self) -> Callable: - original_route_handler = super().get_route_handler() - - async def lnurl_route_handler(request: Request) -> Response: - try: - response = await original_route_handler(request) - return response - except (InvoiceError, PaymentError) as exc: - logger.debug(f"Wallet Error: {exc}") - response = JSONResponse( - status_code=HTTPStatus.OK, - content={"status": "ERROR", "reason": f"{exc.message}"}, - ) - return response - except HTTPException as exc: - logger.debug(f"HTTPException: {exc}") - response = JSONResponse( - status_code=HTTPStatus.OK, - content={"status": "ERROR", "reason": f"{exc.detail}"}, - ) - return response - except Exception as exc: - logger.error("Unknown Error:", exc) - response = JSONResponse( - status_code=HTTPStatus.OK, - content={ - "status": "ERROR", - "reason": f"UNKNOWN ERROR: {exc!s}", - }, - ) - return response - - return lnurl_route_handler - - -__all__ = [ - "LnurlErrorResponse", - "LnurlErrorResponseHandler", - "decode", - "encode", - "handle", -]