Skip to main content

lockbook_server_lib/billing/
billing_model.rs

1use crate::ServerError;
2use crate::config::Config;
3use google_androidpublisher3::api::SubscriptionPurchase;
4use lb_rs::model::api::{
5    AppStoreAccountState, FREE_TIER_USAGE_SIZE, GooglePlayAccountState, PREMIUM_TIER_USAGE_SIZE,
6    StripeAccountState, UnixTimeMillis, UpgradeAccountGooglePlayError,
7};
8use serde::{Deserialize, Serialize};
9use std::fmt::Debug;
10use uuid::Uuid;
11
12#[derive(Serialize, Deserialize, Debug, Clone, Default)]
13pub struct SubscriptionProfile {
14    pub billing_platform: Option<BillingPlatform>,
15    pub last_in_payment_flow: u64,
16}
17
18impl SubscriptionProfile {
19    pub fn data_cap(&self) -> u64 {
20        match &self.billing_platform {
21            Some(platform) => match platform {
22                BillingPlatform::Stripe(info) => {
23                    if info.account_state == StripeAccountState::Ok {
24                        PREMIUM_TIER_USAGE_SIZE
25                    } else {
26                        FREE_TIER_USAGE_SIZE
27                    }
28                }
29                BillingPlatform::GooglePlay(info) => {
30                    if info.account_state == GooglePlayAccountState::OnHold {
31                        FREE_TIER_USAGE_SIZE
32                    } else {
33                        PREMIUM_TIER_USAGE_SIZE
34                    }
35                }
36                BillingPlatform::AppStore(info) => {
37                    if info.account_state == AppStoreAccountState::FailedToRenew
38                        || info.account_state == AppStoreAccountState::Expired
39                    {
40                        FREE_TIER_USAGE_SIZE
41                    } else {
42                        PREMIUM_TIER_USAGE_SIZE
43                    }
44                }
45            },
46            None => FREE_TIER_USAGE_SIZE,
47        }
48    }
49
50    // the rate was $0.12 / gb when this was written
51    pub fn bandwidth_cap(&self) -> usize {
52        let cap = match self.is_premium() {
53            true => self.data_cap(),      // $0.012
54            false => self.data_cap() * 4, // $3.60
55        };
56
57        cap as usize
58    }
59
60    pub fn is_premium(&self) -> bool {
61        self.data_cap() == PREMIUM_TIER_USAGE_SIZE
62    }
63}
64
65#[derive(Serialize, Deserialize, Debug, Clone)]
66pub enum BillingPlatform {
67    Stripe(StripeUserInfo),
68    GooglePlay(GooglePlayUserInfo),
69    AppStore(AppStoreUserInfo),
70}
71
72impl BillingPlatform {
73    pub fn new_play_sub(
74        config: &Config, purchase_token: &str, expiry_information: SubscriptionPurchase,
75    ) -> Result<Self, ServerError<UpgradeAccountGooglePlayError>> {
76        let expiration_time = UnixTimeMillis::try_from(
77            expiry_information
78                .expiry_time_millis
79                .ok_or_else::<ServerError<UpgradeAccountGooglePlayError>, _>(|| {
80                    internal!("Cannot get expiration time of a recovered subscription")
81                })?,
82        )
83        .map_err::<ServerError<UpgradeAccountGooglePlayError>, _>(|e| {
84            internal!("Cannot parse millis into int: {:?}", e)
85        })?;
86
87        Ok(Self::GooglePlay(GooglePlayUserInfo {
88            purchase_token: purchase_token.to_string(),
89            subscription_product_id: config
90                .billing
91                .google
92                .premium_subscription_product_id
93                .clone(),
94            subscription_offer_id: config.billing.google.premium_subscription_offer_id.clone(),
95            expiration_time,
96            account_state: GooglePlayAccountState::Ok,
97        }))
98    }
99}
100
101#[derive(Serialize, Deserialize, Debug, Clone)]
102pub struct GooglePlayUserInfo {
103    pub purchase_token: String,
104    pub subscription_product_id: String,
105    pub subscription_offer_id: String,
106    pub expiration_time: UnixTimeMillis,
107    pub account_state: GooglePlayAccountState,
108}
109
110#[derive(Serialize, Deserialize, Debug, Clone)]
111pub struct StripeUserInfo {
112    pub customer_id: String,
113    pub customer_name: Uuid,
114    pub price_id: String,
115    pub payment_method_id: String,
116    pub last_4: String,
117    pub subscription_id: String,
118    pub expiration_time: UnixTimeMillis,
119    pub account_state: StripeAccountState,
120}
121
122#[derive(Serialize, Deserialize, Debug, Clone)]
123pub struct AppStoreUserInfo {
124    pub account_token: String,
125    pub original_transaction_id: String,
126    pub subscription_product_id: String,
127    pub expiration_time: UnixTimeMillis,
128    pub account_state: AppStoreAccountState,
129}