Skip to main content

lockbook_server_lib/billing/
stripe_service.rs

1use crate::billing::billing_model::StripeUserInfo;
2use crate::document_service::DocumentService;
3use crate::{ClientError, ServerError, ServerState, StripeWebhookError};
4use google_androidpublisher3::hyper::body::Bytes;
5use google_androidpublisher3::hyper::header::HeaderValue;
6use lb_rs::model::api::{
7    PaymentMethod, StripeAccountState, StripeAccountTier, UpgradeAccountStripeError,
8};
9use lb_rs::model::file_metadata::Owner;
10use libsecp256k1::PublicKey;
11
12use stripe::{Event, Invoice};
13use tracing::*;
14use uuid::Uuid;
15
16use super::app_store_client::AppStoreClient;
17use super::google_play_client::GooglePlayClient;
18use super::stripe_client::StripeClient;
19
20impl<S, A, G, D> ServerState<S, A, G, D>
21where
22    S: StripeClient,
23    A: AppStoreClient,
24    G: GooglePlayClient,
25    D: DocumentService,
26{
27    pub async fn create_subscription(
28        &self, public_key: &PublicKey, account_tier: &StripeAccountTier,
29        maybe_user_info: Option<StripeUserInfo>,
30    ) -> Result<StripeUserInfo, ServerError<UpgradeAccountStripeError>> {
31        let owner = Owner(*public_key);
32        let (payment_method, price_id) = match account_tier {
33            StripeAccountTier::Premium(payment_method) => {
34                (payment_method, &self.config.billing.stripe.premium_price_id)
35            }
36        };
37
38        let (customer_id, customer_name, payment_method_id, last_4) = match payment_method {
39            PaymentMethod::NewCard { number, exp_year, exp_month, cvc } => {
40                info!(?owner, "Creating a new card");
41                let payment_method_resp = self
42                    .stripe_client
43                    .create_payment_method(number, *exp_month, *exp_year, cvc)
44                    .await?;
45
46                let last_4 = payment_method_resp
47                    .card
48                    .as_ref()
49                    .ok_or_else::<ServerError<UpgradeAccountStripeError>, _>(|| {
50                        internal!(
51                            "Cannot retrieve card info from payment method response: {:?}",
52                            payment_method_resp
53                        )
54                    })?
55                    .last4
56                    .clone();
57
58                info!(?owner, ?last_4, "Created a new payment method");
59
60                let (customer_id, customer_name) = match &maybe_user_info {
61                    None => {
62                        info!(?owner, "User has no customer_id; creating one with stripe now");
63
64                        let customer_name = Uuid::new_v4();
65                        let customer_resp = self
66                            .stripe_client
67                            .create_customer(
68                                customer_name.to_string(),
69                                payment_method_resp.id.clone(),
70                            )
71                            .await?;
72                        let customer_id = customer_resp.id.to_string();
73
74                        info!(?owner, ?customer_id, "Created customer_id");
75
76                        self.index_db
77                            .lock()
78                            .await
79                            .stripe_ids
80                            .insert(customer_id, Owner(*public_key))?;
81
82                        (customer_resp.id, customer_name)
83                    }
84                    Some(user_info) => {
85                        let customer_id = &user_info.customer_id;
86
87                        info!(?owner, ?customer_id, "User already has customer_id");
88
89                        let customer_id = customer_id.parse()?;
90
91                        info!(?owner, "Disabling old card since a new card has just been added");
92
93                        self.stripe_client
94                            .detach_payment_method_from_customer(
95                                &user_info.payment_method_id.parse()?,
96                            )
97                            .await?;
98
99                        (customer_id, user_info.customer_name)
100                    }
101                };
102
103                info!(
104                    ?owner,
105                    "Creating a setup intent to confirm a users payment method for their subscription"
106                );
107
108                let setup_intent_resp = self
109                    .stripe_client
110                    .create_setup_intent(customer_id.clone(), payment_method_resp.id.clone())
111                    .await?;
112
113                let setup_intent = setup_intent_resp.id.to_string();
114                info!(?owner, ?setup_intent, "Created a setup intent");
115
116                (customer_id, customer_name, payment_method_resp.id.to_string(), last_4)
117            }
118            PaymentMethod::OldCard => {
119                info!(?owner, "Using an old card stored on redis");
120
121                let user_info = maybe_user_info
122                    .ok_or(ClientError(UpgradeAccountStripeError::OldCardDoesNotExist))?;
123
124                (
125                    user_info.customer_id.parse()?,
126                    user_info.customer_name,
127                    user_info.payment_method_id,
128                    user_info.last_4,
129                )
130            }
131        };
132
133        info!(?owner, "Successfully retrieved card");
134
135        let subscription_resp = self
136            .stripe_client
137            .create_subscription(customer_id.clone(), &payment_method_id, price_id)
138            .await?;
139
140        let subscription_id = subscription_resp.id;
141        info!(?owner, ?subscription_id, "Successfully created subscription");
142
143        Ok(StripeUserInfo {
144            customer_id: customer_id.to_string(),
145            customer_name,
146            price_id: price_id.to_string(),
147            payment_method_id: payment_method_id.to_string(),
148            last_4,
149            subscription_id: subscription_id.to_string(),
150            expiration_time: subscription_resp.current_period_end as u64,
151            account_state: StripeAccountState::Ok,
152        })
153    }
154
155    pub async fn get_public_key_from_invoice(
156        &self, invoice: &Invoice,
157    ) -> Result<PublicKey, ServerError<StripeWebhookError>> {
158        let customer_id = match invoice.customer.as_ref().ok_or_else(|| {
159            ClientError(StripeWebhookError::InvalidBody(
160                "Cannot retrieve the customer_id".to_string(),
161            ))
162        })? {
163            stripe::Expandable::Id(id) => id.to_string(),
164            stripe::Expandable::Object(customer) => customer.id.to_string(),
165        };
166
167        let public_key = self
168            .index_db
169            .lock()
170            .await
171            .stripe_ids
172            .get()
173            .get(&customer_id)
174            .copied()
175            .ok_or_else(|| {
176                internal!("There is no public_key related to this customer_id: {:?}", customer_id)
177            })?;
178
179        Ok(public_key.0)
180    }
181
182    pub fn verify_request_and_get_event(
183        &self, request_body: &Bytes, stripe_sig: HeaderValue,
184    ) -> Result<Event, ServerError<StripeWebhookError>> {
185        let payload = std::str::from_utf8(request_body).map_err(|e| {
186            ClientError(StripeWebhookError::InvalidBody(format!("Cannot get body as str: {e:?}")))
187        })?;
188
189        let sig = stripe_sig.to_str().map_err(|e| {
190            ClientError(StripeWebhookError::InvalidHeader(format!(
191                "Cannot get header as str: {e:?}"
192            )))
193        })?;
194
195        info!("Verifying a stripe webhook request");
196
197        Ok(stripe::Webhook::construct_event(
198            payload,
199            sig,
200            &self.config.billing.stripe.signing_secret,
201        )?)
202    }
203}