add zbd to settings

update api methods

add zbd as funding source to settings and js

fix statuses

cast str into int for status method

fix outbound payment hash issue

restore create_invoice

remove print stmts
This commit is contained in:
bitkarrot 2024-02-01 18:36:17 -08:00 committed by Pavol Rusnak
parent d69946db8a
commit d20a35eddc
4 changed files with 76 additions and 31 deletions

View File

@ -210,6 +210,7 @@ class ZBDFundingSource(LNbitsSettings):
zbd_api_endpoint: Optional[str] = Field(default="https://api.zebedee.io/v0/") zbd_api_endpoint: Optional[str] = Field(default="https://api.zebedee.io/v0/")
zbd_api_key: Optional[str] = Field(default=None) zbd_api_key: Optional[str] = Field(default=None)
class AlbyFundingSource(LNbitsSettings): class AlbyFundingSource(LNbitsSettings):
alby_api_endpoint: Optional[str] = Field(default="https://api.getalby.com/") alby_api_endpoint: Optional[str] = Field(default="https://api.getalby.com/")
alby_access_token: Optional[str] = Field(default=None) alby_access_token: Optional[str] = Field(default=None)
@ -249,6 +250,7 @@ class FundingSourcesSettings(
LndGrpcFundingSource, LndGrpcFundingSource,
LnPayFundingSource, LnPayFundingSource,
AlbyFundingSource, AlbyFundingSource,
ZBDFundingSource,
OpenNodeFundingSource, OpenNodeFundingSource,
SparkFundingSource, SparkFundingSource,
LnTipsFundingSource, LnTipsFundingSource,
@ -403,6 +405,7 @@ class SuperUserSettings(LNbitsSettings):
"LnTipsWallet", "LnTipsWallet",
"LNPayWallet", "LNPayWallet",
"AlbyWallet", "AlbyWallet",
"ZBDWallet",
"LNbitsWallet", "LNbitsWallet",
"OpenNodeWallet", "OpenNodeWallet",
] ]

View File

@ -110,7 +110,7 @@ Vue.component('lnbits-funding-sources', {
'ZBD', 'ZBD',
{ {
zbd_api_endpoint: 'Endpoint', zbd_api_endpoint: 'Endpoint',
zbd_access_token: 'Key' zbd_api_key: 'Key'
} }
], ],
[ [

View File

@ -8,7 +8,6 @@ from lnbits.settings import settings
from lnbits.wallets.base import Wallet from lnbits.wallets.base import Wallet
from .alby import AlbyWallet from .alby import AlbyWallet
from .zbd import ZBDWallet
from .cliche import ClicheWallet from .cliche import ClicheWallet
from .corelightning import CoreLightningWallet from .corelightning import CoreLightningWallet
@ -26,6 +25,7 @@ from .lntips import LnTipsWallet
from .opennode import OpenNodeWallet from .opennode import OpenNodeWallet
from .spark import SparkWallet from .spark import SparkWallet
from .void import VoidWallet from .void import VoidWallet
from .zbd import ZBDWallet
def set_wallet_class(class_name: Optional[str] = None): def set_wallet_class(class_name: Optional[str] = None):

View File

@ -1,17 +1,18 @@
import asyncio import asyncio
import hashlib
from typing import AsyncGenerator, Dict, Optional from typing import AsyncGenerator, Dict, Optional
import httpx import httpx
from loguru import logger from loguru import logger
from lnbits.settings import settings from lnbits.settings import settings
from lnbits.wallets.base import PaymentStatus
from .base import ( from .base import (
InvoiceResponse, InvoiceResponse,
PaymentResponse, PaymentResponse,
PaymentStatus, PaymentStatus,
StatusResponse, StatusResponse,
Unsupported,
Wallet, Wallet,
) )
@ -27,7 +28,7 @@ class ZBDWallet(Wallet):
self.endpoint = self.normalize_endpoint(settings.zbd_api_endpoint) self.endpoint = self.normalize_endpoint(settings.zbd_api_endpoint)
self.auth = { self.auth = {
"Authorization": "Bearer " + settings.zbd_api_key, "apikey": settings.zbd_api_key,
"User-Agent": settings.user_agent, "User-Agent": settings.user_agent,
} }
self.client = httpx.AsyncClient(base_url=self.endpoint, headers=self.auth) self.client = httpx.AsyncClient(base_url=self.endpoint, headers=self.auth)
@ -40,16 +41,18 @@ class ZBDWallet(Wallet):
async def status(self) -> StatusResponse: async def status(self) -> StatusResponse:
try: try:
r = await self.client.get("/balance", timeout=10) r = await self.client.get("wallet", timeout=10)
except (httpx.ConnectError, httpx.RequestError): except (httpx.ConnectError, httpx.RequestError):
return StatusResponse(f"Unable to connect to '{self.endpoint}'", 0) return StatusResponse(f"Unable to connect to '{self.endpoint}'", 0)
if r.is_error: if r.is_error:
error_message = r.json()["message"] error_message = r.json()["message"]
return StatusResponse(error_message, 0) return StatusResponse(error_message, 0)
data = r.json()["balance"]
# if no error, multiply balance by 1000 for msats representation in lnbits data = int(r.json()["data"]["balance"])
return StatusResponse(None, data * 1000) # ZBD returns everything as a str not int
# balance is returned in msats already in ZBD
return StatusResponse(None, data)
async def create_invoice( async def create_invoice(
self, self,
@ -60,16 +63,20 @@ class ZBDWallet(Wallet):
**kwargs, **kwargs,
) -> InvoiceResponse: ) -> InvoiceResponse:
# https://api.zebedee.io/v0/charges # https://api.zebedee.io/v0/charges
data: Dict = {"amount": f"{amount}"} if description_hash or unhashed_description:
if description_hash: raise Unsupported("description_hash")
data["description_hash"] = description_hash.hex()
elif unhashed_description: msats_amount = amount * 1000
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest() data: Dict = {
else: "amount": f"{msats_amount}",
data["memo"] = memo or "" "description": memo,
"expiresIn": 3600,
"callbackUrl": "",
"internalId": "",
}
r = await self.client.post( r = await self.client.post(
"/invoices", "charges",
json=data, json=data,
timeout=40, timeout=40,
) )
@ -78,16 +85,22 @@ class ZBDWallet(Wallet):
error_message = r.json()["message"] error_message = r.json()["message"]
return InvoiceResponse(False, None, None, error_message) return InvoiceResponse(False, None, None, error_message)
data = r.json() data = r.json()["data"]
checking_id = data["payment_hash"] checking_id = data["id"] # this is a zbd id
payment_request = data["payment_request"] payment_request = data["invoice"]["request"]
return InvoiceResponse(True, checking_id, payment_request, None) return InvoiceResponse(True, checking_id, payment_request, None)
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
# https://api.zebedee.io/v0/payments # https://api.zebedee.io/v0/payments
r = await self.client.post( r = await self.client.post(
"/payments/bolt11", "payments",
json={"invoice": bolt11}, # assume never need amount in body json={
"invoice": bolt11,
"description": "",
"amount": "",
"internalId": "",
"callbackUrl": "",
},
timeout=None, timeout=None,
) )
@ -96,28 +109,57 @@ class ZBDWallet(Wallet):
return PaymentResponse(False, None, None, None, error_message) return PaymentResponse(False, None, None, None, error_message)
data = r.json() data = r.json()
checking_id = data["payment_hash"]
fee_msat = -data["fee"] # get the payment hash from the zbd api
preimage = data["payment_preimage"] decoded_request = await self.client.post(
"decode-invoice",
json={"invoice": bolt11},
timeout=40,
)
if decoded_request.is_error:
error_message = decoded_request.json()["message"]
return InvoiceResponse(False, None, None, error_message)
decoded_data = decoded_request.json()
checking_id = decoded_data["data"]["paymentHash"]
fee_msat = -int(data["data"]["fee"])
preimage = data["data"]["preimage"]
return PaymentResponse(True, checking_id, fee_msat, preimage, None) return PaymentResponse(True, checking_id, fee_msat, preimage, None)
async def get_invoice_status(self, checking_id: str) -> PaymentStatus: async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
return await self.get_payment_status(checking_id) r = await self.client.get(f"charges/{checking_id}")
if r.is_error:
return PaymentStatus(None)
data = r.json()["data"]
statuses = {
"pending": None,
"paid": True,
"unpaid": None,
"expired": False,
"completed": True,
}
return PaymentStatus(statuses[data.get("status")])
async def get_payment_status(self, checking_id: str) -> PaymentStatus: async def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = await self.client.get(f"/invoices/{checking_id}") r = await self.client.get(f"payments/{checking_id}")
if r.is_error: if r.is_error:
return PaymentStatus(None) return PaymentStatus(None)
data = r.json() data = r.json()["data"]
statuses = { statuses = {
"CREATED": None, "initial": None,
"SETTLED": True, "pending": None,
"completed": True,
"error": None,
"expired": False,
"failed": False,
} }
return PaymentStatus(statuses[data.get("state")], fee_msat=None, preimage=None)
return PaymentStatus(statuses[data.get("status")], fee_msat=None, preimage=None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
self.queue: asyncio.Queue = asyncio.Queue(0) self.queue: asyncio.Queue = asyncio.Queue(0)