1use crate::ServerError::ClientError;
2use crate::billing::app_store_model::{NotificationChange, Subtype};
3use crate::billing::billing_model::{
4 AppStoreUserInfo, BillingPlatform, GooglePlayUserInfo, StripeUserInfo,
5};
6use crate::billing::billing_service::LockBillingWorkflowError::{
7 ExistingRequestPending, UserNotFound,
8};
9use crate::billing::google_play_model::NotificationType;
10use crate::document_service::DocumentService;
11use crate::schema::Account;
12use crate::{RequestContext, ServerError, ServerState};
13use base64::DecodeError;
14use db_rs::Db;
15use lb_rs::model::api::{
16 AdminSetUserTierError, AdminSetUserTierInfo, AdminSetUserTierRequest, AdminSetUserTierResponse,
17 AppStoreAccountState, CancelSubscriptionError, CancelSubscriptionRequest,
18 CancelSubscriptionResponse, FREE_TIER_USAGE_SIZE, GetSubscriptionInfoError,
19 GetSubscriptionInfoRequest, GetSubscriptionInfoResponse, GooglePlayAccountState,
20 PaymentPlatform, StripeAccountState, SubscriptionInfo, UpgradeAccountAppStoreError,
21 UpgradeAccountAppStoreRequest, UpgradeAccountAppStoreResponse, UpgradeAccountGooglePlayError,
22 UpgradeAccountGooglePlayRequest, UpgradeAccountGooglePlayResponse, UpgradeAccountStripeError,
23 UpgradeAccountStripeRequest, UpgradeAccountStripeResponse,
24};
25use lb_rs::model::clock::get_time;
26use lb_rs::model::file_metadata::Owner;
27use lb_rs::model::server_tree::ServerTree;
28use lb_rs::model::tree_like::TreeLike;
29use libsecp256k1::PublicKey;
30use std::collections::HashMap;
31use std::fmt::Debug;
32use std::ops::DerefMut;
33use tracing::*;
34use warp::http::HeaderValue;
35use warp::hyper::body::Bytes;
36
37use super::app_store_client::AppStoreClient;
38use super::google_play_client::GooglePlayClient;
39use super::stripe_client::StripeClient;
40
41impl<S, A, G, D> ServerState<S, A, G, D>
42where
43 S: StripeClient,
44 A: AppStoreClient,
45 G: GooglePlayClient,
46 D: DocumentService,
47{
48 async fn lock_subscription_profile(
49 &self, public_key: &PublicKey,
50 ) -> Result<Account, ServerError<LockBillingWorkflowError>> {
51 let owner = Owner(*public_key);
52 let mut db = self.index_db.lock().await;
53 let tx = db.begin_transaction()?;
54 let mut account = db
55 .accounts
56 .get()
57 .get(&owner)
58 .ok_or(ClientError(UserNotFound))?
59 .clone();
60
61 let current_time = get_time().0 as u64;
62
63 if current_time - account.billing_info.last_in_payment_flow
64 < self.config.billing.millis_between_user_payment_flows
65 {
66 warn!(
67 ?owner,
68 "User/Webhook is already in payment flow, or not enough time that has elapsed since a failed attempt"
69 );
70
71 return Err(ClientError(ExistingRequestPending));
72 }
73
74 account.billing_info.last_in_payment_flow = current_time;
75 db.accounts.insert(owner, account.clone())?;
76
77 debug!(?owner, "User successfully entered payment flow");
78
79 tx.drop_safely()?;
80 Ok(account)
81 }
82
83 async fn release_subscription_profile<T: Debug>(
84 &self, public_key: PublicKey, mut account: Account,
85 ) -> Result<(), ServerError<T>> {
86 account.billing_info.last_in_payment_flow = 0;
87 self.index_db
88 .lock()
89 .await
90 .accounts
91 .insert(Owner(public_key), account)?;
92 Ok(())
93 }
94
95 pub async fn upgrade_account_app_store(
96 &self, context: RequestContext<UpgradeAccountAppStoreRequest>,
97 ) -> Result<UpgradeAccountAppStoreResponse, ServerError<UpgradeAccountAppStoreError>> {
98 let request = &context.request;
99
100 let mut account = self.lock_subscription_profile(&context.public_key).await?;
101
102 debug!("Upgrading the account of a user through app store billing");
103
104 {
105 let db = self.index_db.lock().await;
106 if db
107 .app_store_ids
108 .get()
109 .get(&request.app_account_token)
110 .is_some()
111 {
112 return Err(ClientError(UpgradeAccountAppStoreError::AppStoreAccountAlreadyLinked));
113 }
114 }
115
116 if account.billing_info.is_premium() {
117 return Err(ClientError(UpgradeAccountAppStoreError::AlreadyPremium));
118 }
119
120 let (expires, account_state) = Self::verify_details(
121 &self.app_store_client,
122 &self.config.billing.apple,
123 &request.app_account_token,
124 &request.original_transaction_id,
125 )
126 .await?;
127
128 debug!("Successfully verified app store subscription");
129
130 account.billing_info.billing_platform = Some(BillingPlatform::AppStore(AppStoreUserInfo {
131 account_token: request.app_account_token.clone(),
132 original_transaction_id: request.original_transaction_id.clone(),
133 subscription_product_id: self.config.billing.apple.subscription_product_id.clone(),
134 expiration_time: expires,
135 account_state,
136 }));
137
138 self.index_db
139 .lock()
140 .await
141 .app_store_ids
142 .insert(request.app_account_token.clone(), Owner(context.public_key))?;
143
144 self.release_subscription_profile::<UpgradeAccountAppStoreError>(
145 context.public_key,
146 account,
147 )
148 .await?;
149
150 Ok(UpgradeAccountAppStoreResponse {})
151 }
152
153 pub async fn upgrade_account_google_play(
154 &self, context: RequestContext<UpgradeAccountGooglePlayRequest>,
155 ) -> Result<UpgradeAccountGooglePlayResponse, ServerError<UpgradeAccountGooglePlayError>> {
156 let request = &context.request;
157
158 let mut account = self.lock_subscription_profile(&context.public_key).await?;
159
160 if account.billing_info.is_premium() {
161 return Err(ClientError(UpgradeAccountGooglePlayError::AlreadyPremium));
162 }
163
164 debug!("Upgrading the account of a user through google play billing");
165
166 self.google_play_client
167 .acknowledge_subscription(&self.config, &request.purchase_token)
168 .await?;
169
170 debug!("Acknowledged a user's google play subscription");
171
172 let expiry_info = self
173 .google_play_client
174 .get_subscription(&self.config, &request.purchase_token)
175 .await?;
176
177 account.billing_info.billing_platform = Some(BillingPlatform::new_play_sub(
178 &self.config,
179 &request.purchase_token,
180 expiry_info,
181 )?);
182
183 self.index_db
184 .lock()
185 .await
186 .google_play_ids
187 .insert(request.account_id.clone(), Owner(context.public_key))?;
188
189 self.release_subscription_profile::<UpgradeAccountGooglePlayError>(
190 context.public_key,
191 account,
192 )
193 .await?;
194
195 debug!("Successfully upgraded a user through a google play subscription. public_key");
196
197 Ok(UpgradeAccountGooglePlayResponse {})
198 }
199
200 pub async fn upgrade_account_stripe(
201 &self, context: RequestContext<UpgradeAccountStripeRequest>,
202 ) -> Result<UpgradeAccountStripeResponse, ServerError<UpgradeAccountStripeError>> {
203 let request = &context.request;
204
205 debug!("Attempting to upgrade the account tier of to premium");
206
207 let mut account = self.lock_subscription_profile(&context.public_key).await?;
208
209 if account.billing_info.is_premium() {
210 return Err(ClientError(UpgradeAccountStripeError::AlreadyPremium));
211 }
212
213 let maybe_user_info = account
214 .billing_info
215 .billing_platform
216 .and_then(|info| match info {
217 BillingPlatform::Stripe(stripe_info) => Some(stripe_info),
218 _ => None,
219 });
220
221 let user_info = self
222 .create_subscription(&context.public_key, &request.account_tier, maybe_user_info)
223 .await?;
224
225 account.billing_info.billing_platform = Some(BillingPlatform::Stripe(user_info));
226 self.release_subscription_profile::<UpgradeAccountStripeError>(context.public_key, account)
227 .await?;
228
229 debug!("Successfully upgraded the account tier of from free to premium");
230
231 Ok(UpgradeAccountStripeResponse {})
232 }
233
234 pub async fn get_subscription_info(
235 &self, context: RequestContext<GetSubscriptionInfoRequest>,
236 ) -> Result<GetSubscriptionInfoResponse, ServerError<GetSubscriptionInfoError>> {
237 let platform = self
238 .index_db
239 .lock()
240 .await
241 .accounts
242 .get()
243 .get(&Owner(context.public_key))
244 .ok_or(ClientError(GetSubscriptionInfoError::UserNotFound))?
245 .billing_info
246 .billing_platform
247 .clone();
248
249 let subscription_info = platform.map(|info| match info {
250 BillingPlatform::Stripe(info) => SubscriptionInfo {
251 payment_platform: PaymentPlatform::Stripe { card_last_4_digits: info.last_4 },
252 period_end: info.expiration_time,
253 },
254 BillingPlatform::GooglePlay(info) => SubscriptionInfo {
255 payment_platform: PaymentPlatform::GooglePlay { account_state: info.account_state },
256 period_end: info.expiration_time,
257 },
258 BillingPlatform::AppStore(info) => SubscriptionInfo {
259 payment_platform: PaymentPlatform::AppStore { account_state: info.account_state },
260 period_end: info.expiration_time,
261 },
262 });
263
264 Ok(GetSubscriptionInfoResponse { subscription_info })
265 }
266
267 pub async fn cancel_subscription(
268 &self, context: RequestContext<CancelSubscriptionRequest>,
269 ) -> Result<CancelSubscriptionResponse, ServerError<CancelSubscriptionError>> {
270 let mut account = self.lock_subscription_profile(&context.public_key).await?;
271
272 if account.billing_info.data_cap() == FREE_TIER_USAGE_SIZE {
273 return Err(ClientError(CancelSubscriptionError::NotPremium));
274 }
275
276 {
277 let mut lock = self.index_db.lock().await;
278 let db = lock.deref_mut();
279
280 let mut tree = ServerTree::new(
281 Owner(context.public_key),
282 &mut db.owned_files,
283 &mut db.shared_files,
284 &mut db.file_children,
285 &mut db.metas,
286 )?
287 .to_lazy();
288
289 let usage = tree.calculate_usage(Owner(context.public_key))?;
290
291 if usage > FREE_TIER_USAGE_SIZE {
292 debug!("Cannot downgrade user to free since they are over the data cap");
293 return Err(ClientError(CancelSubscriptionError::UsageIsOverFreeTierDataCap));
294 }
295 }
296
297 match account.billing_info.billing_platform {
298 None => {
299 return Err(internal!(
300 "A user somehow has premium tier usage, but no billing information on redis. public_key: {:?}",
301 context.public_key
302 ));
303 }
304 Some(BillingPlatform::GooglePlay(ref mut info)) => {
305 debug!("Canceling google play subscription of user");
306
307 if let GooglePlayAccountState::Canceled = &info.account_state {
308 return Err(ClientError(CancelSubscriptionError::AlreadyCanceled));
309 }
310
311 self.google_play_client
312 .cancel_subscription(&self.config, &info.purchase_token)
313 .await?;
314
315 info.account_state = GooglePlayAccountState::Canceled;
316 debug!("Successfully canceled google play subscription of user");
317 }
318 Some(BillingPlatform::Stripe(ref mut info)) => {
319 debug!("Canceling stripe subscription of user");
320
321 self.stripe_client
322 .cancel_subscription(&info.subscription_id.parse()?)
323 .await
324 .map_err::<ServerError<CancelSubscriptionError>, _>(|err| {
325 internal!("{:?}", err)
326 })?;
327
328 info.account_state = StripeAccountState::Canceled;
329
330 debug!("Successfully canceled stripe subscription");
331 }
332 Some(BillingPlatform::AppStore(_)) => {
333 return Err(ClientError(CancelSubscriptionError::CannotCancelForAppStore));
334 }
335 }
336
337 self.release_subscription_profile::<CancelSubscriptionError>(context.public_key, account)
338 .await?;
339
340 Ok(CancelSubscriptionResponse {})
341 }
342
343 pub async fn admin_set_user_tier(
344 &self, context: RequestContext<AdminSetUserTierRequest>,
345 ) -> Result<AdminSetUserTierResponse, ServerError<AdminSetUserTierError>> {
346 let request = &context.request;
347
348 {
349 let db = self.index_db.lock().await;
350
351 if !Self::is_admin::<AdminSetUserTierError>(
352 &db,
353 &context.public_key,
354 &self.config.admin.admins,
355 )? {
356 return Err(ClientError(AdminSetUserTierError::NotPermissioned));
357 }
358 }
359
360 let public_key = self
361 .index_db
362 .lock()
363 .await
364 .usernames
365 .get()
366 .get(&request.username)
367 .ok_or(ClientError(AdminSetUserTierError::UserNotFound))?
368 .0;
369 let mut account = self.lock_subscription_profile(&public_key).await?;
370
371 let billing_config = &self.config.billing;
372
373 account.billing_info.billing_platform = match &request.info {
374 AdminSetUserTierInfo::Stripe {
375 customer_id,
376 customer_name,
377 payment_method_id,
378 last_4,
379 subscription_id,
380 expiration_time,
381 account_state,
382 } => Some(BillingPlatform::Stripe(StripeUserInfo {
383 customer_id: customer_id.to_string(),
384 customer_name: *customer_name,
385 price_id: billing_config.stripe.premium_price_id.to_string(),
386 payment_method_id: payment_method_id.to_string(),
387 last_4: last_4.to_string(),
388 subscription_id: subscription_id.to_string(),
389 expiration_time: *expiration_time,
390 account_state: account_state.clone(),
391 })),
392 AdminSetUserTierInfo::GooglePlay { purchase_token, expiration_time, account_state } => {
393 Some(BillingPlatform::GooglePlay(GooglePlayUserInfo {
394 purchase_token: purchase_token.clone(),
395 subscription_product_id: billing_config
396 .google
397 .premium_subscription_product_id
398 .to_string(),
399 subscription_offer_id: billing_config
400 .google
401 .premium_subscription_offer_id
402 .to_string(),
403 expiration_time: *expiration_time,
404 account_state: account_state.clone(),
405 }))
406 }
407 AdminSetUserTierInfo::AppStore {
408 account_token,
409 original_transaction_id,
410 expiration_time,
411 account_state,
412 } => Some(BillingPlatform::AppStore(AppStoreUserInfo {
413 account_token: account_token.to_string(),
414 original_transaction_id: original_transaction_id.to_string(),
415 subscription_product_id: billing_config.apple.subscription_product_id.to_string(),
416 expiration_time: *expiration_time,
417 account_state: account_state.clone(),
418 })),
419 AdminSetUserTierInfo::Free => None,
420 };
421
422 account.username = request.username.clone();
423
424 self.release_subscription_profile::<AdminSetUserTierError>(public_key, account)
425 .await?;
426
427 Ok(AdminSetUserTierResponse {})
428 }
429
430 async fn save_subscription_profile<
431 T: Debug,
432 F: Fn(&mut Account) -> Result<(), ServerError<T>>,
433 >(
434 &self, public_key: &PublicKey, update_subscription_profile: F,
435 ) -> Result<(), ServerError<T>> {
436 let millis_between_lock_attempts = self.config.billing.time_between_lock_attempts;
437 loop {
438 match self.lock_subscription_profile(public_key).await {
439 Ok(ref mut sub_profile) => {
440 update_subscription_profile(sub_profile)?;
441 self.release_subscription_profile(*public_key, sub_profile.clone())
442 .await?;
443 break;
444 }
445 Err(ClientError(ExistingRequestPending)) => {
446 tokio::time::sleep(millis_between_lock_attempts).await;
447 continue;
448 }
449 Err(err) => {
450 return Err(internal!("Cannot get billing lock in webhooks: {:#?}", err));
451 }
452 }
453 }
454
455 Ok(())
456 }
457
458 pub async fn stripe_webhooks(
459 &self, request_body: Bytes, stripe_sig: HeaderValue,
460 ) -> Result<(), ServerError<StripeWebhookError>> {
461 let event = self.verify_request_and_get_event(&request_body, stripe_sig)?;
462
463 let event_type = event.type_;
464 debug!(?event_type, "Verified stripe request");
465
466 match (&event.type_, &event.data.object) {
467 (stripe::EventType::InvoicePaymentFailed, stripe::EventObject::Invoice(invoice)) => {
468 if let Some(stripe::InvoiceBillingReason::SubscriptionCycle) =
469 invoice.billing_reason
470 {
471 let public_key = self.get_public_key_from_invoice(invoice).await?;
472 let owner = Owner(public_key);
473
474 debug!(
475 ?owner,
476 "User's tier is being reduced due to failed renewal payment in stripe"
477 );
478
479 self.save_subscription_profile(&public_key, |account| {
480 if let Some(BillingPlatform::Stripe(ref mut info)) =
481 account.billing_info.billing_platform
482 {
483 info.account_state = StripeAccountState::InvoiceFailed;
484
485 Ok(())
486 } else {
487 Err(internal!(
488 "Cannot get any billing info for user. public_key: {:?}",
489 public_key
490 ))
491 }
492 })
493 .await?;
494 }
495 }
496 (stripe::EventType::InvoicePaid, stripe::EventObject::Invoice(invoice)) => {
497 if let Some(stripe::InvoiceBillingReason::SubscriptionCycle) =
498 invoice.billing_reason
499 {
500 let public_key = self.get_public_key_from_invoice(invoice).await?;
501 let owner = Owner(public_key);
502
503 debug!(
504 ?owner,
505 "User's subscription period_end is being changed after successful renewal",
506 );
507
508 let subscription_period_end = match &invoice.subscription {
509 Some(stripe::Expandable::Object(subscription)) => {
510 subscription.current_period_end
511 }
512 Some(stripe::Expandable::Id(subscription_id)) => {
513 self.stripe_client
514 .get_subscription(subscription_id)
515 .await
516 .map_err::<ServerError<StripeWebhookError>, _>(|err| {
517 internal!("{:?}", err)
518 })?
519 .current_period_end
520 }
521 None => {
522 return Err(internal!(
523 "The subscription should be included in this invoice: {:?}",
524 invoice
525 ));
526 }
527 };
528
529 self.save_subscription_profile(&public_key, |account| {
530 if let Some(BillingPlatform::Stripe(ref mut info)) =
531 account.billing_info.billing_platform
532 {
533 info.account_state = StripeAccountState::Ok;
534 info.expiration_time = subscription_period_end as u64;
535
536 Ok(())
537 } else {
538 Err(internal!(
539 "Cannot get any billing info for user. public_key: {:?}",
540 public_key
541 ))
542 }
543 })
544 .await?;
545 }
546 }
547 (_, _) => {
548 return Err(internal!("Unexpected stripe event: {:?}", event.type_));
549 }
550 }
551
552 Ok(())
553 }
554
555 pub async fn google_play_notification_webhooks(
556 &self, request_body: Bytes, query_parameters: HashMap<String, String>,
557 ) -> Result<(), ServerError<GooglePlayWebhookError>> {
558 let notification = self
559 .verify_request_and_get_notification(request_body, query_parameters)
560 .await?;
561
562 if let Some(sub_notif) = notification.subscription_notification {
563 debug!(?sub_notif, "Notification is for a subscription");
564
565 let subscription = self
566 .google_play_client
567 .get_subscription(&self.config, &sub_notif.purchase_token)
568 .await
569 .map_err(|e| internal!("{:#?}", e))?;
570
571 let notification_type = sub_notif.notification_type();
572 if let NotificationType::SubscriptionPurchased = notification_type {
573 return Ok(());
574 }
575
576 let public_key = self
577 .get_public_key_from_subnotif(&sub_notif, &subscription, ¬ification_type)
578 .await?;
579 let owner = Owner(public_key);
580
581 debug!(
582 ?owner,
583 ?notification_type,
584 "Updating google play user's subscription profile to match new subscription state"
585 );
586
587 self.save_subscription_profile(&public_key, |account| {
588 if let Some(BillingPlatform::GooglePlay(ref mut info)) =
589 account.billing_info.billing_platform
590 {
591 match notification_type {
592 NotificationType::SubscriptionRecovered
593 | NotificationType::SubscriptionRestarted
594 | NotificationType::SubscriptionRenewed => {
595 info.account_state = GooglePlayAccountState::Ok;
596 info.expiration_time = Self::get_subscription_period_end(
597 &subscription,
598 ¬ification_type,
599 public_key,
600 )?;
601 }
602 NotificationType::SubscriptionInGracePeriod => {
603 info.account_state = GooglePlayAccountState::GracePeriod;
604 }
605 NotificationType::SubscriptionOnHold => {
606 info.account_state = GooglePlayAccountState::OnHold;
607 info.expiration_time = Self::get_subscription_period_end(
608 &subscription,
609 ¬ification_type,
610 public_key,
611 )?;
612 }
613 NotificationType::SubscriptionExpired
614 | NotificationType::SubscriptionRevoked => {
615 if info.purchase_token == sub_notif.purchase_token {
616 account.billing_info.billing_platform = None
617 } else {
618 let old_purchase_token = &sub_notif.purchase_token;
619 let new_purchase_token = &info.purchase_token;
620 debug!(
621 ?old_purchase_token,
622 ?new_purchase_token,
623 "Expired or revoked subscription was tied to an old purchase_token"
624 );
625 }
626 }
627 NotificationType::SubscriptionCanceled => {
628 info.account_state = GooglePlayAccountState::Canceled;
629 let cancellation_reason = &subscription.cancel_survey_result;
630 let owner = Owner(public_key);
631 debug!(?cancellation_reason, ?owner, "Subscription cancelled");
632 }
633 NotificationType::SubscriptionPriceChangeConfirmed
634 | NotificationType::SubscriptionDeferred
635 | NotificationType::SubscriptionPaused
636 | NotificationType::SubscriptionPausedScheduleChanged
637 | NotificationType::SubscriptionPurchased => {
638 return Err(internal!(
639 "Unexpected subscription notification: {:?}, public_key: {:?}",
640 notification_type,
641 public_key
642 ))
643 }
644 NotificationType::Unknown => {
645 return Err(internal!(
646 "Unknown subscription change. public_key: {:?}",
647 public_key
648 ))
649 }
650 }
651
652 Ok(())
653 } else {
654 Err(internal!(
655 "Cannot get any billing info for user. public_key: {:?}",
656 public_key
657 ))
658 }
659 })
660 .await?;
661 }
662
663 if let Some(test_notif) = notification.test_notification {
664 let version = &test_notif.version;
665 debug!(?version, "Test notification");
666 }
667
668 if let Some(otp_notif) = notification.one_time_product_notification {
669 return Err(internal!(
670 "Received a one time product notification although there are no registered one time products. one_time_product_notification: {:?}",
671 otp_notif
672 ));
673 }
674
675 Ok(())
676 }
677
678 pub async fn app_store_notification_webhook(
679 &self, body: Bytes,
680 ) -> Result<(), ServerError<AppStoreNotificationError>> {
681 let resp = Self::decode_verify_notification(&self.config.billing.apple, &body)?;
682
683 if let NotificationChange::Subscribed = resp.notification_type {
684 return Ok(());
685 } else if let NotificationChange::Test = resp.notification_type {
686 debug!(?resp, "This is a test notification.");
687 return Ok(());
688 }
689
690 let trans = Self::decode_verify_transaction(
691 &self.config.billing.apple,
692 &resp
693 .clone()
694 .data
695 .encoded_transaction_info
696 .ok_or(ClientError(AppStoreNotificationError::InvalidJWS))?,
697 )?;
698 let public_key = self.get_public_key_from_tx(&trans).await?;
699
700 let owner = Owner(public_key);
701 let maybe_username = &self
702 .index_db
703 .lock()
704 .await
705 .accounts
706 .get()
707 .get(&owner)
708 .map(|acc| acc.username.clone());
709
710 info!(
711 ?owner,
712 ?maybe_username,
713 ?resp.notification_type,
714 ?resp.subtype,
715 "Updating app store user's subscription profile to match new subscription state"
716 );
717
718 self.save_subscription_profile(&public_key, |account| {
719 if let Some(BillingPlatform::AppStore(ref mut info)) =
720 account.billing_info.billing_platform
721 {
722 match resp.notification_type {
723 NotificationChange::DidFailToRenew => {
724 info.account_state = if let Some(Subtype::GracePeriod) = resp.subtype {
725 AppStoreAccountState::GracePeriod
726 } else {
727 AppStoreAccountState::FailedToRenew
728 };
729 }
730 NotificationChange::Expired => {
731 info.account_state = AppStoreAccountState::Expired;
732
733 match resp.subtype {
734 Some(Subtype::BillingRetry) => {
735 info!(
736 ?owner,
737 ?resp,
738 "Subscription failed to renew due to billing issues."
739 );
740 }
741 Some(Subtype::Voluntary) => {
742 info!(?owner, ?resp, "Subscription cancelled");
743 }
744 _ => {
745 return Err(internal!(
746 "Unexpected subtype: {:?}, notification_type {:?}, public_key: {:?}",
747 resp.subtype,
748 resp.notification_type,
749 public_key
750 ))
751 }
752 }
753 }
754 NotificationChange::GracePeriodExpired => {
755 info.account_state = AppStoreAccountState::Expired
756 }
757 NotificationChange::Refund => {
758 info!(?resp, "A user has requested a refund.");
759 }
760 NotificationChange::RefundDeclined => {
761 info!(?resp, "A user's refund request has been denied.");
762 }
763 NotificationChange::DidChangeRenewalStatus => match resp.subtype {
764 Some(Subtype::AutoRenewEnabled) => {
765 info.account_state = AppStoreAccountState::Ok;
766 info.expiration_time = trans.expires_date;
767 }
768 Some(Subtype::AutoRenewDisabled) => {}
769 _ => {
770 return Err(internal!(
771 "Unexpected subtype: {:?}, notification_type {:?}, public_key: {:?}",
772 resp.subtype,
773 resp.notification_type,
774 public_key
775 ))
776 }
777 },
778 NotificationChange::RenewalExtended => {
779 info.expiration_time = trans.expires_date
780 }
781 NotificationChange::DidRenew => {
782 if let Some(Subtype::BillingRecovery) = resp.subtype {
783 info.account_state = AppStoreAccountState::Ok;
784 }
785 info.expiration_time = trans.expires_date
786 }
787 NotificationChange::ConsumptionRequest
788 | NotificationChange::Subscribed
789 | NotificationChange::DidChangeRenewalPref
790 | NotificationChange::OfferRedeemed
791 | NotificationChange::PriceIncrease
792 | NotificationChange::Revoke
793 | NotificationChange::Test => {
794 return Err(internal!(
795 "Unexpected notification change: {:?} {:?}, public_key: {:?}",
796 resp.notification_type,
797 resp.subtype,
798 public_key
799 ))
800 }
801 }
802
803 Ok(())
804 } else {
805 Err(internal!("Cannot get any billing info for user. public_key: {:?}", public_key))
806 }
807 })
808 .await?;
809
810 Ok(())
811 }
812}
813
814pub fn stringify_public_key(pk: &PublicKey) -> String {
815 base64::encode(pk.serialize_compressed())
816}
817
818#[derive(Debug)]
819pub enum StripeWebhookError {
820 VerificationError(String),
821 InvalidHeader(String),
822 InvalidBody(String),
823 ParseError(String),
824}
825
826#[derive(Debug)]
827pub enum LockBillingWorkflowError {
828 UserNotFound,
829 ExistingRequestPending,
830}
831
832#[derive(Debug)]
833pub enum GooglePlayWebhookError {
834 InvalidToken,
835 CannotRetrieveData,
836 CannotDecodePubSubData(DecodeError),
837 CannotRetrieveUserInfo,
838 CannotRetrievePublicKey,
839 CannotParseTime,
840}
841
842#[derive(Debug)]
843pub enum AppStoreNotificationError {
844 InvalidJWS,
845}