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: u64 = Self::get_usage_helper(&mut tree)?
290 .iter()
291 .map(|a| a.size_bytes)
292 .sum();
293
294 if usage > FREE_TIER_USAGE_SIZE {
295 debug!("Cannot downgrade user to free since they are over the data cap");
296 return Err(ClientError(CancelSubscriptionError::UsageIsOverFreeTierDataCap));
297 }
298 }
299
300 match account.billing_info.billing_platform {
301 None => {
302 return Err(internal!(
303 "A user somehow has premium tier usage, but no billing information on redis. public_key: {:?}",
304 context.public_key
305 ));
306 }
307 Some(BillingPlatform::GooglePlay(ref mut info)) => {
308 debug!("Canceling google play subscription of user");
309
310 if let GooglePlayAccountState::Canceled = &info.account_state {
311 return Err(ClientError(CancelSubscriptionError::AlreadyCanceled));
312 }
313
314 self.google_play_client
315 .cancel_subscription(&self.config, &info.purchase_token)
316 .await?;
317
318 info.account_state = GooglePlayAccountState::Canceled;
319 debug!("Successfully canceled google play subscription of user");
320 }
321 Some(BillingPlatform::Stripe(ref mut info)) => {
322 debug!("Canceling stripe subscription of user");
323
324 self.stripe_client
325 .cancel_subscription(&info.subscription_id.parse()?)
326 .await
327 .map_err::<ServerError<CancelSubscriptionError>, _>(|err| {
328 internal!("{:?}", err)
329 })?;
330
331 info.account_state = StripeAccountState::Canceled;
332
333 debug!("Successfully canceled stripe subscription");
334 }
335 Some(BillingPlatform::AppStore(_)) => {
336 return Err(ClientError(CancelSubscriptionError::CannotCancelForAppStore));
337 }
338 }
339
340 self.release_subscription_profile::<CancelSubscriptionError>(context.public_key, account)
341 .await?;
342
343 Ok(CancelSubscriptionResponse {})
344 }
345
346 pub async fn admin_set_user_tier(
347 &self, context: RequestContext<AdminSetUserTierRequest>,
348 ) -> Result<AdminSetUserTierResponse, ServerError<AdminSetUserTierError>> {
349 let request = &context.request;
350
351 {
352 let db = self.index_db.lock().await;
353
354 if !Self::is_admin::<AdminSetUserTierError>(
355 &db,
356 &context.public_key,
357 &self.config.admin.admins,
358 )? {
359 return Err(ClientError(AdminSetUserTierError::NotPermissioned));
360 }
361 }
362
363 let public_key = self
364 .index_db
365 .lock()
366 .await
367 .usernames
368 .get()
369 .get(&request.username)
370 .ok_or(ClientError(AdminSetUserTierError::UserNotFound))?
371 .0;
372 let mut account = self.lock_subscription_profile(&public_key).await?;
373
374 let billing_config = &self.config.billing;
375
376 account.billing_info.billing_platform = match &request.info {
377 AdminSetUserTierInfo::Stripe {
378 customer_id,
379 customer_name,
380 payment_method_id,
381 last_4,
382 subscription_id,
383 expiration_time,
384 account_state,
385 } => Some(BillingPlatform::Stripe(StripeUserInfo {
386 customer_id: customer_id.to_string(),
387 customer_name: *customer_name,
388 price_id: billing_config.stripe.premium_price_id.to_string(),
389 payment_method_id: payment_method_id.to_string(),
390 last_4: last_4.to_string(),
391 subscription_id: subscription_id.to_string(),
392 expiration_time: *expiration_time,
393 account_state: account_state.clone(),
394 })),
395 AdminSetUserTierInfo::GooglePlay { purchase_token, expiration_time, account_state } => {
396 Some(BillingPlatform::GooglePlay(GooglePlayUserInfo {
397 purchase_token: purchase_token.clone(),
398 subscription_product_id: billing_config
399 .google
400 .premium_subscription_product_id
401 .to_string(),
402 subscription_offer_id: billing_config
403 .google
404 .premium_subscription_offer_id
405 .to_string(),
406 expiration_time: *expiration_time,
407 account_state: account_state.clone(),
408 }))
409 }
410 AdminSetUserTierInfo::AppStore {
411 account_token,
412 original_transaction_id,
413 expiration_time,
414 account_state,
415 } => Some(BillingPlatform::AppStore(AppStoreUserInfo {
416 account_token: account_token.to_string(),
417 original_transaction_id: original_transaction_id.to_string(),
418 subscription_product_id: billing_config.apple.subscription_product_id.to_string(),
419 expiration_time: *expiration_time,
420 account_state: account_state.clone(),
421 })),
422 AdminSetUserTierInfo::Free => None,
423 };
424
425 account.username = request.username.clone();
426
427 self.release_subscription_profile::<AdminSetUserTierError>(public_key, account)
428 .await?;
429
430 Ok(AdminSetUserTierResponse {})
431 }
432
433 async fn save_subscription_profile<
434 T: Debug,
435 F: Fn(&mut Account) -> Result<(), ServerError<T>>,
436 >(
437 &self, public_key: &PublicKey, update_subscription_profile: F,
438 ) -> Result<(), ServerError<T>> {
439 let millis_between_lock_attempts = self.config.billing.time_between_lock_attempts;
440 loop {
441 match self.lock_subscription_profile(public_key).await {
442 Ok(ref mut sub_profile) => {
443 update_subscription_profile(sub_profile)?;
444 self.release_subscription_profile(*public_key, sub_profile.clone())
445 .await?;
446 break;
447 }
448 Err(ClientError(ExistingRequestPending)) => {
449 tokio::time::sleep(millis_between_lock_attempts).await;
450 continue;
451 }
452 Err(err) => {
453 return Err(internal!("Cannot get billing lock in webhooks: {:#?}", err));
454 }
455 }
456 }
457
458 Ok(())
459 }
460
461 pub async fn stripe_webhooks(
462 &self, request_body: Bytes, stripe_sig: HeaderValue,
463 ) -> Result<(), ServerError<StripeWebhookError>> {
464 let event = self.verify_request_and_get_event(&request_body, stripe_sig)?;
465
466 let event_type = event.type_;
467 debug!(?event_type, "Verified stripe request");
468
469 match (&event.type_, &event.data.object) {
470 (stripe::EventType::InvoicePaymentFailed, stripe::EventObject::Invoice(invoice)) => {
471 if let Some(stripe::InvoiceBillingReason::SubscriptionCycle) =
472 invoice.billing_reason
473 {
474 let public_key = self.get_public_key_from_invoice(invoice).await?;
475 let owner = Owner(public_key);
476
477 debug!(
478 ?owner,
479 "User's tier is being reduced due to failed renewal payment in stripe"
480 );
481
482 self.save_subscription_profile(&public_key, |account| {
483 if let Some(BillingPlatform::Stripe(ref mut info)) =
484 account.billing_info.billing_platform
485 {
486 info.account_state = StripeAccountState::InvoiceFailed;
487
488 Ok(())
489 } else {
490 Err(internal!(
491 "Cannot get any billing info for user. public_key: {:?}",
492 public_key
493 ))
494 }
495 })
496 .await?;
497 }
498 }
499 (stripe::EventType::InvoicePaid, stripe::EventObject::Invoice(invoice)) => {
500 if let Some(stripe::InvoiceBillingReason::SubscriptionCycle) =
501 invoice.billing_reason
502 {
503 let public_key = self.get_public_key_from_invoice(invoice).await?;
504 let owner = Owner(public_key);
505
506 debug!(
507 ?owner,
508 "User's subscription period_end is being changed after successful renewal",
509 );
510
511 let subscription_period_end = match &invoice.subscription {
512 Some(stripe::Expandable::Object(subscription)) => {
513 subscription.current_period_end
514 }
515 Some(stripe::Expandable::Id(subscription_id)) => {
516 self.stripe_client
517 .get_subscription(subscription_id)
518 .await
519 .map_err::<ServerError<StripeWebhookError>, _>(|err| {
520 internal!("{:?}", err)
521 })?
522 .current_period_end
523 }
524 None => {
525 return Err(internal!(
526 "The subscription should be included in this invoice: {:?}",
527 invoice
528 ));
529 }
530 };
531
532 self.save_subscription_profile(&public_key, |account| {
533 if let Some(BillingPlatform::Stripe(ref mut info)) =
534 account.billing_info.billing_platform
535 {
536 info.account_state = StripeAccountState::Ok;
537 info.expiration_time = subscription_period_end as u64;
538
539 Ok(())
540 } else {
541 Err(internal!(
542 "Cannot get any billing info for user. public_key: {:?}",
543 public_key
544 ))
545 }
546 })
547 .await?;
548 }
549 }
550 (_, _) => {
551 return Err(internal!("Unexpected stripe event: {:?}", event.type_));
552 }
553 }
554
555 Ok(())
556 }
557
558 pub async fn google_play_notification_webhooks(
559 &self, request_body: Bytes, query_parameters: HashMap<String, String>,
560 ) -> Result<(), ServerError<GooglePlayWebhookError>> {
561 let notification = self
562 .verify_request_and_get_notification(request_body, query_parameters)
563 .await?;
564
565 if let Some(sub_notif) = notification.subscription_notification {
566 debug!(?sub_notif, "Notification is for a subscription");
567
568 let subscription = self
569 .google_play_client
570 .get_subscription(&self.config, &sub_notif.purchase_token)
571 .await
572 .map_err(|e| internal!("{:#?}", e))?;
573
574 let notification_type = sub_notif.notification_type();
575 if let NotificationType::SubscriptionPurchased = notification_type {
576 return Ok(());
577 }
578
579 let public_key = self
580 .get_public_key_from_subnotif(&sub_notif, &subscription, ¬ification_type)
581 .await?;
582 let owner = Owner(public_key);
583
584 debug!(
585 ?owner,
586 ?notification_type,
587 "Updating google play user's subscription profile to match new subscription state"
588 );
589
590 self.save_subscription_profile(&public_key, |account| {
591 if let Some(BillingPlatform::GooglePlay(ref mut info)) =
592 account.billing_info.billing_platform
593 {
594 match notification_type {
595 NotificationType::SubscriptionRecovered
596 | NotificationType::SubscriptionRestarted
597 | NotificationType::SubscriptionRenewed => {
598 info.account_state = GooglePlayAccountState::Ok;
599 info.expiration_time = Self::get_subscription_period_end(
600 &subscription,
601 ¬ification_type,
602 public_key,
603 )?;
604 }
605 NotificationType::SubscriptionInGracePeriod => {
606 info.account_state = GooglePlayAccountState::GracePeriod;
607 }
608 NotificationType::SubscriptionOnHold => {
609 info.account_state = GooglePlayAccountState::OnHold;
610 info.expiration_time = Self::get_subscription_period_end(
611 &subscription,
612 ¬ification_type,
613 public_key,
614 )?;
615 }
616 NotificationType::SubscriptionExpired
617 | NotificationType::SubscriptionRevoked => {
618 if info.purchase_token == sub_notif.purchase_token {
619 account.billing_info.billing_platform = None
620 } else {
621 let old_purchase_token = &sub_notif.purchase_token;
622 let new_purchase_token = &info.purchase_token;
623 debug!(
624 ?old_purchase_token,
625 ?new_purchase_token,
626 "Expired or revoked subscription was tied to an old purchase_token"
627 );
628 }
629 }
630 NotificationType::SubscriptionCanceled => {
631 info.account_state = GooglePlayAccountState::Canceled;
632 let cancellation_reason = &subscription.cancel_survey_result;
633 let owner = Owner(public_key);
634 debug!(?cancellation_reason, ?owner, "Subscription cancelled");
635 }
636 NotificationType::SubscriptionPriceChangeConfirmed
637 | NotificationType::SubscriptionDeferred
638 | NotificationType::SubscriptionPaused
639 | NotificationType::SubscriptionPausedScheduleChanged
640 | NotificationType::SubscriptionPurchased => {
641 return Err(internal!(
642 "Unexpected subscription notification: {:?}, public_key: {:?}",
643 notification_type,
644 public_key
645 ))
646 }
647 NotificationType::Unknown => {
648 return Err(internal!(
649 "Unknown subscription change. public_key: {:?}",
650 public_key
651 ))
652 }
653 }
654
655 Ok(())
656 } else {
657 Err(internal!(
658 "Cannot get any billing info for user. public_key: {:?}",
659 public_key
660 ))
661 }
662 })
663 .await?;
664 }
665
666 if let Some(test_notif) = notification.test_notification {
667 let version = &test_notif.version;
668 debug!(?version, "Test notification");
669 }
670
671 if let Some(otp_notif) = notification.one_time_product_notification {
672 return Err(internal!(
673 "Received a one time product notification although there are no registered one time products. one_time_product_notification: {:?}",
674 otp_notif
675 ));
676 }
677
678 Ok(())
679 }
680
681 pub async fn app_store_notification_webhook(
682 &self, body: Bytes,
683 ) -> Result<(), ServerError<AppStoreNotificationError>> {
684 let resp = Self::decode_verify_notification(&self.config.billing.apple, &body)?;
685
686 if let NotificationChange::Subscribed = resp.notification_type {
687 return Ok(());
688 } else if let NotificationChange::Test = resp.notification_type {
689 debug!(?resp, "This is a test notification.");
690 return Ok(());
691 }
692
693 let trans = Self::decode_verify_transaction(
694 &self.config.billing.apple,
695 &resp
696 .clone()
697 .data
698 .encoded_transaction_info
699 .ok_or(ClientError(AppStoreNotificationError::InvalidJWS))?,
700 )?;
701 let public_key = self.get_public_key_from_tx(&trans).await?;
702
703 let owner = Owner(public_key);
704 let maybe_username = &self
705 .index_db
706 .lock()
707 .await
708 .accounts
709 .get()
710 .get(&owner)
711 .map(|acc| acc.username.clone());
712
713 info!(
714 ?owner,
715 ?maybe_username,
716 ?resp.notification_type,
717 ?resp.subtype,
718 "Updating app store user's subscription profile to match new subscription state"
719 );
720
721 self.save_subscription_profile(&public_key, |account| {
722 if let Some(BillingPlatform::AppStore(ref mut info)) =
723 account.billing_info.billing_platform
724 {
725 match resp.notification_type {
726 NotificationChange::DidFailToRenew => {
727 info.account_state = if let Some(Subtype::GracePeriod) = resp.subtype {
728 AppStoreAccountState::GracePeriod
729 } else {
730 AppStoreAccountState::FailedToRenew
731 };
732 }
733 NotificationChange::Expired => {
734 info.account_state = AppStoreAccountState::Expired;
735
736 match resp.subtype {
737 Some(Subtype::BillingRetry) => {
738 info!(
739 ?owner,
740 ?resp,
741 "Subscription failed to renew due to billing issues."
742 );
743 }
744 Some(Subtype::Voluntary) => {
745 info!(?owner, ?resp, "Subscription cancelled");
746 }
747 _ => {
748 return Err(internal!(
749 "Unexpected subtype: {:?}, notification_type {:?}, public_key: {:?}",
750 resp.subtype,
751 resp.notification_type,
752 public_key
753 ))
754 }
755 }
756 }
757 NotificationChange::GracePeriodExpired => {
758 info.account_state = AppStoreAccountState::Expired
759 }
760 NotificationChange::Refund => {
761 info!(?resp, "A user has requested a refund.");
762 }
763 NotificationChange::RefundDeclined => {
764 info!(?resp, "A user's refund request has been denied.");
765 }
766 NotificationChange::DidChangeRenewalStatus => match resp.subtype {
767 Some(Subtype::AutoRenewEnabled) => {
768 info.account_state = AppStoreAccountState::Ok;
769 info.expiration_time = trans.expires_date;
770 }
771 Some(Subtype::AutoRenewDisabled) => {}
772 _ => {
773 return Err(internal!(
774 "Unexpected subtype: {:?}, notification_type {:?}, public_key: {:?}",
775 resp.subtype,
776 resp.notification_type,
777 public_key
778 ))
779 }
780 },
781 NotificationChange::RenewalExtended => {
782 info.expiration_time = trans.expires_date
783 }
784 NotificationChange::DidRenew => {
785 if let Some(Subtype::BillingRecovery) = resp.subtype {
786 info.account_state = AppStoreAccountState::Ok;
787 }
788 info.expiration_time = trans.expires_date
789 }
790 NotificationChange::ConsumptionRequest
791 | NotificationChange::Subscribed
792 | NotificationChange::DidChangeRenewalPref
793 | NotificationChange::OfferRedeemed
794 | NotificationChange::PriceIncrease
795 | NotificationChange::Revoke
796 | NotificationChange::Test => {
797 return Err(internal!(
798 "Unexpected notification change: {:?} {:?}, public_key: {:?}",
799 resp.notification_type,
800 resp.subtype,
801 public_key
802 ))
803 }
804 }
805
806 Ok(())
807 } else {
808 Err(internal!("Cannot get any billing info for user. public_key: {:?}", public_key))
809 }
810 })
811 .await?;
812
813 Ok(())
814 }
815}
816
817pub fn stringify_public_key(pk: &PublicKey) -> String {
818 base64::encode(pk.serialize_compressed())
819}
820
821#[derive(Debug)]
822pub enum StripeWebhookError {
823 VerificationError(String),
824 InvalidHeader(String),
825 InvalidBody(String),
826 ParseError(String),
827}
828
829#[derive(Debug)]
830pub enum LockBillingWorkflowError {
831 UserNotFound,
832 ExistingRequestPending,
833}
834
835#[derive(Debug)]
836pub enum GooglePlayWebhookError {
837 InvalidToken,
838 CannotRetrieveData,
839 CannotDecodePubSubData(DecodeError),
840 CannotRetrieveUserInfo,
841 CannotRetrievePublicKey,
842 CannotParseTime,
843}
844
845#[derive(Debug)]
846pub enum AppStoreNotificationError {
847 InvalidJWS,
848}