fedimint_ln_client/recurring/
api.rs1use fedimint_core::config::FederationId;
2use fedimint_core::util::{FmtCompactErrorAnyhow, SafeUrl};
3use lightning_invoice::Bolt11Invoice;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use crate::recurring::{PaymentCodeRootKey, RecurringPaymentProtocol};
8
9pub struct RecurringdClient {
10 client: reqwest::Client,
11 base_url: SafeUrl,
12}
13
14impl RecurringdClient {
15 pub fn new(base_url: &SafeUrl) -> Self {
16 Self {
17 client: reqwest::Client::new(),
18 base_url: base_url.join_path("lnv1"),
19 }
20 }
21
22 pub async fn register_recurring_payment_code(
23 &self,
24 federation_id: FederationId,
25 protocol: RecurringPaymentProtocol,
26 payment_code_root_key: PaymentCodeRootKey,
27 meta: &str,
28 ) -> Result<RecurringPaymentRegistrationResponse, RecurringdApiError> {
29 let request = RecurringPaymentRegistrationRequest {
32 federation_id,
33 protocol,
34 payment_code_root_key,
35 meta: meta.to_owned(),
36 };
37
38 let response = self
39 .client
40 .put(self.base_url.join_path("paycodes").to_string())
41 .json(&request)
42 .send()
43 .await
44 .map_err(RecurringdApiError::NetworkError)?;
45
46 response
47 .json::<ApiResult<RecurringPaymentRegistrationResponse>>()
48 .await
49 .map_err(|e| RecurringdApiError::DecodingError(e.into()))?
50 .into_result()
51 }
52
53 pub async fn await_new_invoice(
54 &self,
55 payment_code_root_key: PaymentCodeRootKey,
56 invoice_index: u64,
57 ) -> Result<Bolt11Invoice, RecurringdApiError> {
58 let response = self
59 .client
60 .get(
61 self.base_url
62 .join_path(&format!(
63 "paycodes/recipient/{payment_code_root_key}/generated/{invoice_index}"
64 ))
65 .to_string(),
66 )
67 .send()
68 .await
69 .map_err(RecurringdApiError::NetworkError)?;
70 response
71 .json::<ApiResult<Bolt11Invoice>>()
72 .await
73 .map_err(|e| RecurringdApiError::DecodingError(e.into()))?
74 .into_result()
75 }
76}
77
78#[derive(Debug, Error)]
79pub enum RecurringdApiError {
80 #[error("Recurring payment server error: {0}")]
81 ApiError(String),
82 #[error("Invalid response: {}", FmtCompactErrorAnyhow(.0))]
83 DecodingError(anyhow::Error),
84 #[error("Network error: {0}")]
85 NetworkError(#[from] reqwest::Error),
86}
87
88#[derive(Debug, Clone, PartialOrd, PartialEq, Hash, Serialize, Deserialize)]
89pub struct RecurringPaymentRegistrationRequest {
90 pub federation_id: FederationId,
92 pub protocol: RecurringPaymentProtocol,
94 pub payment_code_root_key: PaymentCodeRootKey,
97 pub meta: String,
99}
100
101#[derive(Debug, Clone, PartialOrd, PartialEq, Hash, Serialize, Deserialize)]
102pub struct RecurringPaymentRegistrationResponse {
103 pub recurring_payment_code: String,
105}
106
107#[derive(Debug, Deserialize)]
108#[serde(untagged)]
109enum ApiResult<T> {
110 Ok(T),
111 Err { error: String },
112}
113
114impl<T> ApiResult<T> {
115 pub fn into_result(self) -> Result<T, RecurringdApiError> {
116 match self {
117 ApiResult::Ok(result) => Ok(result),
118 ApiResult::Err { error } => Err(RecurringdApiError::ApiError(error)),
119 }
120 }
121}