mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-20 14:14:49 +01:00
feat: check if invoice already paid on melt
feat: check if internal invoice already settled
This commit is contained in:
@@ -213,6 +213,12 @@ pub async fn post_melt_bolt11(
|
||||
|
||||
let (preimage, amount_spent_quote_unit) = match mint_quote {
|
||||
Some(mint_quote) => {
|
||||
if mint_quote.state == MintQuoteState::Issued
|
||||
|| mint_quote.state == MintQuoteState::Paid
|
||||
{
|
||||
return Err(into_response(Error::RequestAlreadyPaid));
|
||||
}
|
||||
|
||||
let mut mint_quote = mint_quote;
|
||||
|
||||
if mint_quote.amount > inputs_amount_quote_unit {
|
||||
@@ -320,7 +326,12 @@ pub async fn post_melt_bolt11(
|
||||
tracing::error!("Could not reset melt quote state: {}", err);
|
||||
}
|
||||
|
||||
return Err(into_response(Error::PaymentFailed));
|
||||
let err = match err {
|
||||
cdk::cdk_lightning::Error::InvoiceAlreadyPaid => Error::RequestAlreadyPaid,
|
||||
_ => Error::PaymentFailed,
|
||||
};
|
||||
|
||||
return Err(into_response(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Invoice amount not defined
|
||||
#[error("Unknown invoice amount")]
|
||||
UnknownInvoiceAmount,
|
||||
/// Wrong CLN response
|
||||
#[error("Wrong cln response")]
|
||||
WrongClnResponse,
|
||||
/// Unknown invoice
|
||||
#[error("Unknown invoice")]
|
||||
UnknownInvoice,
|
||||
/// Invoice amount not defined
|
||||
#[error("Unknown invoice amount")]
|
||||
UnknownInvoiceAmount,
|
||||
/// Cln Error
|
||||
#[error(transparent)]
|
||||
Cln(#[from] cln_rpc::Error),
|
||||
|
||||
@@ -15,9 +15,11 @@ use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteS
|
||||
use cdk::util::{hex, unix_time};
|
||||
use cdk::{mint, Bolt11Invoice};
|
||||
use cln_rpc::model::requests::{
|
||||
InvoiceRequest, ListinvoicesRequest, PayRequest, WaitanyinvoiceRequest,
|
||||
InvoiceRequest, ListinvoicesRequest, ListpaysRequest, PayRequest, WaitanyinvoiceRequest,
|
||||
};
|
||||
use cln_rpc::model::responses::{
|
||||
ListinvoicesInvoicesStatus, ListpaysPaysStatus, PayStatus, WaitanyinvoiceResponse,
|
||||
};
|
||||
use cln_rpc::model::responses::{ListinvoicesInvoicesStatus, PayStatus, WaitanyinvoiceResponse};
|
||||
use cln_rpc::model::Request;
|
||||
use cln_rpc::primitives::{Amount as CLN_Amount, AmountOrAny};
|
||||
use error::Error;
|
||||
@@ -131,6 +133,22 @@ impl MintLightning for Cln {
|
||||
max_fee_msats: Option<u64>,
|
||||
) -> Result<PayInvoiceResponse, Self::Err> {
|
||||
let mut cln_client = self.cln_client.lock().await;
|
||||
|
||||
let pay_state =
|
||||
check_pay_invoice_status(&mut cln_client, melt_quote.request.to_string()).await?;
|
||||
|
||||
match pay_state {
|
||||
MeltQuoteState::Paid => {
|
||||
tracing::debug!("Melt attempted on invoice already paid");
|
||||
return Err(Self::Err::InvoiceAlreadyPaid);
|
||||
}
|
||||
MeltQuoteState::Pending => {
|
||||
tracing::debug!("Melt attempted on invoice already pending");
|
||||
return Err(Self::Err::InvoicePaymentPending);
|
||||
}
|
||||
MeltQuoteState::Unpaid => (),
|
||||
}
|
||||
|
||||
let cln_response = cln_client
|
||||
.call(Request::Pay(PayRequest {
|
||||
bolt11: melt_quote.request.to_string(),
|
||||
@@ -190,7 +208,7 @@ impl MintLightning for Cln {
|
||||
.call(cln_rpc::Request::Invoice(InvoiceRequest {
|
||||
amount_msat,
|
||||
description,
|
||||
label: Uuid::new_v4().to_string(),
|
||||
label: label.clone(),
|
||||
expiry: Some(unix_expiry - time_now),
|
||||
fallbacks: None,
|
||||
preimage: None,
|
||||
@@ -293,3 +311,38 @@ fn cln_invoice_status_to_mint_state(status: ListinvoicesInvoicesStatus) -> MintQ
|
||||
ListinvoicesInvoicesStatus::EXPIRED => MintQuoteState::Unpaid,
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_pay_invoice_status(
|
||||
cln_client: &mut cln_rpc::ClnRpc,
|
||||
bolt11: String,
|
||||
) -> Result<MeltQuoteState, cdk_lightning::Error> {
|
||||
let cln_response = cln_client
|
||||
.call(Request::ListPays(ListpaysRequest {
|
||||
bolt11: Some(bolt11),
|
||||
payment_hash: None,
|
||||
status: None,
|
||||
}))
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
|
||||
let state = match cln_response {
|
||||
cln_rpc::Response::ListPays(pay_response) => {
|
||||
let pay = pay_response.pays.first();
|
||||
|
||||
match pay {
|
||||
Some(pay) => match pay.status {
|
||||
ListpaysPaysStatus::COMPLETE => MeltQuoteState::Paid,
|
||||
ListpaysPaysStatus::PENDING => MeltQuoteState::Pending,
|
||||
ListpaysPaysStatus::FAILED => MeltQuoteState::Unpaid,
|
||||
},
|
||||
None => MeltQuoteState::Unpaid,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!("CLN returned wrong response kind. When checking pay status");
|
||||
return Err(cdk_lightning::Error::from(Error::WrongClnResponse));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
@@ -202,11 +202,17 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
/// Update mint quote when called for a paid invoice
|
||||
async fn handle_paid_invoice(mint: Arc<Mint>, request_lookup_id: &str) -> Result<()> {
|
||||
tracing::debug!("Invoice with lookup id paid: {}", request_lookup_id);
|
||||
if let Ok(Some(mint_quote)) = mint
|
||||
.localstore
|
||||
.get_mint_quote_by_request_lookup_id(request_lookup_id)
|
||||
.await
|
||||
{
|
||||
tracing::debug!(
|
||||
"Quote {} paid by lookup id {}",
|
||||
mint_quote.id,
|
||||
request_lookup_id
|
||||
);
|
||||
mint.localstore
|
||||
.update_mint_quote_state(&mint_quote.id, cdk::nuts::MintQuoteState::Paid)
|
||||
.await?;
|
||||
|
||||
@@ -14,6 +14,12 @@ use crate::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuot
|
||||
/// CDK Lightning Error
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Invoice already paid
|
||||
#[error("Invoice already paid")]
|
||||
InvoiceAlreadyPaid,
|
||||
/// Invoice pay pending
|
||||
#[error("Invoice pay is pending")]
|
||||
InvoicePaymentPending,
|
||||
/// Lightning Error
|
||||
#[error(transparent)]
|
||||
Lightning(Box<dyn std::error::Error + Send + Sync>),
|
||||
|
||||
@@ -30,6 +30,9 @@ pub enum Error {
|
||||
/// Melt Request is not valid
|
||||
#[error("Melt request is not valid")]
|
||||
MeltRequestInvalid,
|
||||
/// Invoice already paid
|
||||
#[error("Request already paid")]
|
||||
RequestAlreadyPaid,
|
||||
/// Amount is not what expected
|
||||
#[error("Amount miss match")]
|
||||
Amount,
|
||||
|
||||
@@ -124,7 +124,21 @@ impl Mint {
|
||||
expiry: u64,
|
||||
ln_lookup: String,
|
||||
) -> Result<MintQuote, Error> {
|
||||
let quote = MintQuote::new(mint_url, request, unit, amount, expiry, ln_lookup);
|
||||
let quote = MintQuote::new(
|
||||
mint_url,
|
||||
request,
|
||||
unit.clone(),
|
||||
amount,
|
||||
expiry,
|
||||
ln_lookup.clone(),
|
||||
);
|
||||
tracing::debug!(
|
||||
"New mint quote {} for {} {} with request id {}",
|
||||
quote.id,
|
||||
amount,
|
||||
unit,
|
||||
&ln_lookup
|
||||
);
|
||||
|
||||
self.localstore.add_mint_quote(quote.clone()).await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user