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