fix pyright lnbits/wallets

Co-authored-by: dni  <office@dnilabs.com>
This commit is contained in:
Pavol Rusnak 2023-02-02 12:57:36 +00:00 committed by dni ⚡
parent 0607cb1d6e
commit 9c8a79eb9b
No known key found for this signature in database
GPG key ID: 886317704CC4E618
10 changed files with 163 additions and 130 deletions

View file

@ -22,6 +22,8 @@ class ClicheWallet(Wallet):
def __init__(self): def __init__(self):
self.endpoint = settings.cliche_endpoint self.endpoint = settings.cliche_endpoint
if not self.endpoint:
raise Exception("cannot initialize cliche")
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
try: try:
@ -36,7 +38,7 @@ class ClicheWallet(Wallet):
data = json.loads(r) data = json.loads(r)
except: except:
return StatusResponse( return StatusResponse(
f"Failed to connect to {self.endpoint}, got: '{r.text[:200]}...'", 0 f"Failed to connect to {self.endpoint}, got: '{r[:200]}...'", 0
) )
return StatusResponse(None, data["result"]["wallets"][0]["balance"]) return StatusResponse(None, data["result"]["wallets"][0]["balance"])
@ -89,6 +91,13 @@ class ClicheWallet(Wallet):
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
ws = create_connection(self.endpoint) ws = create_connection(self.endpoint)
ws.send(f"pay-invoice --invoice {bolt11}") ws.send(f"pay-invoice --invoice {bolt11}")
checking_id, fee_msat, preimage, error_message, payment_ok = (
None,
None,
None,
None,
None,
)
for _ in range(2): for _ in range(2):
r = ws.recv() r = ws.recv()
data = json.loads(r) data = json.loads(r)
@ -151,9 +160,9 @@ class ClicheWallet(Wallet):
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
while True: while True:
try: try:
ws = await create_connection(self.endpoint) ws = create_connection(self.endpoint)
while True: while True:
r = await ws.recv() r = ws.recv()
data = json.loads(r) data = json.loads(r)
print(data) print(data)
try: try:

View file

@ -7,10 +7,7 @@ from typing import AsyncGenerator, Dict, Optional
import httpx import httpx
from loguru import logger from loguru import logger
from websockets.client import connect
# TODO: https://github.com/lnbits/lnbits/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
from websockets import connect # type: ignore
from lnbits.settings import settings from lnbits.settings import settings
@ -34,11 +31,13 @@ class UnknownError(Exception):
class EclairWallet(Wallet): class EclairWallet(Wallet):
def __init__(self): def __init__(self):
url = settings.eclair_url url = settings.eclair_url
self.url = url[:-1] if url.endswith("/") else url passw = settings.eclair_pass
if not url or not passw:
raise Exception("cannot initialize eclair")
self.url = url[:-1] if url.endswith("/") else url
self.ws_url = f"ws://{urllib.parse.urlsplit(self.url).netloc}/ws" self.ws_url = f"ws://{urllib.parse.urlsplit(self.url).netloc}/ws"
passw = settings.eclair_pass
encodedAuth = base64.b64encode(f":{passw}".encode()) encodedAuth = base64.b64encode(f":{passw}".encode())
auth = str(encodedAuth, "utf-8") auth = str(encodedAuth, "utf-8")
self.auth = {"Authorization": f"Basic {auth}"} self.auth = {"Authorization": f"Basic {auth}"}
@ -71,7 +70,11 @@ class EclairWallet(Wallet):
**kwargs, **kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"amountMsat": amount * 1000} data: Dict = {
"amountMsat": amount * 1000,
"description_hash": b"",
"description": memo,
}
if kwargs.get("expiry"): if kwargs.get("expiry"):
data["expireIn"] = kwargs["expiry"] data["expireIn"] = kwargs["expiry"]
@ -79,8 +82,6 @@ class EclairWallet(Wallet):
data["descriptionHash"] = description_hash.hex() data["descriptionHash"] = description_hash.hex()
elif unhashed_description: elif unhashed_description:
data["descriptionHash"] = hashlib.sha256(unhashed_description).hexdigest() data["descriptionHash"] = hashlib.sha256(unhashed_description).hexdigest()
else:
data["description"] = memo or ""
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
r = await client.post( r = await client.post(
@ -149,6 +150,7 @@ class EclairWallet(Wallet):
} }
data = r.json()[-1] data = r.json()[-1]
fee_msat = 0
if data["status"]["type"] == "sent": if data["status"]["type"] == "sent":
fee_msat = -data["status"]["feesPaid"] fee_msat = -data["status"]["feesPaid"]
preimage = data["status"]["paymentPreimage"] preimage = data["status"]["paymentPreimage"]
@ -223,10 +225,10 @@ class EclairWallet(Wallet):
) as ws: ) as ws:
while True: while True:
message = await ws.recv() message = await ws.recv()
message = json.loads(message) message_json = json.loads(message)
if message and message["type"] == "payment-received": if message_json and message_json["type"] == "payment-received":
yield message["paymentHash"] yield message_json["paymentHash"]
except Exception as exc: except Exception as exc:
logger.error( logger.error(

View file

@ -45,19 +45,18 @@ class FakeWallet(Wallet):
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = { data: Dict = {
"out": False, "out": False,
"amount": amount, "amount": amount * 1000,
"currency": "bc", "currency": "bc",
"privkey": self.privkey, "privkey": self.privkey,
"memo": None, "memo": memo,
"description_hash": None, "description_hash": b"",
"description": "", "description": "",
"fallback": None, "fallback": None,
"expires": None, "expires": kwargs.get("expiry"),
"timestamp": datetime.now().timestamp(),
"route": None, "route": None,
"tags_set": [],
} }
data["expires"] = kwargs.get("expiry")
data["amount"] = amount * 1000
data["timestamp"] = datetime.now().timestamp()
if description_hash: if description_hash:
data["tags_set"] = ["h"] data["tags_set"] = ["h"]
data["description_hash"] = description_hash data["description_hash"] = description_hash
@ -69,7 +68,7 @@ class FakeWallet(Wallet):
data["memo"] = memo data["memo"] = memo
data["description"] = memo data["description"] = memo
randomHash = ( randomHash = (
data["privkey"][:6] self.privkey[:6]
+ hashlib.sha256(str(random.getrandbits(256)).encode()).hexdigest()[6:] + hashlib.sha256(str(random.getrandbits(256)).encode()).hexdigest()[6:]
) )
data["paymenthash"] = randomHash data["paymenthash"] = randomHash
@ -78,12 +77,10 @@ class FakeWallet(Wallet):
return InvoiceResponse(True, checking_id, payment_request) return InvoiceResponse(True, checking_id, payment_request)
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: async def pay_invoice(self, bolt11: str, _: int) -> PaymentResponse:
invoice = decode(bolt11) invoice = decode(bolt11)
if (
hasattr(invoice, "checking_id") if invoice.checking_id and invoice.checking_id[:6] == self.privkey[:6]:
and invoice.checking_id[:6] == self.privkey[:6] # type: ignore
):
await self.queue.put(invoice) await self.queue.put(invoice)
return PaymentResponse(True, invoice.payment_hash, 0) return PaymentResponse(True, invoice.payment_hash, 0)
else: else:
@ -91,10 +88,10 @@ class FakeWallet(Wallet):
ok=False, error_message="Only internal invoices can be used!" ok=False, error_message="Only internal invoices can be used!"
) )
async def get_invoice_status(self, checking_id: str) -> PaymentStatus: async def get_invoice_status(self, _: str) -> PaymentStatus:
return PaymentStatus(None) return PaymentStatus(None)
async def get_payment_status(self, checking_id: str) -> PaymentStatus: async def get_payment_status(self, _: str) -> PaymentStatus:
return PaymentStatus(None) return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:

View file

@ -21,12 +21,13 @@ class LNbitsWallet(Wallet):
def __init__(self): def __init__(self):
self.endpoint = settings.lnbits_endpoint self.endpoint = settings.lnbits_endpoint
key = ( key = (
settings.lnbits_key settings.lnbits_key
or settings.lnbits_admin_key or settings.lnbits_admin_key
or settings.lnbits_invoice_key or settings.lnbits_invoice_key
) )
if not self.endpoint or not key:
raise Exception("cannot initialize lnbits wallet")
self.key = {"X-Api-Key": key} self.key = {"X-Api-Key": key}
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
@ -60,7 +61,7 @@ class LNbitsWallet(Wallet):
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs, **kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"out": False, "amount": amount} data: Dict = {"out": False, "amount": amount, "memo": memo or ""}
if kwargs.get("expiry"): if kwargs.get("expiry"):
data["expiry"] = kwargs["expiry"] data["expiry"] = kwargs["expiry"]
if description_hash: if description_hash:
@ -68,8 +69,6 @@ class LNbitsWallet(Wallet):
if unhashed_description: if unhashed_description:
data["unhashed_description"] = unhashed_description.hex() data["unhashed_description"] = unhashed_description.hex()
data["memo"] = memo or ""
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
r = await client.post( r = await client.post(
url=f"{self.endpoint}/api/v1/payments", headers=self.key, json=data url=f"{self.endpoint}/api/v1/payments", headers=self.key, json=data

View file

@ -105,9 +105,6 @@ class LndWallet(Wallet):
) )
endpoint = settings.lnd_grpc_endpoint endpoint = settings.lnd_grpc_endpoint
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.port = int(settings.lnd_grpc_port)
self.cert_path = settings.lnd_grpc_cert or settings.lnd_cert
macaroon = ( macaroon = (
settings.lnd_grpc_macaroon settings.lnd_grpc_macaroon
@ -122,8 +119,17 @@ class LndWallet(Wallet):
macaroon = AESCipher(description="macaroon decryption").decrypt( macaroon = AESCipher(description="macaroon decryption").decrypt(
encrypted_macaroon encrypted_macaroon
) )
self.macaroon = load_macaroon(macaroon)
cert_path = settings.lnd_grpc_cert or settings.lnd_cert
if not endpoint or not macaroon or not cert_path or not settings.lnd_grpc_port:
raise Exception("cannot initialize lndrest")
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.port = int(settings.lnd_grpc_port)
self.cert_path = settings.lnd_grpc_cert or settings.lnd_cert
self.macaroon = load_macaroon(macaroon)
self.cert_path = cert_path
cert = open(self.cert_path, "rb").read() cert = open(self.cert_path, "rb").read()
creds = grpc.ssl_channel_credentials(cert) creds = grpc.ssl_channel_credentials(cert)
auth_creds = grpc.metadata_call_credentials(self.metadata_callback) auth_creds = grpc.metadata_call_credentials(self.metadata_callback)
@ -140,8 +146,6 @@ class LndWallet(Wallet):
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
try: try:
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest()) resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())
except RpcError as exc:
return StatusResponse(str(exc._details), 0)
except Exception as exc: except Exception as exc:
return StatusResponse(str(exc), 0) return StatusResponse(str(exc), 0)
@ -155,20 +159,23 @@ class LndWallet(Wallet):
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs, **kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
params: Dict = {"value": amount, "private": True} data: Dict = {
"description_hash": b"",
"value": amount,
"private": True,
"memo": memo or "",
}
if kwargs.get("expiry"): if kwargs.get("expiry"):
params["expiry"] = kwargs["expiry"] data["expiry"] = kwargs["expiry"]
if description_hash: if description_hash:
params["description_hash"] = description_hash data["description_hash"] = description_hash
elif unhashed_description: elif unhashed_description:
params["description_hash"] = hashlib.sha256( data["description_hash"] = hashlib.sha256(
unhashed_description unhashed_description
).digest() # as bytes directly ).digest() # as bytes directly
else:
params["memo"] = memo or ""
try: try:
req = ln.Invoice(**params) req = ln.Invoice(**data)
resp = await self.rpc.AddInvoice(req) resp = await self.rpc.AddInvoice(req)
except Exception as exc: except Exception as exc:
error_message = str(exc) error_message = str(exc)
@ -188,8 +195,6 @@ class LndWallet(Wallet):
) )
try: try:
resp = await self.routerpc.SendPaymentV2(req).read() resp = await self.routerpc.SendPaymentV2(req).read()
except RpcError as exc:
return PaymentResponse(False, None, None, None, exc._details)
except Exception as exc: except Exception as exc:
return PaymentResponse(False, None, None, None, str(exc)) return PaymentResponse(False, None, None, None, str(exc))

View file

@ -43,6 +43,15 @@ class LndRestWallet(Wallet):
macaroon = AESCipher(description="macaroon decryption").decrypt( macaroon = AESCipher(description="macaroon decryption").decrypt(
encrypted_macaroon encrypted_macaroon
) )
if not endpoint or not macaroon or not settings.lnd_rest_cert:
raise Exception("cannot initialize lndrest")
endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
endpoint = (
"https://" + endpoint if not endpoint.startswith("http") else endpoint
)
self.endpoint = endpoint
self.macaroon = load_macaroon(macaroon) self.macaroon = load_macaroon(macaroon)
self.auth = {"Grpc-Metadata-macaroon": self.macaroon} self.auth = {"Grpc-Metadata-macaroon": self.macaroon}
@ -74,7 +83,7 @@ class LndRestWallet(Wallet):
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs, **kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"value": amount, "private": True} data: Dict = {"value": amount, "private": True, "memo": memo or ""}
if kwargs.get("expiry"): if kwargs.get("expiry"):
data["expiry"] = kwargs["expiry"] data["expiry"] = kwargs["expiry"]
if description_hash: if description_hash:
@ -85,8 +94,6 @@ class LndRestWallet(Wallet):
data["description_hash"] = base64.b64encode( data["description_hash"] = base64.b64encode(
hashlib.sha256(unhashed_description).digest() hashlib.sha256(unhashed_description).digest()
).decode("ascii") ).decode("ascii")
else:
data["memo"] = memo or ""
async with httpx.AsyncClient(verify=self.cert) as client: async with httpx.AsyncClient(verify=self.cert) as client:
r = await client.post( r = await client.post(

View file

@ -1,12 +1,8 @@
import asyncio import asyncio
import hashlib import hashlib
import json
from http import HTTPStatus
from typing import AsyncGenerator, Dict, Optional from typing import AsyncGenerator, Dict, Optional
import httpx import httpx
from fastapi.exceptions import HTTPException
from loguru import logger
from lnbits.settings import settings from lnbits.settings import settings
@ -24,8 +20,13 @@ class LNPayWallet(Wallet):
def __init__(self): def __init__(self):
endpoint = settings.lnpay_api_endpoint endpoint = settings.lnpay_api_endpoint
wallet_key = settings.lnpay_wallet_key or settings.lnpay_admin_key
if not endpoint or not wallet_key or not settings.lnpay_api_key:
raise Exception("cannot initialize lnpay")
self.wallet_key = wallet_key
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.wallet_key = settings.lnpay_wallet_key or settings.lnpay_admin_key
self.auth = {"X-Api-Key": settings.lnpay_api_key} self.auth = {"X-Api-Key": settings.lnpay_api_key}
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
@ -54,7 +55,6 @@ class LNPayWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"num_satoshis": f"{amount}"} data: Dict = {"num_satoshis": f"{amount}"}
if description_hash: if description_hash:
@ -133,27 +133,27 @@ class LNPayWallet(Wallet):
value = await self.queue.get() value = await self.queue.get()
yield value yield value
async def webhook_listener(self): # async def webhook_listener(self):
text: str = await request.get_data() # text: str = await request.get_data()
try: # try:
data = json.loads(text) # data = json.loads(text)
except json.decoder.JSONDecodeError: # except json.decoder.JSONDecodeError:
logger.error(f"got something wrong on lnpay webhook endpoint: {text[:200]}") # logger.error(f"got something wrong on lnpay webhook endpoint: {text[:200]}")
data = None # data = None
if ( # if (
type(data) is not dict # type(data) is not dict
or "event" not in data # or "event" not in data
or data["event"].get("name") != "wallet_receive" # or data["event"].get("name") != "wallet_receive"
): # ):
raise HTTPException(status_code=HTTPStatus.NO_CONTENT) # raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
lntx_id = data["data"]["wtx"]["lnTx"]["id"] # lntx_id = data["data"]["wtx"]["lnTx"]["id"]
async with httpx.AsyncClient() as client: # async with httpx.AsyncClient() as client:
r = await client.get( # r = await client.get(
f"{self.endpoint}/lntx/{lntx_id}?fields=settled", headers=self.auth # f"{self.endpoint}/lntx/{lntx_id}?fields=settled", headers=self.auth
) # )
data = r.json() # data = r.json()
if data["settled"]: # if data["settled"]:
await self.queue.put(lntx_id) # await self.queue.put(lntx_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT) # raise HTTPException(status_code=HTTPStatus.NO_CONTENT)

View file

@ -21,13 +21,14 @@ from .base import (
class LnTipsWallet(Wallet): class LnTipsWallet(Wallet):
def __init__(self): def __init__(self):
endpoint = settings.lntips_api_endpoint endpoint = settings.lntips_api_endpoint
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
key = ( key = (
settings.lntips_api_key settings.lntips_api_key
or settings.lntips_admin_key or settings.lntips_admin_key
or settings.lntips_invoice_key or settings.lntips_invoice_key
) )
if not endpoint or not key:
raise Exception("cannot initialize lntxbod")
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.auth = {"Authorization": f"Basic {key}"} self.auth = {"Authorization": f"Basic {key}"}
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
@ -53,15 +54,12 @@ class LnTipsWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
data: Dict = {"amount": amount} data: Dict = {"amount": amount, "description_hash": "", "memo": memo or ""}
if description_hash: if description_hash:
data["description_hash"] = description_hash.hex() data["description_hash"] = description_hash.hex()
elif unhashed_description: elif unhashed_description:
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest() data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest()
else:
data["memo"] = memo or ""
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
r = await client.post( r = await client.post(

View file

@ -1,11 +1,10 @@
import asyncio import asyncio
import hmac
from http import HTTPStatus # import hmac
# from http import HTTPStatus
from typing import AsyncGenerator, Optional from typing import AsyncGenerator, Optional
import httpx import httpx
from fastapi.exceptions import HTTPException
from loguru import logger
from lnbits.settings import settings from lnbits.settings import settings
@ -18,19 +17,24 @@ from .base import (
Wallet, Wallet,
) )
# from fastapi import Request, HTTPException
# from loguru import logger
class OpenNodeWallet(Wallet): class OpenNodeWallet(Wallet):
"""https://developers.opennode.com/""" """https://developers.opennode.com/"""
def __init__(self): def __init__(self):
endpoint = settings.opennode_api_endpoint endpoint = settings.opennode_api_endpoint
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
key = ( key = (
settings.opennode_key settings.opennode_key
or settings.opennode_admin_key or settings.opennode_admin_key
or settings.opennode_invoice_key or settings.opennode_invoice_key
) )
if not endpoint or not key:
raise Exception("cannot initialize opennode")
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.auth = {"Authorization": key} self.auth = {"Authorization": key}
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
@ -54,7 +58,6 @@ class OpenNodeWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None, unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
if description_hash or unhashed_description: if description_hash or unhashed_description:
raise Unsupported("description_hash") raise Unsupported("description_hash")
@ -139,17 +142,17 @@ class OpenNodeWallet(Wallet):
value = await self.queue.get() value = await self.queue.get()
yield value yield value
async def webhook_listener(self): # async def webhook_listener(self):
data = await request.form # data = await request.form
if "status" not in data or data["status"] != "paid": # if "status" not in data or data["status"] != "paid":
raise HTTPException(status_code=HTTPStatus.NO_CONTENT) # raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
charge_id = data["id"] # charge_id = data["id"]
x = hmac.new(self.auth["Authorization"].encode("ascii"), digestmod="sha256") # x = hmac.new(self.auth["Authorization"].encode("ascii"), digestmod="sha256")
x.update(charge_id.encode("ascii")) # x.update(charge_id.encode("ascii"))
if x.hexdigest() != data["hashed_order"]: # if x.hexdigest() != data["hashed_order"]:
logger.error("invalid webhook, not from opennode") # logger.error("invalid webhook, not from opennode")
raise HTTPException(status_code=HTTPStatus.NO_CONTENT) # raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
await self.queue.put(charge_id) # await self.queue.put(charge_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT) # raise HTTPException(status_code=HTTPStatus.NO_CONTENT)

View file

@ -28,6 +28,7 @@ class UnknownError(Exception):
class SparkWallet(Wallet): class SparkWallet(Wallet):
def __init__(self): def __init__(self):
assert settings.spark_url
self.url = settings.spark_url.replace("/rpc", "") self.url = settings.spark_url.replace("/rpc", "")
self.token = settings.spark_token self.token = settings.spark_token
@ -46,6 +47,7 @@ class SparkWallet(Wallet):
try: try:
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
assert self.token
r = await client.post( r = await client.post(
self.url + "/rpc", self.url + "/rpc",
headers={"X-Access": self.token}, headers={"X-Access": self.token},
@ -133,38 +135,49 @@ class SparkWallet(Wallet):
bolt11=bolt11, bolt11=bolt11,
maxfee=fee_limit_msat, maxfee=fee_limit_msat,
) )
fee_msat = -int(r["msatoshi_sent"] - r["msatoshi"])
preimage = r["payment_preimage"]
return PaymentResponse(True, r["payment_hash"], fee_msat, preimage, None)
except (SparkError, UnknownError) as exc: except (SparkError, UnknownError) as exc:
listpays = await self.listpays(bolt11) listpays = await self.listpays(bolt11)
if listpays: if not listpays:
pays = listpays["pays"] return PaymentResponse(False, None, None, None, str(exc))
if len(pays) == 0: pays = listpays["pays"]
return PaymentResponse(False, None, None, None, str(exc))
pay = pays[0] if len(pays) == 0:
payment_hash = pay["payment_hash"] return PaymentResponse(False, None, None, None, str(exc))
if len(pays) > 1: pay = pays[0]
raise SparkError( payment_hash = pay["payment_hash"]
f"listpays({payment_hash}) returned an unexpected response: {listpays}"
)
if pay["status"] == "failed": if len(pays) > 1:
return PaymentResponse(False, None, None, None, str(exc)) raise SparkError(
elif pay["status"] == "pending": f"listpays({payment_hash}) returned an unexpected response: {listpays}"
return PaymentResponse(None, payment_hash, None, None, None) )
elif pay["status"] == "complete":
r = pay
r["payment_preimage"] = pay["preimage"]
r["msatoshi"] = int(pay["amount_msat"][0:-4])
r["msatoshi_sent"] = int(pay["amount_sent_msat"][0:-4])
# this may result in an error if it was paid previously
# our database won't allow the same payment_hash to be added twice
# this is good
fee_msat = -int(r["msatoshi_sent"] - r["msatoshi"]) if pay["status"] == "failed":
preimage = r["payment_preimage"] return PaymentResponse(False, None, None, None, str(exc))
return PaymentResponse(True, r["payment_hash"], fee_msat, preimage, None)
elif pay["status"] == "pending":
return PaymentResponse(None, payment_hash, None, None, None)
elif pay["status"] == "complete":
r = pay
r["payment_preimage"] = pay["preimage"]
r["msatoshi"] = int(pay["amount_msat"][0:-4])
r["msatoshi_sent"] = int(pay["amount_sent_msat"][0:-4])
# this may result in an error if it was paid previously
# our database won't allow the same payment_hash to be added twice
# this is good
fee_msat = -int(r["msatoshi_sent"] - r["msatoshi"])
preimage = r["payment_preimage"]
return PaymentResponse(
True, r["payment_hash"], fee_msat, preimage, None
)
else:
return PaymentResponse(False, None, None, None, str(exc))
async def get_invoice_status(self, checking_id: str) -> PaymentStatus: async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
try: try: