phoenixd_rs/
pay_ln.rs

1//! Pay Ln
2
3use anyhow::bail;
4use reqwest::StatusCode;
5use serde::{Deserialize, Serialize};
6
7use crate::{Error, Phoenixd};
8
9/// Pay Invoice Request
10#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
11#[serde(rename_all = "camelCase")]
12pub struct PayInvoiceRequest {
13    /// Amount in sats
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub amount_sat: Option<u64>,
16    /// Bolt11 Invoice
17    pub invoice: String,
18}
19
20///Pay bolt12 offer
21#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct PayBolt12Request {
24    /// Amount in sats
25    pub amount_sat: u64,
26    /// Bolt12 offer
27    pub offer: String,
28    /// Message
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub message: Option<String>,
31}
32
33/// Pay Invoice Response
34#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
35#[serde(rename_all = "camelCase")]
36pub struct PayInvoiceResponse {
37    /// Amount recipient was paid
38    pub recipient_amount_sat: u64,
39    /// Routing fee paid
40    pub routing_fee_sat: u64,
41    /// Payment Id
42    pub payment_id: String,
43    /// Payment hash
44    pub payment_hash: String,
45    /// Payment preimage
46    pub payment_preimage: String,
47}
48
49/// Find Outgoing Response
50#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
51#[serde(rename_all = "camelCase")]
52pub struct GetOutgoingInvoiceResponse {
53    /// Payment Hash
54    pub payment_hash: String,
55    /// Preimage
56    pub preimage: String,
57    /// Paid flag
58    pub is_paid: bool,
59    /// Amount sent
60    pub sent: u64,
61    /// Fees
62    pub fees: u64,
63    /// Invoice
64    pub invoice: Option<String>,
65    /// Completed at
66    pub completed_at: Option<u64>,
67    /// Time created
68    pub created_at: u64,
69}
70
71impl Phoenixd {
72    /// PayInvoice
73    pub async fn pay_bolt11_invoice(
74        &self,
75        invoice: &str,
76        amount_sat: Option<u64>,
77    ) -> anyhow::Result<PayInvoiceResponse> {
78        let url = self.api_url.join("/payinvoice")?;
79
80        let request = PayInvoiceRequest {
81            amount_sat,
82            invoice: invoice.to_string(),
83        };
84
85        let res = self.make_post(url, Some(request)).await?;
86
87        match serde_json::from_value(res.clone()) {
88            Ok(res) => Ok(res),
89            Err(_) => {
90                log::error!("Api error response on payment quote execution");
91                log::error!("{}", res);
92                bail!("Could not execute payment quote")
93            }
94        }
95    }
96
97    /// Pay offer
98    pub async fn pay_bolt12_offer(
99        &self,
100        offer: String,
101        amount_sat: u64,
102        message: Option<String>,
103    ) -> anyhow::Result<PayInvoiceResponse> {
104        let url = self.api_url.join("/payoffer")?;
105
106        let request = PayBolt12Request {
107            amount_sat,
108            offer,
109            message,
110        };
111
112        let res = self.make_post(url, Some(request)).await?;
113
114        match serde_json::from_value(res.clone()) {
115            Ok(res) => Ok(res),
116            Err(_) => {
117                log::error!("Api error response on payment quote execution");
118                log::error!("{}", res);
119                bail!("Could not execute payment quote")
120            }
121        }
122    }
123
124    /// Find outgoing invoice
125    pub async fn get_outgoing_invoice(
126        &self,
127        payment_hash: &str,
128    ) -> Result<GetOutgoingInvoiceResponse, Error> {
129        let url = self
130            .api_url
131            .join(&format!("payments/outgoing/{}", payment_hash))
132            .map_err(|_| Error::InvalidUrl)?;
133
134        let res = match self.make_get(url).await {
135            Ok(res) => res,
136            Err(err) => {
137                if let Error::ReqwestError(err) = &err {
138                    if err.status().unwrap_or_default() == StatusCode::NOT_FOUND {
139                        return Err(Error::NotFound);
140                    }
141                }
142                return Err(err);
143            }
144        };
145
146        match serde_json::from_value(res.clone()) {
147            Ok(res) => Ok(res),
148            Err(err) => {
149                log::error!("Api error response getting payment quote");
150                log::error!("{}", res);
151
152                Err(err.into())
153            }
154        }
155    }
156}