refactor: just refactor (#2740)

---------

Co-authored-by: dni  <office@dnilabs.com>
This commit is contained in:
Vlad Stan
2024-10-18 08:45:08 +02:00
committed by GitHub
co-authored by dni ⚡
parent ee93d2f275
commit f5e59cdf49
2 changed files with 112 additions and 97 deletions
+4 -4
View File
@@ -595,6 +595,10 @@ async def m026_update_payment_table(db):
await db.execute("ALTER TABLE apipayments ADD COLUMN extension TEXT") await db.execute("ALTER TABLE apipayments ADD COLUMN extension TEXT")
await db.execute("ALTER TABLE apipayments ADD COLUMN created_at TIMESTAMP") await db.execute("ALTER TABLE apipayments ADD COLUMN created_at TIMESTAMP")
await db.execute("ALTER TABLE apipayments ADD COLUMN updated_at TIMESTAMP") await db.execute("ALTER TABLE apipayments ADD COLUMN updated_at TIMESTAMP")
await db.execute("ALTER TABLE apipayments ADD COLUMN fiat_amounts TEXT")
async def m027_update_apipayments_data(db):
result = await db.execute("SELECT * FROM apipayments") result = await db.execute("SELECT * FROM apipayments")
payments = result.mappings().all() payments = result.mappings().all()
for payment in payments: for payment in payments:
@@ -616,7 +620,3 @@ async def m026_update_payment_table(db):
"checking_id": payment.get("checking_id"), "checking_id": payment.get("checking_id"),
}, },
) )
async def m027_update_payment_table(db):
await db.execute("ALTER TABLE apipayments ADD COLUMN fiat_amounts TEXT")
+108 -93
View File
@@ -9,6 +9,7 @@ from uuid import UUID, uuid4
import httpx import httpx
from bolt11 import decode as bolt11_decode from bolt11 import decode as bolt11_decode
from bolt11.types import Bolt11
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from fastapi import Depends, WebSocket from fastapi import Depends, WebSocket
from loguru import logger from loguru import logger
@@ -216,23 +217,19 @@ async def _pay_internal_invoice(
if not internal_invoice: if not internal_invoice:
raise PaymentError("Internal payment not found.", status="failed") raise PaymentError("Internal payment not found.", status="failed")
amount_msat = create_payment_model.amount_msat
if ( if (
internal_invoice.amount != abs(create_payment_model.amount_msat) internal_invoice.amount != abs(amount_msat)
or internal_invoice.bolt11 != create_payment_model.bolt11.lower() or internal_invoice.bolt11 != create_payment_model.bolt11.lower()
): ):
raise PaymentError( raise PaymentError(
"Invalid invoice. Bolt11 or amount is not correct", status="failed" "Invalid invoice. Bolt11 or amount is not correct", status="failed"
) )
fee_reserve_total_msat = fee_reserve_total( fee_reserve_total_msat = fee_reserve_total(abs(amount_msat), internal=True)
abs(create_payment_model.amount_msat), internal=True
)
create_payment_model.fee = abs(fee_reserve_total_msat) create_payment_model.fee = abs(fee_reserve_total_msat)
if ( if wallet.balance_msat < abs(amount_msat) + fee_reserve_total_msat:
wallet.balance_msat
< abs(create_payment_model.amount_msat) + fee_reserve_total_msat
):
raise PaymentError("Insufficient balance.", status="failed") raise PaymentError("Insufficient balance.", status="failed")
internal_id = f"internal_{create_payment_model.payment_hash}" internal_id = f"internal_{create_payment_model.payment_hash}"
@@ -269,21 +266,24 @@ async def _verify_external_payment(
raise PaymentError("Payment is still pending.", status="pending") raise PaymentError("Payment is still pending.", status="pending")
if payment.success: if payment.success:
raise PaymentError("Payment already paid.", status="success") raise PaymentError("Payment already paid.", status="success")
if payment.failed:
status = await payment.check_status() # payment failed
if status.success: status = await payment.check_status()
# payment was successful on the fundingsource if status.failed:
payment.status = PaymentState.SUCCESS raise PaymentError(
await update_payment(payment, conn=conn) "Payment is failed node, retrying is not possible.", status="failed"
raise PaymentError( )
"Failed payment was already paid on the fundingsource.",
status="success", if status.success:
) # payment was successful on the fundingsource
if status.failed: payment.status = PaymentState.SUCCESS
raise PaymentError( await update_payment(payment, conn=conn)
"Payment is failed node, retrying is not possible.", status="failed" raise PaymentError(
) "Failed payment was already paid on the fundingsource.",
# status.pending fall through and try again status="success",
)
# status.pending fall through and try again
return payment return payment
@@ -292,36 +292,31 @@ async def _pay_external_invoice(
create_payment_model: CreatePayment, create_payment_model: CreatePayment,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
) -> Payment: ) -> Payment:
fee_reserve_total_msat = fee_reserve_total(
create_payment_model.amount_msat, internal=False
)
create_payment_model.fee = -abs(fee_reserve_total_msat)
checking_id = create_payment_model.payment_hash checking_id = create_payment_model.payment_hash
amount_msat = create_payment_model.amount_msat
# check if there is already a payment with the same checking_id fee_reserve_total_msat = fee_reserve_total(amount_msat, internal=False)
old_payment = await get_standalone_payment(checking_id, conn=conn)
if old_payment:
return await _verify_external_payment(old_payment, conn)
if ( if wallet.balance_msat < abs(amount_msat) + fee_reserve_total_msat:
wallet.balance_msat
< abs(create_payment_model.amount_msat) + fee_reserve_total_msat
):
raise PaymentError( raise PaymentError(
f"You must reserve at least ({round(fee_reserve_total_msat/1000)}" f"You must reserve at least ({round(fee_reserve_total_msat/1000)}"
" sat) to cover potential routing fees.", " sat) to cover potential routing fees.",
status="failed", status="failed",
) )
# check if there is already a payment with the same checking_id
old_payment = await get_standalone_payment(checking_id, conn=conn)
if old_payment:
return await _verify_external_payment(old_payment, conn)
create_payment_model.fee = -abs(fee_reserve_total_msat)
payment = await create_payment( payment = await create_payment(
checking_id=checking_id, checking_id=checking_id,
data=create_payment_model, data=create_payment_model,
conn=conn, conn=conn,
) )
fee_reserve_msat = fee_reserve(create_payment_model.amount_msat, internal=False) fee_reserve_msat = fee_reserve(amount_msat, internal=False)
service_fee_msat = service_fee(create_payment_model.amount_msat, internal=False) service_fee_msat = service_fee(amount_msat, internal=False)
funding_source = get_funding_source() funding_source = get_funding_source()
@@ -378,6 +373,64 @@ async def pay_invoice(
tag: str = "", tag: str = "",
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
) -> Payment: ) -> Payment:
invoice = _validate_payment_request(payment_request, max_sat)
assert invoice.amount_msat
async with db.reuse_conn(conn) if conn else db.connect() as conn:
amount_msat = invoice.amount_msat
wallet = await _check_wallet_for_payment(wallet_id, tag, amount_msat, conn)
if await is_internal_status_success(invoice.payment_hash, conn):
raise PaymentError("Internal invoice already paid.", status="failed")
_, fiat_amounts = await calculate_fiat_amounts(amount_msat / 1000, wallet)
create_payment_model = CreatePayment(
wallet_id=wallet_id,
bolt11=payment_request,
payment_hash=invoice.payment_hash,
amount_msat=-amount_msat,
expiry=invoice.expiry_date,
memo=description or invoice.description or "",
extra=extra,
fiat_amounts=fiat_amounts,
)
payment = await _pay_invoice(wallet, create_payment_model, conn)
await _credit_service_fee_wallet(payment, conn)
return payment
async def _pay_invoice(wallet, create_payment_model, conn):
payment = await _pay_internal_invoice(wallet, create_payment_model, conn)
if not payment:
payment = await _pay_external_invoice(wallet, create_payment_model, conn)
return payment
async def _check_wallet_for_payment(
wallet_id: str,
tag: str,
amount_msat: int,
conn: Optional[Connection],
):
wallet = await get_wallet(wallet_id, conn=conn)
if not wallet:
raise PaymentError(f"Could not fetch wallet '{wallet_id}'.", status="failed")
# check if the payment is made for an extension that the user disabled
status = await check_user_extension_access(wallet.user, tag)
if not status.success:
raise PaymentError(status.message)
await check_wallet_limits(wallet_id, amount_msat, conn)
return wallet
def _validate_payment_request(
payment_request: str, max_sat: Optional[int] = None
) -> Bolt11:
try: try:
invoice = bolt11_decode(payment_request) invoice = bolt11_decode(payment_request)
except Exception as exc: except Exception as exc:
@@ -389,67 +442,29 @@ async def pay_invoice(
if max_sat and invoice.amount_msat > max_sat * 1000: if max_sat and invoice.amount_msat > max_sat * 1000:
raise PaymentError("Amount in invoice is too high.", status="failed") raise PaymentError("Amount in invoice is too high.", status="failed")
async with db.reuse_conn(conn) if conn else db.connect() as conn: return invoice
wallet = await get_wallet(wallet_id, conn=conn)
if not wallet:
raise PaymentError(
f"Could not fetch wallet '{wallet_id}'.", status="failed"
)
# check if the payment is made for an extension that the user disabled
if tag:
status = await check_user_extension_access(wallet.user, tag)
if not status.success:
raise PaymentError(status.message)
await check_wallet_limits(wallet_id, invoice.amount_msat, conn)
if await is_internal_status_success(invoice.payment_hash, conn):
raise PaymentError("Internal invoice already paid.", status="failed")
_, fiat_amounts = await calculate_fiat_amounts(
invoice.amount_msat / 1000, wallet
)
create_payment_model = CreatePayment(
wallet_id=wallet_id,
bolt11=payment_request,
payment_hash=invoice.payment_hash,
amount_msat=invoice.amount_msat * -1,
expiry=invoice.expiry_date,
memo=description or invoice.description or "",
extra=extra,
fiat_amounts=fiat_amounts,
)
payment = await _pay_internal_invoice(wallet, create_payment_model, conn)
if not payment:
payment = await _pay_external_invoice(wallet, create_payment_model, conn)
await _credit_service_fee_wallet(payment, conn)
return payment
async def _credit_service_fee_wallet( async def _credit_service_fee_wallet(
payment: Payment, conn: Optional[Connection] = None payment: Payment, conn: Optional[Connection] = None
): ):
service_fee_msat = service_fee(payment.amount, internal=payment.is_internal) service_fee_msat = service_fee(payment.amount, internal=payment.is_internal)
if settings.lnbits_service_fee_wallet and service_fee_msat: if not settings.lnbits_service_fee_wallet or not service_fee_msat:
create_payment_model = CreatePayment( return
wallet_id=settings.lnbits_service_fee_wallet,
bolt11=payment.bolt11, create_payment_model = CreatePayment(
payment_hash=payment.payment_hash, wallet_id=settings.lnbits_service_fee_wallet,
amount_msat=abs(service_fee_msat), bolt11=payment.bolt11,
memo="Service fee", payment_hash=payment.payment_hash,
) amount_msat=abs(service_fee_msat),
await create_payment( memo="Service fee",
checking_id=f"service_fee_{payment.payment_hash}", )
data=create_payment_model, await create_payment(
status=PaymentState.SUCCESS, checking_id=f"service_fee_{payment.payment_hash}",
conn=conn, data=create_payment_model,
) status=PaymentState.SUCCESS,
conn=conn,
)
async def check_wallet_limits( async def check_wallet_limits(