fix(subscription): recover stepper when a pending checkout is abandoned
Some checks failed
Deploy / Deploy to Production (push) Has been cancelled

A user who picked a plan + brands, clicked the checkout button to
create the pending subscription, then closed the tab without paying
came back to a frozen "Mevcut Plan · Bekliyor" card with no payment
button and no way to edit. Status was "pending", which my conditional
treated as an active subscription and routed away from the stepper.

Frontend
- hasActiveSub narrowed to active|cancelled only. Pending users now
  see the stepper again.
- New effect pre-fills selectedPlanKey, selectedBrandIds,
  billingPeriod from the pending row on first render and jumps
  straight to the payment step.
- New PendingPaymentBanner at the top of the stepper with
  "Odemeye devam et" (scrolls + sets step=payment) and "Vazgec"
  (calls cancel-pending then resets selection).

Backend
- subscriptions.service.create() auto-expires existing pending
  rows for the user before inserting a new one — abandoned checkouts
  no longer accumulate and a fresh attempt with a different plan
  doesn't 409.
- New PATCH /subscriptions/cancel-pending + service method for
  the banner's explicit cancel action.

i18n: subscription.pendingBanner.* (tr/en).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sase Dev
2026-05-13 22:57:13 +00:00
parent 9c05a05a7a
commit 02bf7342a1
5 changed files with 142 additions and 2 deletions

View File

@@ -256,6 +256,13 @@
"title": "Your trial is ending soon!",
"description": "{days} days left. Upgrade now to continue accessing all features.",
"cta": "Upgrade Plan"
},
"pendingBanner": {
"title": "You have a pending payment",
"description": "Checkout for {plan} ({brands}) was left incomplete. Resume it or cancel to pick a different plan.",
"resumeCta": "Resume payment",
"cancelCta": "Cancel",
"cancelled": "Pending subscription cancelled. You can pick a new plan."
}
},
"payment": {

View File

@@ -256,6 +256,13 @@
"title": "Deneme süreniz dolmak üzere!",
"description": "{days} gününüz kaldı. Tüm özelliklere erişmeye devam etmek için hemen yükseltin.",
"cta": "Plana Yükselt"
},
"pendingBanner": {
"title": "Bekleyen ödemen var",
"description": "{plan} planı, {brands} için ödeme yarıda kaldı. Devam edebilir veya farklı plan seçmek için vazgeçebilirsin.",
"resumeCta": "Ödemeye devam et",
"cancelCta": "Vazgeç",
"cancelled": "Bekleyen abonelik iptal edildi. Yeni plan seçebilirsin."
}
},
"payment": {