Update ln-bits to support v1 api (#802)

* feat: v1 websockets

* chore: update lnbits dep
This commit is contained in:
thesimplekid
2025-06-12 08:40:49 +01:00
committed by GitHub
parent edae2abaf2
commit a335b269b7
5 changed files with 91 additions and 77 deletions

View File

@@ -23,12 +23,10 @@ use cdk_common::payment::{
use cdk_common::util::unix_time;
use cdk_common::{mint, Bolt11Invoice};
use error::Error;
use futures::stream::StreamExt;
use futures::Stream;
use lnbits_rs::api::invoice::CreateInvoiceRequest;
use lnbits_rs::LNBitsClient;
use serde_json::Value;
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
pub mod error;
@@ -38,8 +36,7 @@ pub mod error;
pub struct LNbits {
lnbits_api: LNBitsClient,
fee_reserve: FeeReserve,
receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
webhook_url: String,
webhook_url: Option<String>,
wait_invoice_cancel_token: CancellationToken,
wait_invoice_is_active: Arc<AtomicBool>,
settings: Bolt11Settings,
@@ -53,14 +50,12 @@ impl LNbits {
invoice_api_key: String,
api_url: String,
fee_reserve: FeeReserve,
receiver: Arc<Mutex<Option<tokio::sync::mpsc::Receiver<String>>>>,
webhook_url: String,
webhook_url: Option<String>,
) -> Result<Self, Error> {
let lnbits_api = LNBitsClient::new("", &admin_api_key, &invoice_api_key, &api_url, None)?;
Ok(Self {
lnbits_api,
receiver,
fee_reserve,
webhook_url,
wait_invoice_cancel_token: CancellationToken::new(),
@@ -73,6 +68,17 @@ impl LNbits {
},
})
}
/// Subscribe to lnbits ws
pub async fn subscribe_ws(&self) -> Result<(), Error> {
self.lnbits_api
.subscribe_to_websocket()
.await
.map_err(|err| {
tracing::error!("Could not subscribe to lnbits ws");
Error::Anyhow(err)
})
}
}
#[async_trait]
@@ -94,61 +100,50 @@ impl MintPayment for LNbits {
async fn wait_any_incoming_payment(
&self,
) -> Result<Pin<Box<dyn Stream<Item = String> + Send>>, Self::Err> {
let receiver = self
.receiver
.lock()
.await
.take()
.ok_or(anyhow!("No receiver"))?;
let lnbits_api = self.lnbits_api.clone();
let api = self.lnbits_api.clone();
let cancel_token = self.wait_invoice_cancel_token.clone();
let is_active = Arc::clone(&self.wait_invoice_is_active);
Ok(futures::stream::unfold(
(
receiver,
lnbits_api,
cancel_token,
Arc::clone(&self.wait_invoice_is_active),
),
|(mut receiver, lnbits_api, cancel_token, is_active)| async move {
Ok(Box::pin(futures::stream::unfold(
(api, cancel_token, is_active),
|(api, cancel_token, is_active)| async move {
is_active.store(true, Ordering::SeqCst);
let receiver = api.receiver();
let mut receiver = receiver.lock().await;
tokio::select! {
_ = cancel_token.cancelled() => {
// Stream is cancelled
is_active.store(false, Ordering::SeqCst);
tracing::info!("Waiting for phonixd invoice ending");
tracing::info!("Waiting for lnbits invoice ending");
None
}
msg_option = receiver.recv() => {
match msg_option {
Some(msg) => {
let check = lnbits_api.is_invoice_paid(&msg).await;
match msg_option {
Some(msg) => {
let check = api.is_invoice_paid(&msg).await;
match check {
Ok(state) => {
if state {
Some((msg, (receiver, lnbits_api, cancel_token, is_active)))
} else {
None
match check {
Ok(state) => {
if state {
Some((msg, (api, cancel_token, is_active)))
} else {
Some(("".to_string(), (api, cancel_token, is_active)))
}
}
_ => Some(("".to_string(), (api, cancel_token, is_active))),
}
_ => None,
}
None => {
is_active.store(false, Ordering::SeqCst);
None
},
}
None => {
is_active.store(true, Ordering::SeqCst);
None
},
}
}
}
},
)
.boxed())
)))
}
async fn get_payment_quote(
@@ -211,7 +206,7 @@ impl MintPayment for LNbits {
let invoice_info = self
.lnbits_api
.find_invoice(&pay_response.payment_hash)
.get_payment_info(&pay_response.payment_hash)
.await
.map_err(|err| {
tracing::error!("Could not find invoice");
@@ -219,22 +214,23 @@ impl MintPayment for LNbits {
Self::Err::Anyhow(anyhow!("Could not find invoice"))
})?;
let status = match invoice_info.pending {
true => MeltQuoteState::Unpaid,
false => MeltQuoteState::Paid,
let status = match invoice_info.paid {
true => MeltQuoteState::Paid,
false => MeltQuoteState::Unpaid,
};
let total_spent = Amount::from(
(invoice_info
.details
.amount
.checked_add(invoice_info.fee)
.checked_add(invoice_info.details.fee)
.ok_or(Error::AmountOverflow)?)
.unsigned_abs(),
);
Ok(MakePaymentResponse {
payment_lookup_id: pay_response.payment_hash,
payment_proof: Some(invoice_info.payment_hash),
payment_proof: invoice_info.details.preimage,
status,
total_spent,
unit: CurrencyUnit::Sat,
@@ -261,7 +257,7 @@ impl MintPayment for LNbits {
memo: Some(description),
unit: unit.to_string(),
expiry,
webhook: Some(self.webhook_url.clone()),
webhook: self.webhook_url.clone(),
internal: None,
out: false,
};
@@ -283,7 +279,7 @@ impl MintPayment for LNbits {
let expiry = request.expires_at().map(|t| t.as_secs());
Ok(CreateIncomingPaymentResponse {
request_lookup_id: create_invoice_response.payment_hash,
request_lookup_id: create_invoice_response.payment_hash().to_string(),
request: request.to_string(),
expiry,
})
@@ -327,7 +323,7 @@ impl MintPayment for LNbits {
let pay_response = MakePaymentResponse {
payment_lookup_id: payment.details.payment_hash,
payment_proof: Some(payment.preimage),
payment_proof: payment.preimage,
status: lnbits_to_melt_status(&payment.details.status, payment.details.pending),
total_spent: Amount::from(
payment.details.amount.unsigned_abs()
@@ -340,12 +336,16 @@ impl MintPayment for LNbits {
}
}
fn lnbits_to_melt_status(status: &str, pending: bool) -> MeltQuoteState {
match (status, pending) {
("success", false) => MeltQuoteState::Paid,
("failed", false) => MeltQuoteState::Unpaid,
(_, false) => MeltQuoteState::Unknown,
(_, true) => MeltQuoteState::Pending,
fn lnbits_to_melt_status(status: &str, pending: Option<bool>) -> MeltQuoteState {
if pending.unwrap_or_default() {
return MeltQuoteState::Pending;
}
match status {
"success" => MeltQuoteState::Paid,
"failed" => MeltQuoteState::Unpaid,
"pending" => MeltQuoteState::Pending,
_ => MeltQuoteState::Unknown,
}
}
@@ -354,10 +354,9 @@ impl LNbits {
pub async fn create_invoice_webhook_router(
&self,
webhook_endpoint: &str,
sender: tokio::sync::mpsc::Sender<String>,
) -> anyhow::Result<Router> {
self.lnbits_api
.create_invoice_webhook_router(webhook_endpoint, sender)
.create_invoice_webhook_router(webhook_endpoint)
.await
}
}