breez_sdk_spark/sdk/lnurl/
mod.rs1use breez_sdk_common::lnurl::{self, error::LnurlError};
2
3use crate::{
4 BuildUnsignedLnurlPayPackageRequest, LnurlAuthRequestDetails, LnurlCallbackStatus,
5 LnurlPayRequest, LnurlPayResponse, LnurlWithdrawInfo, LnurlWithdrawRequest,
6 LnurlWithdrawResponse, PrepareLnurlPayRequest, PrepareLnurlPayResponse,
7 PublishSignedLnurlPayPackageRequest, PublishSignedLnurlPayResponse, UnsignedTransferPackage,
8 WaitForPaymentIdentifier,
9 error::SdkError,
10 persist::{ObjectCacheRepository, PaymentMetadata},
11};
12use breez_sdk_common::lnurl::withdraw::execute_lnurl_withdraw;
13
14use super::BreezSdk;
15
16mod pay;
17
18#[cfg_attr(feature = "uniffi", uniffi::export(async_runtime = "tokio"))]
19#[allow(clippy::needless_pass_by_value)]
20impl BreezSdk {
21 pub async fn prepare_lnurl_pay(
22 &self,
23 request: PrepareLnurlPayRequest,
24 ) -> Result<PrepareLnurlPayResponse, SdkError> {
25 pay::prepare(self, request).await
26 }
27
28 pub async fn lnurl_pay(&self, request: LnurlPayRequest) -> Result<LnurlPayResponse, SdkError> {
29 pay::send(self, request).await
30 }
31
32 pub async fn build_unsigned_lnurl_pay_package(
33 &self,
34 request: BuildUnsignedLnurlPayPackageRequest,
35 ) -> Result<UnsignedTransferPackage, SdkError> {
36 pay::build_package(self, &request.prepare_response).await
37 }
38
39 pub async fn publish_signed_lnurl_pay_package(
40 &self,
41 request: PublishSignedLnurlPayPackageRequest,
42 ) -> Result<PublishSignedLnurlPayResponse, SdkError> {
43 self.maybe_ensure_spark_private_mode_initialized().await?;
44 pay::publish_signed_package(self, request.signed_package).await
45 }
46
47 pub async fn lnurl_withdraw(
73 &self,
74 request: LnurlWithdrawRequest,
75 ) -> Result<LnurlWithdrawResponse, SdkError> {
76 self.maybe_ensure_spark_private_mode_initialized().await?;
77 let LnurlWithdrawRequest {
78 amount_sats,
79 withdraw_request,
80 completion_timeout_secs,
81 } = request;
82 let withdraw_request: breez_sdk_common::lnurl::withdraw::LnurlWithdrawRequestDetails =
83 withdraw_request.into();
84 if !withdraw_request.is_amount_valid(amount_sats) {
85 return Err(SdkError::InvalidInput(
86 "Amount must be within min/max LNURL withdrawable limits".to_string(),
87 ));
88 }
89
90 let receive = self
93 .receive_bolt11_invoice_inner(
94 withdraw_request.default_description.clone(),
95 Some(amount_sats),
96 None,
97 None,
98 )
99 .await?;
100 let payment_request = receive.invoice.clone();
101 let ssp_receive_id = receive.id;
102
103 let cache = ObjectCacheRepository::new(self.storage.clone());
105 cache
106 .save_payment_metadata(
107 &payment_request,
108 &PaymentMetadata {
109 lnurl_withdraw_info: Some(LnurlWithdrawInfo {
110 withdraw_url: withdraw_request.callback.clone(),
111 }),
112 lnurl_description: Some(withdraw_request.default_description.clone()),
113 ..Default::default()
114 },
115 )
116 .await?;
117
118 let withdraw_response = execute_lnurl_withdraw(
120 self.lnurl_client.as_ref(),
121 &withdraw_request,
122 &payment_request,
123 )
124 .await?;
125 if let lnurl::withdraw::ValidatedCallbackResponse::EndpointError { data } =
126 withdraw_response
127 {
128 return Err(LnurlError::EndpointError(data.reason).into());
129 }
130
131 let completion_timeout_secs = match completion_timeout_secs {
132 Some(secs) if secs > 0 => secs,
133 _ => {
134 return Ok(LnurlWithdrawResponse {
135 payment_request,
136 payment: None,
137 });
138 }
139 };
140
141 let payment = self
143 .wait_for_incoming_payment(
144 WaitForPaymentIdentifier::LightningReceive {
145 invoice: payment_request.clone(),
146 ssp_id: ssp_receive_id,
147 },
148 completion_timeout_secs,
149 )
150 .await
151 .ok();
152 Ok(LnurlWithdrawResponse {
153 payment_request,
154 payment,
155 })
156 }
157
158 pub async fn lnurl_auth(
164 &self,
165 request_data: LnurlAuthRequestDetails,
166 ) -> Result<LnurlCallbackStatus, SdkError> {
167 let Some(lnurl_auth_signer) = self.lnurl_auth_signer.as_ref() else {
169 return Err(SdkError::Generic(
170 "LNURL-auth requires a signer that supports HMAC".to_string(),
171 ));
172 };
173 let request: breez_sdk_common::lnurl::auth::LnurlAuthRequestDetails = request_data.into();
174 let status = breez_sdk_common::lnurl::auth::perform_lnurl_auth(
175 self.lnurl_client.as_ref(),
176 &request,
177 lnurl_auth_signer.as_ref(),
178 )
179 .await
180 .map_err(|e| match e {
181 LnurlError::ServiceConnectivity(msg) => SdkError::NetworkError(msg.to_string()),
182 LnurlError::InvalidUri(msg) => SdkError::InvalidInput(msg),
183 _ => SdkError::Generic(e.to_string()),
184 })?;
185 Ok(status.into())
186 }
187}