paddle_rust_sdk/
prices.rs

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