Skip to content

Commit b11cd54

Browse files
committed
bugfix: Shopify resubscribe and cancel UI + backend fixes
1 parent 4221c48 commit b11cd54

5 files changed

Lines changed: 112 additions & 26 deletions

File tree

frontends/dashboard/src/components/PlansTable.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -407,19 +407,24 @@ export const PlansTable = (props: PlansTableProps) => {
407407
<For each={availablePlansWithCurrent()}>
408408
{(plan) => {
409409
const curPlan = currentPlan();
410-
const isUpgrade = curPlan
411-
? curPlan.amount < plan.amount
412-
: false;
413410

414411
const currentPeriodEnd = plan.current_period_end;
415412
let actionButton = <ActiveTag text="Current Tier" />;
416413

417414
if (!plan.current || currentPeriodEnd) {
418-
if ((curPlan?.amount ?? 0) > 0 && !currentPeriodEnd) {
415+
if (!currentPeriodEnd) {
419416
const onClickFunc = () => {
420417
void updatePlan(plan);
421418
};
422-
const buttonText = isUpgrade ? "Upgrade" : "Downgrade";
419+
420+
const isUpgrade = curPlan
421+
? curPlan.amount < plan.amount
422+
: true;
423+
424+
const buttonText =
425+
isUpgrade || curPlan?.amount == null
426+
? "Upgrade"
427+
: "Downgrade";
423428
actionButton = (
424429
<button
425430
onClick={onClickFunc}

frontends/dashboard/src/components/UsagePlansTable.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ export const UsagePlansTable = (props: PlansTableProps) => {
5151
});
5252
});
5353

54+
const refetchOrgSubPlan = async () => {
55+
const selectedOrgId = props.currentOrgSubPlan?.organization.id ?? "";
56+
57+
const resp = await fetch(`${apiHost}/organization/${selectedOrgId}`, {
58+
credentials: "include",
59+
headers: {
60+
"TR-Organization": selectedOrgId,
61+
},
62+
});
63+
64+
if (resp.ok) {
65+
const data = (await resp.json()) as OrganizationAndSubAndPlan;
66+
setCurrentSubscription(data.subscription ?? null);
67+
setCurrentPlan(data.plan ?? null);
68+
}
69+
};
70+
5471
const updatePlan = async (plan: StripeUsageBasedPlan) => {
5572
const resp = await fetch(
5673
`${apiHost}/stripe/subscription_plan/${
@@ -68,6 +85,8 @@ export const UsagePlansTable = (props: PlansTableProps) => {
6885
if (resp.ok) {
6986
setCurrentPlan(plan);
7087
}
88+
89+
void refetchOrgSubPlan();
7190
};
7291

7392
return (
@@ -78,8 +97,10 @@ export const UsagePlansTable = (props: PlansTableProps) => {
7897
let actionButton = <ActiveTag text="Current Tier" />;
7998

8099
if (plan.id !== curPlan?.id || curSub?.current_period_end != null) {
81-
if (curPlan?.id !== "00000000-0000-0000-0000-000000000000") {
82-
console.log("We have id", plan.id);
100+
if (
101+
curPlan?.id !== "00000000-0000-0000-0000-000000000000" &&
102+
curSub?.current_period_end == null
103+
) {
83104
actionButton = (
84105
<button
85106
onClick={() => void updatePlan(plan)}

server/src/data/models.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4128,12 +4128,24 @@ impl TrieveSubscription {
41284128
stripe_subscription: Option<StripeSubscription>,
41294129
stripe_usage_based_subscription: Option<StripeUsageBasedSubscription>,
41304130
) -> Option<Self> {
4131-
if let Some(usage_subscription) = stripe_usage_based_subscription {
4132-
Some(TrieveSubscription::UsageBased(usage_subscription))
4133-
} else if let Some(subscription) = stripe_subscription {
4134-
Some(TrieveSubscription::Flat(subscription))
4135-
} else {
4136-
None
4131+
match (stripe_subscription, stripe_usage_based_subscription) {
4132+
(Some(flat_sub), None) => Some(TrieveSubscription::Flat(flat_sub)),
4133+
(None, Some(usage_sub)) => Some(TrieveSubscription::UsageBased(usage_sub)),
4134+
(Some(flat_sub), Some(usage_sub)) => {
4135+
match (flat_sub.current_period_end, usage_sub.current_period_end) {
4136+
(Some(flat_period_end), Some(usage_period_ned)) => {
4137+
if flat_period_end > usage_period_ned {
4138+
Some(TrieveSubscription::Flat(flat_sub))
4139+
} else {
4140+
Some(TrieveSubscription::UsageBased(usage_sub))
4141+
}
4142+
}
4143+
(Some(_), None) => Some(TrieveSubscription::UsageBased(usage_sub)),
4144+
(None, Some(_)) => Some(TrieveSubscription::Flat(flat_sub)),
4145+
(None, None) => None,
4146+
}
4147+
}
4148+
(None, None) => None,
41374149
}
41384150
}
41394151

@@ -4159,6 +4171,13 @@ impl TrieveSubscription {
41594171
}
41604172
}
41614173
}
4174+
4175+
pub fn current_period_end(&self) -> Option<chrono::NaiveDateTime> {
4176+
match self {
4177+
TrieveSubscription::Flat(subscription) => subscription.current_period_end,
4178+
TrieveSubscription::UsageBased(subscription) => subscription.current_period_end,
4179+
}
4180+
}
41624181
}
41634182

41644183
impl From<StripeSubscription> for TrieveSubscription {

server/src/handlers/stripe_handler.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::{
1313
create_stripe_plan_query, create_stripe_setup_checkout_session,
1414
create_stripe_subscription_query, create_usage_based_stripe_payment_link,
1515
create_usage_stripe_subscription_query, delete_subscription_by_id_query,
16-
get_all_plans_query, get_all_usage_plans_query, get_invoices_for_org_query,
17-
get_option_subscription_by_organization_id_query,
16+
delete_usage_subscription_by_id_query, get_all_plans_query, get_all_usage_plans_query,
17+
get_invoices_for_org_query, get_option_subscription_by_organization_id_query,
1818
get_option_usage_based_subscription_by_organization_id_query,
1919
get_option_usage_based_subscription_by_subscription_id_query, get_plan_by_id_query,
2020
get_stripe_client, get_trieve_plan_by_id_query, get_trieve_subscription_by_id_query,
@@ -153,6 +153,21 @@ pub async fn webhook(
153153

154154
if plan_type == "usage-based" {
155155
log::info!("Creating usage based stripe subscription");
156+
let optional_existing_subscription =
157+
get_option_usage_based_subscription_by_organization_id_query(
158+
organization_id,
159+
optional_subscription_pool,
160+
)
161+
.await?;
162+
163+
if let Some(existing_subscription) = optional_existing_subscription
164+
{
165+
delete_usage_subscription_by_id_query(
166+
existing_subscription.id,
167+
pool.clone(),
168+
)
169+
.await?;
170+
}
156171
// record current usage
157172
let usage =
158173
get_org_usage_by_id_query(organization_id, pool.clone())
@@ -331,21 +346,20 @@ pub async fn direct_to_payment_link(
331346
query_params: web::Query<CreateDirectPaymentLinkQueryParams>,
332347
pool: web::Data<Pool>,
333348
) -> Result<HttpResponse, actix_web::Error> {
334-
let organization_pool = pool.clone();
335-
let subscription_pool = pool.clone();
336349
let subscription_org_id = path_data.organization_id;
337350

338351
let current_subscription =
339-
get_option_subscription_by_organization_id_query(subscription_org_id, subscription_pool)
340-
.await?;
352+
get_trieve_subscription_by_id_query(subscription_org_id, pool.clone())
353+
.await
354+
.ok();
341355

342-
if current_subscription.is_some_and(|s| s.current_period_end.is_none()) {
356+
if current_subscription.is_some_and(|s| s.current_period_end().is_none()) {
343357
return Ok(HttpResponse::Conflict().finish());
344358
}
345359

346360
let organization_id = path_data.organization_id;
347361
let organization_id_clone = path_data.organization_id;
348-
let _org_plan_sub = get_org_from_id_query(organization_id_clone, organization_pool).await?;
362+
let _org_plan_sub = get_org_from_id_query(organization_id_clone, pool.clone()).await?;
349363
let plan_id = path_data.plan_id;
350364

351365
let payment_link = match query_params.usage_based {
@@ -440,13 +454,13 @@ pub async fn update_subscription_plan(
440454
let trieve_subscription =
441455
get_trieve_subscription_by_id_query(current_subscription_id, pool.clone()).await?;
442456

443-
let trieve_plan = get_trieve_plan_by_id_query(new_plan_id, pool.clone()).await?;
457+
let new_trieve_plan = get_trieve_plan_by_id_query(new_plan_id, pool.clone()).await?;
444458

445459
if !verify_owner(&user, &trieve_subscription.organization_id()) {
446460
return Err(ServiceError::Forbidden.into());
447461
};
448462

449-
match trieve_plan {
463+
match new_trieve_plan {
450464
TrievePlan::Flat(flat_plan) => {
451465
update_to_flat_stripe_subscription(
452466
trieve_subscription.stripe_subscription_id(),

server/src/operators/stripe_operator.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use std::{collections::HashMap, str::FromStr};
22

33
use crate::{
4-
data::models::{
5-
DateRange, OrganizationUsageCount, Pool, StripeInvoice, StripePlan, StripeSubscription,
6-
StripeUsageBasedPlan, StripeUsageBasedSubscription, TrievePlan, TrieveSubscription,
4+
data::{
5+
models::{
6+
DateRange, OrganizationUsageCount, Pool, StripeInvoice, StripePlan, StripeSubscription,
7+
StripeUsageBasedPlan, StripeUsageBasedSubscription, TrievePlan, TrieveSubscription,
8+
},
9+
schema::stripe_usage_based_subscriptions,
710
},
811
errors::ServiceError,
912
get_env,
@@ -478,6 +481,30 @@ pub async fn get_trieve_subscription_by_id_query(
478481
.ok_or(ServiceError::NotFound("Subscription not found".to_string()))
479482
}
480483

484+
pub async fn delete_usage_subscription_by_id_query(
485+
subscription_id: uuid::Uuid,
486+
pool: web::Data<Pool>,
487+
) -> Result<(), ServiceError> {
488+
use crate::data::schema::stripe_usage_based_subscriptions::dsl as stripe_usage_based_subscriptions_columns;
489+
490+
let mut conn = pool
491+
.get()
492+
.await
493+
.expect("Failed to get connection from pool");
494+
diesel::delete(
495+
stripe_usage_based_subscriptions_columns::stripe_usage_based_subscriptions
496+
.filter(stripe_usage_based_subscriptions::id.eq(subscription_id)),
497+
)
498+
.execute(&mut conn)
499+
.await
500+
.map_err(|e| {
501+
log::error!("Failed to delete stripe subscription: {}", e);
502+
ServiceError::BadRequest("Failed to delete stripe subscription".to_string())
503+
})?;
504+
505+
Ok(())
506+
}
507+
481508
pub async fn delete_subscription_by_id_query(
482509
subscription_id: uuid::Uuid,
483510
pool: web::Data<Pool>,

0 commit comments

Comments
 (0)