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    /// NUT23 Error
56    #[error(transparent)]
57    NUT23(#[from] crate::nuts::nut23::Error),
58    /// Custom
59    #[error("`{0}`")]
60    Custom(String),
61}
62
63/// Mint payment trait
64#[async_trait]
65pub trait MintPayment {
66    /// Mint Lightning Error
67    type Err: Into<Error> + From<Error>;
68
69    /// Base Settings
70    async fn get_settings(&self) -> Result<serde_json::Value, Self::Err>;
71
72    /// Create a new invoice
73    async fn create_incoming_payment_request(
74        &self,
75        amount: Amount,
76        unit: &CurrencyUnit,
77        description: String,
78        unix_expiry: Option<u64>,
79    ) -> Result<CreateIncomingPaymentResponse, Self::Err>;
80
81    /// Get payment quote
82    /// Used to get fee and amount required for a payment request
83    async fn get_payment_quote(
84        &self,
85        request: &str,
86        unit: &CurrencyUnit,
87        options: Option<MeltOptions>,
88    ) -> Result<PaymentQuoteResponse, Self::Err>;
89
90    /// Pay request
91    async fn make_payment(
92        &self,
93        melt_quote: mint::MeltQuote,
94        partial_amount: Option<Amount>,
95        max_fee_amount: Option<Amount>,
96    ) -> Result<MakePaymentResponse, Self::Err>;
97
98    /// Listen for invoices to be paid to the mint
99    /// Returns a stream of request_lookup_id once invoices are paid
100    async fn wait_any_incoming_payment(
101        &self,
102    ) -> Result<Pin<Box<dyn Stream<Item = String> + Send>>, Self::Err>;
103
104    /// Is wait invoice active
105    fn is_wait_invoice_active(&self) -> bool;
106
107    /// Cancel wait invoice
108    fn cancel_wait_invoice(&self);
109
110    /// Check the status of an incoming payment
111    async fn check_incoming_payment_status(
112        &self,
113        request_lookup_id: &str,
114    ) -> Result<MintQuoteState, Self::Err>;
115
116    /// Check the status of an outgoing payment
117    async fn check_outgoing_payment(
118        &self,
119        request_lookup_id: &str,
120    ) -> Result<MakePaymentResponse, Self::Err>;
121}
122
123/// Create incoming payment response
124#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
125pub struct CreateIncomingPaymentResponse {
126    /// Id that is used to look up the payment from the ln backend
127    pub request_lookup_id: String,
128    /// Payment request
129    pub request: String,
130    /// Unix Expiry of Invoice
131    pub expiry: Option<u64>,
132}
133
134/// Payment response
135#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
136pub struct MakePaymentResponse {
137    /// Payment hash
138    pub payment_lookup_id: String,
139    /// Payment proof
140    pub payment_proof: Option<String>,
141    /// Status
142    pub status: MeltQuoteState,
143    /// Total Amount Spent
144    pub total_spent: Amount,
145    /// Unit of total spent
146    pub unit: CurrencyUnit,
147}
148
149/// Payment quote response
150#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
151pub struct PaymentQuoteResponse {
152    /// Request look up id
153    pub request_lookup_id: String,
154    /// Amount
155    pub amount: Amount,
156    /// Fee required for melt
157    pub fee: Amount,
158    /// Status
159    pub state: MeltQuoteState,
160}
161
162/// Ln backend settings
163#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
164pub struct Bolt11Settings {
165    /// MPP supported
166    pub mpp: bool,
167    /// Base unit of backend
168    pub unit: CurrencyUnit,
169    /// Invoice Description supported
170    pub invoice_description: bool,
171    /// Paying amountless invoices supported
172    pub amountless: bool,
173}
174
175impl TryFrom<Bolt11Settings> for Value {
176    type Error = crate::error::Error;
177
178    fn try_from(value: Bolt11Settings) -> Result<Self, Self::Error> {
179        serde_json::to_value(value).map_err(|err| err.into())
180    }
181}
182
183impl TryFrom<Value> for Bolt11Settings {
184    type Error = crate::error::Error;
185
186    fn try_from(value: Value) -> Result<Self, Self::Error> {
187        serde_json::from_value(value).map_err(|err| err.into())
188    }
189}