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}