lockbook_server_lib/billing/
stripe_service.rs1use 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}