refactor: add status column to apipayments (#2537)

* refactor: add status column to apipayments

keep track of the payment status with an enum and persist it as string
to db. `pending`, `success`, `failed`.

- database migration
- remove deleting of payments, failed payments stay
This commit is contained in:
dni ⚡
2024-07-24 16:47:26 +03:00
committed by GitHub
parent b14d36a0aa
commit 8f761dfd0f
14 changed files with 301 additions and 258 deletions
+39 -32
View File
@@ -11,11 +11,13 @@ from py_vapid import Vapid
from pywebpush import WebPushException, webpush
from lnbits.core.crud import (
delete_expired_invoices,
delete_webpush_subscriptions,
get_payments,
get_standalone_payment,
update_payment_details,
update_payment_status,
)
from lnbits.core.models import PaymentState
from lnbits.settings import settings
from lnbits.wallets import get_funding_source
@@ -131,45 +133,41 @@ async def check_pending_payments():
the backend and also to delete expired invoices. Incoming payments will be
checked only once, outgoing pending payments will be checked regularly.
"""
outgoing = True
incoming = True
while settings.lnbits_running:
logger.info(
f"Task: checking all pending payments (incoming={incoming},"
f" outgoing={outgoing}) of last 15 days"
)
start_time = time.time()
pending_payments = await get_payments(
since=(int(time.time()) - 60 * 60 * 24 * 15), # 15 days ago
complete=False,
pending=True,
outgoing=outgoing,
incoming=incoming,
exclude_uncheckable=True,
)
for payment in pending_payments:
await payment.check_status()
await asyncio.sleep(0.01) # to avoid complete blocking
logger.info(
f"Task: pending check finished for {len(pending_payments)} payments"
f" (took {time.time() - start_time:0.3f} s)"
)
# we delete expired invoices once upon the first pending check
if incoming:
logger.debug("Task: deleting all expired invoices")
start_time = time.time()
await delete_expired_invoices()
count = len(pending_payments)
if count > 0:
logger.info(f"Task: checking {count} pending payments of last 15 days...")
for i, payment in enumerate(pending_payments):
status = await payment.check_status()
prefix = f"payment ({i+1} / {count})"
if status.failed:
await update_payment_status(
payment.checking_id, status=PaymentState.FAILED
)
logger.debug(f"{prefix} failed {payment.checking_id}")
elif status.success:
await update_payment_details(
checking_id=payment.checking_id,
fee=status.fee_msat,
preimage=status.preimage,
status=PaymentState.SUCCESS,
)
logger.debug(f"{prefix} success {payment.checking_id}")
else:
logger.debug(f"{prefix} pending {payment.checking_id}")
await asyncio.sleep(0.01) # to avoid complete blocking
logger.info(
"Task: expired invoice deletion finished (took"
f" {time.time() - start_time:0.3f} s)"
f"Task: pending check finished for {count} payments"
f" (took {time.time() - start_time:0.3f} s)"
)
# after the first check we will only check outgoing, not incoming
# that will be handled by the global invoice listeners, hopefully
incoming = False
await asyncio.sleep(60 * 30) # every 30 minutes
@@ -183,7 +181,13 @@ async def invoice_callback_dispatcher(checking_id: str):
logger.trace(
f"invoice listeners: sending invoice callback for payment {checking_id}"
)
await payment.check_status()
status = await payment.check_status()
await update_payment_details(
checking_id=payment.checking_id,
fee=status.fee_msat,
preimage=status.preimage,
status=PaymentState.SUCCESS,
)
for name, send_chan in invoice_listeners.items():
logger.trace(f"invoice listeners: sending to `{name}`")
await send_chan.put(payment)
@@ -204,8 +208,11 @@ async def send_push_notification(subscription, title, body, url=""):
{"aud": "", "sub": "mailto:alan@lnbits.com"},
)
except WebPushException as e:
if e.response.status_code == HTTPStatus.GONE:
if e.response and e.response.status_code == HTTPStatus.GONE:
# cleanup unsubscribed or expired push subscriptions
await delete_webpush_subscriptions(subscription.endpoint)
else:
logger.error(f"failed sending push notification: {e.response.text}")
logger.error(
f"failed sending push notification: "
f"{e.response.text if e.response else e}"
)