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