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