We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
LNDRestWallet: catch http errors
This PR catches the errors in `lndrest.py` that can happen when for example the LND certificate has expired. 

This can lead to stuck payments.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lnbits/wallets/lndrest.py`
Content:
```
1 import asyncio
2 import base64
3 import hashlib
4 import json
5 from typing import AsyncGenerator, Dict, Optional
6 
7 import httpx
8 from loguru import logger
9 
10 from lnbits.nodes.lndrest import LndRestNode
11 from lnbits.settings import settings
12 
13 from .base import (
14     InvoiceResponse,
15     PaymentResponse,
16     PaymentStatus,
17     StatusResponse,
18     Wallet,
19 )
20 from .macaroon import AESCipher, load_macaroon
21 
22 
23 class LndRestWallet(Wallet):
24     """https://api.lightning.community/rest/index.html#lnd-rest-api-reference"""
25 
26     __node_cls__ = LndRestNode
27 
28     def __init__(self):
29         endpoint = settings.lnd_rest_endpoint
30         cert = settings.lnd_rest_cert
31 
32         macaroon = (
33             settings.lnd_rest_macaroon
34             or settings.lnd_admin_macaroon
35             or settings.lnd_rest_admin_macaroon
36             or settings.lnd_invoice_macaroon
37             or settings.lnd_rest_invoice_macaroon
38         )
39 
40         encrypted_macaroon = settings.lnd_rest_macaroon_encrypted
41         if encrypted_macaroon:
42             macaroon = AESCipher(description="macaroon decryption").decrypt(
43                 encrypted_macaroon
44             )
45 
46         if not endpoint:
47             raise Exception("cannot initialize lndrest: no endpoint")
48 
49         if not macaroon:
50             raise Exception("cannot initialize lndrest: no macaroon")
51 
52         if not cert:
53             logger.warning(
54                 "no certificate for lndrest provided, this only works if you have a"
55                 " publicly issued certificate"
56             )
57 
58         endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
59         endpoint = (
60             f"https://{endpoint}" if not endpoint.startswith("http") else endpoint
61         )
62         self.endpoint = endpoint
63         self.macaroon = load_macaroon(macaroon)
64 
65         # if no cert provided it should be public so we set verify to True
66         # and it will still check for validity of certificate and fail if its not valid
67         # even on startup
68         self.cert = cert or True
69 
70         self.auth = {"Grpc-Metadata-macaroon": self.macaroon}
71         self.client = httpx.AsyncClient(
72             base_url=self.endpoint, headers=self.auth, verify=self.cert
73         )
74 
75     async def cleanup(self):
76         try:
77             await self.client.aclose()
78         except RuntimeError as e:
79             logger.warning(f"Error closing wallet connection: {e}")
80 
81     async def status(self) -> StatusResponse:
82         try:
83             r = await self.client.get("/v1/balance/channels")
84             r.raise_for_status()
85         except (httpx.ConnectError, httpx.RequestError) as exc:
86             return StatusResponse(f"Unable to connect to {self.endpoint}. {exc}", 0)
87 
88         try:
89             data = r.json()
90             if r.is_error:
91                 raise Exception
92         except Exception:
93             return StatusResponse(r.text[:200], 0)
94 
95         return StatusResponse(None, int(data["balance"]) * 1000)
96 
97     async def create_invoice(
98         self,
99         amount: int,
100         memo: Optional[str] = None,
101         description_hash: Optional[bytes] = None,
102         unhashed_description: Optional[bytes] = None,
103         **kwargs,
104     ) -> InvoiceResponse:
105         data: Dict = {"value": amount, "private": True, "memo": memo or ""}
106         if kwargs.get("expiry"):
107             data["expiry"] = kwargs["expiry"]
108         if description_hash:
109             data["description_hash"] = base64.b64encode(description_hash).decode(
110                 "ascii"
111             )
112         elif unhashed_description:
113             data["description_hash"] = base64.b64encode(
114                 hashlib.sha256(unhashed_description).digest()
115             ).decode("ascii")
116 
117         r = await self.client.post(url="/v1/invoices", json=data)
118 
119         if r.is_error:
120             error_message = r.text
121             try:
122                 error_message = r.json()["error"]
123             except Exception:
124                 pass
125             return InvoiceResponse(False, None, None, error_message)
126 
127         data = r.json()
128         payment_request = data["payment_request"]
129         payment_hash = base64.b64decode(data["r_hash"]).hex()
130         checking_id = payment_hash
131 
132         return InvoiceResponse(True, checking_id, payment_request, None)
133 
134     async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
135         # set the fee limit for the payment
136         lnrpcFeeLimit = dict()
137         lnrpcFeeLimit["fixed_msat"] = f"{fee_limit_msat}"
138 
139         r = await self.client.post(
140             url="/v1/channels/transactions",
141             json={"payment_request": bolt11, "fee_limit": lnrpcFeeLimit},
142             timeout=None,
143         )
144 
145         if r.is_error or r.json().get("payment_error"):
146             error_message = r.json().get("payment_error") or r.text
147             return PaymentResponse(False, None, None, None, error_message)
148 
149         data = r.json()
150         checking_id = base64.b64decode(data["payment_hash"]).hex()
151         fee_msat = int(data["payment_route"]["total_fees_msat"])
152         preimage = base64.b64decode(data["payment_preimage"]).hex()
153         return PaymentResponse(True, checking_id, fee_msat, preimage, None)
154 
155     async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
156         r = await self.client.get(url=f"/v1/invoice/{checking_id}")
157 
158         if r.is_error or not r.json().get("settled"):
159             # this must also work when checking_id is not a hex recognizable by lnd
160             # it will return an error and no "settled" attribute on the object
161             return PaymentStatus(None)
162 
163         return PaymentStatus(True)
164 
165     async def get_payment_status(self, checking_id: str) -> PaymentStatus:
166         """
167         This routine checks the payment status using routerpc.TrackPaymentV2.
168         """
169         # convert checking_id from hex to base64 and some LND magic
170         try:
171             checking_id = base64.urlsafe_b64encode(bytes.fromhex(checking_id)).decode(
172                 "ascii"
173             )
174         except ValueError:
175             return PaymentStatus(None)
176 
177         url = f"/v2/router/track/{checking_id}"
178 
179         # check payment.status:
180         # https://api.lightning.community/?python=#paymentpaymentstatus
181         statuses = {
182             "UNKNOWN": None,
183             "IN_FLIGHT": None,
184             "SUCCEEDED": True,
185             "FAILED": False,
186         }
187 
188         async with self.client.stream("GET", url, timeout=None) as r:
189             async for json_line in r.aiter_lines():
190                 try:
191                     line = json.loads(json_line)
192                     if line.get("error"):
193                         logger.error(
194                             line["error"]["message"]
195                             if "message" in line["error"]
196                             else line["error"]
197                         )
198                         return PaymentStatus(None)
199                     payment = line.get("result")
200                     if payment is not None and payment.get("status"):
201                         return PaymentStatus(
202                             paid=statuses[payment["status"]],
203                             fee_msat=payment.get("fee_msat"),
204                             preimage=payment.get("payment_preimage"),
205                         )
206                     else:
207                         return PaymentStatus(None)
208                 except Exception:
209                     continue
210 
211         return PaymentStatus(None)
212 
213     async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
214         while True:
215             try:
216                 url = "/v1/invoices/subscribe"
217                 async with self.client.stream("GET", url, timeout=None) as r:
218                     async for line in r.aiter_lines():
219                         try:
220                             inv = json.loads(line)["result"]
221                             if not inv["settled"]:
222                                 continue
223                         except Exception:
224                             continue
225 
226                         payment_hash = base64.b64decode(inv["r_hash"]).hex()
227                         yield payment_hash
228             except Exception as exc:
229                 logger.error(
230                     f"lost connection to lnd invoices stream: '{exc}', retrying in 5"
231                     " seconds"
232                 )
233                 await asyncio.sleep(5)
234
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py
--- a/lnbits/wallets/lndrest.py
+++ b/lnbits/wallets/lndrest.py
@@ -195,6 +195,12 @@
                             if "message" in line["error"]
                             else line["error"]
                         )
+                        if (
+                            line["error"].get("code") == 5
+                            and line["error"].get("message")
+                            == "payment isn't initiated"
+                        ):
+                            return PaymentStatus(False)
                         return PaymentStatus(None)
                     payment = line.get("result")
                     if payment is not None and payment.get("status"):

{"golden_diff": "diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py\n--- a/lnbits/wallets/lndrest.py\n+++ b/lnbits/wallets/lndrest.py\n@@ -195,6 +195,12 @@\n                             if \"message\" in line[\"error\"]\n                             else line[\"error\"]\n                         )\n+                        if (\n+                            line[\"error\"].get(\"code\") == 5\n+                            and line[\"error\"].get(\"message\")\n+                            == \"payment isn't initiated\"\n+                        ):\n+                            return PaymentStatus(False)\n                         return PaymentStatus(None)\n                     payment = line.get(\"result\")\n                     if payment is not None and payment.get(\"status\"):\n", "issue": "LNDRestWallet: catch http errors\nThis PR catches the errors in `lndrest.py` that can happen when for example the LND certificate has expired. \r\n\r\nThis can lead to stuck payments.\n", "before_files": [{"content": "import asyncio\nimport base64\nimport hashlib\nimport json\nfrom typing import AsyncGenerator, Dict, Optional\n\nimport httpx\nfrom loguru import logger\n\nfrom lnbits.nodes.lndrest import LndRestNode\nfrom lnbits.settings import settings\n\nfrom .base import (\n    InvoiceResponse,\n    PaymentResponse,\n    PaymentStatus,\n    StatusResponse,\n    Wallet,\n)\nfrom .macaroon import AESCipher, load_macaroon\n\n\nclass LndRestWallet(Wallet):\n    \"\"\"https://api.lightning.community/rest/index.html#lnd-rest-api-reference\"\"\"\n\n    __node_cls__ = LndRestNode\n\n    def __init__(self):\n        endpoint = settings.lnd_rest_endpoint\n        cert = settings.lnd_rest_cert\n\n        macaroon = (\n            settings.lnd_rest_macaroon\n            or settings.lnd_admin_macaroon\n            or settings.lnd_rest_admin_macaroon\n            or settings.lnd_invoice_macaroon\n            or settings.lnd_rest_invoice_macaroon\n        )\n\n        encrypted_macaroon = settings.lnd_rest_macaroon_encrypted\n        if encrypted_macaroon:\n            macaroon = AESCipher(description=\"macaroon decryption\").decrypt(\n                encrypted_macaroon\n            )\n\n        if not endpoint:\n            raise Exception(\"cannot initialize lndrest: no endpoint\")\n\n        if not macaroon:\n            raise Exception(\"cannot initialize lndrest: no macaroon\")\n\n        if not cert:\n            logger.warning(\n                \"no certificate for lndrest provided, this only works if you have a\"\n                \" publicly issued certificate\"\n            )\n\n        endpoint = endpoint[:-1] if endpoint.endswith(\"/\") else endpoint\n        endpoint = (\n            f\"https://{endpoint}\" if not endpoint.startswith(\"http\") else endpoint\n        )\n        self.endpoint = endpoint\n        self.macaroon = load_macaroon(macaroon)\n\n        # if no cert provided it should be public so we set verify to True\n        # and it will still check for validity of certificate and fail if its not valid\n        # even on startup\n        self.cert = cert or True\n\n        self.auth = {\"Grpc-Metadata-macaroon\": self.macaroon}\n        self.client = httpx.AsyncClient(\n            base_url=self.endpoint, headers=self.auth, verify=self.cert\n        )\n\n    async def cleanup(self):\n        try:\n            await self.client.aclose()\n        except RuntimeError as e:\n            logger.warning(f\"Error closing wallet connection: {e}\")\n\n    async def status(self) -> StatusResponse:\n        try:\n            r = await self.client.get(\"/v1/balance/channels\")\n            r.raise_for_status()\n        except (httpx.ConnectError, httpx.RequestError) as exc:\n            return StatusResponse(f\"Unable to connect to {self.endpoint}. {exc}\", 0)\n\n        try:\n            data = r.json()\n            if r.is_error:\n                raise Exception\n        except Exception:\n            return StatusResponse(r.text[:200], 0)\n\n        return StatusResponse(None, int(data[\"balance\"]) * 1000)\n\n    async def create_invoice(\n        self,\n        amount: int,\n        memo: Optional[str] = None,\n        description_hash: Optional[bytes] = None,\n        unhashed_description: Optional[bytes] = None,\n        **kwargs,\n    ) -> InvoiceResponse:\n        data: Dict = {\"value\": amount, \"private\": True, \"memo\": memo or \"\"}\n        if kwargs.get(\"expiry\"):\n            data[\"expiry\"] = kwargs[\"expiry\"]\n        if description_hash:\n            data[\"description_hash\"] = base64.b64encode(description_hash).decode(\n                \"ascii\"\n            )\n        elif unhashed_description:\n            data[\"description_hash\"] = base64.b64encode(\n                hashlib.sha256(unhashed_description).digest()\n            ).decode(\"ascii\")\n\n        r = await self.client.post(url=\"/v1/invoices\", json=data)\n\n        if r.is_error:\n            error_message = r.text\n            try:\n                error_message = r.json()[\"error\"]\n            except Exception:\n                pass\n            return InvoiceResponse(False, None, None, error_message)\n\n        data = r.json()\n        payment_request = data[\"payment_request\"]\n        payment_hash = base64.b64decode(data[\"r_hash\"]).hex()\n        checking_id = payment_hash\n\n        return InvoiceResponse(True, checking_id, payment_request, None)\n\n    async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:\n        # set the fee limit for the payment\n        lnrpcFeeLimit = dict()\n        lnrpcFeeLimit[\"fixed_msat\"] = f\"{fee_limit_msat}\"\n\n        r = await self.client.post(\n            url=\"/v1/channels/transactions\",\n            json={\"payment_request\": bolt11, \"fee_limit\": lnrpcFeeLimit},\n            timeout=None,\n        )\n\n        if r.is_error or r.json().get(\"payment_error\"):\n            error_message = r.json().get(\"payment_error\") or r.text\n            return PaymentResponse(False, None, None, None, error_message)\n\n        data = r.json()\n        checking_id = base64.b64decode(data[\"payment_hash\"]).hex()\n        fee_msat = int(data[\"payment_route\"][\"total_fees_msat\"])\n        preimage = base64.b64decode(data[\"payment_preimage\"]).hex()\n        return PaymentResponse(True, checking_id, fee_msat, preimage, None)\n\n    async def get_invoice_status(self, checking_id: str) -> PaymentStatus:\n        r = await self.client.get(url=f\"/v1/invoice/{checking_id}\")\n\n        if r.is_error or not r.json().get(\"settled\"):\n            # this must also work when checking_id is not a hex recognizable by lnd\n            # it will return an error and no \"settled\" attribute on the object\n            return PaymentStatus(None)\n\n        return PaymentStatus(True)\n\n    async def get_payment_status(self, checking_id: str) -> PaymentStatus:\n        \"\"\"\n        This routine checks the payment status using routerpc.TrackPaymentV2.\n        \"\"\"\n        # convert checking_id from hex to base64 and some LND magic\n        try:\n            checking_id = base64.urlsafe_b64encode(bytes.fromhex(checking_id)).decode(\n                \"ascii\"\n            )\n        except ValueError:\n            return PaymentStatus(None)\n\n        url = f\"/v2/router/track/{checking_id}\"\n\n        # check payment.status:\n        # https://api.lightning.community/?python=#paymentpaymentstatus\n        statuses = {\n            \"UNKNOWN\": None,\n            \"IN_FLIGHT\": None,\n            \"SUCCEEDED\": True,\n            \"FAILED\": False,\n        }\n\n        async with self.client.stream(\"GET\", url, timeout=None) as r:\n            async for json_line in r.aiter_lines():\n                try:\n                    line = json.loads(json_line)\n                    if line.get(\"error\"):\n                        logger.error(\n                            line[\"error\"][\"message\"]\n                            if \"message\" in line[\"error\"]\n                            else line[\"error\"]\n                        )\n                        return PaymentStatus(None)\n                    payment = line.get(\"result\")\n                    if payment is not None and payment.get(\"status\"):\n                        return PaymentStatus(\n                            paid=statuses[payment[\"status\"]],\n                            fee_msat=payment.get(\"fee_msat\"),\n                            preimage=payment.get(\"payment_preimage\"),\n                        )\n                    else:\n                        return PaymentStatus(None)\n                except Exception:\n                    continue\n\n        return PaymentStatus(None)\n\n    async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:\n        while True:\n            try:\n                url = \"/v1/invoices/subscribe\"\n                async with self.client.stream(\"GET\", url, timeout=None) as r:\n                    async for line in r.aiter_lines():\n                        try:\n                            inv = json.loads(line)[\"result\"]\n                            if not inv[\"settled\"]:\n                                continue\n                        except Exception:\n                            continue\n\n                        payment_hash = base64.b64decode(inv[\"r_hash\"]).hex()\n                        yield payment_hash\n            except Exception as exc:\n                logger.error(\n                    f\"lost connection to lnd invoices stream: '{exc}', retrying in 5\"\n                    \" seconds\"\n                )\n                await asyncio.sleep(5)\n", "path": "lnbits/wallets/lndrest.py"}], "after_files": [{"content": "import asyncio\nimport base64\nimport hashlib\nimport json\nfrom typing import AsyncGenerator, Dict, Optional\n\nimport httpx\nfrom loguru import logger\n\nfrom lnbits.nodes.lndrest import LndRestNode\nfrom lnbits.settings import settings\n\nfrom .base import (\n    InvoiceResponse,\n    PaymentResponse,\n    PaymentStatus,\n    StatusResponse,\n    Wallet,\n)\nfrom .macaroon import AESCipher, load_macaroon\n\n\nclass LndRestWallet(Wallet):\n    \"\"\"https://api.lightning.community/rest/index.html#lnd-rest-api-reference\"\"\"\n\n    __node_cls__ = LndRestNode\n\n    def __init__(self):\n        endpoint = settings.lnd_rest_endpoint\n        cert = settings.lnd_rest_cert\n\n        macaroon = (\n            settings.lnd_rest_macaroon\n            or settings.lnd_admin_macaroon\n            or settings.lnd_rest_admin_macaroon\n            or settings.lnd_invoice_macaroon\n            or settings.lnd_rest_invoice_macaroon\n        )\n\n        encrypted_macaroon = settings.lnd_rest_macaroon_encrypted\n        if encrypted_macaroon:\n            macaroon = AESCipher(description=\"macaroon decryption\").decrypt(\n                encrypted_macaroon\n            )\n\n        if not endpoint:\n            raise Exception(\"cannot initialize lndrest: no endpoint\")\n\n        if not macaroon:\n            raise Exception(\"cannot initialize lndrest: no macaroon\")\n\n        if not cert:\n            logger.warning(\n                \"no certificate for lndrest provided, this only works if you have a\"\n                \" publicly issued certificate\"\n            )\n\n        endpoint = endpoint[:-1] if endpoint.endswith(\"/\") else endpoint\n        endpoint = (\n            f\"https://{endpoint}\" if not endpoint.startswith(\"http\") else endpoint\n        )\n        self.endpoint = endpoint\n        self.macaroon = load_macaroon(macaroon)\n\n        # if no cert provided it should be public so we set verify to True\n        # and it will still check for validity of certificate and fail if its not valid\n        # even on startup\n        self.cert = cert or True\n\n        self.auth = {\"Grpc-Metadata-macaroon\": self.macaroon}\n        self.client = httpx.AsyncClient(\n            base_url=self.endpoint, headers=self.auth, verify=self.cert\n        )\n\n    async def cleanup(self):\n        try:\n            await self.client.aclose()\n        except RuntimeError as e:\n            logger.warning(f\"Error closing wallet connection: {e}\")\n\n    async def status(self) -> StatusResponse:\n        try:\n            r = await self.client.get(\"/v1/balance/channels\")\n            r.raise_for_status()\n        except (httpx.ConnectError, httpx.RequestError) as exc:\n            return StatusResponse(f\"Unable to connect to {self.endpoint}. {exc}\", 0)\n\n        try:\n            data = r.json()\n            if r.is_error:\n                raise Exception\n        except Exception:\n            return StatusResponse(r.text[:200], 0)\n\n        return StatusResponse(None, int(data[\"balance\"]) * 1000)\n\n    async def create_invoice(\n        self,\n        amount: int,\n        memo: Optional[str] = None,\n        description_hash: Optional[bytes] = None,\n        unhashed_description: Optional[bytes] = None,\n        **kwargs,\n    ) -> InvoiceResponse:\n        data: Dict = {\"value\": amount, \"private\": True, \"memo\": memo or \"\"}\n        if kwargs.get(\"expiry\"):\n            data[\"expiry\"] = kwargs[\"expiry\"]\n        if description_hash:\n            data[\"description_hash\"] = base64.b64encode(description_hash).decode(\n                \"ascii\"\n            )\n        elif unhashed_description:\n            data[\"description_hash\"] = base64.b64encode(\n                hashlib.sha256(unhashed_description).digest()\n            ).decode(\"ascii\")\n\n        r = await self.client.post(url=\"/v1/invoices\", json=data)\n\n        if r.is_error:\n            error_message = r.text\n            try:\n                error_message = r.json()[\"error\"]\n            except Exception:\n                pass\n            return InvoiceResponse(False, None, None, error_message)\n\n        data = r.json()\n        payment_request = data[\"payment_request\"]\n        payment_hash = base64.b64decode(data[\"r_hash\"]).hex()\n        checking_id = payment_hash\n\n        return InvoiceResponse(True, checking_id, payment_request, None)\n\n    async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:\n        # set the fee limit for the payment\n        lnrpcFeeLimit = dict()\n        lnrpcFeeLimit[\"fixed_msat\"] = f\"{fee_limit_msat}\"\n\n        r = await self.client.post(\n            url=\"/v1/channels/transactions\",\n            json={\"payment_request\": bolt11, \"fee_limit\": lnrpcFeeLimit},\n            timeout=None,\n        )\n\n        if r.is_error or r.json().get(\"payment_error\"):\n            error_message = r.json().get(\"payment_error\") or r.text\n            return PaymentResponse(False, None, None, None, error_message)\n\n        data = r.json()\n        checking_id = base64.b64decode(data[\"payment_hash\"]).hex()\n        fee_msat = int(data[\"payment_route\"][\"total_fees_msat\"])\n        preimage = base64.b64decode(data[\"payment_preimage\"]).hex()\n        return PaymentResponse(True, checking_id, fee_msat, preimage, None)\n\n    async def get_invoice_status(self, checking_id: str) -> PaymentStatus:\n        r = await self.client.get(url=f\"/v1/invoice/{checking_id}\")\n\n        if r.is_error or not r.json().get(\"settled\"):\n            # this must also work when checking_id is not a hex recognizable by lnd\n            # it will return an error and no \"settled\" attribute on the object\n            return PaymentStatus(None)\n\n        return PaymentStatus(True)\n\n    async def get_payment_status(self, checking_id: str) -> PaymentStatus:\n        \"\"\"\n        This routine checks the payment status using routerpc.TrackPaymentV2.\n        \"\"\"\n        # convert checking_id from hex to base64 and some LND magic\n        try:\n            checking_id = base64.urlsafe_b64encode(bytes.fromhex(checking_id)).decode(\n                \"ascii\"\n            )\n        except ValueError:\n            return PaymentStatus(None)\n\n        url = f\"/v2/router/track/{checking_id}\"\n\n        # check payment.status:\n        # https://api.lightning.community/?python=#paymentpaymentstatus\n        statuses = {\n            \"UNKNOWN\": None,\n            \"IN_FLIGHT\": None,\n            \"SUCCEEDED\": True,\n            \"FAILED\": False,\n        }\n\n        async with self.client.stream(\"GET\", url, timeout=None) as r:\n            async for json_line in r.aiter_lines():\n                try:\n                    line = json.loads(json_line)\n                    if line.get(\"error\"):\n                        logger.error(\n                            line[\"error\"][\"message\"]\n                            if \"message\" in line[\"error\"]\n                            else line[\"error\"]\n                        )\n                        if (\n                            line[\"error\"].get(\"code\") == 5\n                            and line[\"error\"].get(\"message\")\n                            == \"payment isn't initiated\"\n                        ):\n                            return PaymentStatus(False)\n                        return PaymentStatus(None)\n                    payment = line.get(\"result\")\n                    if payment is not None and payment.get(\"status\"):\n                        return PaymentStatus(\n                            paid=statuses[payment[\"status\"]],\n                            fee_msat=payment.get(\"fee_msat\"),\n                            preimage=payment.get(\"payment_preimage\"),\n                        )\n                    else:\n                        return PaymentStatus(None)\n                except Exception:\n                    continue\n\n        return PaymentStatus(None)\n\n    async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:\n        while True:\n            try:\n                url = \"/v1/invoices/subscribe\"\n                async with self.client.stream(\"GET\", url, timeout=None) as r:\n                    async for line in r.aiter_lines():\n                        try:\n                            inv = json.loads(line)[\"result\"]\n                            if not inv[\"settled\"]:\n                                continue\n                        except Exception:\n                            continue\n\n                        payment_hash = base64.b64decode(inv[\"r_hash\"]).hex()\n                        yield payment_hash\n            except Exception as exc:\n                logger.error(\n                    f\"lost connection to lnd invoices stream: '{exc}', retrying in 5\"\n                    \" seconds\"\n                )\n                await asyncio.sleep(5)\n", "path": "lnbits/wallets/lndrest.py"}]}