cdk_common/
payment.rs

1//! CDK Mint Lightning
2
3use std::pin::Pin;
4
5use async_trait::async_trait;
6use cashu::MeltOptions;
7use futures::Stream;
8use lightning_invoice::ParseOrSemanticError;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use thiserror::Error;
12
13use crate::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState};
14use crate::{mint, Amount};
15
16/// CDK Lightning Error
17#[derive(Debug, Error)]
18pub enum Error {
19    /// Invoice already paid
20    #[error("Invoice already paid")]
21    InvoiceAlreadyPaid,
22    /// Invoice pay pending
23    #[error("Invoice pay is pending")]
24    InvoicePaymentPending,
25    /// Unsupported unit
26    #[error("Unsupported unit")]
27    UnsupportedUnit,
28    /// Unsupported payment option
29    #[error("Unsupported payment option")]
30    UnsupportedPaymentOption,
31    /// Payment state is unknown
32    #[error("Payment state is unknown")]
33    UnknownPaymentState,
34    /// Lightning Error
35    #[error(transparent)]
36    Lightning(Box<dyn std::error::Error + Send + Sync>),
37    /// Serde Error
38    #[error(transparent)]
39    Serde(#[from] serde_json::Error),
40    /// AnyHow Error
41    #[error(transparent)]
42    Anyhow(#[from] anyhow::Error),
43    /// Parse Error
44    #[error(transparent)]
45    Parse(#[from] ParseOrSemanticError),
46    /// Amount Error
47    #[error(transparent)]
48    Amount(#[from] crate::amount::Error),
49    /// NUT04 Error
50    #[error(transparent)]
51    NUT04(#[from] crate::nuts::nut04::Error),
52    /// NUT05 Error
53    #[error(transparent)]
54    NUT05(#[from] crate::nuts::nut05::Error),
55    /// Custom
56    #[error("`{0}`")]
57    Custom(String),
58}
59
60/// Mint payment trait
61#[async_trait]
62pub trait MintPayment {
63    /// Mint Lightning Error
64    type Err: Into<Error> + From<Error>;
65
66    /// Base Settings
67    async fn get_settings(&self) -> Result<serde_json::Value, Self::Err>;
68
69    /// Create a new invoice
70    async fn create_incoming_payment_request(
71        &self,
72        amount: Amount,
73        unit: &CurrencyUnit,
74        description: String,
75        unix_expiry: Option<u64>,
76    ) -> Result<CreateIncomingPaymentResponse, Self::Err>;
77
78    /// Get payment quote
79    /// Used to get fee and amount required for a payment request
80    async fn get_payment_quote(
81        &self,
82        request: &str,
83        unit: &CurrencyUnit,
84        options: Option<MeltOptions>,
85    ) -> Result<PaymentQuoteResponse, Self::Err>;
86
87    /// Pay request
88    async fn make_payment(
89        &self,
90        melt_quote: mint::MeltQuote,
91        partial_amount: Option<Amount>,
92        max_fee_amount: Option<Amount>,
93    ) -> Result<MakePaymentResponse, Self::Err>;
94
95    /// Listen for invoices to be paid to the mint
96    /// Returns a stream of request_lookup_id once invoices are paid
97    async fn wait_any_incoming_payment(
98        &self,
99    ) -> Result<Pin<Box<dyn Stream<Item = String> + Send>>, Self::Err>;
100
101    /// Is wait invoice active
102    fn is_wait_invoice_active(&self) -> bool;
103
104    /// Cancel wait invoice
105    fn cancel_wait_invoice(&self);
106
107    /// Check the status of an incoming payment
108    async fn check_incoming_payment_status(
109        &self,
110        request_lookup_id: &str,
111    ) -> Result<MintQuoteState, Self::Err>;
112
113    /// Check the status of an outgoing payment
114    async fn check_outgoing_payment(
115        &self,
116        request_lookup_id: &str,
117    ) -> Result<MakePaymentResponse, Self::Err>;
118}
119
120/// Create incoming payment response
121#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
122pub struct CreateIncomingPaymentResponse {
123    /// Id that is used to look up the payment from the ln backend
124    pub request_lookup_id: String,
125    /// Payment request
126    pub request: String,
127    /// Unix Expiry of Invoice
128    pub expiry: Option<u64>,
129}
130
131/// Payment response
132#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
133pub struct MakePaymentResponse {
134    /// Payment hash
135    pub payment_lookup_id: String,
136    /// Payment proof
137    pub payment_proof: Option<String>,
138    /// Status
139    pub status: MeltQuoteState,
140    /// Total Amount Spent
141    pub total_spent: Amount,
142    /// Unit of total spent
143    pub unit: CurrencyUnit,
144}
145
146/// Payment quote response
147#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
148pub struct PaymentQuoteResponse {
149    /// Request look up id
150    pub request_lookup_id: String,
151    /// Amount
152    pub amount: Amount,
153    /// Fee required for melt
154    pub fee: Amount,
155    /// Status
156    pub state: MeltQuoteState,
157}
158
159/// Ln backend settings
160#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
161pub struct Bolt11Settings {
162    /// MPP supported
163    pub mpp: bool,
164    /// Base unit of backend
165    pub unit: CurrencyUnit,
166    /// Invoice Description supported
167    pub invoice_description: bool,
168}
169
170impl TryFrom<Bolt11Settings> for Value {
171    type Error = crate::error::Error;
172
173    fn try_from(value: Bolt11Settings) -> Result<Self, Self::Error> {
174        serde_json::to_value(value).map_err(|err| err.into())
175    }
176}
177
178impl TryFrom<Value> for Bolt11Settings {
179    type Error = crate::error::Error;
180
181    fn try_from(value: Value) -> Result<Self, Self::Error> {
182        serde_json::from_value(value).map_err(|err| err.into())
183    }
184}