Skip to main content

paddle_rust_sdk/
transactions.rs

1//! Builders for making requests to the Paddle API for transaction entities.
2//!
3//! See the [Paddle API](https://developer.paddle.com/api-reference/transactions/overview) documentation for more information.
4
5use std::collections::HashMap;
6
7use chrono::{DateTime, Utc};
8use reqwest::Method;
9use serde::Serialize;
10use serde_with::skip_serializing_none;
11
12use crate::entities::{
13    AddressPreview, BillingDetails, TimePeriod, Transaction, TransactionCheckout,
14    TransactionItemNonCatalogPrice,
15};
16use crate::enums::{CollectionMode, CurrencyCode, TransactionOrigin, TransactionStatus};
17use crate::ids::{
18    AddressID, BusinessID, CustomerID, DiscountID, PriceID, SubscriptionID, TransactionID,
19};
20use crate::nullable::Nullable;
21use crate::paginated::Paginated;
22use crate::{Paddle, Result};
23
24#[allow(non_snake_case)]
25#[skip_serializing_none]
26#[derive(Serialize, Default)]
27struct DateAtFilter {
28    LT: Option<DateTime<Utc>>,
29    LTE: Option<DateTime<Utc>>,
30    GT: Option<DateTime<Utc>>,
31    GTE: Option<DateTime<Utc>>,
32}
33
34#[derive(Serialize)]
35#[serde(untagged)]
36enum DateAt {
37    Exact(DateTime<Utc>),
38    Filter(DateAtFilter),
39}
40
41/// Request builder for fetching transactions from Paddle API.
42#[skip_serializing_none]
43#[derive(Serialize)]
44pub struct TransactionsList<'a> {
45    #[serde(skip)]
46    client: &'a Paddle,
47    after: Option<TransactionID>,
48    billed_at: Option<DateAt>,
49    collection_mode: Option<CollectionMode>,
50    created_at: Option<DateAt>,
51    #[serde(serialize_with = "crate::comma_separated")]
52    customer_id: Option<Vec<CustomerID>>,
53    #[serde(serialize_with = "crate::comma_separated")]
54    id: Option<Vec<TransactionID>>,
55    #[serde(serialize_with = "crate::comma_separated")]
56    include: Option<Vec<String>>,
57    #[serde(serialize_with = "crate::comma_separated")]
58    invoice_number: Option<Vec<String>>,
59    #[serde(serialize_with = "crate::comma_separated_enum")]
60    origin: Option<Vec<TransactionOrigin>>,
61    order_by: Option<String>,
62    #[serde(serialize_with = "crate::comma_separated_enum")]
63    status: Option<Vec<TransactionStatus>>,
64    #[serde(serialize_with = "crate::comma_separated")]
65    subscription_id: Option<Vec<SubscriptionID>>,
66    per_page: Option<usize>,
67    updated_at: Option<DateAt>,
68}
69
70impl<'a> TransactionsList<'a> {
71    pub fn new(client: &'a Paddle) -> Self {
72        Self {
73            client,
74            after: None,
75            billed_at: None,
76            collection_mode: None,
77            created_at: None,
78            customer_id: None,
79            id: None,
80            include: None,
81            invoice_number: None,
82            origin: None,
83            order_by: None,
84            status: None,
85            subscription_id: None,
86            per_page: None,
87            updated_at: None,
88        }
89    }
90
91    /// Return entities after the specified Paddle ID when working with paginated endpoints. Used in the `meta.pagination.next` URL in responses for list operations.
92    pub fn after(&mut self, transaction_id: impl Into<TransactionID>) -> &mut Self {
93        self.after = Some(transaction_id.into());
94        self
95    }
96
97    /// Return entities billed at a specific time.
98    pub fn billed_at(&mut self, date: DateTime<Utc>) -> &mut Self {
99        self.billed_at = Some(DateAt::Exact(date));
100        self
101    }
102
103    /// Return entities billed before the specified time.
104    pub fn billed_at_lt(&mut self, date: DateTime<Utc>) -> &mut Self {
105        self.billed_at = Some(DateAt::Filter(DateAtFilter {
106            LT: Some(date),
107            ..Default::default()
108        }));
109
110        self
111    }
112
113    /// Return entities billed before or on the specified time.
114    pub fn billed_at_lte(&mut self, date: DateTime<Utc>) -> &mut Self {
115        self.billed_at = Some(DateAt::Filter(DateAtFilter {
116            LTE: Some(date),
117            ..Default::default()
118        }));
119
120        self
121    }
122
123    /// Return entities billed after the specified time.
124    pub fn billed_at_gt(&mut self, date: DateTime<Utc>) -> &mut Self {
125        self.billed_at = Some(DateAt::Filter(DateAtFilter {
126            GT: Some(date),
127            ..Default::default()
128        }));
129
130        self
131    }
132
133    /// Return entities billed after or on the specified time.
134    pub fn billed_at_gte(&mut self, date: DateTime<Utc>) -> &mut Self {
135        self.billed_at = Some(DateAt::Filter(DateAtFilter {
136            GTE: Some(date),
137            ..Default::default()
138        }));
139
140        self
141    }
142
143    /// Return entities that match the specified collection mode.
144    pub fn collection_mode(&mut self, mode: CollectionMode) -> &mut Self {
145        self.collection_mode = Some(mode);
146        self
147    }
148
149    /// Return entities created at a specific time.
150    pub fn created_at(&mut self, date: DateTime<Utc>) -> &mut Self {
151        self.created_at = Some(DateAt::Exact(date));
152        self
153    }
154
155    /// Return entities created before the specified time.
156    pub fn created_at_lt(&mut self, date: DateTime<Utc>) -> &mut Self {
157        self.created_at = Some(DateAt::Filter(DateAtFilter {
158            LT: Some(date),
159            ..Default::default()
160        }));
161
162        self
163    }
164
165    /// Return entities created before or on the specified time.
166    pub fn created_at_lte(&mut self, date: DateTime<Utc>) -> &mut Self {
167        self.created_at = Some(DateAt::Filter(DateAtFilter {
168            LTE: Some(date),
169            ..Default::default()
170        }));
171
172        self
173    }
174
175    /// Return entities created after the specified time.
176    pub fn created_at_gt(&mut self, date: DateTime<Utc>) -> &mut Self {
177        self.created_at = Some(DateAt::Filter(DateAtFilter {
178            GT: Some(date),
179            ..Default::default()
180        }));
181
182        self
183    }
184
185    /// Return entities created after or on the specified time.
186    pub fn created_at_gte(&mut self, date: DateTime<Utc>) -> &mut Self {
187        self.created_at = Some(DateAt::Filter(DateAtFilter {
188            GTE: Some(date),
189            ..Default::default()
190        }));
191
192        self
193    }
194
195    /// Return entities related to the specified customers.
196    pub fn customer_id(
197        &mut self,
198        customer_ids: impl IntoIterator<Item = impl Into<CustomerID>>,
199    ) -> &mut Self {
200        self.customer_id = Some(customer_ids.into_iter().map(Into::into).collect());
201        self
202    }
203
204    /// Return only the IDs specified.
205    pub fn id(&mut self, ids: impl IntoIterator<Item = impl Into<TransactionID>>) -> &mut Self {
206        self.id = Some(ids.into_iter().map(Into::into).collect());
207        self
208    }
209
210    /// Include related entities in the response.
211    ///
212    /// Valid values are:
213    ///
214    /// - `address`
215    /// - `adjustments`
216    /// - `adjustments_totals`
217    /// - `available_payment_methods`
218    /// - `business`
219    /// - `customer`
220    /// - `discount`
221    ///
222    pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
223        self.include = Some(
224            entities
225                .into_iter()
226                .map(|s| s.as_ref().to_string())
227                .collect(),
228        );
229        self
230    }
231
232    /// Return entities that match the invoice number.
233    pub fn invoice_numbers(
234        &mut self,
235        numbers: impl IntoIterator<Item = impl AsRef<str>>,
236    ) -> &mut Self {
237        self.invoice_number = Some(
238            numbers
239                .into_iter()
240                .map(|s| s.as_ref().to_string())
241                .collect(),
242        );
243        self
244    }
245
246    /// Return entities related to the specified origin(s).
247    pub fn origin(&mut self, origins: impl IntoIterator<Item = TransactionOrigin>) -> &mut Self {
248        self.origin = Some(origins.into_iter().collect());
249        self
250    }
251
252    /// Order returned entities by the specified field. Valid fields for ordering: `billed_at`, `created_at`, `id`, `updated_at`
253    pub fn order_by_asc(&mut self, field: &str) -> &mut Self {
254        self.order_by = Some(format!("{}[ASC]", field));
255        self
256    }
257
258    /// Order returned entities by the specified field. Valid fields for ordering: `billed_at`, `created_at`, `id`, `updated_at`
259    pub fn order_by_desc(&mut self, field: &str) -> &mut Self {
260        self.order_by = Some(format!("{}[DESC]", field));
261        self
262    }
263
264    /// Return entities that match the specified status.
265    pub fn status(&mut self, statuses: impl IntoIterator<Item = TransactionStatus>) -> &mut Self {
266        self.status = Some(statuses.into_iter().collect());
267        self
268    }
269
270    /// Return entities related to the specified subscription.
271    pub fn subscription_ids(
272        &mut self,
273        subscription_ids: impl IntoIterator<Item = impl Into<SubscriptionID>>,
274    ) -> &mut Self {
275        self.subscription_id = Some(subscription_ids.into_iter().map(Into::into).collect());
276        self
277    }
278
279    /// Set how many entities are returned per page. Paddle returns the maximum number of results if a number greater than the maximum is requested.
280    /// Check `meta.pagination.per_page` in the response to see how many were returned.
281    ///
282    /// Default: `50`; Maximum: `200`.
283    pub fn per_page(&mut self, entities_per_page: usize) -> &mut Self {
284        self.per_page = Some(entities_per_page);
285        self
286    }
287
288    /// Return entities updated at a specific time.
289    pub fn updated_at(&mut self, date: DateTime<Utc>) -> &mut Self {
290        self.updated_at = Some(DateAt::Exact(date));
291        self
292    }
293
294    /// Return entities updated before the specified time.
295    pub fn updated_at_lt(&mut self, date: DateTime<Utc>) -> &mut Self {
296        self.updated_at = Some(DateAt::Filter(DateAtFilter {
297            LT: Some(date),
298            ..Default::default()
299        }));
300
301        self
302    }
303
304    /// Return entities updated before or on the specified time.
305    pub fn updated_at_lte(&mut self, date: DateTime<Utc>) -> &mut Self {
306        self.updated_at = Some(DateAt::Filter(DateAtFilter {
307            LTE: Some(date),
308            ..Default::default()
309        }));
310
311        self
312    }
313
314    /// Return entities updated after the specified time.
315    pub fn updated_at_gt(&mut self, date: DateTime<Utc>) -> &mut Self {
316        self.updated_at = Some(DateAt::Filter(DateAtFilter {
317            GT: Some(date),
318            ..Default::default()
319        }));
320
321        self
322    }
323
324    /// Return entities updated after or on the specified time.
325    pub fn updated_at_gte(&mut self, date: DateTime<Utc>) -> &mut Self {
326        self.updated_at = Some(DateAt::Filter(DateAtFilter {
327            GTE: Some(date),
328            ..Default::default()
329        }));
330
331        self
332    }
333
334    /// Returns a paginator for fetching pages of entities from Paddle
335    pub fn send(&self) -> Paginated<'_, Vec<Transaction>> {
336        Paginated::new(self.client, "/transactions", self)
337    }
338}
339
340#[derive(Serialize)]
341#[serde(untagged)]
342#[allow(clippy::large_enum_variant)]
343pub enum TransactionItem {
344    CatalogItem {
345        price_id: PriceID,
346        quantity: u32,
347    },
348    NonCatalogItem {
349        price: TransactionItemNonCatalogPrice,
350        quantity: u32,
351    },
352}
353
354/// Request builder for creating a transaction in Paddle.
355#[skip_serializing_none]
356#[derive(Serialize)]
357pub struct TransactionCreate<'a> {
358    #[serde(skip)]
359    client: &'a Paddle,
360    #[serde(skip)]
361    include: Option<Vec<String>>,
362    items: Vec<TransactionItem>,
363    status: Option<TransactionStatus>,
364    customer_id: Option<CustomerID>,
365    address_id: Option<AddressID>,
366    business_id: Option<BusinessID>,
367    custom_data: Option<HashMap<String, String>>,
368    currency_code: Option<CurrencyCode>,
369    collection_mode: Option<CollectionMode>,
370    discount_id: Option<DiscountID>,
371    billing_details: Option<BillingDetails>,
372    billing_period: Option<TimePeriod>,
373    checkout: Option<TransactionCheckout>,
374}
375
376impl<'a> TransactionCreate<'a> {
377    pub fn new(client: &'a Paddle) -> Self {
378        Self {
379            client,
380            include: None,
381            items: Vec::default(),
382            status: None,
383            customer_id: None,
384            address_id: None,
385            business_id: None,
386            custom_data: None,
387            currency_code: None,
388            collection_mode: None,
389            discount_id: None,
390            billing_details: None,
391            billing_period: None,
392            checkout: None,
393        }
394    }
395
396    /// Include related entities in the response.
397    ///
398    /// ## Valid values are:
399    ///
400    /// - `address`
401    /// - `adjustments`
402    /// - `adjustments_totals`
403    /// - `available_payment_methods`
404    /// - `business`
405    /// - `customer`
406    /// - `discount`
407    pub fn include(&mut self, includes: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
408        self.include = Some(includes.into_iter().map(Into::into).collect());
409        self
410    }
411
412    /// Append to the list of items to charge for.
413    ///
414    /// You can charge for items that you've added to your catalog by passing the Paddle ID of an existing price entity,
415    ///
416    /// To charge for non-catalog items see append_non_catalog_item.
417    pub fn append_catalog_item(
418        &mut self,
419        price_id: impl Into<PriceID>,
420        quantity: u32,
421    ) -> &mut Self {
422        self.items.push(TransactionItem::CatalogItem {
423            price_id: price_id.into(),
424            quantity,
425        });
426
427        self
428    }
429
430    /// Append to the list of items to charge for.
431    ///
432    /// You can charge for non-catalog items by passing a `TransactionItemNonCatalogPrice` object.
433    pub fn append_non_catalog_item(
434        &mut self,
435        price: TransactionItemNonCatalogPrice,
436        quantity: u32,
437    ) -> &mut Self {
438        self.items
439            .push(TransactionItem::NonCatalogItem { price, quantity });
440        self
441    }
442
443    /// Status of this transaction. You may set a transaction to billed when creating, or omit to let Paddle set the status.
444    ///
445    /// Transactions are created as ready if they have an address_id, customer_id, and items, otherwise they are created as draft.
446    ///
447    /// Marking as billed when creating is typically used when working with manually-collected transactions as part of an invoicing workflow. Billed transactions cannot be updated, only canceled.
448    pub fn status(&mut self, status: TransactionStatus) -> &mut Self {
449        self.status = Some(status);
450        self
451    }
452
453    /// Paddle ID of the customer that this transaction is for.
454    ///
455    /// If omitted, transaction status is `draft`.
456    pub fn customer_id(&mut self, customer_id: impl Into<CustomerID>) -> &mut Self {
457        self.customer_id = Some(customer_id.into());
458        self
459    }
460
461    /// Paddle ID of the address that this transaction is for.
462    ///
463    /// Requires customer_id. If omitted, transaction status is draft.
464    pub fn address_id(&mut self, address_id: impl Into<AddressID>) -> &mut Self {
465        self.address_id = Some(address_id.into());
466        self
467    }
468
469    /// Paddle ID of the business that this transaction is for.
470    ///
471    /// Requires customer_id
472    pub fn business_id(&mut self, business_id: impl Into<BusinessID>) -> &mut Self {
473        self.business_id = Some(business_id.into());
474        self
475    }
476
477    /// Your own structured key-value data.
478    pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
479        self.custom_data = Some(custom_data);
480        self
481    }
482
483    /// Supported three-letter ISO 4217 currency code. Must be `USD`, `EUR`, or `GBP` if `collection_mode` is `manual`.
484    pub fn currency_code(&mut self, currency_code: CurrencyCode) -> &mut Self {
485        self.currency_code = Some(currency_code);
486        self
487    }
488
489    /// How payment is collected for this transaction. `automatic` for checkout, `manual` for invoices. If omitted, defaults to `automatic`.
490    pub fn collection_mode(&mut self, mode: CollectionMode) -> &mut Self {
491        self.collection_mode = Some(mode);
492        self
493    }
494
495    /// Paddle ID of the discount applied to this transaction.
496    pub fn discount_id(&mut self, discount_id: impl Into<DiscountID>) -> &mut Self {
497        self.discount_id = Some(discount_id.into());
498        self
499    }
500
501    /// Details for invoicing. Required if `collection_mode` is `manual`.
502    pub fn billing_details(&mut self, billing_details: BillingDetails) -> &mut Self {
503        self.billing_details = Some(billing_details);
504        self
505    }
506
507    /// Time period that this transaction is for. Set automatically by Paddle for subscription renewals to describe the period that charges are for.
508    pub fn billing_period(&mut self, billing_period: TimePeriod) -> &mut Self {
509        self.billing_period = Some(billing_period);
510        self
511    }
512
513    /// Paddle Checkout URL for creating or updating an automatically-collected transaction, or when creating or updating a manually-collected transaction
514    /// where `billing_details.enable_checkout` is `true`.
515    ///
516    /// Pass the URL for an approved domain, or null to set to your default payment URL.
517    ///
518    /// Paddle returns a unique payment link composed of the URL passed or your default payment URL + ?_ptxn= and the Paddle ID for this transaction.
519    pub fn checkout_url(&mut self, url: String) -> &mut Self {
520        self.checkout = Some(TransactionCheckout { url: Some(url) });
521        self
522    }
523
524    /// Send the request to Paddle and return the response.
525    pub async fn send(&self) -> Result<Transaction> {
526        let url = if let Some(include) = self.include.as_ref() {
527            &format!("/transactions?include={}", include.join(","))
528        } else {
529            "/transactions"
530        };
531
532        self.client.send(self, Method::POST, url).await
533    }
534}
535
536/// Request builder for fetching a specific transaction.
537#[skip_serializing_none]
538#[derive(Serialize)]
539pub struct TransactionGet<'a> {
540    #[serde(skip)]
541    client: &'a Paddle,
542    #[serde(skip)]
543    transaction_id: TransactionID,
544    #[serde(serialize_with = "crate::comma_separated")]
545    include: Option<Vec<String>>,
546}
547
548impl<'a> TransactionGet<'a> {
549    pub fn new(client: &'a Paddle, transaction_id: impl Into<TransactionID>) -> Self {
550        Self {
551            client,
552            transaction_id: transaction_id.into(),
553            include: None,
554        }
555    }
556
557    /// Include related entities in the response.
558    ///
559    /// ## Valid values are:
560    ///
561    /// - `address`
562    /// - `adjustments`
563    /// - `adjustments_totals`
564    /// - `available_payment_methods`
565    /// - `business`
566    /// - `customer`
567    /// - `discount`
568    pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
569        self.include = Some(
570            entities
571                .into_iter()
572                .map(|s| s.as_ref().to_string())
573                .collect(),
574        );
575        self
576    }
577
578    /// Send the request to Paddle and return the response.
579    pub async fn send(&self) -> Result<Transaction> {
580        self.client
581            .send(
582                self,
583                Method::GET,
584                &format!("/transactions/{}", self.transaction_id.as_ref()),
585            )
586            .await
587    }
588}
589
590/// Request builder for updating a transaction.
591#[derive(Serialize)]
592pub struct TransactionUpdate<'a> {
593    #[serde(skip)]
594    client: &'a Paddle,
595    #[serde(skip)]
596    transaction_id: TransactionID,
597    #[serde(skip)]
598    include: Option<Vec<String>>,
599    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
600    status: Nullable<TransactionStatus>,
601    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
602    customer_id: Nullable<CustomerID>,
603    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
604    address_id: Nullable<AddressID>,
605    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
606    business_id: Nullable<BusinessID>,
607    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
608    custom_data: Nullable<HashMap<String, String>>,
609    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
610    currency_code: Nullable<CurrencyCode>,
611    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
612    collection_mode: Nullable<CollectionMode>,
613    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
614    discount_id: Nullable<DiscountID>,
615    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
616    billing_details: Nullable<BillingDetails>,
617    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
618    billing_period: Nullable<TimePeriod>,
619    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
620    items: Nullable<Vec<TransactionItem>>,
621    #[serde(skip_serializing_if = "Nullable::is_unchanged")]
622    checkout: Nullable<TransactionCheckout>,
623}
624
625impl<'a> TransactionUpdate<'a> {
626    pub fn new(client: &'a Paddle, transaction_id: impl Into<TransactionID>) -> Self {
627        Self {
628            client,
629            transaction_id: transaction_id.into(),
630            include: None,
631            status: Nullable::Unchanged,
632            customer_id: Nullable::Unchanged,
633            address_id: Nullable::Unchanged,
634            business_id: Nullable::Unchanged,
635            custom_data: Nullable::Unchanged,
636            currency_code: Nullable::Unchanged,
637            collection_mode: Nullable::Unchanged,
638            discount_id: Nullable::Unchanged,
639            billing_details: Nullable::Unchanged,
640            billing_period: Nullable::Unchanged,
641            items: Nullable::Unchanged,
642            checkout: Nullable::Unchanged,
643        }
644    }
645
646    /// Include related entities in the response.
647    ///
648    /// ## Valid values are:
649    ///
650    /// - `address`
651    /// - `adjustments`
652    /// - `adjustments_totals`
653    /// - `available_payment_methods`
654    /// - `business`
655    /// - `customer`
656    /// - `discount`
657    pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
658        self.include = Some(
659            entities
660                .into_iter()
661                .map(|s| s.as_ref().to_string())
662                .collect(),
663        );
664        self
665    }
666
667    /// Status of this transaction. You may set a transaction to billed or canceled. Billed transactions cannot be changed.
668    ///
669    /// For manually-collected transactions, marking as billed is essentially issuing an invoice.
670    pub fn status(&mut self, status: impl Into<Nullable<TransactionStatus>>) -> &mut Self {
671        self.status = status.into();
672        self
673    }
674
675    /// Paddle ID of the customer that this transaction is for.
676    pub fn customer_id(&mut self, customer_id: impl Into<Nullable<CustomerID>>) -> &mut Self {
677        self.customer_id = customer_id.into();
678        self
679    }
680
681    /// Paddle ID of the address that this transaction is for.
682    pub fn address_id(&mut self, address_id: impl Into<Nullable<AddressID>>) -> &mut Self {
683        self.address_id = address_id.into();
684        self
685    }
686
687    /// Paddle ID of the business that this transaction is for.
688    pub fn business_id(&mut self, business_id: impl Into<Nullable<BusinessID>>) -> &mut Self {
689        self.business_id = business_id.into();
690        self
691    }
692
693    /// Your own structured key-value data.
694    pub fn custom_data(
695        &mut self,
696        custom_data: impl Into<Nullable<HashMap<String, String>>>,
697    ) -> &mut Self {
698        self.custom_data = custom_data.into();
699        self
700    }
701
702    /// Supported three-letter currency code. Must be `USD`, `EUR`, or `GBP` if `collection_mode` is `manual`.
703    pub fn currency_code(
704        &mut self,
705        currency_code: impl Into<Nullable<CurrencyCode>>,
706    ) -> &mut Self {
707        self.currency_code = currency_code.into();
708        self
709    }
710
711    /// How payment is collected for this transaction. `automatic` for checkout, `manual` for invoices.
712    pub fn collection_mode(
713        &mut self,
714        mode: impl Into<Nullable<CollectionMode>>,
715    ) -> &mut Self {
716        self.collection_mode = mode.into();
717        self
718    }
719
720    /// Paddle ID of the discount applied to this transaction.
721    pub fn discount_id(
722        &mut self,
723        discount_id: impl Into<Nullable<DiscountID>>,
724    ) -> &mut Self {
725        self.discount_id = discount_id.into();
726        self
727    }
728
729    /// Details for invoicing. Required if `collection_mode` is `manual`.
730    pub fn billing_details(
731        &mut self,
732        billing_details: impl Into<Nullable<BillingDetails>>,
733    ) -> &mut Self {
734        self.billing_details = billing_details.into();
735        self
736    }
737
738    /// Time period that this transaction is for. Set automatically by Paddle for subscription renewals to describe the period that charges are for.
739    pub fn billing_period(
740        &mut self,
741        billing_period: impl Into<Nullable<TimePeriod>>,
742    ) -> &mut Self {
743        self.billing_period = billing_period.into();
744        self
745    }
746
747    pub fn items(
748        &mut self,
749        items: impl Into<Nullable<Vec<TransactionItem>>>,
750    ) -> &mut Self {
751        self.items = items.into();
752        self
753    }
754
755    /// Paddle Checkout URL for creating or updating an automatically-collected transaction, or when creating or updating a manually-collected transaction
756    /// where `billing_details.enable_checkout` is `true`.
757    ///
758    /// Pass the URL for an approved domain, or null to set to your default payment URL.
759    ///
760    /// Paddle returns a unique payment link composed of the URL passed or your default payment URL + ?_ptxn= and the Paddle ID for this transaction.
761    pub fn checkout_url(&mut self, url: impl Into<Nullable<String>>) -> &mut Self {
762        self.checkout = match url.into() {
763            Nullable::Unchanged => Nullable::Unchanged,
764            Nullable::Null => Nullable::Null,
765            Nullable::Value(url) => Nullable::Value(TransactionCheckout { url: Some(url) }),
766        };
767        self
768    }
769
770    /// Send the request to Paddle and return the response.
771    pub async fn send(&self) -> Result<Transaction> {
772        let mut url = format!("/transactions/{}", self.transaction_id.as_ref());
773
774        if let Some(include) = self.include.as_ref() {
775            url.push_str(&format!("?include={}", include.join(",")));
776        }
777
778        self.client.send(self, Method::PATCH, &url).await
779    }
780}
781
782/// Request builder for generating a transaction preview without creating a transaction entity.
783#[skip_serializing_none]
784#[derive(Serialize)]
785pub struct TransactionPreview<'a> {
786    #[serde(skip)]
787    client: &'a Paddle,
788    items: Vec<TransactionItem>,
789    address: Option<AddressPreview>,
790    customer_ip_address: Option<String>,
791    address_id: Option<AddressID>,
792    business_id: Option<BusinessID>,
793    customer_id: Option<CustomerID>,
794    currency_code: Option<CurrencyCode>,
795    discount_id: Option<DiscountID>,
796    ignore_trials: bool,
797}
798
799impl<'a> TransactionPreview<'a> {
800    pub fn new(client: &'a Paddle) -> Self {
801        Self {
802            client,
803            items: Vec::default(),
804            address: None,
805            customer_ip_address: None,
806            address_id: None,
807            business_id: None,
808            customer_id: None,
809            currency_code: None,
810            discount_id: None,
811            ignore_trials: false,
812        }
813    }
814
815    /// Append to the list of items to charge for.
816    ///
817    /// You can charge for items that you've added to your catalog by passing the Paddle ID of an existing price entity,
818    ///
819    /// To charge for non-catalog items see append_non_catalog_item.
820    pub fn append_catalog_item(
821        &mut self,
822        price_id: impl Into<PriceID>,
823        quantity: u32,
824    ) -> &mut Self {
825        self.items.push(TransactionItem::CatalogItem {
826            price_id: price_id.into(),
827            quantity,
828        });
829
830        self
831    }
832
833    /// Append to the list of items to charge for.
834    ///
835    /// You can charge for non-catalog items by passing a `TransactionItemNonCatalogPrice` object.
836    pub fn append_non_catalog_item(
837        &mut self,
838        price: TransactionItemNonCatalogPrice,
839        quantity: u32,
840    ) -> &mut Self {
841        self.items
842            .push(TransactionItem::NonCatalogItem { price, quantity });
843        self
844    }
845
846    /// Address to charge tax for.
847    pub fn address(&mut self, address: AddressPreview) -> &mut Self {
848        self.address = Some(address);
849        self
850    }
851
852    /// IP address of the customer. Paddle fetches location using this IP address to calculate totals.
853    pub fn customer_ip_address(&mut self, ip: String) -> &mut Self {
854        self.customer_ip_address = Some(ip);
855        self
856    }
857
858    /// Paddle ID of the address that this transaction preview is for.
859    pub fn address_id(&mut self, address_id: impl Into<AddressID>) -> &mut Self {
860        self.address_id = Some(address_id.into());
861        self
862    }
863
864    /// Paddle ID of the business that this transaction is for.
865    pub fn business_id(&mut self, business_id: impl Into<BusinessID>) -> &mut Self {
866        self.business_id = Some(business_id.into());
867        self
868    }
869
870    /// Paddle ID of the customer that this transaction is for.
871    pub fn customer_id(&mut self, customer_id: impl Into<CustomerID>) -> &mut Self {
872        self.customer_id = Some(customer_id.into());
873        self
874    }
875
876    /// Supported three-letter currency code.
877    pub fn currency_code(&mut self, currency_code: CurrencyCode) -> &mut Self {
878        self.currency_code = Some(currency_code);
879        self
880    }
881
882    /// Paddle ID of the discount applied to this transaction.
883    pub fn discount_id(&mut self, discount_id: impl Into<DiscountID>) -> &mut Self {
884        self.discount_id = Some(discount_id.into());
885        self
886    }
887
888    /// Whether trials should be ignored for transaction preview calculations.
889    ///
890    /// By default, recurring items with trials are considered to have a zero charge when previewing. Set to `true` to disable this.
891    pub fn ignore_trials(&mut self, ignore_trials: bool) -> &mut Self {
892        self.ignore_trials = ignore_trials;
893        self
894    }
895
896    /// Send the request to Paddle and return the response.
897    pub async fn send(&self) -> Result<crate::entities::TransactionPreview> {
898        self.client
899            .send(self, Method::POST, "/transactions/preview")
900            .await
901    }
902}
903
904#[derive(Serialize)]
905struct RevisedCustomer {
906    name: String,
907}
908
909#[derive(Serialize, Default)]
910#[skip_serializing_none]
911struct RevisedBusiness {
912    name: Option<String>,
913    tax_identifier: Option<String>,
914}
915
916#[derive(Serialize, Default)]
917#[skip_serializing_none]
918struct RevisedAddress {
919    first_line: Option<String>,
920    second_line: Option<String>,
921    city: Option<String>,
922    region: Option<String>,
923}
924
925#[skip_serializing_none]
926#[derive(Serialize)]
927pub struct TransactionRevise<'a> {
928    #[serde(skip)]
929    client: &'a Paddle,
930    #[serde(skip)]
931    transaction_id: TransactionID,
932    customer: Option<RevisedCustomer>,
933    business: Option<RevisedBusiness>,
934    address: Option<RevisedAddress>,
935}
936
937impl<'a> TransactionRevise<'a> {
938    pub fn new(client: &'a Paddle, transaction_id: impl Into<TransactionID>) -> Self {
939        Self {
940            client,
941            transaction_id: transaction_id.into(),
942            customer: None,
943            business: None,
944            address: None,
945        }
946    }
947
948    /// Revised name of the customer for this transaction.
949    pub fn customer_name(&mut self, name: impl Into<String>) -> &mut Self {
950        self.customer = Some(RevisedCustomer { name: name.into() });
951        self
952    }
953
954    /// Revised name of the business for this transaction.
955    pub fn business_name(&mut self, name: impl Into<String>) -> &mut Self {
956        self.business.get_or_insert_default().name = Some(name.into());
957        self
958    }
959
960    /// Revised tax or VAT number for this transaction.
961    ///
962    /// You can't remove a valid tax or VAT number, only replace it with another valid one.
963    ///
964    /// Paddle automatically creates an adjustment to refund any tax where applicable.
965    pub fn business_tax_identifier(&mut self, tax_identifier: impl Into<String>) -> &mut Self {
966        self.business.get_or_insert_default().tax_identifier = Some(tax_identifier.into());
967        self
968    }
969
970    /// Revised first line of the address for this transaction.
971    pub fn address_first_line(&mut self, first_line: impl Into<String>) -> &mut Self {
972        self.address.get_or_insert_default().first_line = Some(first_line.into());
973        self
974    }
975
976    /// Revised second line of the address for this transaction.
977    pub fn address_second_line(&mut self, second_line: impl Into<String>) -> &mut Self {
978        self.address.get_or_insert_default().second_line = Some(second_line.into());
979        self
980    }
981
982    /// Revised city of the address for this transaction.
983    pub fn address_city(&mut self, city: impl Into<String>) -> &mut Self {
984        self.address.get_or_insert_default().city = Some(city.into());
985        self
986    }
987
988    /// Revised region of the address for this transaction.
989    pub fn address_region(&mut self, region: impl Into<String>) -> &mut Self {
990        self.address.get_or_insert_default().region = Some(region.into());
991        self
992    }
993
994    /// Send the request to Paddle and return the response.
995    pub async fn send(&self) -> Result<Transaction> {
996        let url = format!("/transactions/{}/revise", self.transaction_id.as_ref());
997
998        self.client.send(self, Method::POST, &url).await
999    }
1000}