use std::collections::HashMap;
use chrono::{DateTime, Utc};
use reqwest::Method;
use serde::Serialize;
use serde_with::skip_serializing_none;
use crate::entities::{
AddressPreview, BillingDetails, TimePeriod, Transaction, TransactionCheckout,
TransactionItemNonCatalogPrice,
};
use crate::enums::{CollectionMode, CurrencyCode, TransactionOrigin, TransactionStatus};
use crate::ids::{
AddressID, BusinessID, CustomerID, DiscountID, PriceID, SubscriptionID, TransactionID,
};
use crate::paginated::Paginated;
use crate::{Paddle, Result};
#[allow(non_snake_case)]
#[skip_serializing_none]
#[derive(Serialize, Default)]
struct DateAtFilter {
LT: Option<DateTime<Utc>>,
LTE: Option<DateTime<Utc>>,
GT: Option<DateTime<Utc>>,
GTE: Option<DateTime<Utc>>,
}
#[derive(Serialize)]
#[serde(untagged)]
enum DateAt {
Exact(DateTime<Utc>),
Filter(DateAtFilter),
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct TransactionsList<'a> {
#[serde(skip)]
client: &'a Paddle,
after: Option<TransactionID>,
billed_at: Option<DateAt>,
collection_mode: Option<CollectionMode>,
created_at: Option<DateAt>,
#[serde(serialize_with = "crate::comma_separated")]
customer_id: Option<Vec<CustomerID>>,
#[serde(serialize_with = "crate::comma_separated")]
id: Option<Vec<TransactionID>>,
#[serde(serialize_with = "crate::comma_separated")]
include: Option<Vec<String>>,
#[serde(serialize_with = "crate::comma_separated")]
invoice_number: Option<Vec<String>>,
#[serde(serialize_with = "crate::comma_separated_enum")]
origin: Option<Vec<TransactionOrigin>>,
order_by: Option<String>,
#[serde(serialize_with = "crate::comma_separated_enum")]
status: Option<Vec<TransactionStatus>>,
#[serde(serialize_with = "crate::comma_separated")]
subscription_id: Option<Vec<SubscriptionID>>,
per_page: Option<usize>,
updated_at: Option<DateAt>,
}
impl<'a> TransactionsList<'a> {
pub fn new(client: &'a Paddle) -> Self {
Self {
client,
after: None,
billed_at: None,
collection_mode: None,
created_at: None,
customer_id: None,
id: None,
include: None,
invoice_number: None,
origin: None,
order_by: None,
status: None,
subscription_id: None,
per_page: None,
updated_at: None,
}
}
pub fn after(&mut self, transaction_id: impl Into<TransactionID>) -> &mut Self {
self.after = Some(transaction_id.into());
self
}
pub fn billed_at(&mut self, date: DateTime<Utc>) -> &mut Self {
self.billed_at = Some(DateAt::Exact(date));
self
}
pub fn billed_at_lt(&mut self, date: DateTime<Utc>) -> &mut Self {
self.billed_at = Some(DateAt::Filter(DateAtFilter {
LT: Some(date),
..Default::default()
}));
self
}
pub fn billed_at_lte(&mut self, date: DateTime<Utc>) -> &mut Self {
self.billed_at = Some(DateAt::Filter(DateAtFilter {
LTE: Some(date),
..Default::default()
}));
self
}
pub fn billed_at_gt(&mut self, date: DateTime<Utc>) -> &mut Self {
self.billed_at = Some(DateAt::Filter(DateAtFilter {
GT: Some(date),
..Default::default()
}));
self
}
pub fn billed_at_gte(&mut self, date: DateTime<Utc>) -> &mut Self {
self.billed_at = Some(DateAt::Filter(DateAtFilter {
GTE: Some(date),
..Default::default()
}));
self
}
pub fn collection_mode(&mut self, mode: CollectionMode) -> &mut Self {
self.collection_mode = Some(mode);
self
}
pub fn created_at(&mut self, date: DateTime<Utc>) -> &mut Self {
self.created_at = Some(DateAt::Exact(date));
self
}
pub fn created_at_lt(&mut self, date: DateTime<Utc>) -> &mut Self {
self.created_at = Some(DateAt::Filter(DateAtFilter {
LT: Some(date),
..Default::default()
}));
self
}
pub fn created_at_lte(&mut self, date: DateTime<Utc>) -> &mut Self {
self.created_at = Some(DateAt::Filter(DateAtFilter {
LTE: Some(date),
..Default::default()
}));
self
}
pub fn created_at_gt(&mut self, date: DateTime<Utc>) -> &mut Self {
self.created_at = Some(DateAt::Filter(DateAtFilter {
GT: Some(date),
..Default::default()
}));
self
}
pub fn created_at_gte(&mut self, date: DateTime<Utc>) -> &mut Self {
self.created_at = Some(DateAt::Filter(DateAtFilter {
GTE: Some(date),
..Default::default()
}));
self
}
pub fn customer_id(
&mut self,
customer_ids: impl IntoIterator<Item = impl Into<CustomerID>>,
) -> &mut Self {
self.customer_id = Some(customer_ids.into_iter().map(Into::into).collect());
self
}
pub fn id(&mut self, ids: impl IntoIterator<Item = impl Into<TransactionID>>) -> &mut Self {
self.id = Some(ids.into_iter().map(Into::into).collect());
self
}
pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
self.include = Some(
entities
.into_iter()
.map(|s| s.as_ref().to_string())
.collect(),
);
self
}
pub fn invoice_numbers(
&mut self,
numbers: impl IntoIterator<Item = impl AsRef<str>>,
) -> &mut Self {
self.invoice_number = Some(
numbers
.into_iter()
.map(|s| s.as_ref().to_string())
.collect(),
);
self
}
pub fn origin(&mut self, origins: impl IntoIterator<Item = TransactionOrigin>) -> &mut Self {
self.origin = Some(origins.into_iter().collect());
self
}
pub fn order_by_asc(&mut self, field: &str) -> &mut Self {
self.order_by = Some(format!("{}[ASC]", field));
self
}
pub fn order_by_desc(&mut self, field: &str) -> &mut Self {
self.order_by = Some(format!("{}[DESC]", field));
self
}
pub fn status(&mut self, statuses: impl IntoIterator<Item = TransactionStatus>) -> &mut Self {
self.status = Some(statuses.into_iter().collect());
self
}
pub fn subscription_ids(
&mut self,
subscription_ids: impl IntoIterator<Item = impl Into<SubscriptionID>>,
) -> &mut Self {
self.subscription_id = Some(subscription_ids.into_iter().map(Into::into).collect());
self
}
pub fn per_page(&mut self, entities_per_page: usize) -> &mut Self {
self.per_page = Some(entities_per_page);
self
}
pub fn updated_at(&mut self, date: DateTime<Utc>) -> &mut Self {
self.updated_at = Some(DateAt::Exact(date));
self
}
pub fn updated_at_lt(&mut self, date: DateTime<Utc>) -> &mut Self {
self.updated_at = Some(DateAt::Filter(DateAtFilter {
LT: Some(date),
..Default::default()
}));
self
}
pub fn updated_at_lte(&mut self, date: DateTime<Utc>) -> &mut Self {
self.updated_at = Some(DateAt::Filter(DateAtFilter {
LTE: Some(date),
..Default::default()
}));
self
}
pub fn updated_at_gt(&mut self, date: DateTime<Utc>) -> &mut Self {
self.updated_at = Some(DateAt::Filter(DateAtFilter {
GT: Some(date),
..Default::default()
}));
self
}
pub fn updated_at_gte(&mut self, date: DateTime<Utc>) -> &mut Self {
self.updated_at = Some(DateAt::Filter(DateAtFilter {
GTE: Some(date),
..Default::default()
}));
self
}
pub fn send(&self) -> Paginated<'_, Vec<Transaction>> {
Paginated::new(self.client, "/transactions", self)
}
}
#[derive(Serialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum TransactionItem {
CatalogItem {
price_id: PriceID,
quantity: u32,
},
NonCatalogItem {
price: TransactionItemNonCatalogPrice,
quantity: u32,
},
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct TransactionCreate<'a> {
#[serde(skip)]
client: &'a Paddle,
#[serde(skip)]
include: Option<Vec<String>>,
items: Vec<TransactionItem>,
status: Option<TransactionStatus>,
customer_id: Option<CustomerID>,
address_id: Option<AddressID>,
business_id: Option<BusinessID>,
custom_data: Option<HashMap<String, String>>,
currency_code: Option<CurrencyCode>,
collection_mode: Option<CollectionMode>,
discount_id: Option<DiscountID>,
billing_details: Option<BillingDetails>,
billing_period: Option<TimePeriod>,
checkout: Option<TransactionCheckout>,
}
impl<'a> TransactionCreate<'a> {
pub fn new(client: &'a Paddle) -> Self {
Self {
client,
include: None,
items: Vec::default(),
status: None,
customer_id: None,
address_id: None,
business_id: None,
custom_data: None,
currency_code: None,
collection_mode: None,
discount_id: None,
billing_details: None,
billing_period: None,
checkout: None,
}
}
pub fn include(&mut self, includes: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
self.include = Some(includes.into_iter().map(Into::into).collect());
self
}
pub fn append_catalog_item(
&mut self,
price_id: impl Into<PriceID>,
quantity: u32,
) -> &mut Self {
self.items.push(TransactionItem::CatalogItem {
price_id: price_id.into(),
quantity,
});
self
}
pub fn append_non_catalog_item(
&mut self,
price: TransactionItemNonCatalogPrice,
quantity: u32,
) -> &mut Self {
self.items
.push(TransactionItem::NonCatalogItem { price, quantity });
self
}
pub fn status(&mut self, status: TransactionStatus) -> &mut Self {
self.status = Some(status);
self
}
pub fn customer_id(&mut self, customer_id: impl Into<CustomerID>) -> &mut Self {
self.customer_id = Some(customer_id.into());
self
}
pub fn address_id(&mut self, address_id: impl Into<AddressID>) -> &mut Self {
self.address_id = Some(address_id.into());
self
}
pub fn business_id(&mut self, business_id: impl Into<BusinessID>) -> &mut Self {
self.business_id = Some(business_id.into());
self
}
pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
self.custom_data = Some(custom_data);
self
}
pub fn currency_code(&mut self, currency_code: CurrencyCode) -> &mut Self {
self.currency_code = Some(currency_code);
self
}
pub fn collection_mode(&mut self, mode: CollectionMode) -> &mut Self {
self.collection_mode = Some(mode);
self
}
pub fn discount_id(&mut self, discount_id: impl Into<DiscountID>) -> &mut Self {
self.discount_id = Some(discount_id.into());
self
}
pub fn billing_details(&mut self, billing_details: BillingDetails) -> &mut Self {
self.billing_details = Some(billing_details);
self
}
pub fn billing_period(&mut self, billing_period: TimePeriod) -> &mut Self {
self.billing_period = Some(billing_period);
self
}
pub fn checkout_url(&mut self, url: String) -> &mut Self {
self.checkout = Some(TransactionCheckout { url: Some(url) });
self
}
pub async fn send(&self) -> Result<Transaction> {
let url = if let Some(include) = self.include.as_ref() {
&format!("/transactions?include={}", include.join(","))
} else {
"/transactions"
};
self.client.send(self, Method::POST, url).await
}
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct TransactionGet<'a> {
#[serde(skip)]
client: &'a Paddle,
#[serde(skip)]
transaction_id: TransactionID,
#[serde(serialize_with = "crate::comma_separated")]
include: Option<Vec<String>>,
}
impl<'a> TransactionGet<'a> {
pub fn new(client: &'a Paddle, transaction_id: impl Into<TransactionID>) -> Self {
Self {
client,
transaction_id: transaction_id.into(),
include: None,
}
}
pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
self.include = Some(
entities
.into_iter()
.map(|s| s.as_ref().to_string())
.collect(),
);
self
}
pub async fn send(&self) -> Result<Transaction> {
self.client
.send(
self,
Method::GET,
&format!("/transactions/{}", self.transaction_id.as_ref()),
)
.await
}
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct TransactionUpdate<'a> {
#[serde(skip)]
client: &'a Paddle,
#[serde(skip)]
transaction_id: TransactionID,
#[serde(skip)]
include: Option<Vec<String>>,
status: Option<TransactionStatus>,
customer_id: Option<CustomerID>,
address_id: Option<AddressID>,
business_id: Option<BusinessID>,
custom_data: Option<HashMap<String, String>>,
currency_code: Option<CurrencyCode>,
collection_mode: Option<CollectionMode>,
discount_id: Option<DiscountID>,
billing_details: Option<BillingDetails>,
billing_period: Option<TimePeriod>,
items: Option<Vec<TransactionItem>>,
checkout: Option<TransactionCheckout>,
}
impl<'a> TransactionUpdate<'a> {
pub fn new(client: &'a Paddle, transaction_id: impl Into<TransactionID>) -> Self {
Self {
client,
transaction_id: transaction_id.into(),
include: None,
status: None,
customer_id: None,
address_id: None,
business_id: None,
custom_data: None,
currency_code: None,
collection_mode: None,
discount_id: None,
billing_details: None,
billing_period: None,
items: None,
checkout: None,
}
}
pub fn include(&mut self, entities: impl IntoIterator<Item = impl AsRef<str>>) -> &mut Self {
self.include = Some(
entities
.into_iter()
.map(|s| s.as_ref().to_string())
.collect(),
);
self
}
pub fn status(&mut self, status: TransactionStatus) -> &mut Self {
self.status = Some(status);
self
}
pub fn customer_id(&mut self, customer_id: impl Into<CustomerID>) -> &mut Self {
self.customer_id = Some(customer_id.into());
self
}
pub fn address_id(&mut self, address_id: impl Into<AddressID>) -> &mut Self {
self.address_id = Some(address_id.into());
self
}
pub fn business_id(&mut self, business_id: impl Into<BusinessID>) -> &mut Self {
self.business_id = Some(business_id.into());
self
}
pub fn custom_data(&mut self, custom_data: HashMap<String, String>) -> &mut Self {
self.custom_data = Some(custom_data);
self
}
pub fn currency_code(&mut self, currency_code: CurrencyCode) -> &mut Self {
self.currency_code = Some(currency_code);
self
}
pub fn collection_mode(&mut self, mode: CollectionMode) -> &mut Self {
self.collection_mode = Some(mode);
self
}
pub fn discount_id(&mut self, discount_id: impl Into<DiscountID>) -> &mut Self {
self.discount_id = Some(discount_id.into());
self
}
pub fn billing_details(&mut self, billing_details: BillingDetails) -> &mut Self {
self.billing_details = Some(billing_details);
self
}
pub fn billing_period(&mut self, billing_period: TimePeriod) -> &mut Self {
self.billing_period = Some(billing_period);
self
}
pub fn items(&mut self, items: impl IntoIterator<Item = TransactionItem>) -> &mut Self {
self.items = Some(items.into_iter().collect());
self
}
pub fn checkout_url(&mut self, url: String) -> &mut Self {
self.checkout = Some(TransactionCheckout { url: Some(url) });
self
}
pub async fn send(&self) -> Result<Transaction> {
let mut url = format!("/transactions/{}", self.transaction_id.as_ref());
if let Some(include) = self.include.as_ref() {
url.push_str(&format!("?include={}", include.join(",")));
}
self.client.send(self, Method::PATCH, &url).await
}
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct TransactionPreview<'a> {
#[serde(skip)]
client: &'a Paddle,
items: Vec<TransactionItem>,
address: Option<AddressPreview>,
customer_ip_address: Option<String>,
address_id: Option<AddressID>,
business_id: Option<BusinessID>,
customer_id: Option<CustomerID>,
currency_code: Option<CurrencyCode>,
discount_id: Option<DiscountID>,
ignore_trials: bool,
}
impl<'a> TransactionPreview<'a> {
pub fn new(client: &'a Paddle) -> Self {
Self {
client,
items: Vec::default(),
address: None,
customer_ip_address: None,
address_id: None,
business_id: None,
customer_id: None,
currency_code: None,
discount_id: None,
ignore_trials: false,
}
}
pub fn append_catalog_item(
&mut self,
price_id: impl Into<PriceID>,
quantity: u32,
) -> &mut Self {
self.items.push(TransactionItem::CatalogItem {
price_id: price_id.into(),
quantity,
});
self
}
pub fn append_non_catalog_item(
&mut self,
price: TransactionItemNonCatalogPrice,
quantity: u32,
) -> &mut Self {
self.items
.push(TransactionItem::NonCatalogItem { price, quantity });
self
}
pub fn address(&mut self, address: AddressPreview) -> &mut Self {
self.address = Some(address);
self
}
pub fn customer_ip_address(&mut self, ip: String) -> &mut Self {
self.customer_ip_address = Some(ip);
self
}
pub fn address_id(&mut self, address_id: impl Into<AddressID>) -> &mut Self {
self.address_id = Some(address_id.into());
self
}
pub fn business_id(&mut self, business_id: impl Into<BusinessID>) -> &mut Self {
self.business_id = Some(business_id.into());
self
}
pub fn customer_id(&mut self, customer_id: impl Into<CustomerID>) -> &mut Self {
self.customer_id = Some(customer_id.into());
self
}
pub fn currency_code(&mut self, currency_code: CurrencyCode) -> &mut Self {
self.currency_code = Some(currency_code);
self
}
pub fn discount_id(&mut self, discount_id: impl Into<DiscountID>) -> &mut Self {
self.discount_id = Some(discount_id.into());
self
}
pub fn ignore_trials(&mut self, ignore_trials: bool) -> &mut Self {
self.ignore_trials = ignore_trials;
self
}
pub async fn send(&self) -> Result<crate::entities::TransactionPreview> {
self.client
.send(self, Method::POST, "/transactions/preview")
.await
}
}
#[derive(Serialize)]
struct RevisedCustomer {
name: String,
}
#[derive(Serialize, Default)]
#[skip_serializing_none]
struct RevisedBusiness {
name: Option<String>,
tax_identifier: Option<String>,
}
#[derive(Serialize, Default)]
#[skip_serializing_none]
struct RevisedAddress {
first_line: Option<String>,
second_line: Option<String>,
city: Option<String>,
region: Option<String>,
}
#[skip_serializing_none]
#[derive(Serialize)]
pub struct TransactionRevise<'a> {
#[serde(skip)]
client: &'a Paddle,
#[serde(skip)]
transaction_id: TransactionID,
customer: Option<RevisedCustomer>,
business: Option<RevisedBusiness>,
address: Option<RevisedAddress>,
}
impl<'a> TransactionRevise<'a> {
pub fn new(client: &'a Paddle, transaction_id: impl Into<TransactionID>) -> Self {
Self {
client,
transaction_id: transaction_id.into(),
customer: None,
business: None,
address: None,
}
}
pub fn customer_name(&mut self, name: impl Into<String>) -> &mut Self {
self.customer = Some(RevisedCustomer { name: name.into() });
self
}
pub fn business_name(&mut self, name: impl Into<String>) -> &mut Self {
self.business.get_or_insert_default().name = Some(name.into());
self
}
pub fn business_tax_identifier(&mut self, tax_identifier: impl Into<String>) -> &mut Self {
self.business.get_or_insert_default().tax_identifier = Some(tax_identifier.into());
self
}
pub fn address_first_line(&mut self, first_line: impl Into<String>) -> &mut Self {
self.address.get_or_insert_default().first_line = Some(first_line.into());
self
}
pub fn address_second_line(&mut self, second_line: impl Into<String>) -> &mut Self {
self.address.get_or_insert_default().second_line = Some(second_line.into());
self
}
pub fn address_city(&mut self, city: impl Into<String>) -> &mut Self {
self.address.get_or_insert_default().city = Some(city.into());
self
}
pub fn address_region(&mut self, region: impl Into<String>) -> &mut Self {
self.address.get_or_insert_default().region = Some(region.into());
self
}
pub async fn send(&self) -> Result<Transaction> {
let url = format!("/transactions/{}/revise", self.transaction_id.as_ref());
self.client.send(self, Method::POST, &url).await
}
}