fix(checkout): clear pending checkout intent on 409 already_subscribed

The /pro -> signed-in-main-app handoff stashes a PendingCheckoutIntent in
sessionStorage and replays it via resumePendingCheckout on each app load.
resumePendingCheckout only clears the intent when startCheckout returns
true. With the new 409 already_subscribed path returning false (after
showing the modal), the intent stays in sessionStorage and re-triggers the
same blocked checkout + modal on every subsequent app startup until the
user signs out, cancels the sub, or clears storage.

Fix: clear the intent inside startCheckout's 409 branch. Semantics: a 409
means the intent is fully resolved ("you already have Pro, go manage it"),
not pending. Clearing at that point prevents the loop while preserving the
existing "clear on success" path for legitimate checkouts.

Applies only to the "already_subscribed" branch. Other failure modes
(generic 5xx, timeouts, CORS) still preserve the intent so a legitimate
retry on the next load can proceed.
This commit is contained in:
Elie Habib
2026-04-18 14:55:58 +04:00
parent 33c90c9e7b
commit 111be8bfed

View File

@@ -266,6 +266,14 @@ export async function startCheckout(
// User already has an active/on_hold subscription. Route to billing
// portal instead of opening another Dodo checkout — prevents the
// double-charge that hit cus_0NcmwcAWw0jhVBHVOK58C on 2026-04-17/18.
//
// Consume any pending checkout intent stored in sessionStorage. The
// intent exists only to replay a /pro handoff once the user has
// signed in; a 409 "already subscribed" means the intent is fully
// resolved, and leaving it behind would re-trigger this same modal
// on every subsequent app startup until the sub is cancelled or the
// session ends.
clearPendingCheckoutIntent();
await showAlreadySubscribedDialog(err.error);
return false;
}