paddle_rust_sdk/
prices.rs

1//! Request builders for working with price entities in Paddle API.
2//!
3//! See the [Paddle API](https://developer.paddle.com/api-reference/prices/overview) documentation for more information.
4
5use std::collections::HashMap;
6use std::ops::Range;
7
8use reqwest::Method;
9use serde::Serialize;
10use serde_with::skip_serializing_none;
11
12use crate::entities::{Duration, Money, Price, PriceQuantity, UnitPriceOverride};
13use crate::enums::{CatalogType, CountryCodeSupported, CurrencyCode, Interval, Status, TaxMode};
14use crate::ids::{PriceID, ProductID};
15use crate::paginated::Paginated;
16use crate::{Paddle, Result};
17
18/// Request builder for fetching prices from Paddle API.
19#[skip_serializing_none]
20#[derive(Serialize)]
21pub struct PricesList<'a> {
22    #[serde(skip)]
23    client: &'a Paddle,
24    after: Option<PriceID>,
25    #[serde(serialize_with = "crate::comma_separated")]
26    id: Option<Vec<PriceID>>,
27    #[serde(serialize_with = "crate::comma_separated")]
28    include: Option<Vec<String>>,
29    order_by: Option<String>,
30    per_page: Option<usize>,
31    #[serde(serialize_with = "crate::comma_separated")]
32    product_id: Option<Vec<ProductID>>,
33    status: Option<Status>,
34    recurring: Option<bool>,
35    r#type: Option<CatalogType>,
36}
37
38impl<'a> PricesList<'a> {
39    pub fn new(client: &'a Paddle) -> Self {
40        Self {
41            client,
42            after: None,
43            id: None,
44            include: None,
45            order_by: None,
46            per_page: None,
47            product_id: None,
48            status: None,
49            recurring: None,
50            r#type: None,
51        }
52    }
53
54    /// Return entities after the specified Paddle ID when working with paginated endpoints. Used in the `meta.pagination.next` URL in responses for list operations.
55    pub fn after(&mut self, price_id: impl Into<PriceID>) -> &mut Self {
56        self.after = Some(price_id.into());
57        self
58    }
59
60    /// Return only the IDs specified.
61    pub fn ids(&mut self, price_ids: impl IntoIterator<Item = impl Into<PriceID>>) -> &mut Self {
62        self.id = Some(price_ids.into_iter().map(Into::into).collect());
63        self
64    }
65
66    /// Include related entities in the response. Valid values are: "product".
67    pub fn include(&mut self, includes: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
68        self.include = Some(includes.into_iter().map(Into::into).collect());
69        self
70    }
71
72    /// Order returned entities by the specified field. Valid fields for ordering: billing_cycle.frequency, billing_cycle.interval, id, product_id, quantity.maximum, quantity.minimum, status, tax_mode, unit_price.amount, and unit_price.currency_code
73    pub fn order_by_asc(&mut self, field: &str) -> &mut Self {
74        self.order_by = Some(format!("{}[ASC]", field));
75        self
76    }
77
78    /// Order returned entities by the specified field. Valid fields for ordering: billing_cycle.frequency, billing_cycle.interval, id, product_id, quantity.maximum, quantity.minimum, status, tax_mode, unit_price.amount, and unit_price.currency_code
79    pub fn order_by_desc(&mut self, field: &str) -> &mut Self {
80        self.order_by = Some(format!("{}[DESC]", field));
81        self
82    }
83
84    /// Set how many entities are returned per page. Paddle returns the maximum number of results if a number greater than the maximum is requested.
85    /// Check `meta.pagination.per_page` in the response to see how many were returned.
86    ///
87    /// Default: `50`; Maximum: `200`.
88    pub fn per_page(&mut self, entities_per_page: usize) -> &mut Self {
89        self.per_page = Some(entities_per_page);
90        self
91    }
92
93    /// Return only prices for the specified product IDs.
94    pub fn product_ids(
95        &mut self,
96        product_ids: impl IntoIterator<Item = impl Into<ProductID>>,
97    ) -> &mut Self {
98        self.product_id = Some(product_ids.into_iter().map(Into::into).collect());
99        self
100    }
101
102    /// Return only prices with the specified status.
103    pub fn status(&mut self, status: Status) -> &mut Self {
104        self.status = Some(status);
105        self
106    }
107
108    /// Determine whether returned entities are for recurring prices (true) or one-time prices (false)
109    pub fn recurring(&mut self, value: bool) -> &mut Self {
110        self.recurring = Some(value);
111        self
112    }
113
114    /// Return only prices with the specified type.
115    pub fn r#type(&mut self, catalog_type: CatalogType) -> &mut Self {
116        self.r#type = Some(catalog_type);
117        self
118    }
119
120    /// Returns a paginator for fetching pages of entities from Paddle
121    pub fn send(&self) -> Paginated<'_, Vec<Price>> {
122        Paginated::new(self.client, "/prices", self)
123    }
124}
125
126/// Request builder for creating a new price in Paddle API.
127#[skip_serializing_none]
128#[derive(Serialize)]
129pub struct PricesCreate<'a> {
130    #[serde(skip)]
131    client: &'a Paddle,
132    description: String,
133    product_id: ProductID,
134    unit_price: Money,
135    r#type: Option<CatalogType>,
136    name: Option<String>,
137    billing_cycle: Option<Duration>,
138    trial_period: Option<Duration>,
139    tax_mode: TaxMode,
140    unit_price_overrides: Option<Vec<UnitPriceOverride>>,
141    quantity: Option<PriceQuantity>,
142    custom_data: Option<HashMap<String, String>>,
143}
144
145impl<'a> PricesCreate<'a> {
146    pub fn new(
147        client: &'a Paddle,
148        product_id: impl Into<ProductID>,
149        description: impl Into<String>,
150        amount: u64,
151        currency: CurrencyCode,
152    ) -> Self {
153        Self {
154            client,
155            description: description.into(),
156            product_id: product_id.into(),
157            unit_price: Money {
158                amount: amount.to_string(),
159                currency_code: currency,
160            },
161            r#type: None,
162            name: None,
163            billing_cycle: None,
164            trial_period: None,
165            tax_mode: TaxMode::AccountSetting,
166            unit_price_overrides: None,
167            quantity: None,
168            custom_data: None,
169        }
170    }
171
172    /// Set the price type.
173    pub fn catalog_type(&mut self, catalog_type: CatalogType) -> &mut Self {
174        self.r#type = Some(catalog_type);
175        self
176    }
177
178    /// Name of this price, shown to customers at checkout and on invoices. Typically describes how often the related product bills.
179    pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
180        self.name = Some(name.into());
181        self
182    }
183
184    /// How often this price should be charged.
185    pub fn billing_cycle(&mut self, frequency: u64, interval: Interval) -> &mut Self {
186        self.billing_cycle = Some(Duration {
187            interval,
188            frequency,
189        });
190
191        self
192    }
193
194    /// Trial period for the product related to this price. The billing cycle begins once the trial period is over. Requires billing_cycle.
195    pub fn trial_period(&mut self, frequency: u64, interval: Interval) -> &mut Self {
196        self.trial_period = Some(Duration {
197            interval,
198            frequency,
199        });
200
201        self
202    }
203
204    /// How tax is calculated for this price. If omitted, defaults to TaxMode::AccountSetting.
205    /// See [TaxMode] for more information.
206    pub fn tax_mode(&mut self, tax_mode: TaxMode) -> &mut Self {
207        self.tax_mode = tax_mode;
208        self
209    }
210
211    /// Use to override the base price with a custom price and currency for a country or group of countries.
212    /// See [UnitPriceOverride] for more information.
213    /// See [CountryCodeSupported] for more information.
214    /// See [Money] for more information.
215    /// See [CurrencyCode] for more information.
216    pub fn add_unit_price_override(
217        &mut self,
218        country_codes: impl IntoIterator<Item = CountryCodeSupported>,
219        amount: u64,
220        currency: CurrencyCode,
221    ) -> &mut Self {
222        if self.unit_price_overrides.is_none() {
223            self.unit_price_overrides = Some(vec![]);
224        }
225
226        self.unit_price_overrides
227            .as_mut()
228            .unwrap()
229            .push(UnitPriceOverride {
230                country_codes: country_codes.into_iter().collect(),
231                unit_price: Money {
232                    amount: amount.to_string(),
233                    currency_code: currency,
234                },
235            });
236
237        self
238    }
239
240    /// Use to override the base price with a custom price and currency for a country or group of countries.
241    /// This will replace any existing overrides.
242    /// Use `add_unit_price_override` to add additional overrides.
243    /// See [UnitPriceOverride] for more information.
244    /// See [CountryCodeSupported] for more information.
245    /// See [Money] for more information.
246    /// See [CurrencyCode] for more information.
247    pub fn set_unit_price_overrides(&mut self, overrides: Vec<UnitPriceOverride>) -> &mut Self {
248        self.unit_price_overrides = Some(overrides);
249        self
250    }
251
252    /// Limits on how many times the related product can be purchased at this price. Useful for discount campaigns. If omitted, defaults to 1..100.
253    pub fn quantity(&mut self, range: Range<u64>) -> &mut Self {
254        self.quantity = Some(PriceQuantity {
255            minimum: range.start,
256            maximum: range.end,
257        });
258        self
259    }
260
261    /// Set custom data for this price.
262    pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
263        self.custom_data = Some(custom_data);
264        self
265    }
266
267    /// Send the request to Paddle and return the response.
268    pub async fn send(&self) -> Result<Price> {
269        self.client.send(self, Method::POST, "/prices").await
270    }
271}
272
273/// Request builder for fetching a specific price from Paddle API.
274#[skip_serializing_none]
275#[derive(Serialize)]
276pub struct PriceGet<'a> {
277    #[serde(skip)]
278    client: &'a Paddle,
279    #[serde(skip)]
280    price_id: PriceID,
281    #[serde(serialize_with = "crate::comma_separated")]
282    include: Option<Vec<String>>,
283}
284
285impl<'a> PriceGet<'a> {
286    pub fn new(client: &'a Paddle, price_id: impl Into<PriceID>) -> Self {
287        Self {
288            client,
289            price_id: price_id.into(),
290            include: None,
291        }
292    }
293
294    /// Include related entities in the response. Allowed values: "product".
295    pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
296        self.include = Some(
297            entities
298                .into_iter()
299                .map(|s| s.as_ref().to_string())
300                .collect(),
301        );
302        self
303    }
304
305    /// Send the request to Paddle and return the response.
306    pub async fn send(&self) -> Result<Price> {
307        self.client
308            .send(
309                self,
310                Method::GET,
311                &format!("/prices/{}", self.price_id.as_ref()),
312            )
313            .await
314    }
315}
316
317/// Request builder for updating a price in Paddle API.
318#[skip_serializing_none]
319#[derive(Serialize)]
320pub struct PriceUpdate<'a> {
321    #[serde(skip)]
322    client: &'a Paddle,
323    #[serde(skip)]
324    price_id: PriceID,
325    description: Option<String>,
326    r#type: Option<CatalogType>,
327    name: Option<String>,
328    billing_cycle: Option<Duration>,
329    trial_period: Option<Duration>,
330    tax_mode: Option<TaxMode>,
331    unit_price: Option<Money>,
332    unit_price_overrides: Option<Vec<UnitPriceOverride>>,
333    quantity: Option<PriceQuantity>,
334    status: Option<Status>,
335    custom_data: Option<HashMap<String, String>>,
336}
337
338impl<'a> PriceUpdate<'a> {
339    pub fn new(client: &'a Paddle, price_id: impl Into<PriceID>) -> Self {
340        Self {
341            client,
342            price_id: price_id.into(),
343            description: None,
344            r#type: None,
345            name: None,
346            billing_cycle: None,
347            trial_period: None,
348            tax_mode: None,
349            unit_price: None,
350            unit_price_overrides: None,
351            quantity: None,
352            status: None,
353            custom_data: None,
354        }
355    }
356
357    /// Update the price description.
358    pub fn description(&mut self, description: impl Into<String>) -> &mut Self {
359        self.description = Some(description.into());
360        self
361    }
362
363    /// Update the price type.
364    pub fn catalog_type(&mut self, catalog_type: CatalogType) -> &mut Self {
365        self.r#type = Some(catalog_type);
366        self
367    }
368
369    /// Update the price name. Name is shown to customers at checkout and on invoices. Typically describes how often the related product bills.
370    pub fn name(&mut self, name: impl Into<String>) -> &mut Self {
371        self.name = Some(name.into());
372        self
373    }
374
375    /// Update how often this price should be charged.
376    pub fn billing_cycle(&mut self, frequency: u64, interval: Interval) -> &mut Self {
377        self.billing_cycle = Some(Duration {
378            interval,
379            frequency,
380        });
381
382        self
383    }
384
385    /// Update the trial period for the product related to this price.
386    pub fn trial_period(&mut self, frequency: u64, interval: Interval) -> &mut Self {
387        self.trial_period = Some(Duration {
388            interval,
389            frequency,
390        });
391
392        self
393    }
394
395    /// Update how tax is calculated for this price.
396    pub fn tax_mode(&mut self, tax_mode: TaxMode) -> &mut Self {
397        self.tax_mode = Some(tax_mode);
398        self
399    }
400
401    /// Update the base price. This price applies to all customers, except for customers located in countries where you have unit_price_overrides.
402    pub fn unit_price(&mut self, amount: u64, currency: CurrencyCode) -> &mut Self {
403        self.unit_price = Some(Money {
404            amount: amount.to_string(),
405            currency_code: currency,
406        });
407        self
408    }
409
410    /// Use to override the base price with a custom price and currency for a country or group of countries.
411    pub fn add_unit_price_override(
412        &mut self,
413        country_codes: impl IntoIterator<Item = CountryCodeSupported>,
414        amount: u64,
415        currency: CurrencyCode,
416    ) -> &mut Self {
417        if self.unit_price_overrides.is_none() {
418            self.unit_price_overrides = Some(vec![]);
419        }
420
421        self.unit_price_overrides
422            .as_mut()
423            .unwrap()
424            .push(UnitPriceOverride {
425                country_codes: country_codes.into_iter().collect(),
426                unit_price: Money {
427                    amount: amount.to_string(),
428                    currency_code: currency,
429                },
430            });
431
432        self
433    }
434
435    /// Use to override the base price with a custom price and currency for a country or group of countries.
436    pub fn set_unit_price_overrides(&mut self, overrides: Vec<UnitPriceOverride>) -> &mut Self {
437        self.unit_price_overrides = Some(overrides);
438        self
439    }
440
441    /// Update how many times the related product can be purchased at this price.
442    pub fn quantity(&mut self, range: Range<u64>) -> &mut Self {
443        self.quantity = Some(PriceQuantity {
444            minimum: range.start,
445            maximum: range.end,
446        });
447        self
448    }
449
450    /// Update whether this entity can be used in Paddle.
451    pub fn status(&mut self, status: Status) -> &mut Self {
452        self.status = Some(status);
453        self
454    }
455
456    /// Set custom data for the price.
457    pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
458        self.custom_data = Some(custom_data);
459        self
460    }
461
462    /// Send the request to Paddle and return the response.
463    pub async fn send(&self) -> Result<Price> {
464        self.client
465            .send(
466                self,
467                Method::PATCH,
468                &format!("/prices/{}", self.price_id.as_ref()),
469            )
470            .await
471    }
472}