Premium listing cancellation
Persona
A pastor who has been paying $9.95/mo for 3–6 months. The premium features weren't used enough to justify the cost, or budget forced a priority shift. They want a clean, guilt-free exit. They expect their data to be safe if they change their mind.
Entry points
- Admin dashboard Billing section — Settings → Billing → "Cancel Premium" link → Stripe Customer Portal.
- Stripe Customer Portal — "Manage My Subscription" link in admin or Stripe emails.
- Email or support request — Support provides Stripe portal link or initiates manually.
Click-through flow
Steps
-
Initiate cancellation — Settings tab → Billing shows "PewSearch Premium – $9.95/month," next billing date, "Cancel Premium" text link. Inline note: "Your plan will stay active until [date]. Cancel at Stripe to proceed." Link redirects to Stripe Customer Portal URL for this church's subscription.
-
Confirm in Stripe Customer Portal — Stripe shows subscription details and "Cancel subscription" button. Confirmation modal: "Your subscription will be cancelled on [end_date]. You won't be charged again, but you can reactivate anytime." Stripe sets
cancel_at_period_end = trueand firescustomer.subscription.updatedwebhook. -
Receive cancellation confirmation and dashboard banner — Email: "Your PewSearch Premium has been cancelled – [Church Name]." Plan continues through end_date; reactivate link included. Dashboard: persistent amber banner on every tab: "Your plan cancels on [end_date] — [Reactivate]." API webhook updates
premium_churches.cancel_at_period_end = true; status staysactive. -
Continue using Premium until billing period ends — All dashboard tabs (Training, Requests, Settings) remain fully functional. Editing, photo uploads, and staff management all work. Public listing retains verified badge, custom photos, staff grid, and ministries section.
-
Billing period ends — listing reverts and dashboard enters read-only — Stripe fires
customer.subscription.deleted. API setspremium_churches.status = 'cancelled'. Service ended email sent. Public listing at/churches/[slug]reverts to free template: no custom photos, no staff grid, no verified badge, "Claim this church" banner reappears. Dashboard: Status badge "Cancelled," all tabs read-only, data visible but not editable.
Acceptance spec
Canonical: knowledge/acceptance/cancelled.md (CX-1 through CX-4: cancellation flow; CX-7 through CX-11: post-cancellation state).
Success criteria
- Cancellation is self-serve; no need to email support.
- Full access and editing rights until the billing period ends.
- Dashboard clearly shows the end date.
- After end date, data is safe and visible in read-only mode.
- Public listing gracefully reverts to free tier (no broken images, no 404).
- Clear emails at each stage; no surprise charges.
- Can reactivate anytime; old data is restored immediately.
Known failure modes
-
Premium features show after billing period ends.
/churches/[slug]must re-fetchpremium_churches.statuson every render (no caching). IncludeWHERE status='active'when determining premium content rendering. -
Admin dashboard still writable after end date. Middleware must check
premium_churches.status = 'active'. If false, render all form inputs asdisabled. -
Win-back emails too aggressive. Send only 2 win-back emails max: 7 days post-cancellation, 30 days post-cancellation. No more after that.
-
Webhook delay on cancellation email. Stripe's portal immediately shows "Your subscription will be cancelled" — this is sufficient visual feedback. Email is a courtesy; mild delay is acceptable.