payment.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. //! CDK Mint Lightning
  2. use std::convert::Infallible;
  3. use std::pin::Pin;
  4. use async_trait::async_trait;
  5. use cashu::util::hex;
  6. use cashu::{Bolt11Invoice, MeltOptions};
  7. use futures::Stream;
  8. use lightning::offers::offer::Offer;
  9. use lightning_invoice::ParseOrSemanticError;
  10. use serde::{Deserialize, Serialize};
  11. use serde_json::Value;
  12. use thiserror::Error;
  13. use crate::mint::MeltPaymentRequest;
  14. use crate::nuts::{CurrencyUnit, MeltQuoteState};
  15. use crate::Amount;
  16. /// CDK Lightning Error
  17. #[derive(Debug, Error)]
  18. pub 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. /// Amount mismatch
  35. #[error("Amount is not what is expected")]
  36. AmountMismatch,
  37. /// Lightning Error
  38. #[error(transparent)]
  39. Lightning(Box<dyn std::error::Error + Send + Sync>),
  40. /// Serde Error
  41. #[error(transparent)]
  42. Serde(#[from] serde_json::Error),
  43. /// AnyHow Error
  44. #[error(transparent)]
  45. Anyhow(#[from] anyhow::Error),
  46. /// Parse Error
  47. #[error(transparent)]
  48. Parse(#[from] ParseOrSemanticError),
  49. /// Amount Error
  50. #[error(transparent)]
  51. Amount(#[from] crate::amount::Error),
  52. /// NUT04 Error
  53. #[error(transparent)]
  54. NUT04(#[from] crate::nuts::nut04::Error),
  55. /// NUT05 Error
  56. #[error(transparent)]
  57. NUT05(#[from] crate::nuts::nut05::Error),
  58. /// NUT23 Error
  59. #[error(transparent)]
  60. NUT23(#[from] crate::nuts::nut23::Error),
  61. /// Hex error
  62. #[error("Hex error")]
  63. Hex(#[from] hex::Error),
  64. /// Invalid hash
  65. #[error("Invalid hash")]
  66. InvalidHash,
  67. /// Custom
  68. #[error("`{0}`")]
  69. Custom(String),
  70. }
  71. impl From<Infallible> for Error {
  72. fn from(_: Infallible) -> Self {
  73. unreachable!("Infallible cannot be constructed")
  74. }
  75. }
  76. /// Payment identifier types
  77. #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
  78. #[serde(tag = "type", content = "value")]
  79. pub enum PaymentIdentifier {
  80. /// Label identifier
  81. Label(String),
  82. /// Offer ID identifier
  83. OfferId(String),
  84. /// Payment hash identifier
  85. PaymentHash([u8; 32]),
  86. /// Bolt12 payment hash
  87. Bolt12PaymentHash([u8; 32]),
  88. /// Custom Payment ID
  89. CustomId(String),
  90. }
  91. impl PaymentIdentifier {
  92. /// Create new [`PaymentIdentifier`]
  93. pub fn new(kind: &str, identifier: &str) -> Result<Self, Error> {
  94. match kind.to_lowercase().as_str() {
  95. "label" => Ok(Self::Label(identifier.to_string())),
  96. "offer_id" => Ok(Self::OfferId(identifier.to_string())),
  97. "payment_hash" => Ok(Self::PaymentHash(
  98. hex::decode(identifier)?
  99. .try_into()
  100. .map_err(|_| Error::InvalidHash)?,
  101. )),
  102. "bolt12_payment_hash" => Ok(Self::Bolt12PaymentHash(
  103. hex::decode(identifier)?
  104. .try_into()
  105. .map_err(|_| Error::InvalidHash)?,
  106. )),
  107. "custom" => Ok(Self::CustomId(identifier.to_string())),
  108. _ => Err(Error::UnsupportedPaymentOption),
  109. }
  110. }
  111. /// Payment id kind
  112. pub fn kind(&self) -> String {
  113. match self {
  114. Self::Label(_) => "label".to_string(),
  115. Self::OfferId(_) => "offer_id".to_string(),
  116. Self::PaymentHash(_) => "payment_hash".to_string(),
  117. Self::Bolt12PaymentHash(_) => "bolt12_payment_hash".to_string(),
  118. Self::CustomId(_) => "custom".to_string(),
  119. }
  120. }
  121. }
  122. impl std::fmt::Display for PaymentIdentifier {
  123. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  124. match self {
  125. Self::Label(l) => write!(f, "{l}"),
  126. Self::OfferId(o) => write!(f, "{o}"),
  127. Self::PaymentHash(h) => write!(f, "{}", hex::encode(h)),
  128. Self::Bolt12PaymentHash(h) => write!(f, "{}", hex::encode(h)),
  129. Self::CustomId(c) => write!(f, "{c}"),
  130. }
  131. }
  132. }
  133. /// Options for creating a BOLT11 incoming payment request
  134. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
  135. pub struct Bolt11IncomingPaymentOptions {
  136. /// Optional description for the payment request
  137. pub description: Option<String>,
  138. /// Amount for the payment request in sats
  139. pub amount: Amount,
  140. /// Optional expiry time as Unix timestamp in seconds
  141. pub unix_expiry: Option<u64>,
  142. }
  143. /// Options for creating a BOLT12 incoming payment request
  144. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
  145. pub struct Bolt12IncomingPaymentOptions {
  146. /// Optional description for the payment request
  147. pub description: Option<String>,
  148. /// Optional amount for the payment request in sats
  149. pub amount: Option<Amount>,
  150. /// Optional expiry time as Unix timestamp in seconds
  151. pub unix_expiry: Option<u64>,
  152. }
  153. /// Options for creating an incoming payment request
  154. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  155. pub enum IncomingPaymentOptions {
  156. /// BOLT11 payment request options
  157. Bolt11(Bolt11IncomingPaymentOptions),
  158. /// BOLT12 payment request options
  159. Bolt12(Box<Bolt12IncomingPaymentOptions>),
  160. }
  161. /// Options for BOLT11 outgoing payments
  162. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  163. pub struct Bolt11OutgoingPaymentOptions {
  164. /// Bolt11
  165. pub bolt11: Bolt11Invoice,
  166. /// Maximum fee amount allowed for the payment
  167. pub max_fee_amount: Option<Amount>,
  168. /// Optional timeout in seconds
  169. pub timeout_secs: Option<u64>,
  170. /// Melt options
  171. pub melt_options: Option<MeltOptions>,
  172. }
  173. /// Options for BOLT12 outgoing payments
  174. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  175. pub struct Bolt12OutgoingPaymentOptions {
  176. /// Offer
  177. pub offer: Offer,
  178. /// Maximum fee amount allowed for the payment
  179. pub max_fee_amount: Option<Amount>,
  180. /// Optional timeout in seconds
  181. pub timeout_secs: Option<u64>,
  182. /// Bolt12 Invoice
  183. pub invoice: Option<Vec<u8>>,
  184. /// Melt options
  185. pub melt_options: Option<MeltOptions>,
  186. }
  187. /// Options for creating an outgoing payment
  188. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  189. pub enum OutgoingPaymentOptions {
  190. /// BOLT11 payment options
  191. Bolt11(Box<Bolt11OutgoingPaymentOptions>),
  192. /// BOLT12 payment options
  193. Bolt12(Box<Bolt12OutgoingPaymentOptions>),
  194. }
  195. impl TryFrom<crate::mint::MeltQuote> for OutgoingPaymentOptions {
  196. type Error = Error;
  197. fn try_from(melt_quote: crate::mint::MeltQuote) -> Result<Self, Self::Error> {
  198. match melt_quote.request {
  199. MeltPaymentRequest::Bolt11 { bolt11 } => Ok(OutgoingPaymentOptions::Bolt11(Box::new(
  200. Bolt11OutgoingPaymentOptions {
  201. max_fee_amount: Some(melt_quote.fee_reserve),
  202. timeout_secs: None,
  203. bolt11,
  204. melt_options: melt_quote.options,
  205. },
  206. ))),
  207. MeltPaymentRequest::Bolt12 { offer, invoice } => {
  208. let melt_options = match melt_quote.options {
  209. None => None,
  210. Some(MeltOptions::Mpp { mpp: _ }) => return Err(Error::UnsupportedUnit),
  211. Some(options) => Some(options),
  212. };
  213. Ok(OutgoingPaymentOptions::Bolt12(Box::new(
  214. Bolt12OutgoingPaymentOptions {
  215. max_fee_amount: Some(melt_quote.fee_reserve),
  216. timeout_secs: None,
  217. offer: *offer,
  218. invoice,
  219. melt_options,
  220. },
  221. )))
  222. }
  223. }
  224. }
  225. }
  226. /// Mint payment trait
  227. #[async_trait]
  228. pub trait MintPayment {
  229. /// Mint Lightning Error
  230. type Err: Into<Error> + From<Error>;
  231. /// Base Settings
  232. async fn get_settings(&self) -> Result<serde_json::Value, Self::Err>;
  233. /// Create a new invoice
  234. async fn create_incoming_payment_request(
  235. &self,
  236. unit: &CurrencyUnit,
  237. options: IncomingPaymentOptions,
  238. ) -> Result<CreateIncomingPaymentResponse, Self::Err>;
  239. /// Get payment quote
  240. /// Used to get fee and amount required for a payment request
  241. async fn get_payment_quote(
  242. &self,
  243. unit: &CurrencyUnit,
  244. options: OutgoingPaymentOptions,
  245. ) -> Result<PaymentQuoteResponse, Self::Err>;
  246. /// Pay request
  247. async fn make_payment(
  248. &self,
  249. unit: &CurrencyUnit,
  250. options: OutgoingPaymentOptions,
  251. ) -> Result<MakePaymentResponse, Self::Err>;
  252. /// Listen for invoices to be paid to the mint
  253. /// Returns a stream of request_lookup_id once invoices are paid
  254. async fn wait_any_incoming_payment(
  255. &self,
  256. ) -> Result<Pin<Box<dyn Stream<Item = WaitPaymentResponse> + Send>>, Self::Err>;
  257. /// Is wait invoice active
  258. fn is_wait_invoice_active(&self) -> bool;
  259. /// Cancel wait invoice
  260. fn cancel_wait_invoice(&self);
  261. /// Check the status of an incoming payment
  262. async fn check_incoming_payment_status(
  263. &self,
  264. payment_identifier: &PaymentIdentifier,
  265. ) -> Result<Vec<WaitPaymentResponse>, Self::Err>;
  266. /// Check the status of an outgoing payment
  267. async fn check_outgoing_payment(
  268. &self,
  269. payment_identifier: &PaymentIdentifier,
  270. ) -> Result<MakePaymentResponse, Self::Err>;
  271. }
  272. /// Wait any invoice response
  273. #[derive(Debug, Clone, Hash, Serialize, Deserialize)]
  274. pub struct WaitPaymentResponse {
  275. /// Request look up id
  276. /// Id that relates the quote and payment request
  277. pub payment_identifier: PaymentIdentifier,
  278. /// Payment amount
  279. pub payment_amount: Amount,
  280. /// Unit
  281. pub unit: CurrencyUnit,
  282. /// Unique id of payment
  283. // Payment hash
  284. pub payment_id: String,
  285. }
  286. /// Create incoming payment response
  287. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  288. pub struct CreateIncomingPaymentResponse {
  289. /// Id that is used to look up the payment from the ln backend
  290. pub request_lookup_id: PaymentIdentifier,
  291. /// Payment request
  292. pub request: String,
  293. /// Unix Expiry of Invoice
  294. pub expiry: Option<u64>,
  295. }
  296. /// Payment response
  297. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  298. pub struct MakePaymentResponse {
  299. /// Payment hash
  300. pub payment_lookup_id: PaymentIdentifier,
  301. /// Payment proof
  302. pub payment_proof: Option<String>,
  303. /// Status
  304. pub status: MeltQuoteState,
  305. /// Total Amount Spent
  306. pub total_spent: Amount,
  307. /// Unit of total spent
  308. pub unit: CurrencyUnit,
  309. }
  310. /// Payment quote response
  311. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  312. pub struct PaymentQuoteResponse {
  313. /// Request look up id
  314. pub request_lookup_id: PaymentIdentifier,
  315. /// Amount
  316. pub amount: Amount,
  317. /// Fee required for melt
  318. pub fee: Amount,
  319. /// Currency unit of `amount` and `fee`
  320. pub unit: CurrencyUnit,
  321. /// Status
  322. pub state: MeltQuoteState,
  323. /// Payment Quote Options
  324. pub options: Option<PaymentQuoteOptions>,
  325. }
  326. /// Payment quote options
  327. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  328. pub enum PaymentQuoteOptions {
  329. /// Bolt12 payment options
  330. Bolt12 {
  331. /// Bolt12 invoice
  332. invoice: Option<Vec<u8>>,
  333. },
  334. }
  335. /// Ln backend settings
  336. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  337. pub struct Bolt11Settings {
  338. /// MPP supported
  339. pub mpp: bool,
  340. /// Base unit of backend
  341. pub unit: CurrencyUnit,
  342. /// Invoice Description supported
  343. pub invoice_description: bool,
  344. /// Paying amountless invoices supported
  345. pub amountless: bool,
  346. /// Bolt12 supported
  347. pub bolt12: bool,
  348. }
  349. impl TryFrom<Bolt11Settings> for Value {
  350. type Error = crate::error::Error;
  351. fn try_from(value: Bolt11Settings) -> Result<Self, Self::Error> {
  352. serde_json::to_value(value).map_err(|err| err.into())
  353. }
  354. }
  355. impl TryFrom<Value> for Bolt11Settings {
  356. type Error = crate::error::Error;
  357. fn try_from(value: Value) -> Result<Self, Self::Error> {
  358. serde_json::from_value(value).map_err(|err| err.into())
  359. }
  360. }