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