gl_client/lnurl/
mod.rs

1mod models;
2mod pay;
3mod utils;
4mod withdraw;
5
6use self::models::{
7    LnUrlHttpClient, PayRequestCallbackResponse, PayRequestResponse, WithdrawRequestResponse,
8};
9use self::utils::{parse_invoice, parse_lnurl};
10use crate::node::ClnClient;
11use crate::pb::cln::{amount_or_any, Amount, AmountOrAny};
12use anyhow::{anyhow, Result};
13use models::LnUrlHttpClearnetClient;
14use pay::{resolve_lnurl_to_invoice, validate_invoice_from_callback_response};
15use url::Url;
16use withdraw::{build_withdraw_request_callback_url, parse_withdraw_request_response_from_url};
17
18pub struct LNURL<T: LnUrlHttpClient> {
19    http_client: T,
20}
21
22impl<T: LnUrlHttpClient> LNURL<T> {
23    pub fn new(http_client: T) -> Self {
24        LNURL { http_client }
25    }
26
27    pub fn new_with_clearnet_client() -> LNURL<LnUrlHttpClearnetClient> {
28        let http_client = LnUrlHttpClearnetClient::new();
29        LNURL { http_client }
30    }
31
32    pub async fn get_pay_request_response(&self, lnurl: &str) -> Result<PayRequestResponse> {
33        let url = parse_lnurl(lnurl)?;
34
35        let lnurl_pay_request_response: PayRequestResponse =
36            self.http_client.get_pay_request_response(&url).await?;
37
38        if lnurl_pay_request_response.tag != "payRequest" {
39            return Err(anyhow!("Expected tag to say 'payRequest'"));
40        }
41
42        Ok(lnurl_pay_request_response)
43    }
44
45    pub async fn get_pay_request_callback_response(
46        &self,
47        base_callback_url: &str,
48        amount_msats: u64,
49        metadata: &str,
50    ) -> Result<PayRequestCallbackResponse> {
51        let mut url = Url::parse(base_callback_url)?;
52        url.query_pairs_mut()
53            .append_pair("amount", &amount_msats.to_string());
54
55        let callback_response: PayRequestCallbackResponse = self
56            .http_client
57            .get_pay_request_callback_response(&url.to_string())
58            .await?;
59
60        let invoice = parse_invoice(&callback_response.pr)?;
61        validate_invoice_from_callback_response(&invoice, amount_msats, metadata)?;
62        Ok(callback_response)
63    }
64
65    pub async fn pay(
66        &self,
67        lnurl: &str,
68        amount_msats: u64,
69        node: &mut ClnClient,
70    ) -> Result<tonic::Response<crate::pb::cln::PayResponse>> {
71        let invoice = resolve_lnurl_to_invoice(&self.http_client, lnurl, amount_msats).await?;
72
73        node.pay(crate::pb::cln::PayRequest {
74            bolt11: invoice.to_string(),
75            ..Default::default()
76        })
77        .await
78        .map_err(|e| anyhow!(e))
79    }
80
81    pub async fn get_withdraw_request_response(
82        &self,
83        lnurl: &str,
84    ) -> Result<WithdrawRequestResponse> {
85        let url = parse_lnurl(lnurl)?;
86        let withdrawal_request_response = parse_withdraw_request_response_from_url(&url);
87
88        //If it's not a quick withdraw, then get the withdrawal_request_response from the web.
89        let withdrawal_request_response = match withdrawal_request_response {
90            Some(w) => w,
91            None => {
92                self.http_client
93                    .get_withdrawal_request_response(&url)
94                    .await?
95            }
96        };
97
98        Ok(withdrawal_request_response)
99    }
100
101    pub async fn withdraw(
102        &self,
103        lnurl: &str,
104        amount_msats: u64,
105        node: &mut ClnClient,
106    ) -> Result<()> {
107        let withdraw_request_response = self.get_withdraw_request_response(lnurl).await?;
108
109        let amount = AmountOrAny {
110            value: Some(amount_or_any::Value::Amount(Amount { msat: amount_msats })),
111        };
112        let invoice = node
113            .invoice(crate::pb::cln::InvoiceRequest {
114                amount_msat: Some(amount),
115                description: withdraw_request_response.default_description.clone(),
116                ..Default::default()
117            })
118            .await
119            .map_err(|e| anyhow!(e))?
120            .into_inner();
121
122        let callback_url =
123            build_withdraw_request_callback_url(&withdraw_request_response, invoice.bolt11)?;
124
125        let _ = self
126            .http_client
127            .send_invoice_for_withdraw_request(&callback_url);
128
129        Ok(())
130    }
131}