fix(platform): fix Stripe customer creation on FREE downgrade and dialog display issues

- Guard cancel_stripe_subscription with stripe_customer_id check to prevent
  creating orphaned Stripe customers for users who never had a paid subscription
- Fix upgrade dialog showing raw tier key (PRO) instead of human-readable label (Pro)
- Fix misleading footer text that contradicted downgrade-to-FREE behaviour
  (downgrades to Free are scheduled at period end, not immediate)
This commit is contained in:
majdyz
2026-04-15 13:25:42 +07:00
parent b435814826
commit c421a66fa5
2 changed files with 15 additions and 4 deletions

View File

@@ -1353,7 +1353,15 @@ async def cancel_stripe_subscription(user_id: str) -> bool:
Raises stripe.StripeError if any modification fails, so the caller can avoid
updating the DB tier when Stripe is inconsistent.
"""
customer_id = await get_stripe_customer_id(user_id)
# Guard: only proceed if the user already has a Stripe customer ID. Calling
# get_stripe_customer_id for a user who has never had a paid subscription would
# create an orphaned, potentially-billable Stripe Customer object — we avoid that
# by returning False early so the caller can downgrade the DB tier directly.
user = await get_user_by_id(user_id)
if not user.stripe_customer_id:
return False
customer_id = user.stripe_customer_id
try:
cancelled_count = await _cancel_customer_subscriptions(
customer_id, at_period_end=True

View File

@@ -196,8 +196,9 @@ export function SubscriptionTierSection() {
{currentTier !== "FREE" && isPaymentEnabled && (
<p className="text-sm text-neutral-500">
Your subscription is managed through Stripe. Changes take effect
immediately.
Your subscription is managed through Stripe. Upgrades and paid-tier
changes take effect immediately; downgrades to Free are scheduled for
the end of the current billing period.
</p>
)}
@@ -249,7 +250,9 @@ export function SubscriptionTierSection() {
subscription.proration_credit_cents > 0 &&
`Your unused ${currentTier.charAt(0) + currentTier.slice(1).toLowerCase()} subscription ($${(subscription.proration_credit_cents / 100).toFixed(2)}) will be added to your account balance. `}
You will be redirected to Stripe to complete your upgrade to{" "}
{pendingUpgradeTier}.
{TIERS.find((t) => t.key === pendingUpgradeTier)?.label ??
pendingUpgradeTier}
.
</p>
<Dialog.Footer>
<Button