paddle_rust_sdk/
lib.rs

1//! # Paddle API Client
2//!
3//! This is a Rust client for the Paddle API, which allows you to interact with Paddle's services.
4
5use std::fmt::Display;
6
7use entities::CustomerAuthenticationToken;
8use reqwest::{header::CONTENT_TYPE, IntoUrl, Method, Url};
9use serde::{de::DeserializeOwned, Serialize};
10
11pub mod entities;
12pub mod enums;
13pub mod error;
14pub mod ids;
15
16pub mod addresses;
17pub mod customers;
18pub mod discounts;
19pub mod prices;
20pub mod products;
21
22pub mod response;
23
24use enums::{CountryCodeSupported, CurrencyCode, DiscountType, TaxCategory};
25use ids::{AddressID, CustomerID, DiscountID, PriceID, ProductID};
26
27use response::{ErrorResponse, Response, SuccessResponse};
28
29use error::{Error, PaddleError};
30
31type Result<T> = std::result::Result<SuccessResponse<T>, Error>;
32
33/// Paddle API client
34///
35/// This struct is used to create a new Paddle client instance.
36#[derive(Clone, Debug)]
37pub struct Paddle {
38    base_url: Url,
39    api_key: String,
40}
41
42impl Paddle {
43    pub const PRODUCTION: &'static str = "https://api.paddle.com";
44    pub const SANDBOX: &'static str = "https://sandbox-api.paddle.com";
45
46    /// Creates a new Paddle client instance.
47    ///
48    /// Example:
49    /// ```
50    /// use paddle::Paddle;
51    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
52    /// ```
53    #[allow(clippy::result_large_err)]
54    pub fn new(
55        api_key: impl Into<String>,
56        base_url: impl IntoUrl,
57    ) -> std::result::Result<Self, Error> {
58        Ok(Self {
59            base_url: base_url.into_url()?,
60            api_key: api_key.into(),
61        })
62    }
63
64    /// Returns a request builder for fetching products. Use the after method to page through results.
65    ///
66    /// By default, Paddle returns products that are active. Use the status method to return products that are archived.
67    /// Use the include method to include related price entities in the response.
68    ///
69    /// # Example:
70    /// ```
71    /// use paddle::Paddle;
72    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
73    /// let products = client.products_list().send().await.unwrap();
74    /// ```
75    pub fn products_list(&self) -> products::ProductsList {
76        products::ProductsList::new(self)
77    }
78
79    /// Returns a request builder for creating a new product.
80    ///
81    /// # Example:
82    /// ```
83    /// use paddle::Paddle;
84    /// use paddle::enums::TaxCategory;
85    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
86    /// let product = client.products_create("My Product", TaxCategory::Standard).send().await.unwrap();
87    /// ```
88    pub fn product_create(
89        &self,
90        name: impl Into<String>,
91        tax_category: TaxCategory,
92    ) -> products::ProductCreate {
93        products::ProductCreate::new(self, name, tax_category)
94    }
95
96    /// Returns a request builder for fetching a specific product.
97    ///
98    /// # Example:
99    /// ```
100    /// use paddle::Paddle;
101    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
102    /// let product = client.product_get("pro_01jqx9rd...").send().await.unwrap();
103    /// ```
104    pub fn product_get(&self, product_id: impl Into<ProductID>) -> products::ProductGet {
105        products::ProductGet::new(self, product_id)
106    }
107
108    /// Returns a request builder for updating a specific product.
109    ///
110    /// # Example:
111    /// ```
112    /// use paddle::Paddle;
113    /// use paddle::enums::TaxCategory;
114    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
115    /// let product = client.product_update("pro_01jqx9rd...").name("My New Name").send().await.unwrap();
116    /// ```
117    pub fn product_update(&self, product_id: impl Into<ProductID>) -> products::ProductUpdate {
118        products::ProductUpdate::new(self, product_id)
119    }
120
121    /// Returns a request builder listing prices
122    ///
123    /// # Example:
124    /// ```
125    /// use paddle::Paddle;
126    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
127    /// let prices = client.prices_list().send().await.unwrap();
128    /// ```
129    pub fn prices_list(&self) -> prices::PricesList {
130        prices::PricesList::new(self)
131    }
132
133    /// Returns a request builder for creating a new price.
134    ///
135    /// * `product_id` - Paddle ID for the product that this price is for.
136    /// * `description` - Internal description for this price, not shown to customers. Typically notes for your team.
137    /// * `amount` - Amount of the price in the smallest unit of the currency (e.g. 1000 cents for 10 USD).
138    /// * `currency` - Currency code for the price. Use the [CurrencyCode] enum to specify the currency.
139    ///
140    /// # Example:
141    /// ```
142    /// use paddle::Paddle;
143    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
144    /// let price = client.price_create("pro_01jqx9rd...", "Low price", 19.99, CurrencyCode::USD).send().await.unwrap();
145    /// ```
146    pub fn price_create(
147        &self,
148        product_id: impl Into<ProductID>,
149        description: impl Into<String>,
150        amount: u64,
151        currency: CurrencyCode,
152    ) -> prices::PricesCreate {
153        prices::PricesCreate::new(self, product_id, description, amount, currency)
154    }
155
156    /// Returns a request builder for fetching a specific price by id.
157    ///
158    /// # Example:
159    /// ```
160    /// use paddle::Paddle;
161    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
162    /// let price = client.price_get("price_01jqx9rd...").send().await.unwrap();
163    /// ```
164    pub fn price_get(&self, price_id: impl Into<PriceID>) -> prices::PriceGet {
165        prices::PriceGet::new(self, price_id)
166    }
167
168    /// Returns a request builder for updating a specific price.
169    ///
170    /// # Example:
171    /// ```
172    /// use paddle::Paddle;
173    /// use paddle::enums::TaxCategory;
174    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
175    /// let price = client.price_update("pri_01jqxv...").name("Updated Name").send().await.unwrap();
176    /// ```
177    pub fn price_update(&self, price_id: impl Into<PriceID>) -> prices::PriceUpdate {
178        prices::PriceUpdate::new(self, price_id)
179    }
180
181    /// Returns a request builder for fetching discounts.
182    ///
183    /// # Example:
184    /// ```
185    /// use paddle::Paddle;
186    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
187    /// let discounts = client.discounts_list().send().await.unwrap();
188    /// ```
189    pub fn discounts_list(&self) -> discounts::DiscountsList {
190        discounts::DiscountsList::new(self)
191    }
192
193    /// Returns a request builder for creating discounts.
194    ///
195    /// # Example:
196    /// ```
197    /// use paddle::Paddle;
198    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
199    /// let discount = client.discount_create("15", "Winter Holidays", DiscountType::Percentage).send().await.unwrap();
200    /// ```
201    pub fn discount_create(
202        &self,
203        amount: impl Into<String>,
204        description: impl Into<String>,
205        discount_type: DiscountType,
206    ) -> discounts::DiscountCreate {
207        discounts::DiscountCreate::new(self, amount, description, discount_type)
208    }
209
210    /// Returns a request builder for fetching a specific discount by id.
211    ///
212    /// # Example:
213    /// ```
214    /// use paddle::Paddle;
215    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
216    /// let discount = client.discount_get("dsc_01jqzpbmnq...").send().await.unwrap();
217    /// ```
218    pub fn discount_get(&self, discount_id: impl Into<DiscountID>) -> discounts::DiscountGet {
219        discounts::DiscountGet::new(self, discount_id)
220    }
221
222    /// Returns a request builder for creating discounts.
223    ///
224    /// # Example:
225    /// ```
226    /// use paddle::Paddle;
227    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
228    /// let discount = client.discount_update("dsc_01jqzpbmnq...").amount("18").send().await.unwrap();
229    /// ```
230    pub fn discount_update(&self, discount_id: impl Into<DiscountID>) -> discounts::DiscountUpdate {
231        discounts::DiscountUpdate::new(self, discount_id)
232    }
233
234    /// Returns a request builder for fetching customers. Use the after method to page through results.
235    ///
236    /// By default, Paddle returns customers that are `active`. Use the status query parameter to return customers that are archived.
237    ///
238    /// # Example:
239    /// ```
240    /// use paddle::Paddle;
241    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
242    /// let customers = client.customers_list().send().await.unwrap();
243    /// ```
244    pub fn customers_list(&self) -> customers::CustomersList {
245        customers::CustomersList::new(self)
246    }
247
248    /// Returns a request builder for creating a new customer.
249    ///
250    /// # Example:
251    /// ```
252    /// use paddle::Paddle;
253    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
254    /// let customers = client.customer_create("test@example.com").send().await.unwrap();
255    /// ```
256    pub fn customer_create(&self, email: impl Into<String>) -> customers::CustomerCreate {
257        customers::CustomerCreate::new(self, email.into())
258    }
259
260    /// Returns a request builder for fetching a specific customer by id.
261    ///
262    /// # Example:
263    /// ```
264    /// use paddle::Paddle;
265    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
266    /// let discount = client.customer_get("ctm_01jqztc78e1xfdgwhcgjzdrvgd").send().await.unwrap();
267    /// ```
268    pub fn customer_get(&self, customer_id: impl Into<CustomerID>) -> customers::CustomerGet {
269        customers::CustomerGet::new(self, customer_id)
270    }
271
272    /// Returns a request builder for updating customer data.
273    ///
274    /// # Example:
275    /// ```
276    /// use paddle::Paddle;
277    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
278    /// let discount = client.customer_update("ctm_01jqztc78e1xfdgwhcgjzdrvgd").email("new_email@example.com").send().await.unwrap();
279    /// ```
280    pub fn customer_update(&self, customer_id: impl Into<CustomerID>) -> customers::CustomerUpdate {
281        customers::CustomerUpdate::new(self, customer_id)
282    }
283
284    /// Returns a request builder for fetching a list of credit balances for each currency for a customer.
285    ///
286    /// Each balance has three totals:
287    ///
288    /// * `available` - total available to use.
289    /// * `reserved` - total temporarily reserved for billed transactions.
290    /// * `used` - total amount of credit used.
291    ///
292    /// Credit is added to the available total initially. When used, it moves to the used total.
293    ///
294    /// The reserved total is used when a credit balance is applied to a transaction that's marked as billed, like when working with an issued invoice. It's not available for other transactions at this point, but isn't considered used until the transaction is completed. If a billed transaction is canceled, any reserved credit moves back to available.
295    ///
296    /// Credit balances are created automatically by Paddle when you take an action that results in Paddle creating a credit for a customer, like making prorated changes to a subscription. An empty data array is returned where a customer has no credit balances.
297    ///
298    /// The response is not paginated.
299    ///
300    /// # Example:
301    /// ```
302    /// use paddle::Paddle;
303    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
304    /// let discount = client.customer_credit_balances("ctm_01jqztc78e1xfdgwhcgjzdrvgd").send().await.unwrap();
305    /// ```
306    pub fn customer_credit_balances(
307        &self,
308        customer_id: impl Into<CustomerID>,
309    ) -> customers::CustomerCreditBalances {
310        customers::CustomerCreditBalances::new(self, customer_id)
311    }
312
313    /// Generates an authentication token for a customer.
314    ///
315    /// You can pass a generated authentication token to Paddle.js when opening a checkout to let customers work with saved payment methods.
316    ///
317    /// Authentication tokens are temporary and shouldn't be cached. They're valid until the expires_at date returned in the response.
318    pub async fn generate_auth_token(
319        &self,
320        customer_id: impl Display,
321    ) -> Result<CustomerAuthenticationToken> {
322        let client = reqwest::Client::new();
323
324        let url = format!("{}customers/{}/auth-token", self.base_url, customer_id);
325
326        let res: Response<_> = client
327            .post(url)
328            .bearer_auth(self.api_key.clone())
329            .send()
330            .await?
331            .json()
332            .await?;
333
334        match res {
335            Response::Success(success) => Ok(success),
336            Response::Error(error) => Err(Error::Paddle(error)),
337        }
338    }
339
340    /// Returns a request builder for fetching customers addresses.
341    ///
342    /// By default, Paddle returns addresses that are `active`. Use the status query parameter to return addresses that are archived.
343    ///
344    /// # Example:
345    /// ```
346    /// use paddle::Paddle;
347    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
348    /// let customers = client.addresses_list("ctm_01jqztc78e1xfdgwhcgjzdrvgd").send().await.unwrap();
349    /// ```
350    pub fn addresses_list(&self, customer_id: impl Into<CustomerID>) -> addresses::AddressesList {
351        addresses::AddressesList::new(self, customer_id)
352    }
353
354    /// Returns a request builder for creating a new customer address.
355    ///
356    /// # Example:
357    /// ```
358    /// use paddle::Paddle;
359    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
360    /// let customers = client.address_create("ctm_01jqztc78e1xfdgwhcgjzdrvgd", CountryCodeSupported::US).send().await.unwrap();
361    /// ```
362    pub fn address_create(
363        &self,
364        customer_id: impl Into<CustomerID>,
365        country_code: CountryCodeSupported,
366    ) -> addresses::AddressCreate {
367        addresses::AddressCreate::new(self, customer_id, country_code)
368    }
369
370    /// Returns a request builder for getting an address for a customer using its ID and related customer ID.
371    ///
372    /// # Example:
373    /// ```
374    /// use paddle::Paddle;
375    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
376    /// let customers = client.address_get("ctm_01jqztc78e1xfdgwhcgjzdrvgd", "add_01hv8gwdfkw5z6d1yy6pa3xyrz").send().await.unwrap();
377    /// ```
378    pub fn address_get(
379        &self,
380        customer_id: impl Into<CustomerID>,
381        address_id: impl Into<AddressID>,
382    ) -> addresses::AddressGet {
383        addresses::AddressGet::new(self, customer_id, address_id)
384    }
385
386    /// Returns a request builder for updating an address for a customer using its ID and related customer ID.
387    ///
388    /// # Example:
389    /// ```
390    /// use paddle::Paddle;
391    /// let client = Paddle::new("your_api_key", Paddle::SANDBOX).unwrap();
392    /// let customers = client.address_update("add_01hv8gwdfkw5z6d1yy6pa3xyrz", "add_01hv8gwdfkw5z6d1yy6pa3xyrz").first_line("Test").send().await.unwrap();
393    /// ```
394    pub fn address_update(
395        &self,
396        customer_id: impl Into<CustomerID>,
397        address_id: impl Into<AddressID>,
398    ) -> addresses::AddressUpdate {
399        addresses::AddressUpdate::new(self, customer_id, address_id)
400    }
401
402    async fn send<T: DeserializeOwned>(
403        &self,
404        req: impl Serialize,
405        method: Method,
406        path: &str,
407    ) -> Result<T> {
408        let url = self.base_url.join(path)?;
409        let client = reqwest::Client::new();
410
411        let mut builder = client
412            .request(method.clone(), url)
413            .bearer_auth(self.api_key.clone())
414            .header(CONTENT_TYPE, "application/json; charset=utf-8");
415
416        builder = match method {
417            reqwest::Method::GET => builder.query(&req),
418            reqwest::Method::POST | reqwest::Method::PUT | reqwest::Method::PATCH => {
419                builder.json(&req)
420            }
421            _ => builder,
422        };
423
424        // let text = builder.send().await?.text().await?;
425        // println!("{}", text);
426        // todo!();
427
428        let res: Response<_> = builder.send().await?.json().await?;
429
430        match res {
431            Response::Success(success) => Ok(success),
432            Response::Error(error) => Err(Error::Paddle(error)),
433        }
434    }
435}
436
437fn comma_separated<S, T>(
438    values: &Option<Vec<T>>,
439    serializer: S,
440) -> std::result::Result<S::Ok, S::Error>
441where
442    S: serde::Serializer,
443    T: AsRef<str>,
444{
445    match values {
446        Some(values) => {
447            let values = values
448                .iter()
449                .map(|v| v.as_ref())
450                .collect::<Vec<_>>()
451                .join(",");
452
453            serializer.serialize_str(&values)
454        }
455        None => serializer.serialize_none(),
456    }
457}
458
459// fn comma_separated_enum<S, T>(
460//     values: &Vec<T>,
461//     serializer: S,
462// ) -> std::result::Result<S::Ok, S::Error>
463// where
464//     S: serde::Serializer,
465//     T: Serialize,
466// {
467//     let mut serialized = vec![];
468
469//     for val in values {
470//         let serialized_value = serde_json::to_string(val).map_err(serde::ser::Error::custom)?;
471//         serialized.push(serialized_value);
472//     }
473
474//     serializer.serialize_str(serialized.join(",").as_str())
475// }