Pastor cancels their Pro Website subscription
Persona
A pastor who set up their Pro Website 6–12 months ago and has decided to pause or stop. Budget constraint, switching platforms, merged with another church, or simply deprioritized. They want a clean exit in under 2 minutes without talking to support. They may want to export posts and photos before leaving.
Entry points
- Admin Settings → Billing — "Cancel Plan" text link.
- Email win-back campaign — 7-day and 30-day follow-up emails with "Reactivate" CTA; pastor may click to cancel or reactivate.
- Support request — pastor emails asking to cancel; support directs to self-serve flow.
Click-through flow
Steps
-
Navigate to Admin Settings → Billing — Shows "Pro Website Bundled — $19.95/month," next billing date. "Cancel Plan" link (not a prominent button).
-
Review cancellation note — Inline: "Your plan continues until [date]. No refunds for the current period. All data preserved — reactivate anytime." Buttons: "Manage My Subscription →" (primary) and "Nevermind" (dismisses note).
-
Stripe Customer Portal — Stripe native cancellation: plan overview, billing period, end date, "Cancel subscription" button, optional reason dropdown. Pastor clicks "Cancel subscription." Stripe sets
cancel_at_period_end = true. -
Receive cancellation confirmation email — Trigger:
customer.subscription.updatedwebhook. Email: "Your ChurchWiseAI plan has been cancelled — [Church Name]," confirms end date, CTA "Reactivate here." -
Dashboard during cancelling period — Amber banner on every tab: "Your plan cancels on [date] — Reactivate to keep going." Full functionality remains: edit posts, upload photos, configure agents, view data. Settings → Export Data section shows "Export My Data" (green button).
-
Optional: Export data — Downloads a ZIP:
church_info.json,posts.json,requests.csv,images/folder. If export >100MB, returns a signed URL (24h valid) via email instead of direct stream. -
Billing period ends → service cutoff and read-only dashboard — Stripe fires
customer.subscription.deleted. Cron setspremium_churches.status = 'cancelled'. Public site shows fallback: "[Church Name]'s website is not currently active. Please contact the church directly at [phone] or [email]." Dashboard is read-only. Service ended email sent. Custom domain: held 30 days — if reactivated within window, domain is restored; if not, released.
Acceptance spec
Canonical: knowledge/acceptance/cancelled.md
Key coverage: self-serve cancellation UX, full features through period-end, read-only dashboard with export, email sequence (confirmation + 7-day/30-day win-back), public site offline state, reactivation (no second trial, all data restored).
Success criteria
- Cancellation in under 2 minutes without contacting support.
- No "Are you sure?" dark patterns or guilt-trip modals.
- Site does not disappear mid-billing period.
- Public site shows polite fallback message (not a 404).
- Reactivation is one click away from email or dashboard.
Known failure modes
-
customer.subscription.deletedwebhook fails. Inbox pattern retries with backoff. Verify cron is processing the event. Source:memory/project_stripe_webhook_inbox.md. -
Read-only mode not enforced. Forms must be server-side gated:
PUT /api/premium/posts/[id]should return 403 ifpremium_churches.status = 'cancelled'. -
Export file too large. If ZIP >100MB, stream fails. Return signed URL email fallback instead of direct download.
-
Custom domain left pointing at ChurchWiseAI. Post-cancellation, CNAME still resolves to Vercel (404 or fallback). Include DNS cleanup instructions in the service-ended email.
-
Reactivation creates a duplicate
premium_churchesrow. Reactivation logic must check for existing cancelled row first and reuse it (setstatus='active') rather than inserting a new row.