From 352fd23c0bd47f5d9e8b949dd5a444188c6ad1e9 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 13 Mar 2024 17:17:33 +0200 Subject: [PATCH] refactor: dev-friendly payment status name (#2322) * refactor: dev-friendly payment status name * refactor: change `PaymentStatus(True, ...)` to `PaymentSuccessStatus(...)` --- lnbits/core/models.py | 4 ++-- lnbits/core/services.py | 11 +++++++--- lnbits/wallets/alby.py | 3 ++- lnbits/wallets/base.py | 12 ++++++++++ lnbits/wallets/cliche.py | 5 +++-- lnbits/wallets/corelightning.py | 34 ++++++++++++++++++----------- lnbits/wallets/corelightningrest.py | 5 +++-- lnbits/wallets/eclair.py | 5 +++-- lnbits/wallets/fake.py | 3 ++- lnbits/wallets/lnbits.py | 22 ++++++++++--------- lnbits/wallets/lndgrpc.py | 23 +++++++++---------- lnbits/wallets/lndrest.py | 17 +++++++++------ lnbits/wallets/lnpay.py | 3 ++- lnbits/wallets/lntips.py | 5 +++-- lnbits/wallets/opennode.py | 5 +++-- lnbits/wallets/spark.py | 27 +++++++++++++---------- lnbits/wallets/void.py | 5 +++-- lnbits/wallets/zbd.py | 5 +++-- 18 files changed, 120 insertions(+), 74 deletions(-) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 39efe25a6..68c6c3e42 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -18,7 +18,7 @@ from lnbits.helpers import url_for from lnbits.lnurl import encode as lnurl_encode from lnbits.settings import settings from lnbits.wallets import get_wallet_class -from lnbits.wallets.base import PaymentStatus +from lnbits.wallets.base import PaymentPendingStatus, PaymentStatus class BaseWallet(BaseModel): @@ -258,7 +258,7 @@ class Payment(FromRowModel): conn: Optional[Connection] = None, ) -> PaymentStatus: if self.is_uncheckable: - return PaymentStatus(None) + return PaymentPendingStatus() logger.debug( f"Checking {'outgoing' if self.is_out else 'incoming'} " diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 4a21012a2..9edeb0f47 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -31,7 +31,12 @@ from lnbits.settings import ( ) from lnbits.utils.exchange_rates import fiat_amount_as_satoshis, satoshis_amount_as_fiat from lnbits.wallets import FAKE_WALLET, get_wallet_class, set_wallet_class -from lnbits.wallets.base import PaymentResponse, PaymentStatus +from lnbits.wallets.base import ( + PaymentPendingStatus, + PaymentResponse, + PaymentStatus, + PaymentSuccessStatus, +) from .crud import ( check_internal, @@ -577,10 +582,10 @@ async def check_transaction_status( wallet_id, payment_hash, conn=conn ) if not payment: - return PaymentStatus(None) + return PaymentPendingStatus() if not payment.pending: # note: before, we still checked the status of the payment again - return PaymentStatus(True, fee_msat=payment.fee) + return PaymentSuccessStatus(fee_msat=payment.fee) status: PaymentStatus = await payment.check_status() return status diff --git a/lnbits/wallets/alby.py b/lnbits/wallets/alby.py index 2b6ebc483..b7977f846 100644 --- a/lnbits/wallets/alby.py +++ b/lnbits/wallets/alby.py @@ -9,6 +9,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -111,7 +112,7 @@ class AlbyWallet(Wallet): r = await self.client.get(f"/invoices/{checking_id}") if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() data = r.json() diff --git a/lnbits/wallets/base.py b/lnbits/wallets/base.py index c85d5a5a9..031cccc88 100644 --- a/lnbits/wallets/base.py +++ b/lnbits/wallets/base.py @@ -52,6 +52,18 @@ class PaymentStatus(NamedTuple): return "unknown (should never happen)" +class PaymentSuccessStatus(PaymentStatus): + paid = True + + +class PaymentFailedStatus(PaymentStatus): + paid = False + + +class PaymentPendingStatus(PaymentStatus): + paid = None + + class Wallet(ABC): async def cleanup(self): pass diff --git a/lnbits/wallets/cliche.py b/lnbits/wallets/cliche.py index 062b84da7..b9c56a644 100644 --- a/lnbits/wallets/cliche.py +++ b/lnbits/wallets/cliche.py @@ -10,6 +10,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -139,7 +140,7 @@ class ClicheWallet(Wallet): if data.get("error") is not None and data["error"].get("message"): logger.error(data["error"]["message"]) - return PaymentStatus(None) + return PaymentPendingStatus() statuses = {"pending": None, "complete": True, "failed": False} return PaymentStatus(statuses[data["result"]["status"]]) @@ -152,7 +153,7 @@ class ClicheWallet(Wallet): if data.get("error") is not None and data["error"].get("message"): logger.error(data["error"]["message"]) - return PaymentStatus(None) + return PaymentPendingStatus() payment = data["result"] statuses = {"pending": None, "complete": True, "failed": False} return PaymentStatus( diff --git a/lnbits/wallets/corelightning.py b/lnbits/wallets/corelightning.py index 7cdf27b33..8a755d3bc 100644 --- a/lnbits/wallets/corelightning.py +++ b/lnbits/wallets/corelightning.py @@ -12,8 +12,11 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentFailedStatus, + PaymentPendingStatus, PaymentResponse, PaymentStatus, + PaymentSuccessStatus, StatusResponse, Unsupported, Wallet, @@ -149,30 +152,33 @@ class CoreLightningWallet(Wallet): try: r: dict = self.ln.listinvoices(payment_hash=checking_id) # type: ignore except RpcError: - return PaymentStatus(None) + return PaymentPendingStatus() if not r["invoices"]: - return PaymentStatus(None) + return PaymentPendingStatus() invoice_resp = r["invoices"][-1] if invoice_resp["payment_hash"] == checking_id: if invoice_resp["status"] == "paid": - return PaymentStatus(True) + return PaymentSuccessStatus() elif invoice_resp["status"] == "unpaid": - return PaymentStatus(None) + return PaymentPendingStatus() elif invoice_resp["status"] == "expired": - return PaymentStatus(False) + return PaymentFailedStatus() else: logger.warning(f"supplied an invalid checking_id: {checking_id}") - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: try: r: dict = self.ln.listpays(payment_hash=checking_id) # type: ignore except Exception: - return PaymentStatus(None) - if "pays" not in r or not r["pays"]: - return PaymentStatus(None) + return PaymentPendingStatus() + if "pays" not in r: + return PaymentPendingStatus() + if not r["pays"]: + # no payment with this payment_hash is found + return PaymentFailedStatus() payment_resp = r["pays"][-1] @@ -183,14 +189,16 @@ class CoreLightningWallet(Wallet): payment_resp["amount_sent_msat"] - payment_resp["amount_msat"] ) - return PaymentStatus(True, fee_msat, payment_resp["preimage"]) + return PaymentSuccessStatus( + fee_msat=fee_msat, preimage=payment_resp["preimage"] + ) elif status == "failed": - return PaymentStatus(False) + return PaymentFailedStatus() else: - return PaymentStatus(None) + return PaymentPendingStatus() else: logger.warning(f"supplied an invalid checking_id: {checking_id}") - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: diff --git a/lnbits/wallets/corelightningrest.py b/lnbits/wallets/corelightningrest.py index 94414230c..dda13b354 100644 --- a/lnbits/wallets/corelightningrest.py +++ b/lnbits/wallets/corelightningrest.py @@ -12,6 +12,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -184,7 +185,7 @@ class CoreLightningRestWallet(Wallet): return PaymentStatus(self.statuses.get(data["invoices"][0]["status"])) except Exception as e: logger.error(f"Error getting invoice status: {e}") - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: r = await self.client.get( @@ -211,7 +212,7 @@ class CoreLightningRestWallet(Wallet): return PaymentStatus(self.statuses.get(pay["status"]), fee_msat, preimage) except Exception as e: logger.error(f"Error getting payment status: {e}") - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 5367185d6..a1578fe80 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -13,6 +13,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -180,7 +181,7 @@ class EclairWallet(Wallet): } return PaymentStatus(statuses.get(data["status"]["type"])) except Exception: - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: try: @@ -211,7 +212,7 @@ class EclairWallet(Wallet): statuses.get(data["status"]["type"]), fee_msat, preimage ) except Exception: - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 4725baff6..6d3b5772b 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -19,6 +19,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -121,7 +122,7 @@ class FakeWallet(Wallet): return PaymentStatus(paid) async def get_payment_status(self, _: str) -> PaymentStatus: - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: diff --git a/lnbits/wallets/lnbits.py b/lnbits/wallets/lnbits.py index fa08ec04d..1693397fa 100644 --- a/lnbits/wallets/lnbits.py +++ b/lnbits/wallets/lnbits.py @@ -9,8 +9,10 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, + PaymentSuccessStatus, StatusResponse, Wallet, ) @@ -120,27 +122,27 @@ class LNbitsWallet(Wallet): url=f"/api/v1/payments/{checking_id}", ) if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() return PaymentStatus(r.json()["paid"]) except Exception: - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: - try: - r = await self.client.get(url=f"/api/v1/payments/{checking_id}") - r.raise_for_status() - except Exception: - return PaymentStatus(None) + r = await self.client.get(url=f"/api/v1/payments/{checking_id}") + if r.is_error: + return PaymentPendingStatus() data = r.json() if "paid" not in data or not data["paid"]: - return PaymentStatus(None) + return PaymentPendingStatus() if "details" not in data: - return PaymentStatus(None) + return PaymentPendingStatus() - return PaymentStatus(True, data["details"]["fee"], data["preimage"]) + return PaymentSuccessStatus( + fee_msat=data["details"]["fee"], preimage=data["preimage"] + ) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: url = f"{self.endpoint}/api/v1/payments/sse" diff --git a/lnbits/wallets/lndgrpc.py b/lnbits/wallets/lndgrpc.py index d8fd6d6e1..54776e8e0 100644 --- a/lnbits/wallets/lndgrpc.py +++ b/lnbits/wallets/lndgrpc.py @@ -16,8 +16,10 @@ from lnbits.utils.crypto import AESCipher from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, + PaymentSuccessStatus, StatusResponse, Wallet, ) @@ -203,15 +205,15 @@ class LndWallet(Wallet): except ValueError: # this may happen if we switch between backend wallets # that use different checking_id formats - return PaymentStatus(None) + return PaymentPendingStatus() try: resp = await self.rpc.LookupInvoice(ln.PaymentHash(r_hash=r_hash)) except grpc.RpcError: - return PaymentStatus(None) + return PaymentPendingStatus() if resp.settled: - return PaymentStatus(True) + return PaymentSuccessStatus() - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: """ @@ -224,7 +226,7 @@ class LndWallet(Wallet): except ValueError: # this may happen if we switch between backend wallets # that use different checking_id formats - return PaymentStatus(None) + return PaymentPendingStatus() resp = self.routerpc.TrackPaymentV2( router.TrackPaymentRequest(payment_hash=r_hash) @@ -247,16 +249,15 @@ class LndWallet(Wallet): try: async for payment in resp: if len(payment.htlcs) and statuses[payment.status]: - return PaymentStatus( - True, - -payment.htlcs[-1].route.total_fees_msat, - bytes_to_hex(payment.htlcs[-1].preimage), + return PaymentSuccessStatus( + fee_msat=-payment.htlcs[-1].route.total_fees_msat, + preimage=bytes_to_hex(payment.htlcs[-1].preimage), ) return PaymentStatus(statuses[payment.status]) except Exception: # most likely the payment wasn't found - return PaymentStatus(None) + return PaymentPendingStatus() - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py index 6b364beaa..f032bfcde 100644 --- a/lnbits/wallets/lndrest.py +++ b/lnbits/wallets/lndrest.py @@ -13,8 +13,11 @@ from lnbits.utils.crypto import AESCipher from .base import ( InvoiceResponse, + PaymentFailedStatus, + PaymentPendingStatus, PaymentResponse, PaymentStatus, + PaymentSuccessStatus, StatusResponse, Wallet, ) @@ -168,9 +171,9 @@ class LndRestWallet(Wallet): if r.is_error or not r.json().get("settled"): # this must also work when checking_id is not a hex recognizable by lnd # it will return an error and no "settled" attribute on the object - return PaymentStatus(None) + return PaymentPendingStatus() - return PaymentStatus(True) + return PaymentSuccessStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: """ @@ -182,7 +185,7 @@ class LndRestWallet(Wallet): "ascii" ) except ValueError: - return PaymentStatus(None) + return PaymentPendingStatus() url = f"/v2/router/track/{checking_id}" @@ -210,8 +213,8 @@ class LndRestWallet(Wallet): and line["error"].get("message") == "payment isn't initiated" ): - return PaymentStatus(False) - return PaymentStatus(None) + return PaymentFailedStatus() + return PaymentPendingStatus() payment = line.get("result") if payment is not None and payment.get("status"): return PaymentStatus( @@ -220,11 +223,11 @@ class LndRestWallet(Wallet): preimage=payment.get("payment_preimage"), ) else: - return PaymentStatus(None) + return PaymentPendingStatus() except Exception: continue - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: while True: diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 65d04ea5d..46aaab003 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -9,6 +9,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -134,7 +135,7 @@ class LNPayWallet(Wallet): ) if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() data = r.json() preimage = data["payment_preimage"] diff --git a/lnbits/wallets/lntips.py b/lnbits/wallets/lntips.py index 410699c70..a269adb68 100644 --- a/lnbits/wallets/lntips.py +++ b/lnbits/wallets/lntips.py @@ -11,6 +11,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -132,7 +133,7 @@ class LnTipsWallet(Wallet): data = r.json() return PaymentStatus(data["paid"]) except Exception: - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: try: @@ -147,7 +148,7 @@ class LnTipsWallet(Wallet): paid_to_status = {False: None, True: True} return PaymentStatus(paid_to_status[data.get("paid")]) except Exception: - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: last_connected = None diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index d71d2aec6..7b71f80ff 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -8,6 +8,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -116,7 +117,7 @@ class OpenNodeWallet(Wallet): async def get_invoice_status(self, checking_id: str) -> PaymentStatus: r = await self.client.get(f"/v1/charge/{checking_id}") if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() data = r.json()["data"] statuses = {"processing": None, "paid": True, "unpaid": None} return PaymentStatus(statuses[data.get("status")]) @@ -125,7 +126,7 @@ class OpenNodeWallet(Wallet): r = await self.client.get(f"/v1/withdrawal/{checking_id}") if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() data = r.json()["data"] statuses = { diff --git a/lnbits/wallets/spark.py b/lnbits/wallets/spark.py index 6cf6c7bb5..1276744a8 100644 --- a/lnbits/wallets/spark.py +++ b/lnbits/wallets/spark.py @@ -11,8 +11,11 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentFailedStatus, + PaymentPendingStatus, PaymentResponse, PaymentStatus, + PaymentSuccessStatus, StatusResponse, Wallet, ) @@ -196,33 +199,33 @@ class SparkWallet(Wallet): try: r = await self.listinvoices(label=checking_id) except (SparkError, UnknownError): - return PaymentStatus(None) + return PaymentPendingStatus() if not r or not r.get("invoices"): - return PaymentStatus(None) + return PaymentPendingStatus() if r["invoices"][0]["status"] == "paid": - return PaymentStatus(True) + return PaymentSuccessStatus() else: - return PaymentStatus(False) + return PaymentFailedStatus() async def get_payment_status(self, checking_id: str) -> PaymentStatus: # check if it's 32 bytes hex if len(checking_id) != 64: - return PaymentStatus(None) + return PaymentPendingStatus() try: int(checking_id, 16) except ValueError: - return PaymentStatus(None) + return PaymentPendingStatus() # ask sparko try: r = await self.listpays(payment_hash=checking_id) except (SparkError, UnknownError): - return PaymentStatus(None) + return PaymentPendingStatus() if not r["pays"]: - return PaymentStatus(False) + return PaymentFailedStatus() if r["pays"][0]["payment_hash"] == checking_id: status = r["pays"][0]["status"] if status == "complete": @@ -230,10 +233,12 @@ class SparkWallet(Wallet): int(r["pays"][0]["amount_sent_msat"][0:-4]) - int(r["pays"][0]["amount_msat"][0:-4]) ) - return PaymentStatus(True, fee_msat, r["pays"][0]["preimage"]) + return PaymentSuccessStatus( + fee_msat=fee_msat, preimage=r["pays"][0]["preimage"] + ) if status == "failed": - return PaymentStatus(False) - return PaymentStatus(None) + return PaymentFailedStatus() + return PaymentPendingStatus() raise KeyError("supplied an invalid checking_id") async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: diff --git a/lnbits/wallets/void.py b/lnbits/wallets/void.py index 22eba7695..0ecfed770 100644 --- a/lnbits/wallets/void.py +++ b/lnbits/wallets/void.py @@ -4,6 +4,7 @@ from loguru import logger from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -31,10 +32,10 @@ class VoidWallet(Wallet): ) async def get_invoice_status(self, *_, **__) -> PaymentStatus: - return PaymentStatus(None) + return PaymentPendingStatus() async def get_payment_status(self, *_, **__) -> PaymentStatus: - return PaymentStatus(None) + return PaymentPendingStatus() async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: yield "" diff --git a/lnbits/wallets/zbd.py b/lnbits/wallets/zbd.py index 747b995d8..4dee4a86e 100644 --- a/lnbits/wallets/zbd.py +++ b/lnbits/wallets/zbd.py @@ -9,6 +9,7 @@ from lnbits.settings import settings from .base import ( InvoiceResponse, + PaymentPendingStatus, PaymentResponse, PaymentStatus, StatusResponse, @@ -121,7 +122,7 @@ class ZBDWallet(Wallet): async def get_invoice_status(self, checking_id: str) -> PaymentStatus: r = await self.client.get(f"charges/{checking_id}") if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() data = r.json()["data"] statuses = { @@ -136,7 +137,7 @@ class ZBDWallet(Wallet): async def get_payment_status(self, checking_id: str) -> PaymentStatus: r = await self.client.get(f"payments/{checking_id}") if r.is_error: - return PaymentStatus(None) + return PaymentPendingStatus() data = r.json()["data"]