Skip to main content

Pastor upgrades from Voice to Pro Bundle

Persona

A pastor whose church has grown to 150+ attendees. They're happy with the voice agent and now want a chat widget, FAQ management, and analytics. Pro Bundle ($119.95/mo) costs less than buying Voice + Chat separately. Decision cycle is short — they just need the upgrade to be seamless.

Entry points

  • Admin dashboard Billing section: Settings → Billing card → "Upgrade Plan" → /pricing?upgrade=true&current_plan=starter_voice.
  • Email campaign: "Ready to add chat? Pro Bundle saves $14.95/mo" — CTA links to /pricing?plan=pro_both&upgrade=true.
  • Stripe Customer Portal: Plan change from portal redirects through webhook.

Click-through flow

Steps

  1. Access Billing section — Settings tab → Billing shows "Starter Voice — $49.95/mo", next billing date, and "Upgrade Plan" link.

  2. Pricing page shows upgrade options — Current plan has "Current Plan" badge. Pro Both card shows "Upgrade — saves $14.95/mo" and the full feature list. Annual toggle is HIDDEN (bundles are monthly only).

  3. Proration confirmation — Modal shows amount due for remaining days in current cycle. Proration math is displayed for transparency (Stripe calculates; client displays without modification).

  4. Multi-item subscription guard — Handler checks: Starter Voice → Pro Both is a safe same-family upgrade. Invalid path example: trying to hold Starter Voice + Starter Chat as two separate subscriptions — returns 400 "Your plan cannot be merged with Chat right now." Valid ladders: Starter Voice → Pro Voice, Pro Voice → Pro Both, Starter Voice → Pro Both.

  5. Stripe checkout — Product "ChurchWiseAI Pro Bundle – $119.95/mo", prorated amount due, saved card pre-filled. On success, redirects to /admin/[token]?upgraded=true.

  6. Webhook processes subscription updatecustomer.subscription.updated → webhook inbox → cron. updateSubscriptionFromStripe() reads subscription items array, updates premium_churches.plan = 'cwa_pro_both' (raw Stripe key, never the collapsed tier from normalizePlanTier()). Sets chatbot_enabled = true.

  7. Dashboard updates and confirmation email — Chat, Analytics, and FAQ tabs appear immediately. Welcome email with Pro Bundle setup next-steps (chat widget embed, first FAQ, analytics tour).

Acceptance spec

Canonical: knowledge/acceptance/pro-both.md (62 touchpoints, COMPLETE).

FeatureStarter VoicePro VoicePro Both
Price$49.95/mo$99.95/mo$119.95/mo (saves $29.90/mo)
Chat agents4
FAQs50 limit
Analytics30-day view

Plan column contract: premium_churches.plan holds canonical Stripe tier key (cwa_pro_both). Never write normalizePlanTier() output back to this column. See knowledge/architecture/db/plan-column-contract.md.

Success criteria

  1. "Upgrade Plan" is easy to find in Settings → Billing.
  2. Proration math is transparent (exact amount due for remainder of cycle).
  3. Checkout completes in under 2 minutes.
  4. Dashboard immediately shows Chat, FAQ, and Analytics tabs.
  5. Welcome email arrives within 5 minutes.
  6. Existing voice data (phone number, greeting, calls, prayer requests) is NOT lost.
  7. Voice agent continues answering calls during and after the upgrade.

Known failure modes

  • Multi-item subscription created by accident. If the guard misses, pastor ends up with two Stripe price items. Symptom: both Voice and Chat tabs accessible, but tier gating breaks. Verify updateSubscriptionFromStripe() checks subscription.items.length === 1 after upgrade.

  • Plan column not updated. If webhook doesn't write plan = 'cwa_pro_both', dashboard gates as Starter. Pastor pays for Pro but sees Starter UI. Regression test: upgrade, then check SELECT plan FROM premium_churches WHERE id = '[church_id]'.

  • Chat not auto-enabled. Webhook must set chatbot_enabled = true when channel LIKE '%both%'.

  • Proration display wrong. Never compute proration in JavaScript — Stripe is the source of truth. Display Stripe's calculation unchanged.