Skip to main content

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

  1. Admin dashboard Billing section — Settings → Billing → "Cancel Premium" link → Stripe Customer Portal.
  2. Stripe Customer Portal — "Manage My Subscription" link in admin or Stripe emails.
  3. Email or support request — Support provides Stripe portal link or initiates manually.

Click-through flow

Steps

  1. 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.

  2. 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 = true and fires customer.subscription.updated webhook.

  3. 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 stays active.

  4. 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.

  5. Billing period ends — listing reverts and dashboard enters read-only — Stripe fires customer.subscription.deleted. API sets premium_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-fetch premium_churches.status on every render (no caching). Include WHERE 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 as disabled.

  • 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.