Skip to main content

lago_client/queries/
invoice.rs

1use lago_types::{
2    error::{LagoError, Result},
3    requests::invoice::{
4        CreateInvoiceRequest, DownloadInvoiceRequest, GetInvoiceRequest, InvoicePreviewRequest,
5        ListCustomerInvoicesRequest, ListInvoicesRequest, RefreshInvoiceRequest,
6        RetryInvoicePaymentRequest, RetryInvoiceRequest, UpdateInvoiceRequest, VoidInvoiceRequest,
7    },
8    responses::invoice::{
9        CreateInvoiceResponse, DownloadInvoiceResponse, GetInvoiceResponse, InvoicePreviewResponse,
10        ListInvoicesResponse, RefreshInvoiceResponse, RetryInvoicePaymentResponse,
11        RetryInvoiceResponse, UpdateInvoiceResponse, VoidInvoiceResponse,
12    },
13};
14use url::Url;
15
16use crate::client::LagoClient;
17
18/// Invoice-related operations for the Lago client
19impl LagoClient {
20    /// Retrieves a list of invoices with optional filtering parameters
21    ///
22    /// # Arguments
23    /// * `request` - Optional filtering parameters for the invoice list
24    ///
25    /// # Returns
26    /// A `Result` containing the list of invoices or an error
27    pub async fn list_invoices(
28        &self,
29        request: Option<ListInvoicesRequest>,
30    ) -> Result<ListInvoicesResponse> {
31        let request = request.unwrap_or_default();
32        let region = self.config.region()?;
33        let mut url = Url::parse(&format!("{}/invoices", region.endpoint()))
34            .map_err(|e| LagoError::Configuration(format!("Invalid URL: {e}")))?;
35
36        let query_params = request.to_query_params();
37
38        if !query_params.is_empty() {
39            let query_string = query_params
40                .iter()
41                .map(|(k, v)| format!("{k}={v}"))
42                .collect::<Vec<_>>()
43                .join("&");
44            url.set_query(Some(&query_string));
45        }
46
47        self.make_request("GET", url.as_str(), None::<&()>).await
48    }
49
50    /// Retrieves a specific invoice by its ID
51    ///
52    /// # Arguments
53    /// * `request` - The request containing the invoice ID to retrieve
54    ///
55    /// # Returns
56    /// A `Result` containing the invoice data or an error
57    pub async fn get_invoice(&self, request: GetInvoiceRequest) -> Result<GetInvoiceResponse> {
58        let region = self.config.region()?;
59        let url = format!("{}/invoices/{}", region.endpoint(), request.invoice_id);
60        self.make_request("GET", &url, None::<&()>).await
61    }
62
63    /// Previews an invoice without creating it
64    ///
65    /// This endpoint allows you to retrieve an estimated invoice before finalization.
66    /// It can be used to preview invoices for new subscriptions or existing customers.
67    ///
68    /// # Arguments
69    /// * `request` - The invoice preview request containing customer and subscription details
70    ///
71    /// # Returns
72    /// A `Result` containing the previewed invoice or an error
73    pub async fn preview_invoice(
74        &self,
75        request: InvoicePreviewRequest,
76    ) -> Result<InvoicePreviewResponse> {
77        let region = self.config.region()?;
78        let url = format!("{}/invoices/preview", region.endpoint());
79        self.make_request("POST", &url, Some(&request)).await
80    }
81
82    /// Creates a one-off invoice for a customer
83    ///
84    /// This endpoint allows you to create a one-off invoice with add-on charges
85    /// for a specific customer.
86    ///
87    /// # Arguments
88    /// * `request` - The request containing the invoice details and fees
89    ///
90    /// # Returns
91    /// A `Result` containing the created invoice or an error
92    pub async fn create_invoice(
93        &self,
94        request: CreateInvoiceRequest,
95    ) -> Result<CreateInvoiceResponse> {
96        let region = self.config.region()?;
97        let url = format!("{}/invoices", region.endpoint());
98        self.make_request("POST", &url, Some(&request)).await
99    }
100
101    /// Updates an existing invoice
102    ///
103    /// This endpoint allows you to update the payment status or metadata
104    /// of an existing invoice.
105    ///
106    /// # Arguments
107    /// * `request` - The request containing the invoice ID and update data
108    ///
109    /// # Returns
110    /// A `Result` containing the updated invoice or an error
111    pub async fn update_invoice(
112        &self,
113        request: UpdateInvoiceRequest,
114    ) -> Result<UpdateInvoiceResponse> {
115        let region = self.config.region()?;
116        let url = format!("{}/invoices/{}", region.endpoint(), request.lago_id);
117        self.make_request("PUT", &url, Some(&request)).await
118    }
119
120    /// Retrieves a list of invoices for a specific customer
121    ///
122    /// # Arguments
123    /// * `request` - The request containing the customer ID and optional filters
124    ///
125    /// # Returns
126    /// A `Result` containing the list of invoices or an error
127    pub async fn list_customer_invoices(
128        &self,
129        request: ListCustomerInvoicesRequest,
130    ) -> Result<ListInvoicesResponse> {
131        let region = self.config.region()?;
132        let mut url = Url::parse(&format!(
133            "{}/customers/{}/invoices",
134            region.endpoint(),
135            urlencoding::encode(&request.external_customer_id)
136        ))
137        .map_err(|e| LagoError::Configuration(format!("Invalid URL: {e}")))?;
138
139        let query_params = request.to_query_params();
140
141        if !query_params.is_empty() {
142            let query_string = query_params
143                .iter()
144                .map(|(k, v)| format!("{k}={v}"))
145                .collect::<Vec<_>>()
146                .join("&");
147            url.set_query(Some(&query_string));
148        }
149
150        self.make_request("GET", url.as_str(), None::<&()>).await
151    }
152
153    /// Refreshes a draft invoice
154    ///
155    /// This endpoint re-fetches the customer information and recomputes the taxes
156    /// for a draft invoice. Only draft invoices can be refreshed.
157    ///
158    /// # Arguments
159    /// * `request` - The request containing the invoice ID to refresh
160    ///
161    /// # Returns
162    /// A `Result` containing the refreshed invoice or an error
163    pub async fn refresh_invoice(
164        &self,
165        request: RefreshInvoiceRequest,
166    ) -> Result<RefreshInvoiceResponse> {
167        let region = self.config.region()?;
168        let url = format!("{}/invoices/{}/refresh", region.endpoint(), request.lago_id);
169        self.make_request("PUT", &url, None::<&()>).await
170    }
171
172    /// Downloads an invoice PDF
173    ///
174    /// This endpoint triggers the generation of the invoice PDF if not already
175    /// generated, and returns the invoice with a file_url field containing
176    /// the URL to download the PDF.
177    ///
178    /// # Arguments
179    /// * `request` - The request containing the invoice ID to download
180    ///
181    /// # Returns
182    /// A `Result` containing the invoice with file_url or an error
183    pub async fn download_invoice(
184        &self,
185        request: DownloadInvoiceRequest,
186    ) -> Result<DownloadInvoiceResponse> {
187        let region = self.config.region()?;
188        let url = format!(
189            "{}/invoices/{}/download",
190            region.endpoint(),
191            request.lago_id
192        );
193        self.make_request("POST", &url, None::<&()>).await
194    }
195
196    /// Retries a failed invoice finalization
197    ///
198    /// This endpoint retries the finalization process for invoices that
199    /// failed during generation. Only failed invoices can be retried.
200    ///
201    /// # Arguments
202    /// * `request` - The request containing the invoice ID to retry
203    ///
204    /// # Returns
205    /// A `Result` containing the retried invoice or an error
206    pub async fn retry_invoice(
207        &self,
208        request: RetryInvoiceRequest,
209    ) -> Result<RetryInvoiceResponse> {
210        let region = self.config.region()?;
211        let url = format!("{}/invoices/{}/retry", region.endpoint(), request.lago_id);
212        self.make_request("POST", &url, None::<&()>).await
213    }
214
215    /// Retries a failed invoice payment
216    ///
217    /// This endpoint resends the invoice for collection and retries the payment
218    /// with the payment provider. Only invoices with failed payment status can
219    /// be retried.
220    ///
221    /// # Arguments
222    /// * `request` - The request containing the invoice ID to retry payment for
223    ///
224    /// # Returns
225    /// A `Result` containing the invoice or an error
226    pub async fn retry_invoice_payment(
227        &self,
228        request: RetryInvoicePaymentRequest,
229    ) -> Result<RetryInvoicePaymentResponse> {
230        let region = self.config.region()?;
231        let url = format!(
232            "{}/invoices/{}/retry_payment",
233            region.endpoint(),
234            request.lago_id
235        );
236        self.make_request("POST", &url, None::<&()>).await
237    }
238
239    /// Voids a finalized invoice
240    ///
241    /// This endpoint voids a finalized invoice, changing its status to "voided".
242    /// Only finalized invoices can be voided.
243    ///
244    /// # Arguments
245    /// * `request` - The request containing the invoice ID to void
246    ///
247    /// # Returns
248    /// A `Result` containing the voided invoice or an error
249    pub async fn void_invoice(&self, request: VoidInvoiceRequest) -> Result<VoidInvoiceResponse> {
250        let region = self.config.region()?;
251        let url = format!("{}/invoices/{}/void", region.endpoint(), request.lago_id);
252        self.make_request("POST", &url, None::<&()>).await
253    }
254}