use crate::models::ids::{ ChargeId, ProductId, ProductPriceId, UserSubscriptionId, }; use ariadne::ids::UserId; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Serialize, Deserialize)] pub struct Product { pub id: ProductId, pub metadata: ProductMetadata, pub prices: Vec, pub unitary: bool, } #[derive(Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum ProductMetadata { Midas, Pyro { cpu: u32, ram: u32, swap: u32, storage: u32, }, Medal { cpu: u32, ram: u32, swap: u32, storage: u32, region: String, }, } impl ProductMetadata { pub fn is_pyro(&self) -> bool { matches!(self, ProductMetadata::Pyro { .. }) } pub fn is_medal(&self) -> bool { matches!(self, ProductMetadata::Medal { .. }) } pub fn is_midas(&self) -> bool { matches!(self, ProductMetadata::Midas) } } #[derive(Serialize, Deserialize)] pub struct ProductPrice { pub id: ProductPriceId, pub product_id: ProductId, pub prices: Price, pub currency_code: String, } #[derive(Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum Price { OneTime { price: i32, }, Recurring { intervals: HashMap, }, } impl Price { pub fn get_interval(&self, interval: PriceDuration) -> Option { match self { Price::OneTime { .. } => None, Price::Recurring { intervals } => intervals.get(&interval).copied(), } } } #[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug, Copy, Clone)] #[serde(rename_all = "kebab-case")] pub enum PriceDuration { FiveDays, Monthly, Quarterly, Yearly, } impl PriceDuration { pub fn duration(&self) -> chrono::Duration { match self { PriceDuration::FiveDays => chrono::Duration::days(5), PriceDuration::Monthly => chrono::Duration::days(30), PriceDuration::Quarterly => chrono::Duration::days(90), PriceDuration::Yearly => chrono::Duration::days(365), } } pub fn from_string(string: &str) -> PriceDuration { match string { "five-days" => PriceDuration::FiveDays, "monthly" => PriceDuration::Monthly, "quarterly" => PriceDuration::Quarterly, "yearly" => PriceDuration::Yearly, _ => PriceDuration::Monthly, } } pub fn as_str(&self) -> &'static str { match self { PriceDuration::Monthly => "monthly", PriceDuration::Quarterly => "quarterly", PriceDuration::Yearly => "yearly", PriceDuration::FiveDays => "five-days", } } pub fn iterator() -> impl Iterator { vec![ PriceDuration::Monthly, PriceDuration::Quarterly, PriceDuration::Yearly, PriceDuration::FiveDays, ] .into_iter() } } #[derive(Serialize, Deserialize)] pub struct UserSubscription { pub id: UserSubscriptionId, pub user_id: UserId, pub price_id: ProductPriceId, pub interval: PriceDuration, pub status: SubscriptionStatus, pub created: DateTime, pub metadata: Option, } impl From for UserSubscription { fn from( x: crate::database::models::user_subscription_item::DBUserSubscription, ) -> Self { Self { id: x.id.into(), user_id: x.user_id.into(), price_id: x.price_id.into(), interval: x.interval, status: x.status, created: x.created, metadata: x.metadata, } } } #[derive(Serialize, Deserialize, Eq, PartialEq, Copy, Clone)] #[serde(rename_all = "kebab-case")] pub enum SubscriptionStatus { Provisioned, Unprovisioned, } impl SubscriptionStatus { pub fn from_string(string: &str) -> SubscriptionStatus { match string { "provisioned" => SubscriptionStatus::Provisioned, "unprovisioned" => SubscriptionStatus::Unprovisioned, _ => SubscriptionStatus::Provisioned, } } pub fn as_str(&self) -> &'static str { match self { SubscriptionStatus::Provisioned => "provisioned", SubscriptionStatus::Unprovisioned => "unprovisioned", } } } #[derive(Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum SubscriptionMetadata { Pyro { id: String, region: Option }, Medal { id: String }, } impl SubscriptionMetadata { pub fn is_medal(&self) -> bool { matches!(self, SubscriptionMetadata::Medal { .. }) } pub fn is_pyro(&self) -> bool { matches!(self, SubscriptionMetadata::Pyro { .. }) } } #[derive(Serialize, Deserialize)] pub struct Charge { pub id: ChargeId, pub user_id: UserId, pub price_id: ProductPriceId, pub amount: i64, pub currency_code: String, pub status: ChargeStatus, pub due: DateTime, pub last_attempt: Option>, #[serde(flatten)] pub type_: ChargeType, pub subscription_id: Option, pub subscription_interval: Option, pub platform: PaymentPlatform, pub parent_charge_id: Option, pub net: Option, } #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum ChargeType { OneTime, Subscription, Proration, Refund, } impl ChargeType { pub fn as_str(&self) -> &'static str { match self { ChargeType::OneTime => "one-time", ChargeType::Subscription => "subscription", ChargeType::Proration => "proration", ChargeType::Refund => "refund", } } pub fn from_string(string: &str) -> ChargeType { match string { "one-time" => ChargeType::OneTime, "subscription" => ChargeType::Subscription, "proration" => ChargeType::Proration, "refund" => ChargeType::Refund, _ => ChargeType::OneTime, } } } #[derive(Serialize, Deserialize, Eq, PartialEq, Copy, Clone, Debug)] #[serde(rename_all = "kebab-case")] pub enum ChargeStatus { /// Open charges are for the next billing interval Open, Processing, Succeeded, Failed, Cancelled, /// Expiring charges are charges that aren't expected to be processed /// but can be promoted to a full charge, like for trials/freebies. When /// due, the underlying subscription is unprovisioned. Expiring, } impl ChargeStatus { pub fn from_string(string: &str) -> ChargeStatus { match string { "processing" => ChargeStatus::Processing, "succeeded" => ChargeStatus::Succeeded, "failed" => ChargeStatus::Failed, "open" => ChargeStatus::Open, "cancelled" => ChargeStatus::Cancelled, "expiring" => ChargeStatus::Expiring, _ => ChargeStatus::Failed, } } pub fn as_str(&self) -> &'static str { match self { ChargeStatus::Processing => "processing", ChargeStatus::Succeeded => "succeeded", ChargeStatus::Failed => "failed", ChargeStatus::Open => "open", ChargeStatus::Cancelled => "cancelled", ChargeStatus::Expiring => "expiring", } } } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum PaymentPlatform { Stripe, None, } impl PaymentPlatform { pub fn from_string(string: &str) -> PaymentPlatform { match string { "stripe" => PaymentPlatform::Stripe, "none" => PaymentPlatform::None, _ => PaymentPlatform::Stripe, } } pub fn as_str(&self) -> &'static str { match self { PaymentPlatform::Stripe => "stripe", PaymentPlatform::None => "none", } } }