diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 10b33073..28203c58 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -33,6 +33,10 @@ class PaymentFailure(Exception): pass +class InvoiceFailure(Exception): + pass + + async def create_invoice( *, wallet_id: str, @@ -50,7 +54,7 @@ async def create_invoice( amount=amount, memo=invoice_memo, description_hash=description_hash ) if not ok: - raise Exception(error_message or "Unexpected backend error.") + raise InvoiceFailure(error_message or "Unexpected backend error.") invoice = bolt11.decode(payment_request) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 02cb788a..7c8cf8b9 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -12,7 +12,13 @@ from lnbits import bolt11 from lnbits.decorators import api_check_wallet_key, api_validate_post_request from .. import core_app, db -from ..services import PaymentFailure, create_invoice, pay_invoice, perform_lnurlauth +from ..services import ( + PaymentFailure, + InvoiceFailure, + create_invoice, + pay_invoice, + perform_lnurlauth, +) from ..tasks import sse_listeners @@ -67,15 +73,20 @@ async def api_payments_create_invoice(): memo = g.data["memo"] async with db.connect() as conn: - payment_hash, payment_request = await create_invoice( - wallet_id=g.wallet.id, - amount=g.data["amount"], - memo=memo, - description_hash=description_hash, - extra=g.data.get("extra"), - webhook=g.data.get("webhook"), - conn=conn, - ) + try: + payment_hash, payment_request = await create_invoice( + wallet_id=g.wallet.id, + amount=g.data["amount"], + memo=memo, + description_hash=description_hash, + extra=g.data.get("extra"), + webhook=g.data.get("webhook"), + conn=conn, + ) + except InvoiceFailure as e: + return jsonify({"message": str(e)}), 520 + except Exception as exc: + raise exc invoice = bolt11.decode(payment_request) diff --git a/lnbits/extensions/amilk/views_api.py b/lnbits/extensions/amilk/views_api.py index 1ebfe02c..4b8cad18 100644 --- a/lnbits/extensions/amilk/views_api.py +++ b/lnbits/extensions/amilk/views_api.py @@ -37,12 +37,15 @@ async def api_amilkit(amilk_id): except LnurlException: abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.") - payment_hash, payment_request = await create_invoice( - wallet_id=milk.wallet, - amount=withdraw_res.max_sats, - memo=memo, - extra={"tag": "amilk"}, - ) + try: + payment_hash, payment_request = await create_invoice( + wallet_id=milk.wallet, + amount=withdraw_res.max_sats, + memo=memo, + extra={"tag": "amilk"}, + ) + except Exception as e: + return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR r = httpx.get( withdraw_res.callback.base, diff --git a/lnbits/extensions/bleskomat/models.py b/lnbits/extensions/bleskomat/models.py index d014f25a..cec138e6 100644 --- a/lnbits/extensions/bleskomat/models.py +++ b/lnbits/extensions/bleskomat/models.py @@ -86,10 +86,13 @@ class BleskomatLnurl(NamedTuple): raise LnurlValidationError("Maximum number of uses already reached") tag = self.tag if tag == "withdrawRequest": - payment_hash = await pay_invoice( - wallet_id=self.wallet, - payment_request=query["pr"], - ) + try: + payment_hash = await pay_invoice( + wallet_id=self.wallet, + payment_request=query["pr"], + ) + except Exception as exc: + raise LnurlValidationError(f"Failed to pay invoice: {exc.message}") if not payment_hash: raise LnurlValidationError("Failed to pay invoice") diff --git a/lnbits/extensions/lnticket/views_api.py b/lnbits/extensions/lnticket/views_api.py index 3c77ced0..6201a801 100644 --- a/lnbits/extensions/lnticket/views_api.py +++ b/lnbits/extensions/lnticket/views_api.py @@ -116,12 +116,16 @@ async def api_ticket_make_ticket(form_id): nwords = len(re.split(r"\s+", g.data["ltext"])) sats = g.data["sats"] - payment_hash, payment_request = await create_invoice( - wallet_id=form.wallet, - amount=sats, - memo=f"ticket with {nwords} words on {form_id}", - extra={"tag": "lnticket"}, - ) + + try: + payment_hash, payment_request = await create_invoice( + wallet_id=form.wallet, + amount=sats, + memo=f"ticket with {nwords} words on {form_id}", + extra={"tag": "lnticket"}, + ) + except Exception as e: + return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR ticket = await create_ticket( payment_hash=payment_hash, wallet=form.wallet, **g.data diff --git a/lnbits/extensions/offlineshop/lnurl.py b/lnbits/extensions/offlineshop/lnurl.py index adee1d03..13944a29 100644 --- a/lnbits/extensions/offlineshop/lnurl.py +++ b/lnbits/extensions/offlineshop/lnurl.py @@ -64,15 +64,19 @@ async def lnurl_callback(item_id): ) shop = await get_shop(item.shop) - payment_hash, payment_request = await create_invoice( - wallet_id=shop.wallet, - amount=int(amount_received / 1000), - memo=item.name, - description_hash=hashlib.sha256( - (await item.lnurlpay_metadata()).encode("utf-8") - ).digest(), - extra={"tag": "offlineshop", "item": item.id}, - ) + + try: + payment_hash, payment_request = await create_invoice( + wallet_id=shop.wallet, + amount=int(amount_received / 1000), + memo=item.name, + description_hash=hashlib.sha256( + (await item.lnurlpay_metadata()).encode("utf-8") + ).digest(), + extra={"tag": "offlineshop", "item": item.id}, + ) + except Exception as exc: + return jsonify(LnurlErrorResponse(reason=exc.message).dict()) resp = LnurlPayActionResponse( pr=payment_request, diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index 1edc562e..c11cd4be 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -1,15 +1,10 @@ -import re from quart import g, jsonify, request from http import HTTPStatus -from lnbits.core import crud -import json -import httpx -from lnbits.core.crud import get_user, get_wallet +from lnbits.core.crud import get_user from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from .util import isValidDomain, isvalidIPAddress from . import subdomains_ext from .crud import ( create_subdomain, @@ -169,12 +164,16 @@ async def api_subdomain_make_subdomain(domain_id): ## ALL OK - create an invoice and return it to the user sats = g.data["sats"] - payment_hash, payment_request = await create_invoice( - wallet_id=domain.wallet, - amount=sats, - memo=f"subdomain {g.data['subdomain']}.{domain.domain} for {sats} sats for {g.data['duration']} days", - extra={"tag": "lnsubdomain"}, - ) + + try: + payment_hash, payment_request = await create_invoice( + wallet_id=domain.wallet, + amount=sats, + memo=f"subdomain {g.data['subdomain']}.{domain.domain} for {sats} sats for {g.data['duration']} days", + extra={"tag": "lnsubdomain"}, + ) + except Exception as e: + return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR subdomain = await create_subdomain( payment_hash=payment_hash, wallet=domain.wallet, **g.data diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 39c3d917..e699e5b4 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -119,11 +119,10 @@ async def api_lnurl_callback(unique_hash): await update_withdraw_link(link.id, **changes) except ValueError as e: - return jsonify({"status": "ERROR", "reason": str(e)}), HTTPStatus.OK + return jsonify({"status": "ERROR", "reason": str(e)}) except PermissionError: - return ( - jsonify({"status": "ERROR", "reason": "Withdraw link is empty."}), - HTTPStatus.OK, - ) + return jsonify({"status": "ERROR", "reason": "Withdraw link is empty."}) + except Exception as e: + return jsonify({"status": "ERROR", "reason": str(e)}) return jsonify({"status": "OK"}), HTTPStatus.OK diff --git a/lnbits/wallets/spark.py b/lnbits/wallets/spark.py index d80e3cc9..5d0f024a 100644 --- a/lnbits/wallets/spark.py +++ b/lnbits/wallets/spark.py @@ -49,7 +49,7 @@ class SparkWallet(Wallet): timeout=40, ) except (OSError, httpx.ConnectError, httpx.RequestError) as exc: - raise SparkError("error connecting to spark: " + str(exc)) + raise UnknownError("error connecting to spark: " + str(exc)) try: data = r.json() @@ -123,7 +123,7 @@ class SparkWallet(Wallet): payment_hash = pay["payment_hash"] if len(pays) > 1: - raise Exception( + raise SparkError( f"listpays({payment_hash}) returned an unexpected response: {listpays}" )