From 5a307cac964a693657f40b9d59f02fc837ed4b88 Mon Sep 17 00:00:00 2001 From: Andi Pieper Date: Wed, 21 Feb 2024 21:23:48 +0100 Subject: [PATCH] feat(plus): user subscription transitions (#421) * do not record events that do not change the subscription status --- src/db/fxa_webhook.rs | 12 +++++++++++- tests/api/fxa_webhooks.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/db/fxa_webhook.rs b/src/db/fxa_webhook.rs index 99fba3d5..6161c314 100644 --- a/src/db/fxa_webhook.rs +++ b/src/db/fxa_webhook.rs @@ -167,6 +167,7 @@ pub async fn update_subscription_state_from_webhook( status: FxaEventStatus::Pending, payload: serde_json::value::to_value(&update).unwrap(), }; + debug!("Got subscription state change event: {:?}", update); if let Some(user) = user { let ignore = schema::webhook_events::table @@ -239,7 +240,16 @@ pub async fn update_subscription_state_from_webhook( // Record the subscription state change in its table. let old_subscription = user.get_subscription_type(); if let Some(old_subscription) = old_subscription { - // We have the user id, the old and new subscription, store it. + // Do not record transitions that are not transitioning anything. + // This can happen if the user cancels, but their subscription + // has some time left (monthly/yearly subscription). + // When the subscription actually ends, the system will send us a + // new event. + if old_subscription == subscription { + return Ok(()); + } + // We have the user id, the old and new subscription, + // they are different, so go ahead storing it. let subscription_change = SubscriptionChangeInsert { user_id: user.id, old_subscription_type: old_subscription, diff --git a/tests/api/fxa_webhooks.rs b/tests/api/fxa_webhooks.rs index 7f3387fe..f59f0ab3 100644 --- a/tests/api/fxa_webhooks.rs +++ b/tests/api/fxa_webhooks.rs @@ -463,6 +463,41 @@ async fn record_subscription_state_transitions_test() -> Result<(), Error> { ); } + // Now, reate an event where the new subscription is matching the old one. + // We do not record those. + { + let json_str = std::fs::read_to_string( + "tests/data/set_tokens/set_token_subscription_state_change_to_10m.json", + ) + .unwrap(); + let mut claim: Value = serde_json::from_str(&json_str).unwrap(); + // Add some time to the event to be sure it is after the previous event. + claim["iat"] = json!(1654425317000i64 + 600000); + claim["events"]["https://schemas.accounts.firefox.com/event/subscription-state-change"] + ["changeTime"] = json!(1654425317000i64 + 600000); + // We also add some unknown capability to the event to check that they are ignored correctly. + claim["events"]["https://schemas.accounts.firefox.com/event/subscription-state-change"] + ["capabilities"] = json!(["mdn_plus_10m"]); + let set_token = token_from_claim(&claim); + + let res = logged_in_client.trigger_webhook(&set_token).await; + assert!(res.response().status().is_success()); + + // check the transition is not recorded + let transitions = schema::user_subscription_transitions::table + .order(schema::user_subscription_transitions::created_at) + .load::(&mut conn)?; + assert_eq!(transitions.len(), 2); + assert_eq!( + transitions[0].created_at, + NaiveDateTime::from_timestamp_opt(1654425317, 0).unwrap() + ); + assert_eq!( + transitions[1].created_at, + NaiveDateTime::from_timestamp_opt(1654425617, 0).unwrap() + ); + } + drop_stubr(stubr).await; Ok(()) }