mirror of
https://github.com/lnbits/lnbits.git
synced 2025-09-29 05:12:39 +02:00
fix: phoenixd pending payments (#3075)
Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
@@ -7,6 +7,7 @@ from collections.abc import AsyncGenerator
|
||||
from typing import Any, Optional
|
||||
|
||||
import httpx
|
||||
from httpx import RequestError, TimeoutException
|
||||
from loguru import logger
|
||||
from websockets.legacy.client import connect
|
||||
|
||||
@@ -15,6 +16,7 @@ from lnbits.settings import settings
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
PaymentFailedStatus,
|
||||
PaymentPendingStatus,
|
||||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
@@ -173,13 +175,29 @@ class PhoenixdWallet(Wallet):
|
||||
)
|
||||
|
||||
r.raise_for_status()
|
||||
|
||||
except TimeoutException:
|
||||
# be safe and return pending on timeouts
|
||||
msg = f"Timeout connecting to {self.endpoint}. keep pending..."
|
||||
logger.warning(msg)
|
||||
return PaymentResponse(ok=None, error_message=msg)
|
||||
except RequestError as exc:
|
||||
# RequestError is raised when the request never hit the destination server
|
||||
msg = f"Unable to connect to {self.endpoint}."
|
||||
logger.warning(msg)
|
||||
logger.warning(exc)
|
||||
return PaymentResponse(ok=False, error_message=msg)
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
return PaymentResponse(
|
||||
ok=None, error_message=f"Unable to connect to {self.endpoint}."
|
||||
)
|
||||
|
||||
try:
|
||||
data = r.json()
|
||||
|
||||
if "routingFeeSat" not in data and "reason" in data:
|
||||
return PaymentResponse(error_message=data["reason"])
|
||||
|
||||
if r.is_error or "paymentHash" not in data:
|
||||
error_message = data["message"] if "message" in data else r.text
|
||||
if "routingFeeSat" not in data and ("reason" in data or "message" in data):
|
||||
error_message = data.get("reason", data.get("message", "Unknown error"))
|
||||
return PaymentResponse(error_message=error_message)
|
||||
|
||||
checking_id = data["paymentHash"]
|
||||
@@ -208,40 +226,75 @@ class PhoenixdWallet(Wallet):
|
||||
)
|
||||
|
||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||
try:
|
||||
r = await self.client.get(f"/payments/incoming/{checking_id}")
|
||||
if r.is_error:
|
||||
r = await self.client.get(
|
||||
f"/payments/incoming/{checking_id}?all=true&limit=1000"
|
||||
)
|
||||
if r.is_error:
|
||||
if r.status_code == 404:
|
||||
# invoice does not exist in phoenixd, so it was never paid
|
||||
return PaymentFailedStatus()
|
||||
else:
|
||||
# otherwise something unexpected happened, and we keep it pending
|
||||
logger.warning(
|
||||
f"Error getting invoice status: {r.text}, keeping pending"
|
||||
)
|
||||
return PaymentPendingStatus()
|
||||
try:
|
||||
data = r.json()
|
||||
|
||||
if data["isPaid"]:
|
||||
fee_msat = data["fees"]
|
||||
preimage = data["preimage"]
|
||||
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=preimage)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# should never return invalid json, but just in case we keep it pending
|
||||
logger.warning(
|
||||
f"Phoenixd: invalid json response: {r.text}, keeping pending"
|
||||
)
|
||||
return PaymentPendingStatus()
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting invoice status: {e}")
|
||||
|
||||
if "isPaid" not in data or "fees" not in data or "preimage" not in data:
|
||||
# should never return missing fields, but just in case we keep it pending
|
||||
return PaymentPendingStatus()
|
||||
|
||||
if data["isPaid"] is True:
|
||||
fee_msat = data["fees"]
|
||||
preimage = data["preimage"]
|
||||
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=preimage)
|
||||
|
||||
return PaymentPendingStatus()
|
||||
|
||||
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||
# TODO: how can we detect a failed payment?
|
||||
try:
|
||||
r = await self.client.get(f"/payments/outgoing/{checking_id}")
|
||||
if r.is_error:
|
||||
r = await self.client.get(
|
||||
f"/payments/outgoing/{checking_id}?all=true&limit=1000"
|
||||
)
|
||||
if r.is_error:
|
||||
if r.status_code == 404:
|
||||
# payment does not exist in phoenixd, so it was never paid
|
||||
return PaymentFailedStatus()
|
||||
else:
|
||||
# otherwise something unexpected happened, and we keep it pending
|
||||
logger.warning(
|
||||
f"Error getting payment status: {r.text}, keeping pending"
|
||||
)
|
||||
return PaymentPendingStatus()
|
||||
try:
|
||||
data = r.json()
|
||||
|
||||
if data["isPaid"]:
|
||||
fee_msat = data["fees"]
|
||||
preimage = data["preimage"]
|
||||
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=preimage)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
# should never return invalid json, but just in case we keep it pending
|
||||
logger.warning(
|
||||
f"Phoenixd: invalid json response: {r.text}, keeping pending"
|
||||
)
|
||||
return PaymentPendingStatus()
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting invoice status: {e}")
|
||||
|
||||
if "isPaid" not in data or "fees" not in data or "preimage" not in data:
|
||||
# should never happen, but just in case we keep it pending
|
||||
logger.warning(
|
||||
f"Phoenixd: missing required fields: {data}, keeping pending"
|
||||
)
|
||||
return PaymentPendingStatus()
|
||||
|
||||
if data["isPaid"] is True:
|
||||
fee_msat = data["fees"]
|
||||
preimage = data["preimage"]
|
||||
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=preimage)
|
||||
return PaymentPendingStatus()
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
while settings.lnbits_running:
|
||||
try:
|
||||
@@ -263,7 +316,7 @@ class PhoenixdWallet(Wallet):
|
||||
yield message_json["paymentHash"]
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
logger.warning(
|
||||
f"lost connection to phoenixd invoices stream: '{exc}'"
|
||||
"retrying in 5 seconds"
|
||||
)
|
||||
|
@@ -2182,8 +2182,16 @@
|
||||
"get_invoice_status_endpoint": []
|
||||
},
|
||||
"phoenixd": {
|
||||
"description": "phoenixd.py doesn't handle the 'failed' status for `get_invoice_status`",
|
||||
"get_invoice_status_endpoint": []
|
||||
"get_invoice_status_endpoint": [
|
||||
{
|
||||
"description": "http 404",
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2389,14 +2397,6 @@
|
||||
"description": "bad json",
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
},
|
||||
{
|
||||
"description": "http 404",
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2674,8 +2674,16 @@
|
||||
]
|
||||
},
|
||||
"phoenixd": {
|
||||
"description": "phoenixd.py doesn't handle the 'failed' status for `get_invoice_status`",
|
||||
"get_payment_status_endpoint": []
|
||||
"get_payment_status_endpoint": [
|
||||
{
|
||||
"description": "http 404",
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2932,14 +2940,6 @@
|
||||
"description": "bad json",
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
},
|
||||
{
|
||||
"description": "http 404",
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user