payment.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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. #[cfg(feature = "prometheus")]
  8. use cdk_prometheus::METRICS;
  9. use futures::Stream;
  10. use lightning::offers::offer::Offer;
  11. use lightning_invoice::ParseOrSemanticError;
  12. use serde::{Deserialize, Serialize};
  13. use serde_json::Value;
  14. use thiserror::Error;
  15. use crate::mint::MeltPaymentRequest;
  16. use crate::nuts::{CurrencyUnit, MeltQuoteState};
  17. use crate::Amount;
  18. /// CDK Lightning Error
  19. #[derive(Debug, Error)]
  20. pub enum Error {
  21. /// Invoice already paid
  22. #[error("Invoice already paid")]
  23. InvoiceAlreadyPaid,
  24. /// Invoice pay pending
  25. #[error("Invoice pay is pending")]
  26. InvoicePaymentPending,
  27. /// Unsupported unit
  28. #[error("Unsupported unit")]
  29. UnsupportedUnit,
  30. /// Unsupported payment option
  31. #[error("Unsupported payment option")]
  32. UnsupportedPaymentOption,
  33. /// Payment state is unknown
  34. #[error("Payment state is unknown")]
  35. UnknownPaymentState,
  36. /// Amount mismatch
  37. #[error("Amount is not what is expected")]
  38. AmountMismatch,
  39. /// Lightning Error
  40. #[error(transparent)]
  41. Lightning(Box<dyn std::error::Error + Send + Sync>),
  42. /// Serde Error
  43. #[error(transparent)]
  44. Serde(#[from] serde_json::Error),
  45. /// AnyHow Error
  46. #[error(transparent)]
  47. Anyhow(#[from] anyhow::Error),
  48. /// Parse Error
  49. #[error(transparent)]
  50. Parse(#[from] ParseOrSemanticError),
  51. /// Amount Error
  52. #[error(transparent)]
  53. Amount(#[from] crate::amount::Error),
  54. /// NUT04 Error
  55. #[error(transparent)]
  56. NUT04(#[from] crate::nuts::nut04::Error),
  57. /// NUT05 Error
  58. #[error(transparent)]
  59. NUT05(#[from] crate::nuts::nut05::Error),
  60. /// NUT23 Error
  61. #[error(transparent)]
  62. NUT23(#[from] crate::nuts::nut23::Error),
  63. /// Hex error
  64. #[error("Hex error")]
  65. Hex(#[from] hex::Error),
  66. /// Invalid hash
  67. #[error("Invalid hash")]
  68. InvalidHash,
  69. /// Custom
  70. #[error("`{0}`")]
  71. Custom(String),
  72. }
  73. impl From<Infallible> for Error {
  74. fn from(_: Infallible) -> Self {
  75. unreachable!("Infallible cannot be constructed")
  76. }
  77. }
  78. /// Payment identifier types
  79. #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
  80. #[serde(tag = "type", content = "value")]
  81. pub enum PaymentIdentifier {
  82. /// Label identifier
  83. Label(String),
  84. /// Offer ID identifier
  85. OfferId(String),
  86. /// Payment hash identifier
  87. PaymentHash([u8; 32]),
  88. /// Bolt12 payment hash
  89. Bolt12PaymentHash([u8; 32]),
  90. /// Payment id
  91. PaymentId([u8; 32]),
  92. /// Custom Payment ID
  93. CustomId(String),
  94. }
  95. impl PaymentIdentifier {
  96. /// Create new [`PaymentIdentifier`]
  97. pub fn new(kind: &str, identifier: &str) -> Result<Self, Error> {
  98. match kind.to_lowercase().as_str() {
  99. "label" => Ok(Self::Label(identifier.to_string())),
  100. "offer_id" => Ok(Self::OfferId(identifier.to_string())),
  101. "payment_hash" => Ok(Self::PaymentHash(
  102. hex::decode(identifier)?
  103. .try_into()
  104. .map_err(|_| Error::InvalidHash)?,
  105. )),
  106. "bolt12_payment_hash" => Ok(Self::Bolt12PaymentHash(
  107. hex::decode(identifier)?
  108. .try_into()
  109. .map_err(|_| Error::InvalidHash)?,
  110. )),
  111. "custom" => Ok(Self::CustomId(identifier.to_string())),
  112. "payment_id" => Ok(Self::PaymentId(
  113. hex::decode(identifier)?
  114. .try_into()
  115. .map_err(|_| Error::InvalidHash)?,
  116. )),
  117. _ => Err(Error::UnsupportedPaymentOption),
  118. }
  119. }
  120. /// Payment id kind
  121. pub fn kind(&self) -> String {
  122. match self {
  123. Self::Label(_) => "label".to_string(),
  124. Self::OfferId(_) => "offer_id".to_string(),
  125. Self::PaymentHash(_) => "payment_hash".to_string(),
  126. Self::Bolt12PaymentHash(_) => "bolt12_payment_hash".to_string(),
  127. Self::PaymentId(_) => "payment_id".to_string(),
  128. Self::CustomId(_) => "custom".to_string(),
  129. }
  130. }
  131. }
  132. impl std::fmt::Display for PaymentIdentifier {
  133. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  134. match self {
  135. Self::Label(l) => write!(f, "{l}"),
  136. Self::OfferId(o) => write!(f, "{o}"),
  137. Self::PaymentHash(h) => write!(f, "{}", hex::encode(h)),
  138. Self::Bolt12PaymentHash(h) => write!(f, "{}", hex::encode(h)),
  139. Self::PaymentId(h) => write!(f, "{}", hex::encode(h)),
  140. Self::CustomId(c) => write!(f, "{c}"),
  141. }
  142. }
  143. }
  144. /// Options for creating a BOLT11 incoming payment request
  145. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
  146. pub struct Bolt11IncomingPaymentOptions {
  147. /// Optional description for the payment request
  148. pub description: Option<String>,
  149. /// Amount for the payment request in sats
  150. pub amount: Amount,
  151. /// Optional expiry time as Unix timestamp in seconds
  152. pub unix_expiry: Option<u64>,
  153. }
  154. /// Options for creating a BOLT12 incoming payment request
  155. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
  156. pub struct Bolt12IncomingPaymentOptions {
  157. /// Optional description for the payment request
  158. pub description: Option<String>,
  159. /// Optional amount for the payment request in sats
  160. pub amount: Option<Amount>,
  161. /// Optional expiry time as Unix timestamp in seconds
  162. pub unix_expiry: Option<u64>,
  163. }
  164. /// Options for creating a custom incoming payment request
  165. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  166. pub struct CustomIncomingPaymentOptions {
  167. /// Payment method name (e.g., "paypal", "venmo")
  168. pub method: String,
  169. /// Optional description for the payment request
  170. pub description: Option<String>,
  171. /// Amount for the payment request
  172. pub amount: Amount,
  173. /// Optional expiry time as Unix timestamp in seconds
  174. pub unix_expiry: Option<u64>,
  175. /// Extra payment-method-specific fields as JSON string
  176. ///
  177. /// These fields are passed through to the payment processor for
  178. /// method-specific validation (e.g., ehash share).
  179. pub extra_json: Option<String>,
  180. }
  181. /// Options for creating an incoming payment request
  182. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  183. pub enum IncomingPaymentOptions {
  184. /// BOLT11 payment request options
  185. Bolt11(Bolt11IncomingPaymentOptions),
  186. /// BOLT12 payment request options
  187. Bolt12(Box<Bolt12IncomingPaymentOptions>),
  188. /// Custom payment method options
  189. Custom(Box<CustomIncomingPaymentOptions>),
  190. }
  191. /// Options for BOLT11 outgoing payments
  192. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  193. pub struct Bolt11OutgoingPaymentOptions {
  194. /// Bolt11
  195. pub bolt11: Bolt11Invoice,
  196. /// Maximum fee amount allowed for the payment
  197. pub max_fee_amount: Option<Amount>,
  198. /// Optional timeout in seconds
  199. pub timeout_secs: Option<u64>,
  200. /// Melt options
  201. pub melt_options: Option<MeltOptions>,
  202. }
  203. /// Options for BOLT12 outgoing payments
  204. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  205. pub struct Bolt12OutgoingPaymentOptions {
  206. /// Offer
  207. pub offer: Offer,
  208. /// Maximum fee amount allowed for the payment
  209. pub max_fee_amount: Option<Amount>,
  210. /// Optional timeout in seconds
  211. pub timeout_secs: Option<u64>,
  212. /// Melt options
  213. pub melt_options: Option<MeltOptions>,
  214. }
  215. /// Options for custom outgoing payments
  216. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  217. pub struct CustomOutgoingPaymentOptions {
  218. /// Payment method name
  219. pub method: String,
  220. /// Payment request string (method-specific format)
  221. pub request: String,
  222. /// Maximum fee amount allowed for the payment
  223. pub max_fee_amount: Option<Amount>,
  224. /// Optional timeout in seconds
  225. pub timeout_secs: Option<u64>,
  226. /// Melt options
  227. pub melt_options: Option<MeltOptions>,
  228. /// Extra payment-method-specific fields as JSON string
  229. ///
  230. /// These fields are passed through to the payment processor for
  231. /// method-specific validation.
  232. pub extra_json: Option<String>,
  233. }
  234. /// Options for creating an outgoing payment
  235. #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  236. pub enum OutgoingPaymentOptions {
  237. /// BOLT11 payment options
  238. Bolt11(Box<Bolt11OutgoingPaymentOptions>),
  239. /// BOLT12 payment options
  240. Bolt12(Box<Bolt12OutgoingPaymentOptions>),
  241. /// Custom payment method options
  242. Custom(Box<CustomOutgoingPaymentOptions>),
  243. }
  244. impl TryFrom<crate::mint::MeltQuote> for OutgoingPaymentOptions {
  245. type Error = Error;
  246. fn try_from(melt_quote: crate::mint::MeltQuote) -> Result<Self, Self::Error> {
  247. match melt_quote.request {
  248. MeltPaymentRequest::Bolt11 { bolt11 } => Ok(OutgoingPaymentOptions::Bolt11(Box::new(
  249. Bolt11OutgoingPaymentOptions {
  250. max_fee_amount: Some(melt_quote.fee_reserve),
  251. timeout_secs: None,
  252. bolt11,
  253. melt_options: melt_quote.options,
  254. },
  255. ))),
  256. MeltPaymentRequest::Bolt12 { offer } => {
  257. let melt_options = match melt_quote.options {
  258. None => None,
  259. Some(MeltOptions::Mpp { mpp: _ }) => return Err(Error::UnsupportedUnit),
  260. Some(options) => Some(options),
  261. };
  262. Ok(OutgoingPaymentOptions::Bolt12(Box::new(
  263. Bolt12OutgoingPaymentOptions {
  264. max_fee_amount: Some(melt_quote.fee_reserve),
  265. timeout_secs: None,
  266. offer: *offer,
  267. melt_options,
  268. },
  269. )))
  270. }
  271. MeltPaymentRequest::Custom { method, request } => Ok(OutgoingPaymentOptions::Custom(
  272. Box::new(CustomOutgoingPaymentOptions {
  273. method,
  274. request,
  275. max_fee_amount: Some(melt_quote.fee_reserve),
  276. timeout_secs: None,
  277. melt_options: melt_quote.options,
  278. extra_json: None,
  279. }),
  280. )),
  281. }
  282. }
  283. }
  284. /// Mint payment trait
  285. #[async_trait]
  286. pub trait MintPayment {
  287. /// Mint Lightning Error
  288. type Err: Into<Error> + From<Error>;
  289. /// Start the payment processor
  290. /// Called when the mint starts up to initialize the payment processor
  291. async fn start(&self) -> Result<(), Self::Err> {
  292. // Default implementation - do nothing
  293. Ok(())
  294. }
  295. /// Stop the payment processor
  296. /// Called when the mint shuts down to gracefully stop the payment processor
  297. async fn stop(&self) -> Result<(), Self::Err> {
  298. // Default implementation - do nothing
  299. Ok(())
  300. }
  301. /// Base Settings
  302. async fn get_settings(&self) -> Result<SettingsResponse, Self::Err>;
  303. /// Create a new invoice
  304. async fn create_incoming_payment_request(
  305. &self,
  306. unit: &CurrencyUnit,
  307. options: IncomingPaymentOptions,
  308. ) -> Result<CreateIncomingPaymentResponse, Self::Err>;
  309. /// Get payment quote
  310. /// Used to get fee and amount required for a payment request
  311. async fn get_payment_quote(
  312. &self,
  313. unit: &CurrencyUnit,
  314. options: OutgoingPaymentOptions,
  315. ) -> Result<PaymentQuoteResponse, Self::Err>;
  316. /// Pay request
  317. async fn make_payment(
  318. &self,
  319. unit: &CurrencyUnit,
  320. options: OutgoingPaymentOptions,
  321. ) -> Result<MakePaymentResponse, Self::Err>;
  322. /// Listen for invoices to be paid to the mint
  323. /// Returns a stream of request_lookup_id once invoices are paid
  324. async fn wait_payment_event(
  325. &self,
  326. ) -> Result<Pin<Box<dyn Stream<Item = Event> + Send>>, Self::Err>;
  327. /// Is wait invoice active
  328. fn is_wait_invoice_active(&self) -> bool;
  329. /// Cancel wait invoice
  330. fn cancel_wait_invoice(&self);
  331. /// Check the status of an incoming payment
  332. async fn check_incoming_payment_status(
  333. &self,
  334. payment_identifier: &PaymentIdentifier,
  335. ) -> Result<Vec<WaitPaymentResponse>, Self::Err>;
  336. /// Check the status of an outgoing payment
  337. async fn check_outgoing_payment(
  338. &self,
  339. payment_identifier: &PaymentIdentifier,
  340. ) -> Result<MakePaymentResponse, Self::Err>;
  341. }
  342. /// An event emitted which should be handled by the mint
  343. #[derive(Debug, Clone, Hash)]
  344. pub enum Event {
  345. /// A payment has been received.
  346. PaymentReceived(WaitPaymentResponse),
  347. }
  348. impl Default for Event {
  349. fn default() -> Self {
  350. // We use this as a sentinel value for no-op events
  351. // The actual processing will filter these out
  352. Event::PaymentReceived(WaitPaymentResponse {
  353. payment_identifier: PaymentIdentifier::CustomId("default".to_string()),
  354. payment_amount: Amount::from(0),
  355. unit: CurrencyUnit::Msat,
  356. payment_id: "default".to_string(),
  357. })
  358. }
  359. }
  360. /// Wait any invoice response
  361. #[derive(Debug, Clone, Hash, Serialize, Deserialize)]
  362. pub struct WaitPaymentResponse {
  363. /// Request look up id
  364. /// Id that relates the quote and payment request
  365. pub payment_identifier: PaymentIdentifier,
  366. /// Payment amount
  367. pub payment_amount: Amount,
  368. /// Unit
  369. pub unit: CurrencyUnit,
  370. /// Unique id of payment
  371. // Payment hash
  372. pub payment_id: String,
  373. }
  374. /// Create incoming payment response
  375. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  376. pub struct CreateIncomingPaymentResponse {
  377. /// Id that is used to look up the payment from the ln backend
  378. pub request_lookup_id: PaymentIdentifier,
  379. /// Payment request
  380. pub request: String,
  381. /// Unix Expiry of Invoice
  382. pub expiry: Option<u64>,
  383. /// Extra payment-method-specific fields
  384. ///
  385. /// These fields are flattened into the JSON representation, allowing
  386. /// custom payment methods to include additional data without nesting.
  387. #[serde(flatten, default)]
  388. pub extra_json: Option<serde_json::Value>,
  389. }
  390. /// Payment response
  391. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  392. pub struct MakePaymentResponse {
  393. /// Payment hash
  394. pub payment_lookup_id: PaymentIdentifier,
  395. /// Payment proof
  396. pub payment_proof: Option<String>,
  397. /// Status
  398. pub status: MeltQuoteState,
  399. /// Total Amount Spent
  400. pub total_spent: Amount,
  401. /// Unit of total spent
  402. pub unit: CurrencyUnit,
  403. }
  404. /// Payment quote response
  405. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  406. pub struct PaymentQuoteResponse {
  407. /// Request look up id
  408. pub request_lookup_id: Option<PaymentIdentifier>,
  409. /// Amount
  410. pub amount: Amount,
  411. /// Fee required for melt
  412. pub fee: Amount,
  413. /// Currency unit of `amount` and `fee`
  414. pub unit: CurrencyUnit,
  415. /// Status
  416. pub state: MeltQuoteState,
  417. }
  418. /// BOLT11 settings
  419. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
  420. pub struct Bolt11Settings {
  421. /// Multi-part payment (MPP) supported
  422. pub mpp: bool,
  423. /// Amountless invoice support
  424. pub amountless: bool,
  425. /// Invoice description supported
  426. pub invoice_description: bool,
  427. }
  428. /// BOLT12 settings
  429. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
  430. pub struct Bolt12Settings {
  431. /// Amountless offer support
  432. pub amountless: bool,
  433. }
  434. /// Payment processor settings response
  435. /// Mirrors the proto SettingsResponse structure
  436. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  437. pub struct SettingsResponse {
  438. /// Base unit of backend
  439. pub unit: String,
  440. /// BOLT11 settings (None if not supported)
  441. pub bolt11: Option<Bolt11Settings>,
  442. /// BOLT12 settings (None if not supported)
  443. pub bolt12: Option<Bolt12Settings>,
  444. /// Custom payment methods settings (method name -> settings data)
  445. #[serde(default)]
  446. pub custom: std::collections::HashMap<String, String>,
  447. }
  448. impl From<SettingsResponse> for Value {
  449. fn from(value: SettingsResponse) -> Self {
  450. serde_json::to_value(value).unwrap_or(Value::Null)
  451. }
  452. }
  453. impl TryFrom<Value> for SettingsResponse {
  454. type Error = crate::error::Error;
  455. fn try_from(value: Value) -> Result<Self, Self::Error> {
  456. serde_json::from_value(value).map_err(|err| err.into())
  457. }
  458. }
  459. /// Metrics wrapper for MintPayment implementations
  460. ///
  461. /// This wrapper implements the Decorator pattern to collect metrics on all
  462. /// MintPayment trait methods. It wraps any existing MintPayment implementation
  463. /// and automatically records timing and operation metrics.
  464. #[derive(Clone)]
  465. #[cfg(feature = "prometheus")]
  466. pub struct MetricsMintPayment<T> {
  467. inner: T,
  468. }
  469. #[cfg(feature = "prometheus")]
  470. impl<T> MetricsMintPayment<T>
  471. where
  472. T: MintPayment,
  473. {
  474. /// Create a new metrics wrapper around a MintPayment implementation
  475. pub fn new(inner: T) -> Self {
  476. Self { inner }
  477. }
  478. /// Get reference to the underlying implementation
  479. pub fn inner(&self) -> &T {
  480. &self.inner
  481. }
  482. /// Consume the wrapper and return the inner implementation
  483. pub fn into_inner(self) -> T {
  484. self.inner
  485. }
  486. }
  487. #[async_trait]
  488. #[cfg(feature = "prometheus")]
  489. impl<T> MintPayment for MetricsMintPayment<T>
  490. where
  491. T: MintPayment + Send + Sync,
  492. {
  493. type Err = T::Err;
  494. async fn get_settings(&self) -> Result<SettingsResponse, Self::Err> {
  495. let start = std::time::Instant::now();
  496. METRICS.inc_in_flight_requests("get_settings");
  497. let result = self.inner.get_settings().await;
  498. let duration = start.elapsed().as_secs_f64();
  499. METRICS.record_mint_operation_histogram("get_settings", result.is_ok(), duration);
  500. METRICS.dec_in_flight_requests("get_settings");
  501. result
  502. }
  503. async fn create_incoming_payment_request(
  504. &self,
  505. unit: &CurrencyUnit,
  506. options: IncomingPaymentOptions,
  507. ) -> Result<CreateIncomingPaymentResponse, Self::Err> {
  508. let start = std::time::Instant::now();
  509. METRICS.inc_in_flight_requests("create_incoming_payment_request");
  510. let result = self
  511. .inner
  512. .create_incoming_payment_request(unit, options)
  513. .await;
  514. let duration = start.elapsed().as_secs_f64();
  515. METRICS.record_mint_operation_histogram(
  516. "create_incoming_payment_request",
  517. result.is_ok(),
  518. duration,
  519. );
  520. METRICS.dec_in_flight_requests("create_incoming_payment_request");
  521. result
  522. }
  523. async fn get_payment_quote(
  524. &self,
  525. unit: &CurrencyUnit,
  526. options: OutgoingPaymentOptions,
  527. ) -> Result<PaymentQuoteResponse, Self::Err> {
  528. let start = std::time::Instant::now();
  529. METRICS.inc_in_flight_requests("get_payment_quote");
  530. let result = self.inner.get_payment_quote(unit, options).await;
  531. let duration = start.elapsed().as_secs_f64();
  532. let success = result.is_ok();
  533. if let Ok(ref quote) = result {
  534. let amount: f64 = u64::from(quote.amount) as f64;
  535. let fee: f64 = u64::from(quote.fee) as f64;
  536. METRICS.record_lightning_payment(amount, fee);
  537. }
  538. METRICS.record_mint_operation_histogram("get_payment_quote", success, duration);
  539. METRICS.dec_in_flight_requests("get_payment_quote");
  540. result
  541. }
  542. async fn wait_payment_event(
  543. &self,
  544. ) -> Result<Pin<Box<dyn Stream<Item = Event> + Send>>, Self::Err> {
  545. let start = std::time::Instant::now();
  546. METRICS.inc_in_flight_requests("wait_payment_event");
  547. let result = self.inner.wait_payment_event().await;
  548. let duration = start.elapsed().as_secs_f64();
  549. let success = result.is_ok();
  550. METRICS.record_mint_operation_histogram("wait_payment_event", success, duration);
  551. METRICS.dec_in_flight_requests("wait_payment_event");
  552. result
  553. }
  554. async fn make_payment(
  555. &self,
  556. unit: &CurrencyUnit,
  557. options: OutgoingPaymentOptions,
  558. ) -> Result<MakePaymentResponse, Self::Err> {
  559. let start = std::time::Instant::now();
  560. METRICS.inc_in_flight_requests("make_payment");
  561. let result = self.inner.make_payment(unit, options).await;
  562. let duration = start.elapsed().as_secs_f64();
  563. let success = result.is_ok();
  564. METRICS.record_mint_operation_histogram("make_payment", success, duration);
  565. METRICS.dec_in_flight_requests("make_payment");
  566. result
  567. }
  568. fn is_wait_invoice_active(&self) -> bool {
  569. self.inner.is_wait_invoice_active()
  570. }
  571. fn cancel_wait_invoice(&self) {
  572. self.inner.cancel_wait_invoice()
  573. }
  574. async fn check_incoming_payment_status(
  575. &self,
  576. payment_identifier: &PaymentIdentifier,
  577. ) -> Result<Vec<WaitPaymentResponse>, Self::Err> {
  578. let start = std::time::Instant::now();
  579. METRICS.inc_in_flight_requests("check_incoming_payment_status");
  580. let result = self
  581. .inner
  582. .check_incoming_payment_status(payment_identifier)
  583. .await;
  584. let duration = start.elapsed().as_secs_f64();
  585. METRICS.record_mint_operation_histogram(
  586. "check_incoming_payment_status",
  587. result.is_ok(),
  588. duration,
  589. );
  590. METRICS.dec_in_flight_requests("check_incoming_payment_status");
  591. result
  592. }
  593. async fn check_outgoing_payment(
  594. &self,
  595. payment_identifier: &PaymentIdentifier,
  596. ) -> Result<MakePaymentResponse, Self::Err> {
  597. let start = std::time::Instant::now();
  598. METRICS.inc_in_flight_requests("check_outgoing_payment");
  599. let result = self.inner.check_outgoing_payment(payment_identifier).await;
  600. let duration = start.elapsed().as_secs_f64();
  601. let success = result.is_ok();
  602. METRICS.record_mint_operation_histogram("check_outgoing_payment", success, duration);
  603. METRICS.dec_in_flight_requests("check_outgoing_payment");
  604. result
  605. }
  606. }
  607. /// Type alias for Mint Payment trait
  608. pub type DynMintPayment = std::sync::Arc<dyn MintPayment<Err = Error> + Send + Sync>;